简体   繁体   English

Android使用getExternalFilesDir保存图片不一致

[英]Android inconsistent picture saving with getExternalFilesDir

In my app, I am trying to take a picture, save it, and add it to an ImageView with this code I got from the Android Developers tutorial 在我的应用中,我试图用我从Android开发者教程中获得的代码拍摄一张照片,将其保存并添加到ImageView中。

private static final int REQUEST_IMAGE_CAPTURE = 1;
private String lastImagePath;

//Create temporary image file using getExternalFilesDir()
private File createImageFile() throws IOException{
    String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timestamp + "_";
    File storageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(imageFileName, ".jpg", storageDir);
    lastImagePath = image.getAbsolutePath();
    return image;
}

//Start picture intent and save to file
private void takePicture(){
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File photoFile = null;
    if(takePictureIntent.resolveActivity(getActivity().getPackageManager())!= null){
        try{
            photoFile = createImageFile();
        }catch (IOException e){
            Log.e(TAG, "Error Creating image file");
            e.printStackTrace();
        }
        if (photoFile !=null){
            Log.e(TAG, photoFile.getAbsolutePath());
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK){

        //Ensure the path has been set
        if(lastImagePath !=null){
            Log.e(TAG, "LastImagePath: " + lastImagePath);

            //DEBUG:see if it actually created the file
            File file = new File(lastImagePath);
            if(file.exists()){
                Log.e(TAG, "File exists");
            }

            //Decode file
            Bitmap image = BitmapFactory.decodeFile(lastImagePath);

            //DEBUG: see if image is null
            if(image == null){
                Log.e(TAG, "Image is null");
            }else{
                Log.e(TAG, "Image is not null");
            }

            //convert full sized image to thumbnail and add it to imageView
            Bitmap thumbnail = ThumbnailUtils.extractThumbnail(image, 200, 200);
            image1.setImageBitmap(thumbnail);
        }else{
            Log.e(TAG, "lastImagePath is null");
        }
    }
}

Sometimes this code works as expected and takes an image, saves it, loads it back into the program and adds it to the image view. 有时,这些代码会按预期工作并拍摄图像,然后将其保存,加载回程序中并将其添加到图像视图中。 But other times, it doesn't work at all and seems to fail to take the image. 但是在其他时候,它根本不起作用,而且似乎无法拍摄图像。

Here is the logcat output for a successful image capture where the image appears in the ImageView 这是成功捕获图像的logcat输出,其中图像出现在ImageView中

05-04 17:27:49.218 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: /storage/emulated/0/Android/data/com.noah.stickynotes_clientside/files/Pictures/JPEG_20160504_172748_-1847663626.jpg
05-04 17:28:03.886 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: LastImagePath: /storage/emulated/0/Android/data/com.noah.stickynotes_clientside/files/Pictures/JPEG_20160504_172748_-1847663626.jpg
05-04 17:28:04.359 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: File exists
05-04 17:28:04.825 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: Image is not null

And here is the logcat output for a failed image capture where there is no image in the ImageView 这是失败的图像捕获的logcat输出,其中ImageView中没有图像

05-04 17:29:00.070 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: /storage/emulated/0/Android/data/com.noah.stickynotes_clientside/files/Pictures/JPEG_20160504_172900_-1359930406.jpg
05-04 17:29:06.581 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: LastImagePath: /storage/emulated/0/Android/data/com.noah.stickynotes_clientside/files/Pictures/JPEG_20160504_172900_-1359930406.jpg
05-04 17:29:06.583 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: File exists
05-04 17:29:06.583 4964-4964/com.noah.stickynotes_clientside E/SUBMIT_FRAGMENT: Image is null

This seems to work randomly and fails 80% of the time. 这似乎是随机工作的,并且80%的时间失败了。 I'm using getExternalFilesDir because I want the photos to be private to the application. 我使用getExternalFilesDir是因为我希望照片对应用程序是私有的。 I also added the WRITE_EXTERNAL_STORAGE permission to my Manifest file. 我还向清单文件中添加了WRITE_EXTERNAL_STORAGE权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

I also understand that I could use the thumbnail from the image but I want to have the full sized image because I will be adding it to a class later on. 我也理解我可以使用图像中的缩略图,但是我想拥有完整尺寸的图像,因为稍后我将其添加到课程中。

Anyone have any ideas on whats going wrong? 有人对出什么问题有任何想法吗?

Your process is being terminated while your app is in the background. 当您的应用程序在后台运行时,您的过程将终止。 This is normal for Android, though you may not be expecting it here. 这对于Android来说是正常的,尽管您可能不会在这里看到它。 You need to hold onto your EXTRA_OUTPUT in the saved instance state Bundle . 您需要在已保存实例状态Bundle保留EXTRA_OUTPUT

Also note that since file: Uri values are being banned , you should consider switching to using FileProvider . 另请注意, 由于禁止了file: Uri ,因此您应考虑切换到使用FileProvider

This sample app demonstrates both of those things. 该示例应用程序演示了这两个方面。 Admittedly, the code is more complicated: 诚然,代码更复杂:

/***
 Copyright (c) 2008-2016 CommonsWare, LLC
 Licensed under the Apache License, Version 2.0 (the "License"); you may not
 use this file except in compliance with the License. You may obtain a copy
 of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
 by applicable law or agreed to in writing, software distributed under the
 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
 OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License.

 From _The Busy Coder's Guide to Android Development_
 https://commonsware.com/Android
 */

package com.commonsware.android.camcon;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.util.List;

public class CameraContentDemoActivity extends Activity {
  private static final String EXTRA_FILENAME=
    "com.commonsware.android.camcon.EXTRA_FILENAME";
  private static final String FILENAME="CameraContentDemo.jpeg";
  private static final int CONTENT_REQUEST=1337;
  private static final String AUTHORITY=
    BuildConfig.APPLICATION_ID+".provider";
  private static final String PHOTOS="photos";
  private File output=null;
  private Uri outputUri=null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent i=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    if (savedInstanceState==null) {
      output=new File(new File(getFilesDir(), PHOTOS), FILENAME);

      if (output.exists()) {
        output.delete();
      }
      else {
        output.getParentFile().mkdirs();
      }
    }
    else {
      output=(File)savedInstanceState.getSerializable(EXTRA_FILENAME);
    }

    outputUri=FileProvider.getUriForFile(this, AUTHORITY, output);

    if (savedInstanceState==null) {
      i.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);

      if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
        i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      }
      else {
        List<ResolveInfo> resInfoList=
          getPackageManager()
            .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);

        for (ResolveInfo resolveInfo : resInfoList) {
          String packageName = resolveInfo.activityInfo.packageName;
          grantUriPermission(packageName, outputUri,
            Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
              Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
      }

      startActivityForResult(i, CONTENT_REQUEST);
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putSerializable(EXTRA_FILENAME, output);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode,
                                  Intent data) {
    if (requestCode == CONTENT_REQUEST) {
      if (resultCode == RESULT_OK) {
        Intent i=new Intent(Intent.ACTION_VIEW);

        i.setDataAndType(outputUri, "image/jpeg");
        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(i);
        finish();
      }
    }
  }
}

With respect to your problem, I am saving my output location in the Bundle in onSaveInstanceState() and using it again in onCreate() if I have a Bundle that is being restored. 关于您的问题,我将输出位置保存在Bundle中的onSaveInstanceState() ,如果要还原的Bundle再次在onCreate()使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM