【问题标题】:Android inconsistent picture saving with getExternalFilesDirAndroid与getExternalFilesDir保存图片不一致
【发布时间】:2016-05-04 22:24:34
【问题描述】:

在我的应用程序中,我正在尝试使用从Android Developers tutorial 获得的代码来拍照、保存并添加到 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");
        }
    }
}

有时此代码会按预期工作,并获取图像、保存图像、将其加载回程序并将其添加到图像视图中。但其他时候,它根本不起作用,似乎无法拍摄图像。

这是成功捕获图像的 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

这是一个失败的图像捕获的 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

这似乎是随机工作的,并且 80%​​ 的时间都失败了。我使用 getExternalFilesDir 因为我希望照片对应用程序是私有的。我还在我的 Manifest 文件中添加了 WRITE_EXTERNAL_STORAGE 权限。

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

我也知道我可以使用图像中的缩略图,但我想要完整大小的图像,因为我稍后会将它添加到课程中。

有人对出了什么问题有任何想法吗?

【问题讨论】:

    标签: java android image camera


    【解决方案1】:

    当您的应用处于后台时,您的进程正在终止。这对于 Android 来说是正常的,尽管您可能不会在这里期待它。您需要在保存的实例状态Bundle 中保留您的EXTRA_OUTPUT

    还要注意since file: Uri values are being banned,你应该考虑改用FileProvider

    This sample app 演示了这两件事。诚然,代码更复杂:

    /***
     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();
          }
        }
      }
    }
    

    关于您的问题,我将我的输出位置保存在onSaveInstanceState() 中的Bundle 中,如果我有正在恢复的Bundle,则在onCreate() 中再次使用它。

    【讨论】:

    • 感谢您的快速回复,但我还是有点困惑。我实现了将文件位置保存在onSaveInstanceState() 中的Bundle 中的概念,但似乎该函数从未被调用。我不知道是不是因为 OP 中的代码在一个片段而不是一个活动中,或者这是否会影响我的问题。
    • @NoahLutz:“但似乎该函数永远不会被调用”——它应该,只要你的应用程序进入后台(即它被停止但没有被销毁)。您甚至不需要为此启动相机。当您的活动+片段在前台时,只需按 HOME。 “我不知道是不是因为 OP 中的代码位于片段而不是活动中,或者这是否会影响我的问题”——片段有自己的 onSaveInstanceState(),应该被调用。
    • 好的,我得到onSaveInstanceState() 来执行并将位置保存在包中,由于某种原因,apk 第一次部署失败,但现在它似乎可以工作了!非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 2021-02-05
    • 1970-01-01
    相关资源
    最近更新 更多