本博客依据官方提供的文档,为园友们提供实现的代码。建议园友们对照官方文档阅读本博客:http://developer.android.com/training/secure-file-sharing/index.html
为保障不同应用间文件共享的安全性,Android平台提供了FileProvider组件来保障共享的安全。FileProvider组件根据程序的配置,为其他应用(我们简称为接受应用)提供一个对外的Content URI,同时为接受应用分配临时的访问权限,该权限在接受应用退出时自动过期。
FileProvider是Android支持库中提供的方法,在使用之前,需要得到支持库的支持。为了使用FileProvider,需要在工程的AndroidManifest.xml文件中声明provider,格式类似于:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.fileshare.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepath" /> </provider>
其中,android:authorities属性用生成URIs的authority,一般由工程的包名+fileprovider组成。<meta-data>节点声明应用要共享的文件目录,目录的配置文件位于res/xml文件夹下。共享目录的配置如下所示:
<?xml version="1.0" encoding="UTF-8"?> <paths> <files-path path="imagedir/" name="image" /> </paths>
其中path是应用的files目录的子目录,也即应用要共享的文件的目录,name是为了告诉FileProvider,将其值添加到Content URIs中,具体的配置说明请参看这里:http://developer.android.com/training/secure-file-sharing/setup-sharing.html#DefineMetaData
假设在工程的files的imagedir目录下有个名为default_image.jpg的文件,则根据之前的配置,其URI为content:/com.example.fileshare.fileprovider/imagedir/default_image.jpg。
配置好了FileProvider后,还需要为接收应用提供一个选择界面,该界面主要是列出共享的文件供用户选择。界面根据用户的选择将对应的文件返回给接受应用。其实现如下:
public class FileSelector extends ListActivity { private File privateRootDirs; //共享文件的父目录 private File imageDirs; //共享文件目录 private File[] imageFiles; //共享的文件 private String[] imageFileName; //共享的文件名 private ListView lvFileList; //显示共享文件的列表 private Uri fileUri; //FileProvider生成的Content URI private Intent resultIntent; //将用户选择的结果返回给接受应用的Intent @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); privateRootDirs = getFilesDir(); imageDirs = new File(privateRootDirs, "imagedir"); imageFiles = imageDirs.listFiles(); imageFileName = new String[imageFiles.length]; for(int i=0; i<imageFiles.length; i++) { imageFileName[i] = imageFiles[i].getName(); } lvFileList = getListView(); setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, imageFileName)); lvFileList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //根据官网提供的代码片段,这里在生成File实例时,需要传入“共享文件目录”参数 //否则为报“java.lang.IllegalArgumentException: Failed to find configured root that contains ...“异常 File requestFile = new File(imageDirs, imageFileName[position]); fileUri = FileProvider.getUriForFile(FileSelector.this, "com.example.fileshare.fileprovider", requestFile); if(fileUri != null) { resultIntent = new Intent(); resultIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); resultIntent.setDataAndType(fileUri, getContentResolver().getType(fileUri)); FileSelector.this.setResult(Activity.RESULT_OK, resultIntent); finish(); } } }); } }
在声明给Activity时,需要添加如下的filter:
<activity android:name=".FileSelector" > <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="image/*" /> <data android:mimeType="text/plain" /> </intent-filter> </activity>
为验证程序的正确性,需要重新创建一个新的工程,在工程代码中,调用FileSelector:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestFileIntent = new Intent(); requestFileIntent.setAction(Intent.ACTION_PICK); requestFileIntent.setType("image/jpg"); startActivityForResult(requestFileIntent, 0); }
为了显示用户在FileSelector选择的文件信息,复写如下方法:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); System.out.println("Stop here"); if(resultCode == Activity.RESULT_OK) { Uri resultUri = data.getData(); try { ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(resultUri, "r"); FileDescriptor fd = pfd.getFileDescriptor(); String mimeType = getContentResolver().getType(resultUri); Cursor returnCursor = getContentResolver().query(resultUri, null, null, null, null); int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); returnCursor.moveToFirst(); System.out.println("MIMEType: " + mimeType + " nameIndex: " + nameIndex + " sizeIndex: " + sizeIndex); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
参考代码可以从这里下载:http://pan.baidu.com/s/11RO0D
PS:由于个人组织能力太烂,这是本人首次写博客,今天先写到这里吧,请大家容我慢慢整理思路。。。