根据 Android Training课程写的FileProvider小例子
2014-07-17 10:45
253 查看
FileProvider的使用
一个应用经常需要向其他应用发送一个甚至多个文件。例如,一个图库应用可能需要向图片编辑器提供多个文件,或者一个文件管理器可能希望允许用户在外部存储的不同区域之间复制粘贴文件。一种让应用可以分享文件的方法,接收文件应用所发出的请求进行响应。
在所有情况下,唯一的一个将一个文件从你的应用发送至另一个应用的安全方法是向接收文件应用发送这个文件的URI,然后对这个URI授予临时的可访问权限。具有URI临时访问权限的URI是安全的,因为访问权限只授权于接收这个URI的应用,并且它们会自动过期。Android的FileProvider组件提供了getUriForFile())方法来创建一个文件的URI。
以上摘自androidtraining中文课程。这两天写了一个关于FileProvider的例子,基本是按照训练课程的思路做的。如果你有兴趣一览,不胜荣幸。
例子里写了两个应用,一个是服务器,一个是客户端。服务器应用给客户端应用提供分享的文件,客户端用来显示文件的内容。
服务器应用
1) 设置配置文件
修改AndroidManifest.xml文件,内容如下:
<provider android:exported="false" android:grantUriPermissions="true" android:authorities="@string/fileprovider_authority" android:name="android.support.v4.content.FileProvider">
<meta-data android:resource="@xml/filepaths" android:name="android.support.FILE_PROVIDER_PATHS"/>
</provider>
其中的android:authorities值定义在res/values/string.xml。显示如下:
在/res/xml下定义了filepaths.xml文件。内容如下:
原training课程中只是使用了files-path,我另外加了一个external-path。files-path指明你要分享的文件的根级目录必须是getFilesDir()目录,path=”files/”,是这个根级目录下的一级目录。external-path指明的分享根级目录是Environment.getExternalStorageDirectory()。注意两个name值是不能相同的。
1) 创建分享文件
测试时,必须有文件分享才行,所以直接创建了一个activity用来生成分享文件。主界面如下:
点击“创建内部文件”按钮,会在应用的getFilesDir()/files/目录下创建10个txt文件,命名为“内部文件0.txt”,“内部文件1.txt”等,内容为“这是第0个内部文件”,“这是第1个内部文件”等。
点击“创建外部文件”按钮,会在Environment.getExternalStorageDirectory()/files/目录下创建10个txt文件,命名与内容与上面的内部文件类似,只不过用“外部”代替了“内部”字符串。
创建完后,你就可以按后退键退出应用了。
1) 建立响应分享请求的Activity
我们给这个activity命名为FileSelectActivity。还要声明相应的intent-filter才行。它的配置文件内容为:
/AndroidManifest.xml
我们设置的MIME类型为“text/plain”,表明只接收纯文本类型。当然了,如果你想让它响应别的类型的文件请求,请修改此值。
当有用户请求文件时,这个activity就会显示出来,界面如下:
我们使用了一个ExpandableListView来显示内部和外部文件列表,当用户点击的时候,就会把结果返回给请求者。
下面是设置结果的代码:
上面有一句resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),如果没有这句话,原请求者去读文件的时候会发生安全异常错误的。
客户端应用
客户端应用就比较简单了,它只是去请求一个文件,然后从返回结果中读取文件内容就行了。它的主界面如下:
点击“请求文件”按钮,就会打开服务器应用,进入分享文件列表,选择后就会返回到本界面,然后显示文件名和文件内容。
“请求文件”按钮的响应事件代码如下:
private void requestFile() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("text/plain");
startActivityForResult(intent, 0);
}
注意,首先应该先安装服务器应用,否则系统中可能不存在能响应此intent的组件,那么你的客户端应用可能崩溃。
当从客户端得到结果时,我们需要分别读取文件的名字和大小,还有文件的内容信息。
如果上面的获取成功的话,显示效果将如下:
有兴趣的同学请参考源代码
一个应用经常需要向其他应用发送一个甚至多个文件。例如,一个图库应用可能需要向图片编辑器提供多个文件,或者一个文件管理器可能希望允许用户在外部存储的不同区域之间复制粘贴文件。一种让应用可以分享文件的方法,接收文件应用所发出的请求进行响应。
在所有情况下,唯一的一个将一个文件从你的应用发送至另一个应用的安全方法是向接收文件应用发送这个文件的URI,然后对这个URI授予临时的可访问权限。具有URI临时访问权限的URI是安全的,因为访问权限只授权于接收这个URI的应用,并且它们会自动过期。Android的FileProvider组件提供了getUriForFile())方法来创建一个文件的URI。
以上摘自androidtraining中文课程。这两天写了一个关于FileProvider的例子,基本是按照训练课程的思路做的。如果你有兴趣一览,不胜荣幸。
例子里写了两个应用,一个是服务器,一个是客户端。服务器应用给客户端应用提供分享的文件,客户端用来显示文件的内容。
服务器应用
1) 设置配置文件
修改AndroidManifest.xml文件,内容如下:
<provider android:exported="false" android:grantUriPermissions="true" android:authorities="@string/fileprovider_authority" android:name="android.support.v4.content.FileProvider">
<meta-data android:resource="@xml/filepaths" android:name="android.support.FILE_PROVIDER_PATHS"/>
</provider>
其中的android:authorities值定义在res/values/string.xml。显示如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">服务器</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="makeIntFile">创建内部文件</string> <string name="makeExtFile">创建外部文件</string> <string name="fileprovider_authority">com.example.serverapp</string> <string name="file_select">请选择文件</string> <string name="action_cancel">取消</string> <string name="action_done">完成</string> </resources>
在/res/xml下定义了filepaths.xml文件。内容如下:
<paths> <files-path path="files/" name="intfiles" /> <external-path path="files/" name="extfiles" /> </paths>
原training课程中只是使用了files-path,我另外加了一个external-path。files-path指明你要分享的文件的根级目录必须是getFilesDir()目录,path=”files/”,是这个根级目录下的一级目录。external-path指明的分享根级目录是Environment.getExternalStorageDirectory()。注意两个name值是不能相同的。
1) 创建分享文件
测试时,必须有文件分享才行,所以直接创建了一个activity用来生成分享文件。主界面如下:
点击“创建内部文件”按钮,会在应用的getFilesDir()/files/目录下创建10个txt文件,命名为“内部文件0.txt”,“内部文件1.txt”等,内容为“这是第0个内部文件”,“这是第1个内部文件”等。
点击“创建外部文件”按钮,会在Environment.getExternalStorageDirectory()/files/目录下创建10个txt文件,命名与内容与上面的内部文件类似,只不过用“外部”代替了“内部”字符串。
创建完后,你就可以按后退键退出应用了。
1) 建立响应分享请求的Activity
我们给这个activity命名为FileSelectActivity。还要声明相应的intent-filter才行。它的配置文件内容为:
/AndroidManifest.xml
<activity android:name="FileSelectActivity" android:label="@string/file_select"> <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"/> </intent-filter> </activity>
我们设置的MIME类型为“text/plain”,表明只接收纯文本类型。当然了,如果你想让它响应别的类型的文件请求,请修改此值。
当有用户请求文件时,这个activity就会显示出来,界面如下:
我们使用了一个ExpandableListView来显示内部和外部文件列表,当用户点击的时候,就会把结果返回给请求者。
下面是设置结果的代码:
@Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { // TODO Auto-generated method stub File file = (File)parent.getExpandableListAdapter().getChild(groupPosition, childPosition); Uri fileUri; try { fileUri = FileProvider.getUriForFile( FileSelectActivity.this, getResources().getString(R.string.fileprovider_authority), file); Intent resultIntent = new Intent(); if (fileUri != null) { resultIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION); // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType( fileUri, getContentResolver().getType(fileUri)); // Set the result setResult(Activity.RESULT_OK, result 4000 Intent); } else { resultIntent.setDataAndType(null, ""); setResult(RESULT_CANCELED, resultIntent); } finish(); } catch (Exception e) { e.printStackTrace(); } 。。。。。。
上面有一句resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),如果没有这句话,原请求者去读文件的时候会发生安全异常错误的。
客户端应用
客户端应用就比较简单了,它只是去请求一个文件,然后从返回结果中读取文件内容就行了。它的主界面如下:
点击“请求文件”按钮,就会打开服务器应用,进入分享文件列表,选择后就会返回到本界面,然后显示文件名和文件内容。
“请求文件”按钮的响应事件代码如下:
private void requestFile() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("text/plain");
startActivityForResult(intent, 0);
}
注意,首先应该先安装服务器应用,否则系统中可能不存在能响应此intent的组件,那么你的客户端应用可能崩溃。
当从客户端得到结果时,我们需要分别读取文件的名字和大小,还有文件的内容信息。
private void readFile(Uri returnUri) { Context context = getActivity(); ParcelFileDescriptor inputPFD; //获取文件句柄 try { inputPFD = context.getContentResolver().openFileDescriptor(returnUri, "r"); } catch (FileNotFoundException e) { e.printStackTrace(); tvFileContent.setText("获取文件句柄失败"); tvFileName.setText("获取文件句柄失败"); return; } //获取文件名字和大小 Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); /* * Get the column indexes of the data in the Cursor, * move to the first row in the Cursor, get the data, * and display it. */ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); returnCursor.moveToFirst(); tvFileName.setText("文件名:"+returnCursor.getString(nameIndex)+", 大小:"+ Long.toString(returnCursor.getLong(sizeIndex))+" B"); returnCursor.close(); //读取文件内容 String content = ""; FileReader fr = null; char[] buffer = new char[1024]; try { StringBuilder strBuilder = new StringBuilder(); fr = new FileReader(inputPFD.getFileDescriptor()); while (fr.read(buffer) != -1) { strBuilder.append(buffer); } fr.close(); content = strBuilder.toString(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if (content.length() != 0) { tvFileContent.setText(content); } else { tvFileContent.setText("<内容空>"); } try { inputPFD.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
如果上面的获取成功的话,显示效果将如下:
有兴趣的同学请参考源代码
相关文章推荐
- Android训练课程(Android Training) - NFC基础
- Android File类 根据官方文档理解
- Android官方开发文档Training系列课程中文版:构建第一款安卓应用之入门指南
- Android官方开发文档Training系列课程中文版:构建第一款安卓应用之启动另一个Activity
- Android官方开发文档Training系列课程中文版:构建第一款安卓应用之创建用户界面
- 【Android Training - Connectivity】优化下载的效率[Lesson 4 - 根据网络类型更改下载模式]
- Android官方开发文档Training系列课程中文版:添加ActionBar之ActionBar浮层效果
- Android File类 根据官方文档理解
- Android训练课程(Android Training) - 高效的显示图片
- Android官方开发文档Training系列课程中文版:添加ActionBar之添加Action按钮
- Android训练课程(Android Training) - NFC基础
- Android训练课程(Android Training) - 使用Volley传输网络数据(Transmitting Network Data Using Volley)
- Android训练课程(Android Training) - 测试你的Android Activity
- [置顶] Android官方开发文档Training系列课程中文版:目录
- Android训练课程(Android Training) - 使用Volley传输网络数据(Transmitting Network Data Using Volley)
- Android File类 根据官方文档理解
- Android训练课程(Android Training) - 添加活动栏(使用action bar)
- Android训练课程(Android Training) - 添加活动栏(使用action bar)
- Android官方开发文档Training系列课程中文版:构建第一款安卓应用之工程创建
- Android官方开发文档Training系列课程中文版:添加ActionBar之自定义ActionBar样式