Android 拍照和图库功能(适配Android 6.0和7.0系统和华为机型问题)

众所周知,调用相机拍照和图库中获取图片的功能,基本上是每个程序App必备的。

实现适配Android每个版本,国内手机,要处理的问题却也不少。例如:Android6.0权限问题,Android7.0 FileProvider问题,华为手机图库获取不到图片的问题。

本篇内容概述

  • 调用系统相机拍照

  • 图库选取图片

  • 处理华为图库获取不到图片问题

  • 处理部分手机拍照后,图片旋转角度问题

  • RxJava加载图片,向上取整计算合适比例。

  • EasyPermission库处理去读写权限( 适配Android6.0系统及其以上)

  • FileProvider访问文件(适配Android7.0系统及其以上)

  • 跳转其他程序,Activity被系统因内存不足回收,处理数据保存问题。

项目前期配置

依赖库添加

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    //谷歌官方权限库
    compile 'pub.devrel:easypermissions:1.0.1'
    //异步消息通知库
    compile 'io.reactivex:rxjava:1.3.3'
    compile 'io.reactivex:rxandroid:1.2.1'
}

编码方式:Java+retrolambda库实现Java8特性

Android拍照功能


1. 赋予读写权限

从Android6.0开始,需要动态赋予权限,而不是安装时候赋予权限。拍照功能需要用到写入磁盘的权限。

在AndroidManifest.xml中注册读写权限:

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

第一步:检查权限和申请读写权限。 这里,使用EasyPermission库处理权限问题。

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{

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

        checkWritePermission();
    }
    /**
     * 检查读写权限权限
     */
    private void checkWritePermission() {
        boolean result = PermissionManager.checkPermission(this, Constance.PERMS_WRITE);
        if (!result) {
            PermissionManager.requestPermission(this, Constance.WRITE_PERMISSION_TIP 
                , Constance.WRITE_PERMISSION_CODE, Constance.PERMS_WRITE);
        }
    }
    /**
     * 重写onRequestPermissionsResult,用于接受请求结果
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //将请求结果传递EasyPermission库处理
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }
    /**
     * 请求权限成功
     *
     * @param requestCode
     * @param perms
     */
    @Override
    public void onPermissionsGranted(int requestCode, List<String> perms) {
        ToastUtils.showToast(getApplicationContext(), "用户授权成功");
    }
    /**
     * 请求权限失败
     *
     * @param requestCode
     * @param perms
     */
    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        ToastUtils.showToast(getApplicationContext(), "用户授权失败");
        /**
         * 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
         * 这时候,需要跳转到设置界面去,让用户手动开启。
         */
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            new AppSettingsDialog.Builder(this).build().show();
        }
    }
}

权限管理类PermissionManager,代码如下:

public class PermissionManager {
    /**
     * @param context
     * return true:已经获取权限
     * return false: 未获取权限,主动请求权限
     */
   // @AfterPermissionGranted(Constance.WRITE_PERMISSION_CODE) 是可选的
    public static boolean checkPermission(Activity context, String[] perms) {
        return EasyPermissions.hasPermissions(context, perms);
    }
    /**
     * 请求权限
     * @param context
     */
    public static void requestPermission(Activity context,String tip,int requestCode,String[] perms) {
        EasyPermissions.requestPermissions(context, tip,requestCode,perms);
    }
}

更多详情,请阅读Android EasyPermissions官方库,高效处理权限

2. Intent调用相机进行拍照

开启相机拍照是通过Intent来实现,在Intent中指定输出图片路径,相机拍照成功后,系统会将图片数据自动输出到指定路径,生成对应的图片。关闭相机后,会在对应的Activity中的onActivityResult()中返回结果,是否拍照成功的标示。

private String picturePath;

/**
  *Activity中通过Intent调用相机,指定输出图片路径。
  */
@Override
public void camera() {
        this.picturePath = FileUtils.getBitmapDiskFile(this.getApplicationContext());
        CameraUtils.openCamera(this, Constance.PICTURE_CODE, this.picturePath);
}


public class CameraUtils {

    /**
     * 打开相机
     * @param context
     * @param requestCode
     * @return
     */
    public static void openCamera(Activity context, int requestCode, String picturePath){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(context.getPackageManager()) != null) {
            /**
             * 指定拍照存储路径
             * 7.0 及其以上使用FileProvider替换'file://'访问
             */
            if (Build.VERSION.SDK_INT>=24){
                //这里的BuildConfig,需要是程序包下BuildConfig。
                intent.putExtra(MediaStore.EXTRA_OUTPUT,  
                        FileProvider.getUriForFile(context.getApplicationContext(), 
                        BuildConfig.APPLICATION_ID+".provider",new File(picturePath)));

                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            }else{
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(picturePath)));
            }
            context.startActivityForResult(intent, requestCode);
        }
    }
}

