引言:
调用系统相机拍照是安卓中比较常见,也比较简单的功能。那么拍照后得到的照片,我们有两种处理:第一种,我们只把它们显示出来(不保存在文件中,但是这种只能显示缩略图)。第二种,我们将照片写入到文件中,即保存起来,那么就可以重复使用。对于写入到文件这种操作,7.0以上版本需要特殊照顾,以避免出现FileUriExposedException异常。
第一种(获取缩略图):
首先,启动系统相机:

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }

然后,重写回调方法:

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==REQUEST_IMAGE_CAPTURE&& resultCode == RESULT_OK){
            Bundle extras=data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            save(imageBitmap);
        }
    }

现在,imageBitmap就是我们的缩略图,可以直接显示在UI界面。其实,缩略图也是可以保存的,我这里的save方法,就是将它写入到外部存储中。

/**
     * 处理系统相机拍照后对图片的保存,但是实际上保存的仅仅是一张缩略图,有些时候并不能满足需要
     * 而完整图片的获取在7.0以上版本并不容易,用原始方法的话会报异常,
     * @param bitmap
     */
    private void save(Bitmap bitmap){
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
        String filename = format.format(date) + ".jpg";
        File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File folder=new File(dir,"my_pictures");
        /**
         * File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
         * getExternalStorageDirectory()方法已经废弃,官方也不推荐使用,getExternalFilesDir是
         * 官方文档中使用的API
         */

        if(!folder.exists()){
            folder.mkdir();
        }
        File file=new File(folder,filename);
        try {
            FileOutputStream outputStream=new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
            outputStream.flush();
            outputStream.close();
            Toast.makeText(this, "Take finish", Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.w("Tag",file.getPath());  //打印存储路径
    }

一张缩略图,放大了就会模糊,可能不太够用。所以不建议这样做,那么下面开始第二种方法的讲解。
第二种(存入到文件中):

/**
     * 调用系统相机拍照并保存的原始方法,7.0以上需要适配
     * 这里涉及到的问题就是内容共享,要使用到Provider类
     */
    private void dispatchTakePictureIntent() {
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
        String filename = format.format(date) + ".jpg";
        File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        //以dir文件为父级文件夹创建子文件
        File folder=new File(dir,"my_pictures");
        /**
         * File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
         * getExternalStorageDirectory()方法已经废弃,官方也不推荐使用,getExternalFilesDir是
         * 官方文档中使用的API
         */

        if(!folder.exists()){
        	//创建目录
            folder.mkdir();
        }
        File file=new File(folder,filename);

        Uri uri=null;
        if(Build.VERSION.SDK_INT>=24){
            uri= FileProvider.getUriForFile(MainActivity.this,"com.example.camera.fileprovider",file);
        }else{
            uri=Uri.fromFile(file);
        }

        Log.w("Tag0",uri+"\n"+getExternalFilesDir(Environment.DIRECTORY_PICTURES));

        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);

        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }

我们发现对Intent的操作多了一行,takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
这句话大概是将拍摄完成的照片存储到对应uri的路径,原来旧版很容易实现,定义好存储路径,直接一行代码,通过文件获取路径就可以了。7.0以上对内容共享做了限制,需要使用到Provider类,然后调用FileProvider类的方法获取uri,下面给出适配的相关操作:
1.在res目录下新建xml文件夹,然后创建根节点为paths的路径文件:
file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="/Android/data/com.example.camera/files/Pictures/my_pictures"/>
</paths>

external-path标签就代表着外部存储,path属性相当于它之下的目录,我们可以不再往下写,直接写一个"/",其实最终文件存储的路径都一样,只是我们得到的Uri不同,那就来对比一下吧:

完整uri

Android 系统相机 安卓系统 相机_保存文件


不完整uri

Android 系统相机 安卓系统 相机_7.0_02


打印的第一行是我们最终调用getUriForFile得到的uri,第二行是getExternalFilesDir(Environment.DIRECTORY_PICTURES)返回的路径。

2.在清单文件中注册provider:

<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.camera.fileprovider"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                />
        </provider>

都是固定用法,注意authorities请使用"包名.fileprovider",貌似后面是什么影响不大,但是包名必须写对,在getUriForFile方法中要传入authorities对应String型参数。
android:grantUriPermissions=“true”,不可以省略,必须为true,不然会报错。
文献参考:
Android Developers官方文档:拍照这里面还介绍了如何将照片显示在相册(默认情况是不会直接显示在相册的),以及图片的解码。