在所有情况下,唯一安全的方式,提供从您的应用程序文件到另一个应用程序是发送接收的应用程序文件的内容URI和临时授予访问权限到URI。临时URI的访问权限内容URI是安全的,因为它们只适用于接收URI的应用程序,它们自动过期。在Android FileProvider组件提供了方法getUriForFile(),用于产生一个文件的内容的URI。
通过Android FlieProvider组件和临时权限授予,安全的从你的应用共享文件到其他应用。
设置文件共享
为了安全的分享文件,需要配置你的应用。android的FileProvider生成的文件内容的URL是根据在XML里设置的规格。
定义FileProvider
在mainfest里规定FileProvider作为你的应用所需要的条目。该项指定生成的内容的URI以及可以共享的XML文件。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
android:authorities属性就是指定要使用生成的内容的URI通过FileProvider。
在< meta-data >指向指定要共享的目录的XML文件。android:resource属性是该文件的路径和名称。
指定可共享目录
创建res/xml/filepaths.xml文件。
<paths>
<files-path path="images/" name="myimages" />
</paths>
该例子中< file-path >标签内的目录共享文件到内部存储目录。该返回的URI为
content://com.example.myapp.fileprovider/myimages/default_image.jpg
注:< paths >可以有多个子元素,每个指定不同的目录共享。详情查阅FileProvider
共享文件
一旦设置了你的应用共享使用内容的URI的文件。你就可以响应其他应用对这些文件作出回应。提供响应这些请求的一种方法是从其他应用程序可以调用的服务器应用程序提供文件选择接口。这种方法允许客户端应用程序让用户从服务器应用程序中选择一个文件,然后接收所选文件的内容URI。
接收文件请求
详情请查阅android学习(六) 获取Activity的结果
public class MainActivity extends Activity {
private Intent mRequestFileIntent;
private ParcelFileDescriptor mInputPFD;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRequestFileIntent = new Intent(Intent.ACTION_PICK);
mRequestFileIntent.setType("image/*");
...
}
...
protected void requestFile() {
tartActivityForResult(mRequestFileIntent, 0);
...
}
...
}
创建文件选择Activity
要设置文件选择Activity,需要在manifest设置Intent filter。action:ACTION_PICK**categories:**CATEGORY_DEFAULT and CATEGORY_OPENABLE.还有一些MIME type。例如:
<application>
...
<activity
android:name=".FileSelectActivity"
android:label="@File Selector" >
<intent-filter>
<action
android:name="android.intent.action.PICK"/>
<category
android:name="android.intent.category.DEFAULT"/>
<category
android:name="android.intent.category.OPENABLE"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
在代码中定义文件选择Activity
public class MainActivity extends AppCompatActivity {
private ListView mFileListView;
private ImageView imageView;
//该应用程序的内部存储的根路径
private File mPrivateRootDir;
//images的子目录
private File mImageDir;
//在images的子目录的文件数组
private File[] mImageFiles;
//mInageFiles对应的文件名数组
private String[] mImageFileNames;
private Intent mResultIntent;
//初始化Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//设置一个Intent请求的文件返回给应用
mResultIntent = new Intent("com.example.hp.myapplication.ACTION_RETURN_FILE");
//从内部存储获取文件的子目录
mPrivateRootDir = this.getFilesDir().getAbsoluteFile();
//获取images的子目录
mImageDir = new File(mPrivateRootDir,"images");
if(!mImageDir.exists()){
mImageDir.mkdirs();
}
//获取images目录下的文件
mImageFiles = mImageDir.listFiles();
//这是activity的结果为null
setResult(Activity.RESULT_CANCELED,null);
}
注意:filepaths.xml里的files-path下的path:xxx是表示你获取的getFileDir()的files目录下的文件。name:xxx表示filesProvider生成的文件名。
文件权限授予
现在,你有要与其他应用程序所共享的文件内容URI,你需要允许客户端应用程序访问文件。要允许访问文件需要Intent.addFlags()进行临时的授权。
mFileListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView,
View view,
int position,
long rowId) {
...
if (fileUri != null) {
mResultIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
...
}
...
});
分享文件给请求的应用
protected void onCreate(Bundle savedInstanceState) {
...
mFileListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView,
View view,
int position,
long rowId) {
...
if (fileUri != null) {
...
//放入Uri和MIME type在mResultIntent
mResultIntent.setDataAndType(
fileUri,
getContentResolver().getType(fileUri));
MainActivity.this.setResult(Activity.RESULT_OK,
mResultIntent);
} else {
mResultIntent.setDataAndType(null, "");
MainActivity.this.setResult(RESULT_CANCELED,
mResultIntent);
}
//访问完后最好销毁Activity
finish();
}
});
访问请求文件和检索文件信息
之前一个客户端应用程序试图与它有一个内容URI文件的工作,应用程序可以请求关于从服务器应用程序的文件信息,包括文件的数据类型和文件大小。数据类型可以帮助客户端应用程序,以确定它是否可以处理的文件,文件大小可以帮助客户端应用程序建立缓冲和缓存文件。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode!=RESULT_OK){
return;
}else {
Uri returnUri = data.getData();
Cursor returnCursor = getContentResolver().query(returnUri,null,null,null,null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
returnCursor.getString(nameIndex);
ParcelFileDescriptor descriptor = null;
try {
descriptor = getContentResolver().openFileDescriptor(returnUri,"r");
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("MainActivity","文件无法找到");
}
//获取FileDescriptor
FileDescriptor fileDescriptor = descriptor.getFileDescriptor();
}
}