使用Thread获取图像资源时,Intent选择器保持打开状态

Kyl*_*ner 5 android android-asynctask

我正在构建一个照片上传表单,该表单必须从相机或图库或任何其他可用资源(Dropbox/Google Drive/Astro文件管理器等)获取图像.我认为除了一个小组件外我都能正常工作.

当所选图像来自网络资源(例如Google云端硬盘)时,可能需要很长时间才能返回活动.我想使这个异步,以避免用户在图像下载时盯着黑屏.

我写了很多代码来完成整个工作,但这是它的一般过程:

  1. 活动开始并显示表单.

  2. Intent选择器自动打开并要求用户从相机或其他应用程序中选择图像.

  3. 用户选择某些应用程序(如Google云端硬盘),选择图像并关闭该应用,然后返回所选图像Uri作为结果.

  4. 缓存文件的内容以解决方法KitKat权限,如果活动关闭并需要恢复,则可以阻止重新读取图像.

  5. 使用缓存文件作为源异步显示缩放位图.

  6. Intent选择器关闭,图像显示在ImageButton中,并且表单对用户可见,在提交之前填写.

  7. 点击提交按钮后,将启动异步任务,允许将多部分上载到服务器.

  8. 异步上载使用回调函数来通知用户上载进度.

我陷入了#3和#4之间的过渡期.

如果我使用Thread或AsyncTask Intent选择器将保持打开状态,但图像也将显示在表单上.

如果我只是强迫用户等待(连续) 用户被卡住看黑屏,则意图选择器关闭,图像显示在表单上.

打开Intent选择器

/**
 * see http://stackoverflow.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);
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
    */
}
Run Code Online (Sandbox Code Playgroud)

编辑:

我发现这将/不会发生取决于API级别.

不会发生:

  • KitKat(Nexus 4)
  • ICS MR1(仿真器)

确实发生了:

  • KitKat(模拟器)
  • ICS MR1(Galaxy Tab 10.1)

编辑2:

以下是一些不应该发生的截图; 图像来自KitKat模拟器.

启动活动

意图选择器打开

从库或其他资源中选择图像

从图库中选择图像

onActivityResult返回并且选择器保持打开状态

意图选择器打开并显示图像

这是它应该如何照顾onActivityResult

关闭意图选择器并显示图像

Kyl*_*ner 2

感觉很愚蠢,但我找到了解决方案。

问题出在第二步:“意图选择器自动打开......”

为了做到这一点,我检查位图是否可用,如果不可用,则应显示占位符图像,然后打开意图选择器。我在方法内部有这个逻辑onStart,通常在onActivityResult. 但是,因为这一切都是异步发生的,所以我不能依赖这个顺序。

我要做的就是放入一个布尔标志,它可以告诉我我们是否在等待位图,如果是,则不打开选择器。