1. 程式人生 > >Android7.0實現頭像更換功能

Android7.0實現頭像更換功能

引言

在實現頭像更換功能時,期間發現android7.0和7.0以下的版本有一些區別,這裡記錄下來希望可以幫助大家。

問題

按照以下的程式碼執行時呼叫相機會出現閃退

private void camera(){
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (cameraIntent.resolveActivity(getPackageManager()) != null) {
            mTmpFile = new File(FileUtils.createRootPath(getBaseContext()) + "/"
+ System.currentTimeMillis() + ".jpg"); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile)); startActivityForResult(cameraIntent, REQUEST_CAMERA); } }

Uri.fromFile(mTmpFile);導致閃退錯誤提示

android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.example.zhaoyigang.photodemo3/cache/1496225693867.jpg exposed beyond app through ClipData.Item.getUri()
查詢資料發現出於安全原因,Android7.0在這個方面最大變化就在於以前適用的File Uri需要更改為Content Uri。

android7.0行為變更
在應用間共享檔案
對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file:// URI。如果一項包含檔案 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常。

要在應用間共享檔案,您應傳送一項 content:// URI,並授予 URI 臨時訪問許可權。進行此授權的最簡單方式是使用 FileProvider 類。如需瞭解有關許可權和共享檔案的詳細資訊,請參閱共享檔案。

解決

  • 首先在manifest中新增provider
<manifest 
    ...

    <application
        ...

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="你的包名.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>

</manifest>
  • 其次在資原始檔下建立相應的xml檔案(如上:則建立provider_paths.xml)
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • 最後就是在activity中使用
private void camera(){
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (cameraIntent.resolveActivity(getPackageManager()) != null) {
            mTmpFile = new File(FileUtils.createRootPath(getBaseContext()) + "/" + System.currentTimeMillis() + ".jpg");
            FileUtils.createFile(mTmpFile);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                        FileProvider.getUriForFile(getBaseContext(), BuildConfig.APPLICATION_ID + ".provider", mTmpFile));
            }else {
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile));
            }
            cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile));
            startActivityForResult(cameraIntent, REQUEST_CAMERA);
        }
    }

關於截圖

我的截圖功能是呼叫的系統截圖功能,這裡有一個小問題

private void crop(String imagePath){
        //mCropImageFile = FileUtils.createTmpFile(getBaseContext());
        mCropImageFile = getmCropImageFile();
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(getImageContentUri(new File(imagePath)), "image/*");
        intent.putExtra("crop", true);
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 500);
        intent.putExtra("outputY", 500);
        intent.putExtra("scale", true);
        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mCropImageFile));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);
        startActivityForResult(intent, REQUEST_CROP);
    }

intent.putExtra(“return-data”, false);中return-data如果是true,那麼intent會返回一個bitmap,而這裡截圖過程中雖然顯示的是原圖,但擷取後顯示的圖片卻非常模糊。原因是圖片沒有儲存擷取的只是縮圖。為了獲取到裁切後的原圖,我們選擇將剪下的圖片儲存在本地,然後呼叫本地的圖片,並不直接返回Bitmap.

具體的程式碼

作者:趙毅剛:原文地址