这里你会发觉多了,一个匹配android7.0的FileProvider,用于处理file://访问的问题。接下来,会讲解到它。

工具类FileUtils生成图片的路径,代码如下:

public class FileUtils {
    /**
     * 获得存储bitmap的文件
     * getExternalFilesDir()提供的是私有的目录,在app卸载后会被删除
     *
     * @param context
     * @param
     * @return
     */
    public static String getBitmapDiskFile(Context context) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
              || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalFilesDir(DIRECTORY_PICTURES).getAbsolutePath();
        } else {
            cachePath =context.getFilesDir().getAbsolutePath();
        }
        return new File(cachePath +File.separator+ getBitmapFileName()).getAbsolutePath();
    }

    public static final String bitmapFormat = ".png";

    /**
     * 生成bitmap的文件名:日期,md5加密
     *
     * @return
     */
    public static String getBitmapFileName() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            String currentDate = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            mDigest.update(currentDate.getBytes("utf-8"));
            byte[] b = mDigest.digest();
            for (int i = 0; i < b.length; ++i) {
                String hex = Integer.toHexString(0xFF & b[i]);
                if (hex.length() == 1) {
                    stringBuilder.append('0');
                }
                stringBuilder.append(hex);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileName = stringBuilder.toString() + bitmapFormat;
        return fileName;
    }
}

3. 处理anroid7.0中禁止file的Uri问题

anroid7.0 行为变更:

android 7.0发生了一些行为变化,禁止应用程序向外部公开file://的URI。

尝试传递file://URI会触发FileUriExposedException。

应用程序之间共享数据,应该发送content://的URI,且授予URI临时访问权限。推举使用FileProvider。更多详情,阅读android 7.0行为变更。

配置FileProvider:

在src\main\res路径下创建xml文件夹,然后在创建一个provider_paths.xml文件,编写以下代码

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

    <files-path name="Pictures" path="/"></files-path>
    <external-path path="Android/data/${applicationId}/" name="files_root" />
    <root-path
        name="root"
        path="/" />
</paths>

接下来,在AndroidManifest.xml中注册FileProvider:为FileProvidre配置,指定authorities,name ,不许对外共享,临时授权,访问目录配置

     <!-- FileProvider配置访问路径,适配7.0及其以上 -->
        <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>

配置完成后,便可以直接使用定义authorities所对应的FileProvider。

更多详情,请阅读Android 7.0 报android.os.FileUriExposedException异常

若是配置过程中遇到问题,请阅读Android FileProvider配置报错android.content.pm.ProviderInfo.loadXmlMetaData问题

4. 处理系统内存不足时候,导致界面回收,数据丢失的问题

当跳转到其它运行程序时候,系统可能因内存不足,回收了当前的Activity。而Activity当前数据没有保存,即使系统重新创建该Activity后,也会出现空白页面。

当系统因内存不足,回收activity前,会执行onSaveInstanceState(Bundle outState),因此,将拍照后的图片路径存储起来。

private String picturePath;

   /**
     * 防止系统内存不足销毁Activity
     * ,这里保存数据,便于恢复。
     * @param outState
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(TAG, picturePath);
    }

当系统重新创建该Activity后,从onCreate()中参数中获取,图片路径:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recoverState(savedInstanceState);
    }
    /**
     * 恢复被系统销毁的数据
     * @param savedInstanceState
     */
    private void recoverState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            this.picturePath = savedInstanceState.getString(TAG);
        }
    }

这里,举一个例子:

一个界面需要拍照很多张图片,然后显示出。因需要多次打开相机程序,再返回来加载生成的图片。这种需要,铁定容易碰到以上问题。

Activity被系统回收,具备偶然性,但存在问题,终究还是要处理。

这里,延伸一点:

android保存数据,要么放在内存中,要么放在磁盘中。磁盘读写是IO操作,又得筛选数据,面对这种需求,不推举使用。

5. RxJava异步加载拍照图片,向上取整加载

当拍照完成或者取消,都会在Activity的onActivityResult()中返回结果,是否拍照成功的标示。

