在開發Android項目的時候,我們會用到相機,有些時候只是開發一個普通的掃碼,僅僅賦予一下 權限 就好了,但是有些時候是需要拍照和從相冊中獲取照片的。
我們在Android 5.0以及5.0之前調用相機可以這樣寫
Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); File savePhoto = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"/test/"+System.currentTimeMillis() + ".png"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(savePhoto)); startActivityForResult(intent,200);
這樣寫在6.0之前是完全沒有問題的,拍照也可以按照指定的路徑進行存儲,一切的一切都是OK的,除非部分機型會有問題
到了6.0,在調用相機就得這樣寫
當我們開發者把sdk升級到了23后,這樣寫就會存在一點缺陷。那就是權限管理,需要動態進行獲取了。OK,然后我們改成動態獲取的,如下:
if(checkSelfPermission(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ requestPermissions(new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE},200); return; } Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); File savePhoto = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"/test/"+System.currentTimeMillis() + ".png"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(savePhoto)); startActivityForResult(intent,200);
這樣寫和5.0幾乎沒有區別,只是在前邊加了一個權限校驗。因為6.0到了動態權限時代
然而到了7.0,一切的一切都不一樣了,因為Android 7.0不允許intent帶有file://的URI離開自身的應用了,要不然會拋出FileUriExposedException
想要在自己應用和其他應用之間共享File數據,只能使用content://的方式
所以我們在想按照5.0和6.0的方式去調用相機是不可行的了,我們需要在7.0的時候。把所有應用與應用之間的文件傳遞改成content://的方式,並且還需要把該URI賦予臨時的訪問權限,使用如下:
1.現在清單文件里配置一個provider
<provider android:name="android.support.v4.content.FileProvider" android:authorities="應用包名" android:exported="false" android:grantUriPermissions="true"> </provider>
2.在xml目錄下創建file_paths文件
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="my_images" path="images/"/> </paths>
在file_paths文件夾內聲明的都是需要對外共享的目錄,比如上面的配置等於 new File(Context.getFilesDir(),"images") 路徑
paths內還可以聲明很多種類型的標簽,每一種標簽都代表了一個路徑,如下:
<files-path /> = getFilesDir() <cache-path /> = getCacheDir() <external-path /> = Environment.getExternalStorageDirectory() <external-files-path /> = Context#getExternalFilesDir(String) 或 Context.getExternalFilesDir(null) <external-cache-path /> = Context.getExternalCacheDir() <external-media-path /> = Context.getExternalMediaDirs()
我們在配置的時候。name的作用就是為了隱藏后邊的真實路徑,為了安全考慮
而后邊的path則是需要共享的路徑,用標簽所代表的路徑加上path上的值,就是完整的路徑。
寫好path文件后,我們在回到清單文件內繼續更新 android.support.v4.content.FileProvider 這個Provider的配置,需要把剛剛的file_paths文件和這個provider關聯起來,如下
<provider android:name="android.support.v4.content.FileProvider" android:authorities="應用包名" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
3.寫完這些配置信息后,我們就可以在應用內直接獲取需要共享文件的content://URI了,如下
File imagePath = new File(Context.getFilesDir(), "images"); File newFile = new File(imagePath, "test.jpg"); Uri contentUri = FileProvider.getUriForFile(getContext(), "應用包名", newFile);
getUriForFile的第二個參數內也可以不是應用包名,只要和清單文件內的authorities一致即可
這個contentUri的最后結果就是 content://應用包名/my_images/test.jpg
之所以會生成這個uri,相信很多同學看到這里就明白了,因為這個uri是要對其他應用共享的,所以不能直接共享真實路徑,便衍生了FileProvider這種東西來專門生成這個Uri,他的生成規則便是 content:// 應用包名(或者是其他字符串)/ 偽名 (path文件內配置的name屬性) / 文件名
4.然后賦予臨時訪問權限
我們可以調用 Context.grantUriPermission 方法來給Uri賦予權限,調用 revokeUriPermission() 函數來撤銷權限,也可以通過 Intent.setFlags() 的方式來賦予臨時訪問權限
最終在7.0上調用相機就成了這個樣子,前提是file_paths文件已經配置OK
if(checkSelfPermission(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ requestPermissions(new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE},200); return; } Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); File imagePath = new File(Context.getFilesDir(), "images"); File newFile = new File(imagePath, "test.jpg"); Uri contentUri = FileProvider.getUriForFile(getContext(), "應用包名", newFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(intent,200);
到這里就結束了,我也是剛剛踩完相機的坑,所以來一篇博客來記錄這個經歷,由於該內容全部都是用記事本手敲的,所以或多或少可能會有一些拼寫錯誤之類的,歡迎在評論區指出錯誤,謝謝!