相机在Nougat 7.0中无法工作

8

我的相机代码在所有Android版本中都可以正常工作,但在Nougat 7.0中会出现以下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference

除了Android 7.0版本之外,它在所有其他版本的Android上都可以正常工作。我已经授予相机和图库运行时权限,但相机仍然无法正常工作。以下是相关代码:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (resultCode == RESULT_OK) {
        if(requestCode == Constants.CROPPED_PIC_REQUEST_CODE){
            CropImage.ActivityResult result = (CropImage.ActivityResult) data.getExtras().get(CropImage.CROP_IMAGE_EXTRA_RESULT);
            Uri selectedImageUri = result == null ? null : result.getUri();
            Bitmap bitmap = null;
            Log.d("SetUpProfile","Uri cropped is "+outputFileUri);
            bitmap = getBitmap(selectedImageUri);
    //                    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImageUri);
            circleImageView.setImageBitmap(bitmap);
            finalBitmap = bitmap;
        }
        else if (requestCode == Constants.YOUR_SELECT_PICTURE_REQUEST_CODE) {
            final boolean isCamera;
            if (data == null) {
                isCamera = true;
            } else {
                final String action = data.getAction();
                if (action == null) {
                    isCamera = false;
                } else {
                    isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                }
            }

            Uri selectedImageUri;
            if (isCamera) {
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
                String value = prefs.getString("path", "error");
                selectedImageUri = Uri.parse(value);
            } else {
                selectedImageUri = data == null ? null : data.getData();
            }

            Intent i = new Intent(Five.this,CropImageActivity.class);
            i.putExtra("ImageURI", selectedImageUri.toString());
            startActivityForResult(i,Constants.CROPPED_PIC_REQUEST_CODE);

        }
    }
}

这是我的logcat:

FATAL EXCEPTION: main
Process: com.sancsvision.wayndr, PID: 31570
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=11, result=-1, data=Intent {  }} to activity {com.sancsvision.wayndr/com.sancsvision.wayndr.Five}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference                     
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4053)              
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4096)        
    at android.app.ActivityThread.-wrap20(ActivityThread.java)                      
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1516)         
    at android.os.Handler.dispatchMessage(Handler.java:102)                     
    at android.os.Looper.loop(Looper.java:154)                      
    at android.app.ActivityThread.main(ActivityThread.java:6077)                        
    at java.lang.reflect.Method.invoke(Native Method)                       
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)      
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)                                                     
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
    at com.sancsvision.wayndr.Five.onActivityResult(Five.java:259)
    at android.app.Activity.dispatchActivityResult(Activity.java:6917)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4049)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4096) 
    at android.app.ActivityThread.-wrap20(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1516) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6077) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

请提供一个 [mcve] 来展示你的问题。这应该包括完整的 Java 堆栈跟踪,以及堆栈跟踪中引用的所有 Java 代码。我猜测你正在使用 ACTION_IMAGE_CAPTURE 并尝试从返回的 Intent 中获取一个 Uri,但这不是 ACTION_IMAGE_CAPTURE 的工作方式。 - CommonsWare
@CommonsWare,请检查已编辑的问题。 - Anand Jain
1
请提供一个 [mcve] 来展示你的问题。这将包括 完整的 Java 堆栈跟踪,以及堆栈跟踪中引用的所有 Java 代码。 - CommonsWare
@CommonsWare,请检查修改后的问题。 - Anand Jain
可能 selectedImageUrinull,因为它是 Uri 上唯一的 toString()。您需要设置一些断点并通过调试器逐步执行,以查看您正在采取哪些路径以及为什么该 Uri 变成了 null。仅从检查代码来看,我们无法确定发生了什么。 - CommonsWare
显示剩余3条评论
4个回答

3

尝试这个,问题不在于意图,而是一旦你拍照并将其保存到SD卡上,然后在Nougat中获取URI时会有所不同...

在您的应用程序上实现FileProvider非常容易。首先,您需要在AndroidManifest.xml中添加一个FileProvider标签,如下所示:AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

然后在res文件夹下的xml文件夹中创建provider_paths.xml文件。如果不存在该文件夹,则需要创建。

res/xml/provider_paths.xml

 <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
    </paths>

完成!现在已声明FileProvider并准备好使用。

最后一步是更改MainActivity.java中以下代码行:

 Uri photoURI = Uri.fromFile(createImageFile());

to

Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
        BuildConfig.APPLICATION_ID + ".provider",
        createImageFile());

完成了!你的应用现在应该可以在任何版本的安卓系统上完美运行,包括Android Nougat。干杯!


1

如果@Themasterhimself提供了更多的内容,而不仅仅是一个链接,那么这应该是被接受的答案。我的意思是,如果您提供了完整的示例或与其相关的代码片段。 - MiguelHincapieC

0

在点击相机按钮或相机图像单击事件时执行此操作

Uri fileUri;
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