在磁盘中生成的图片是一个文件,加载文件是IO操作,耗时,考虑RxJava异步加载。

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            //拍照返回
            case Constance.PICTURE_CODE:
                if (resultCode == Activity.RESULT_OK) {
                    loadPictureBitmap();
                }
                break;
            default:

                break;
        }
    }

   private void loadPictureBitmap() {
       Observable<Bitmap> bitmapObservable= ObservableUtils.loadPictureBitmap(getApplicationContext(), picturePath, show_iv);
        executeObservableTask(bitmapObservable);
    }
    private void executeObservableTask(Observable<Bitmap> observable) {
        Subscription subscription = observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(bitmap ->
                                show_iv.setImageBitmap(bitmap)
                        , error ->
                                ToastUtils.showToast(getApplicationContext(), "加载图片出错")
                );
        this.compositeSubscription.add(subscription);
    }

一个工具类ObservableUtils,构建Observable对象:

public class ObservableUtils {
    /**
     * 加载拍照的相片
     *
     * @param context
     * @param picturePath
     * @param imageView
     * @return
     */
    public static Observable<Bitmap> loadPictureBitmap(Context context, String picturePath, ImageView imageView) {
        return Observable.create(subscriber -> {
            Bitmap bitmap = BitmapUtils.decodeFileBitmap(context, picturePath 
                           , imageView.getWidth(), imageView.getHeight());
            subscriber.onNext(bitmap);
        });
    }

}

在Activity中显示的ImageView是具备大小的,按尺寸加载对应比率的Bitamp,可以节省内存。这里采用向上取整方式,计算合适的比率。

public class BitmapUtils {

