使用线程获取图像资源时,意图选择器保持打开状态
Posted
技术标签:
【中文标题】使用线程获取图像资源时,意图选择器保持打开状态【英文标题】:Intent chooser stays open when using a Thread to get an image resource 【发布时间】:2014-08-04 05:11:49 【问题描述】:我正在构建一个照片上传表单,它必须从相机或画廊或任何其他可用资源(Dropbox/Google Drive/Astro 文件管理器等)中获取图像。我想除了一个小组件外,我已经完成了所有工作。
当所选图像来自网络资源(例如 Google 云端硬盘)时,可能需要很长时间才能返回活动。我想让这个异步,以避免在图像下载时让用户盯着黑屏。
我已经编写了很多代码来让这一切正常工作,但这里是它的一般过程:
Activity 启动并显示表单。
Intent 选择器会自动打开并要求用户从相机或其他应用中选择图像。
用户选择某个应用(例如 Google Drive),选择一张图片,然后该应用关闭,返回所选图片Uri
作为结果。
缓存文件内容以解决 KitKat 权限问题,如果 Activity 关闭并需要恢复,该权限可能会阻止重新读取图像。
以缓存文件为源,异步显示缩放位图。
Intent 选择器关闭,图像显示在 ImageButton 中,用户可以看到要在提交前填写的表单。
点击提交按钮后,将启动一个异步任务,允许分段上传到服务器。
异步上传使用回调函数通知用户上传进度。
我在 #3 和 #4 之间的转换中陷入困境。
如果我使用线程或异步任务 Intent 选择器将保持打开状态,但图像也会显示在表单上。
如果我只是强迫用户等待(串行) 用户一直盯着黑屏,然后 Intent Chooser 关闭,图像显示在表单上。
打开 Intent 选择器
/**
* see http://***.com/a/12347567/940217
*/
private void openImageIntent()
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final PackageManager packageManager = getPackageManager();
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile;
try
uploadableImage.makeCameraTempFile();
photoFile = uploadableImage.getCameraTempFile();
uploadableImage.setCameraFilepath(photoFile.getAbsolutePath());
catch (IOException ex)
// Error occurred while creating the File
photoFile = null;
uploadableImage.invalidate();
Log.e(TAG, "Error while creating the temporary file needed for image capture from camera.");
ex.printStackTrace();
// Continue only if the File was successfully created
if (photoFile != null && packageManager != null)
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for (ResolveInfo res : listCam)
// Create the File where the photo should go
if (res.activityInfo == null)
continue;
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
cameraIntents.add(intent);
else
Toast.makeText(this, "Could not make temporary file.", Toast.LENGTH_LONG).show();
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[cameraIntents.size()]));
startActivityForResult(chooserIntent, REQUEST_IMAGE_CAPTURE);
onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
if (resultCode == RESULT_OK && requestCode == REQUEST_IMAGE_CAPTURE)
if (tempUpFile != null)
tempUpFile.invalidate();
if (data == null)
Log.d(TAG, "Data was null -- source was from camera");
uploadableImage.preserveCameraFile();
setPic();
else
Uri selectedImageUri = data.getData();
if (selectedImageUri == null)
Log.e(TAG, "An error that should never occur has occurred -- selectedImageUri was null when trying to get the data from the Image Capture activity result.");
Toast.makeText(this, "Unexpected error when trying to get an image from the gallery or file manager.", Toast.LENGTH_LONG).show();
return;
uploadableImage.setSourceImageUri(selectedImageUri, imageButton);
setPic(); // only do this if doing this serially.
else if (resultCode == RESULT_CANCELED)
uploadableImage.invalidate();
if (tempUpFile != null)
uploadableImage = tempUpFile;
setPic();
UploadableImage#setSourceImageUri
public void setSourceImageUri(Uri selectedImageUri, final ImageView iView)
// Important!! - clean up after ourselves!
deleteFile(this.cameraFile);
this.cameraFile = null;
InputStream tempInStream = null;
OutputStream tempOutStream = null;
try
this.cacheFile = createFile();
tempInStream = context.getContentResolver().openInputStream(selectedImageUri);
tempOutStream = new FileOutputStream(this.cacheFile);
catch (IOException e)
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
if (tempInStream == null || tempOutStream == null)
Toast.makeText(context, "Error while trying to "+(tempInStream == null ? "read" :"store")+" the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "Error while trying to get the " + (tempInStream == null ? "input" : "output") + " stream");
return;
// Works, but makes the UI hang on a black screen while waiting for the resource.
try
copy(tempInStream, tempOutStream);
catch (IOException e)
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
/*
// Works, but leaves the IntentChooser open
final InputStream inStream = tempInStream;
final OutputStream outStream = tempOutStream;
new Thread(new Runnable()
public void run()
try
copy(inStream, outStream);
// FIXME: somehow notify/display the bitmap in the UI
// --maybe use Message and Handler? callback Interface?
catch (IOException e)
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
).start();
*/
编辑:
我发现这将/不会发生,具体取决于 API 级别。
不发生:
KitKat (Nexus 4) ICS MR1(仿真器)确实发生:
KitKat(模拟器) ICS MR1(Galaxy Tab 10.1)编辑 2:
这里是一些不应该发生的截图;图片来自 KitKat 模拟器。
开始活动
从图库或其他资源中选择一张图片
onActivityResult 返回并且选择器保持打开状态
这就是它应该如何处理 onActivityResult
【问题讨论】:
@PearsonArtPhoto 截图已添加。 【参考方案1】:感觉很愚蠢,但我找到了解决方案。
问题出在第二步:“Intent 选择器自动打开...”
为了做到这一点,我检查了位图是否可用,如果没有,那么它应该显示占位符图像,然后打开 Intent 选择器。我在onStart
方法中有这个逻辑,它通常在onActivityResult
之后运行。但是,因为这一切都是异步发生的,所以我不能依赖这个顺序。
我必须做的是放入一个布尔标志,它可以告诉我我们是否正在等待位图,然后如果我们等待则不要打开选择器。
【讨论】:
以上是关于使用线程获取图像资源时,意图选择器保持打开状态的主要内容,如果未能解决你的问题,请参考以下文章
如何仅从 Google Photos 应用程序中选择图像? Android 谷歌照片意图