if (cameraIntent.resolveActivity(getPackageManager()) != null) {
     ContentValues values = new ContentValues(1);
     values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
     fileUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
     cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
     Log.d("FILEURI",fileUri+"");
     editor.putString("Fileurl", fileUri+"");
     editor.commit();
     cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
     startActivityForResult(cameraIntent, CAMERA_REQUEST);
}

在onActivityResult方法中执行此操作。
if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {

    try {
         String utlstr =preferences.getString("Fileurl","");
         Log.d("sad",utlstr);
         Uri uri = Uri.parse(utlstr);

         final File file = inputStreamToFile(getContentResolver().openInputStream(uri), "png");

     } catch (FileNotFoundException e) {
         e.printStackTrace();
     }        
 }

0

我已经使用Android API >21的相机完成了图像上传到服务器的操作。

import android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;


public class MainActivity extends AppCompatActivity implements         View.OnClickListener {

private static final int PICK_FILE_REQUEST = 1;
private static final String TAG = MainActivity.class.getSimpleName();
private ArrayList<String> selectedFilePath;
private String SERVER_URL =     "http://192.168.1.10:8080/imageUploadWebApi.php";
Button btAttachment;
Button btCamera;
Button bUpload;
TextView tvFileName;
ProgressDialog dialog;
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 123;
Uri outputFileUri;
String mCurrentPhotoPath;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
    } else {
        Init();
    }

    if ( Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission( this, android.Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{android.Manifest.permission.CAMERA},
                MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
            if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                Init();
            }
            break;
        default:
            break;
    }
}

private void Init() {
    btAttachment = (Button)findViewById(R.id.btAttachment);
    btCamera=(Button)findViewById(R.id.btCamera);
    bUpload = (Button) findViewById(R.id.b_upload);
    tvFileName = (TextView) findViewById(R.id.tv_file_name);
    btAttachment.setOnClickListener(this);
    btCamera.setOnClickListener(this);
    bUpload.setOnClickListener(this);
    selectedFilePath=new ArrayList<>();
}

@Override
public void onClick(View v) {

    switch (v.getId())
    {
        case R.id.btAttachment:
            if(selectedFilePath.size()<5) {
                //on attachment icon click
                showFileChooser();
            }
            else
                Toast.makeText(this,"You can upload only 5 Images at a time ",Toast.LENGTH_LONG).show();

            break;

        case R.id.btCamera:
            if(selectedFilePath.size()<5) {
                try {

                    Intent cameraIntent= new Intent(
                            android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        outputFileUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", createImageFile());
                        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
                    } else {
                        File imagesFolder = new File(Environment.getExternalStorageDirectory(), "Images");
                        imagesFolder.mkdirs()
                        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                        String imageFileName = "Img_" + timeStamp;
                        String fname =  imageFileName + ".png";
                        File file = new File(imagesFolder, fname);
                        outputFileUri = Uri.fromFile(file);
                        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
                    }

                    startActivityForResult(cameraIntent, 100);
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
            else
                Toast.makeText(this,"You can upload only 5 Images at a time ",Toast.LENGTH_LONG).show();
            break;

        case R.id.b_upload:
            selectedFile();
            break;

    }
        }

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "Img_" + timeStamp;
    File storageDir = new File(Environment.getExternalStorageDirectory(), "Images");
    File file=new File(storageDir,imageFileName+".png");
    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = file.getAbsolutePath();//"file:" + image.getAbsolutePath();
    return file;
}

private void selectedFile()
{
    if(selectedFilePath.size() != 0){
        dialog = ProgressDialog.show(MainActivity.this,"","Uploading File...",true);

        for (final String selectedFile:selectedFilePath) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    //creating new thread to handle Http Operations
                    uploadFile(selectedFile);
                }
            }).start();
        }
    }else{
        Toast.makeText(MainActivity.this,"Please choose a File First", Toast.LENGTH_SHORT).show();
    }
}