    /**
     * @param context
     * @param path
     * @param targetWith
     * @param targetHeight
     * @return
     */
    public synchronized static Bitmap decodeFileBitmap(Context context, String path, int targetWith, int targetHeight) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            decodeStreamToBitmap(context, path, options);
            options.inSampleSize = calculateScaleSize(options, targetWith, targetHeight);
            options.inJustDecodeBounds = false;
            Bitmap bitmap = decodeStreamToBitmap(context, path, options);
            return getNormalBitmap(bitmap, path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Bitmap decodeStreamToBitmap(Context context, String path, BitmapFactory.Options options) {
        Bitmap bitmap = null;
        ContentResolver contentResolver = context.getContentResolver();
        try {
            //MIME type需要添加前缀
            InputStream inputStream = contentResolver.openInputStream( 
                      Uri.parse(path.contains("file:") ? path : "file://" + path));
            bitmap = BitmapFactory.decodeStream(inputStream, null, options);
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    /**
     * 采用向上取整的方式,计算压缩尺寸
     *
     * @param options
     * @param targetWith
     * @param targetHeight
     * @return
     */
    private static int calculateScaleSize(BitmapFactory.Options options, int targetWith, int targetHeight) {
        int simpleSize;
        if (targetWith > 0 && targetHeight > 0) {
            int scaleWith = (int) Math.ceil((options.outWidth * 1.0f) / targetWith);
            int scaleHeight = (int) Math.ceil((options.outHeight * 1.0f) / targetHeight);
            simpleSize = Math.max(scaleWith, scaleHeight);
        } else {
            simpleSize = 1;
        }
        if (simpleSize == 0) {
            simpleSize = 1;
        }
        return simpleSize;
    }

}

细心的人会发觉getNormalBitmap(bitmap, path),这个用于处理图片旋转的问题。

6. 处理部分手机拍照后,图片旋转角度问题

当图片角度旋转后,若是直接加载出来,对用户体验是非常差劲的。可通过ExifInterface对象,进行角度判断,加以处理。

 /**
     * 根据存储的bitmap中旋转角度,来创建正常的bitmap
     *
     * @param bitmap
     * @param path
     * @return
     */
    private static Bitmap getNormalBitmap(Bitmap bitmap, String path) {
        int rotate = getBitmapRotate(path);
        Bitmap normalBitmap;
        switch (rotate) {
            case 90:
            case 180:
            case 270:
                try {
                    Matrix matrix = new Matrix();
                    matrix.postRotate(rotate);
                    normalBitmap = Bitmap.createBitmap(bitmap, 0, 0, 
                                 bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    normalBitmap = bitmap;
                }
                break;
            default:
                normalBitmap = bitmap;
                break;
        }
        return normalBitmap;
    }

    /**
     * ExifInterface :这个类为jpeg文件记录一些image 的标记
     * 这里,获取图片的旋转角度
     *
     * @param path
     * @return
     */
    private static int getBitmapRotate(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 
                              ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return degree;
    }

实现一个完美的拍照功能,填了6个坑,真心不容易,相信不少的开发者都遇到过这些问题。接下来,检验成果的时候到了。

运行效果

这里写图片描述

Android图库功能


实现图库选择相片的代码很简单,通过Intent开启图库,然后选择需要的图片,会在activity中onActivityResult()中返回Uri。接下来,根据Uri查询到对应的图片路径,最后根据路径加载Bitmap,显示到UI上。

1. 读取权限处理

图库也是需要读取权限的,但上面的拍照功能具备了写入权限,写入权限包含读取权限,因此,这里不需要再做处理。更多详情,可以阅读 Android 6.0 访问图库时,报错 requires android.permission.READ_EXTERNAL_STORAGE异常.

2. 通过Intent开启相册

    /**
     * 打开图库
     * @param context
     * @param requestCode
     */
    public static void openGallery(Activity context, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
        context.startActivityForResult(intent, requestCode);
    }

3. 处理图库程序返回的Uri:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {

            //图库返回
            case Constance.GALLERY_CODE:
                if (resultCode == Activity.RESULT_OK) {
                    Uri uri = data.getData();
                    loadGalleryBitmap(uri);
                }
                break;
            default:

                break;
        }
    }

很多小伙伴们都发觉,在华为某些型号的手机上,通过图库返回的Uri,查询不出来对应的图片路径。这就相当悲催了的事情。

4. 处理华为手机图库查询不到图片路径

除开权限问题外,还有处理Uri的authority问题。

采用RxJava执行异步操作,处理Uri查询图片路径,根据路径加载合适的bitmap。

    private void loadPictureBitmap() {
       Observable<Bitmap> bitmapObservable= ObservableUtils.loadPictureBitmap(  
                            getApplicationContext() , picturePath, show_iv);
        executeObservableTask(bitmapObservable);
    }
    private void executeObservableTask(Observable<Bitmap> observable) {
        Subscription subscription = observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(bitmap ->
                                show_iv.setImageBitmap(bitmap)
                        , error ->
                                ToastUtils.showToast(getApplicationContext(), "加载图片出错")
                );
        this.compositeSubscription.add(subscription);
    }

查询到图片路径后,直接生成对应的bitmap:

public class ObservableUtils {
     /**
     * 加载拍照的相片
     *
     * @param context
     * @param picturePath
     * @param imageView
     * @return
     */
    public static Observable<Bitmap> loadPictureBitmap(Context context, String picturePath, ImageView imageView) {
        return Observable.create(subscriber -> {
            Bitmap bitmap = BitmapUtils.decodeFileBitmap(context, picturePath 
                             , imageView.getWidth(), imageView.getHeight());
            subscriber.onNext(bitmap);
        });
    }
    /**
     * 加载图库中选取的相片
     * @param context
     * @param uri
     * @param imageView
     * @return
     */
    public static Observable<Bitmap> loadGalleryBitmap(Context context, Uri uri, ImageView imageView) {
        return Observable.create(subscriber -> {
            String picturePath = CameraUtils.uriConvertPath(context, uri);
            subscriber.onNext(picturePath);
        }).flatMap(path -> loadPictureBitmap(context, (String) path, imageView));
    }
}

解决方法来源于网络:

public class CameraUtils {
    /**
     * 从相册中返回的Uri查询到对应图片的Path
     * @param context
     * @param uri
     * @return
     */
    public static String uriConvertPath(Context context,Uri uri){
        String path = null;
        String scheme = uri.getScheme();
        if (scheme.equals("content")) {
            path =getPath(context, uri);
        } else {
            path = uri.getEncodedPath();
        }
        return path;
    }
    /**
     * <br>功能简述:4.4及以上获取图片的方法
     * <br>功能详细描述:
     * <br>注意:
     * @param context
     * @param uri
     * @return
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] { split[1] };
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            if (isGooglePhotosUri(uri)){
                return uri.getLastPathSegment();}
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }
    private static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null){
                cursor.close();}
        }
        return null;
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
   private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
   private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
   private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
   private static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
}

踩完坑,直接看效果如何。

5. 效果如下

这里写图片描述

Android的拍照和图库选择图片功能介绍完了,期间遇到的坑,心里都有数。本项目的代码也会分享出来,下面有连接。

项目案例:https://github.com/13767004362/EasyPermissionDemo


相关资源

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/78516755