1. 程式人生 > >Android 7.0使用相機功能

Android 7.0使用相機功能

最近在專案中有用到相機的功能,通常用法

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        |Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
   //file是圖片路勁
    Uri uri = Uri.fromFile(file);
    intent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
    //設定MediaStore.EXTRA_OUTPUT的輸出路徑為imageFileUri
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    activity.startActivityForResult(intent, requestCode);

這樣就可以調轉相機,前兩天還好好的呢,可是今天發現不行了,直接就崩潰了。。錯誤日誌如下,許可權異常了。。。

android.os.FileUriExposedException:         file:///storage/emulated/0/DCIM/IMG_1041503431.jpg

看了下原始碼,在targetSdk>=24的時候就得使用content://了

//建立一個圖片儲存的Uri 在7.0上必須使用contentProvider建立,否則會崩

ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());          uri=activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

這時候在7.0手機上執行發現沒事了,可是在低版本上執行,就又不行了,所以還得做下區分,最終的使用姿勢

  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        //建立一個圖片儲存的Uri 在7.0上必須使用contentProvider建立,否則會崩
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
        uri = activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    } else {
        uri = Uri.fromFile(file);
    }
    intent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
    //設定MediaStore.EXTRA_OUTPUT的輸出路徑為imageFileUri
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    activity.startActivityForResult(intent, requestCode);

就是判斷下版本>=24的時候就使用contentValues這種方式,當然還有一種就是在清單檔案中註冊contentProvider的方式,如下:

<provider         android:name="android.support.v4.content.FileProvider" //固定值            android:authorities="com.test.test.fileprovider"//路徑 前面為包名,後面為fileprovider固定值,使用包名便於區分
android:exported="false"//是否支援其它應用呼叫當前元件 ,要求為flase
android:grantUriPermissions="true">
     <meta-data
 android:name="android.support.FILE_PROVIDER_PATHS"//固定值
 android:resource="@xml/filepaths"/>//在res目錄下定義的filepaths.xml檔案,名字可以自定義

還需要在res資料夾下建立xml資料夾,在建立filePath.xml

<paths>
<!-- xml檔案是唯一設定分享的目錄 ,不能用程式碼設定
1.<files-path>getFilesDir()  /data/data//files目錄
2.<cache-path>getCacheDir()  /data/data//cache目錄
3.<external-path>Environment.getExternalStorageDirectory()
  SDCard/Android/data/你的應用的包名/files/ 目錄
4.<external-files-path> Context#getExternalFilesDir(String) Context.getExternalFilesDir(null).            
5.<external-cache-path>      Context.getExternalCacheDir().
 -->
<!--  path :代表設定的目錄下一級目錄 eg:<external-path path="images/" 整個目錄為Environment.getExternalStorageDirectory()+"/images/"
        name: 代表定義在Content中的欄位 eg:name = "myimages" ,並且請求的內容的檔名為default_image.jpg
            則 返回一個URI   content://com.example.myapp.fileprovider/myimages/default_image.jpg-->
<!--當path 為空時 5個全配置就可以解決-->
<!--下載apk-->
<external-path path="" name="sdcard_files" />
<!--相機相簿裁剪-->
<external-files-path   path="" name="camera_has_sdcard"/>
   <files-path path=""     name="camera_no_sdcard"/>
    </paths>

上面註釋各個標籤的意思已經寫清楚了。用哪個看你自己了。。

使用的姿勢如下,代替了contentValues那些

一定要注意這裡的com.test.test.fileprovider一定要與清單檔案中的一樣,否則會報錯滴。。
 Uri uriForFile = FileProvider.getUriForFile(getActivity(), "com.renwohua.conch.fileprovider", mCameraFile);    intentFromCapture.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile);          intentFromCapture.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);      intentFromCapture.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

還有一個就是安裝apk,這是都會用的吧。也是前兩天用

 Intent installIntent = new Intent(Intent.ACTION_VIEW);
 installIntent.addCategory(Intent.CATEGORY_DEFAULT);
 installIntent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
 activity.startActivityForResult(installIntent,0x1001);

這樣使用沒有問題,問了監聽是否安裝使用了forResult,可是今天在7.0上就完蛋了。。。。一看還是一樣的錯誤,又不行了,需要使用contentProvider來獲取路徑,真坑啊,忽然就改了,,,幸好還沒上線。。。解決辦法如下:

  Intent installIntent = new Intent(Intent.ACTION_VIEW);          installIntent.addCategory(Intent.CATEGORY_DEFAULT);        installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {        installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  String providerString = getFileProviderString(activity);
 Uri uri = FileProvider.getUriForFile(activity.getApplicationContext(), providerString, apkFile);
        installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
    } else {
        installIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
    }
    activity.getApplication().startActivity(installIntent);

在這裡不能使用forResult了,因為使用了Intent.FLAG_ACTIVITY_NEW_TASK標記,如果想監聽是否取消另想辦法嘍。。