一、概念
在Android 7.0以前的系统版本中,我们可以直接调用Uri.fromFile(file)来获取file的uri,这个uri以"file:///"为首,且无时效性限制,接受到此uri的应用可以对它进行保存后,随时获取此file。而在Android 7.0以后的系统版本中,我们只能通过FileProvider.getUriFromFile(file)来提供一个以"content://"为首的uri,这个uri有一定的时效性,其生命周期伴随着intent关联的组件(可以是Activity,也可以是Service)的生命周期结束而结束,所以很大程度上保障了开发者的应用私有文件的安全。
FileProvider是ContentProvider的子类,实现了query()和delete()方法,并使insert()和update()方法直接抛出异常,同时新增了getUriFromFile(File file)方法来让我们在应用中获取用于分享此file的 uri。
二、用法
1.在AndroidManifest中定义FileProvider
...
...
...
需要设置android:authorities属性,用于标识属于你的应用的 FileProvider;另外还需要在meta-data标签内设置android:resource属性,用于配置文件路径。
2.在xml文件夹下定义路径配置文件
路径配置文件限制了FileProvider可以获取到你的应有私有目录下具体的子文件夹,比如说你的应用目录下有 imgs 和 docs 两个子文件夹,而你只希望向外分享图片,则可以将下面的path属性值设置为 path="imgs"。
其中,name属性值用于组成最后生成的uri的一部分,所以这个属性可以任意定制,而前面的标签名的不同则决定了文件夹位置的不同。
"files_path" 对应 Context.getFilesDir() 的路径,物理路径为:data/data/
"cache-path" 对应 Context.getCacheDir(),物理路径为:/data/data/
"external-path" 对应 Environment.getExternalStorageDirectory()。
"external-files-path" 对应 Context.getExternalFilesDir(String)、Context.getExternalFilesDir(null)。
"external-cache-path" 对应 Context.getExternalCacheDir()。
path属性就是第一段所提到的,当你的文件夹下有多个子文件夹,你只希望提供某一个文件夹的时候,可以在属性中填入相应的文件夹名。为空时,FileProvider可以为应用文件夹下的所有文件生成uri。
3.获取uri
File file = new File(Context.getFilesDir(), "default_image.jpg");
Uri contentUri = FileProvider.getUriForFile(getContext(), "com.mydomain.fileprovider", file);
可以获取到一个uri: content://com.mydomain.fileprovider/my_images/default_image.jpg
其中,"com.mydomain.fileprovider/"后的"my_images"就是前面路径配置文件中name属性指定的。
4.分享
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, requestCode);
以上只是适用于Android 7.0以上的系统,对于Android 7.0以下的系统,我们直接调用Uri.fromFile(file)获取uri即可。
三、例子
例1:
//build.gradle(module)
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tomorrow.architetest"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-compat:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
//MainActivity
package com.tomorrow.architetest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_CODE_IMAGE_CAPTURE = 10;
private String mPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_main);
mPath = getExternalCacheDir() + "/path/test.jpg";
Log.d(TAG, "zwm, mPath: " + mPath);
//Uri uri = FileProvider.getUriForFile(this, "com.tomorrow.fileprovider", new File(mPath));
Uri uri = Uri.fromFile(new File(mPath));
Log.d(TAG, "zwm, uri: " + uri);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE);
}
}
//输出log
2019-11-22 16:08:14.236 6871-6871/com.tomorrow.architetest D/MainActivity: zwm, onCreate
2019-11-22 16:08:15.307 6871-6871/com.tomorrow.architetest D/MainActivity: zwm, uri: file:///storage/emulated/0/Android/data/com.tomorrow.architetest/cache/test.jpg
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tomorrow.architetest/com.tomorrow.architetest.MainActivity}: android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.tomorrow.architetest/cache/test.jpg exposed beyond app through ClipData.Item.getUri()
例2:
//build.gradle(module)
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tomorrow.architetest"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-compat:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
//AndroidManifest.xml
//res/xml/file_paths.xml
//MainActivity
package com.tomorrow.architetest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_CODE_IMAGE_CAPTURE = 10;
private String mPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_main);
mPath = getExternalCacheDir() + "/path/test.jpg";
Log.d(TAG, "zwm, mPath: " + mPath);
Uri uri = FileProvider.getUriForFile(this, "com.tomorrow.fileprovider", new File(mPath));
Log.d(TAG, "zwm, uri: " + uri);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE);
}
}
//输出log
2019-11-22 18:07:51.398 11579-11579/com.tomorrow.architetest D/MainActivity: zwm, onCreate
2019-11-22 18:07:51.516 11579-11579/com.tomorrow.architetest D/MainActivity: zwm, mPath: /storage/emulated/0/Android/data/com.tomorrow.architetest/cache/path/test.jpg
2019-11-22 18:07:51.517 11579-11579/com.tomorrow.architetest D/MainActivity: zwm, uri: content://com.tomorrow.fileprovider/name/test.jpg