private void showFileChooser() {
    Intent intent = new Intent();
    //sets the select file to all types of files
    intent.setType("*/*");
    //allows to select data and return it
    intent.setAction(Intent.ACTION_GET_CONTENT);
    //starts new activity to select file and return data
    startActivityForResult(Intent.createChooser(intent,"Choose File to Upload.."),PICK_FILE_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(resultCode == Activity.RESULT_OK){
        if(requestCode == PICK_FILE_REQUEST){
            if(data == null){
                //no data present
                return;
            }

            Uri selectedFileUri =data.getData();
            selectedFilePath.add(FilePath.getPath(this,selectedFileUri));
            Log.i(TAG,"Selected File Path:" + selectedFilePath);

            if(selectedFilePath.size() != 0 ){
                String fileName="";
                for(int i=0;i<selectedFilePath.size();i++)
                {
                    String[] parts = selectedFilePath.get(i).split("/");
                    final String RelativefileName = parts[parts.length-1];
                    fileName=RelativefileName+"\n"+fileName;
                }
                tvFileName.setText(fileName);

            }else{
                Toast.makeText(this,"Cannot upload file to server",Toast.LENGTH_SHORT).show();
            }
        }
        else if(requestCode == 100)
        {
            try {

                if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
                    // Bitmap bitmap;
                    File file = null;
                    FileInputStream fis;
                    BitmapFactory.Options opts;
                    int resizeScale;
                    Bitmap bmp;
                    file = new File(outputFileUri.getPath());
                    // This bit determines only the width/height of the
                    // bitmap
                    // without loading the contents
                    opts = new BitmapFactory.Options();
                    opts.inJustDecodeBounds = true;
                    fis = new FileInputStream(file);
                    BitmapFactory.decodeStream(fis, null, opts);
                    fis.close();


                    selectedFilePath.add(FilePath.getPath(this,outputFileUri));
                }
                else
                {
                    selectedFilePath.add(mCurrentPhotoPath);
                }

                Log.i(TAG,"Selected File Path:" + selectedFilePath);

                if(selectedFilePath.size() != 0 ){
                    String fileName="";
                    for(int i=0;i<selectedFilePath.size();i++)
                    {
                        String[] parts = selectedFilePath.get(i).split("/");
                        final String RelativefileName = parts[parts.length-1];
                        fileName=RelativefileName+"\n"+fileName;
                    }
                    tvFileName.setText(fileName);
                }else{
                    Toast.makeText(this,"Cannot upload file to server",Toast.LENGTH_SHORT).show();
                }

            } catch (Exception e) {

                e.printStackTrace();
            }
        }
    }
}

public String getPath(Uri uri) {
    String[] projection = { MediaStore.Images.Media.DATA };
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    return cursor.getString(0);
}


//android upload file to server
public int uploadFile(final String selectedFilePath){

    int serverResponseCode = 0;

    HttpURLConnection connection;
    DataOutputStream dataOutputStream;
    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****";

    int bytesRead,bytesAvailable,bufferSize;
    byte[] buffer;
    int maxBufferSize = 1 * 1024 * 1024;
    File selectedFile = new File(selectedFilePath);


    String[] parts = selectedFilePath.split("/");
    final String fileName = parts[parts.length-1];

    if (!selectedFile.isFile()){
        dialog.dismiss();

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tvFileName.setText("Source File Doesn't Exist: " + selectedFilePath);
            }
        });
        return 0;
    }else{
        try{
            FileInputStream fileInputStream = new FileInputStream(selectedFile);
            URL url = new URL(SERVER_URL);
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);//Allow Inputs
            connection.setDoOutput(true);//Allow Outputs
            connection.setUseCaches(false);//Don't use a cached Copy
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("ENCTYPE", "multipart/form-data");
            connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
            connection.setRequestProperty("uploaded_file",selectedFilePath);

            //creating new dataoutputstream
            dataOutputStream = new DataOutputStream(connection.getOutputStream());

            //writing bytes to data outputstream
            dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
            dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\";filename=\""
                    + selectedFilePath + "\"" + lineEnd);

            dataOutputStream.writeBytes(lineEnd);

            //returns no. of bytes present in fileInputStream
            bytesAvailable = fileInputStream.available();
            //selecting the buffer size as minimum of available bytes or 1 MB
            bufferSize = Math.min(bytesAvailable,maxBufferSize);
            //setting the buffer as byte array of size of bufferSize
            buffer = new byte[bufferSize];

            //reads bytes from FileInputStream(from 0th index of buffer to buffersize)
            bytesRead = fileInputStream.read(buffer,0,bufferSize);

            //loop repeats till bytesRead = -1, i.e., no bytes are left to read
            while (bytesRead > 0){
                //write the bytes read from inputstream
                dataOutputStream.write(buffer,0,bufferSize);
                bytesAvailable = fileInputStream.available();
                bufferSize = Math.min(bytesAvailable,maxBufferSize);
                bytesRead = fileInputStream.read(buffer,0,bufferSize);
            }

            dataOutputStream.writeBytes(lineEnd);
            dataOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

            serverResponseCode = connection.getResponseCode();
            String serverResponseMessage = connection.getResponseMessage();

            Log.i(TAG, "Server Response is: " + serverResponseMessage + ": " + serverResponseCode);

            //response code of 200 indicates the server status OK
            if(serverResponseCode == 200){
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvFileName.setText("File Upload completed.");
                    }
                });
            }

            //closing the input and output streams
            fileInputStream.close();
            dataOutputStream.flush();
            dataOutputStream.close();
            this.selectedFilePath.clear();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this,"File Not Found",Toast.LENGTH_SHORT).show();
                }
            });
        } catch (MalformedURLException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this, "URL error!", Toast.LENGTH_SHORT).show();

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this, "Cannot Read/Write File!", Toast.LENGTH_SHORT).show();
        }
        dialog.dismiss();

        return serverResponseCode;
    }

}
     }

它可以在安卓7.0版本上运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接