最近在做一个文件分享的Demo,一连看了两天文档,终于把它给弄出来了。下面是文档中的一段话,大致的意思是通过FileProvider在应用程序之间共享文件的安全的。进程间文件的分享,假设两个应用程序分为Server(共享数据一端)和Client(请求数据一端)。
在所有情况下,将文件从你的应用程序发送至其它应用程序的唯一的安全方法是向接收文件的应用程序发送这个文件的content URI,并对该URI授予临时的访问权限。具有URI临时访问权限的content URI是安全的,因为他们仅应用于接收这个URI的应用程序,并且它们会自动过期。Android的FileProvider组件提供了getUriForFile())方法来创建一个文件的contentURI。需要对应用进行配置来提供安全的文件句柄(Content URI的形式)。
在Server(共享数据一端)端,FileProvider的使用需要在程序中进行一些配置。因为文件要安全地从你的应用程序发送给其它应用程序,Android的FileProvider组件会基于你在XML文件中的具体配置,为文件创建Content URI。
首先,FileProvider包含在Support-V4Library中,使用时应查看应用程序是否已包含有。然后,使用ContentProvider那就必须在Manifest文件中进行注册。注册一个provider如下:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.junxu.sharefile.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>
</provider>
上面的android:authorities属性字段指定了你希望使用的Authority,该Authority针对于FileProvider所生成的contentURI。在这个例子中,这个Authority是
“com.junxu.sharefile.fileprovider”。一般情况下,用应用程序包名(android:package的值)之后继续追加“fileprovider”来作为Authority的值。 name注属性的值固定,表示support.v4中的FileProvider类。
contentProvider的属性介绍访问:http://blog.csdn.net/berber78/article/details/39252727
<provider>下的子标签<meta-data>指向了一个XML文件,该文件指定了你希望共享的目录路径。“android:resource”属性字段是这个文件的路径和名字(无“.xml”后缀)。
一旦你在你的Manifest清单文件中为你的应用添加了FileProvider,你需要指定你希望共享文件的目录路径。为了指定这个路径,首先在“res/xml/”下创建文件“filepaths.xml”。在这个文件中,为每一个想要共享目录添加一个XML标签。下面的是一个“res/xml/filepaths.xml”的内容样例。这个例子也说明了如何在你的内部存储区域共享一个“files/”目录的子目录:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<paths >
<files-pathname="myimages" path="images/" />
</paths >
</resources>
在这个例子中,<files-path>标签共享的是在应用的内部存储中“files/”目录下的目录。“path”属性字段指出了该子目录为“files/”目录下的子目录“images/”。“name”属性字段告知FileProvider向在“files/images/”子目录中的文件的Content URI添加路径分段(path segment)标记:“myimages”。比如上面的<files-path>标签表示共享/data/data/package(包名)/files/images文件夹中的文件或图片。即用path的值接在/data/data/package(包名)/files之后,生成的Uri为content://Authority/ myimages/ in.png(文件名),这个Uri就相当于文件:/data/data/package(包名)/files/images/in.png向外暴露的个体其他应用程序访问的路径。 (注意在生成Uri是,需确保在/data/data/package(包名)/files/下有你要共享的文件夹及文件,即确保共享的数据存在)
<paths>标签可以有多个子标签,每一个子标签用来指定不同的共享目录。除了<files-path>标签,你还可以使用<external-path>来共享位于外部存储的目录;另外,<cache-path>标签用来共享在你的内部缓存目录下的子目录。到现在FileProvider的配置已经完成了。
创建一个Activity,这个Activity用于接收其他应用程序发送的文件请求。当其他应用程序需要获取文件时,通过startActivityForResult启动这个Activity,所以这个Activity将共享文件夹下的所有文件显示在这个Activity上,供其他应用程序选择,当用户程序选择了文件之后,生成这个文件的Uri,然后通过setResult返回,并结束本Activity。
为了能让其他应用程序可以启动这个Activity,需要在Manifest文件中注册并配置这个Activity。
<activity
android:name="com.junxu.sharefile.FileSelectActivity"
android:label=".FileSelect">
<intent-filter >
<actionandroid:name="android.intent.action.PICK" />
<categoryandroid:name="android.intent.category.DEFAULT" />
<categoryandroid:name="android.intent.category.OPENABLE"/>
<dataandroid:mimeType="text/plain" />
<dataandroid:mimeType="image/*"/>
</intent-filter>
</activity>
在其Intent过滤器中,匹配ACTION_PICK这一Action,以及CATEGORY_DEFAULT和CATEGORY_OPENABLE这两种Category。另外,还需要为你的应用程序设置MIME类型过滤器,来表明你的应用程序可以向其他应用程序提供哪种类型的文件。
Activity的属性介绍参考:http://blog.csdn.net/voiceofnet/article/details/7770311
//获得手机内部存储路径(/data/data/(包名) )
mPrivateRootDir = getFilesDir();
mImagesDir = new File(mPrivateRootDir, "images");//路径(/data/data/(包名) /iamges)
mImageFiles =mImagesDir.listFiles(); //获取路径下的所有文件
//将文件保存在list对象中,然后通过listView显示出来
for(int i=0;i<mImageFiles.length;i++){
String path = mImageFiles[i].getAbsolutePath();
Bitmap bm = BitmapFactory.decodeFile(path);
FileInfo fInfo = newFileInfo(path.substring(path.lastIndexOf("/")+1), path, bm);
files.add(fInfo);
}
@Override
public voidonItemClick(AdapterView<?> parent, View view, int position, long id) {
FilerequestFile = new File(files.get(position).getPath());
Stringauthority="com.junxu.sharefile.fileprovider";// authority
Log.d("********",files.get(position).getPath());
try{
UrifileUri = FileProvider.getUriForFile(FileSelectActivity.this, authority,requestFile);//生成对应文件的Uri
Log.d("********",fileUri.toString());
if(fileUri!=null){
//设置权限, 所授予的权限是临时的,并且当接收文件的应用程序的任务栈终止后,会自动过期
mResultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//设置数据类型
mResultIntent.setDataAndType(fileUri,getContentResolver().getType(fileUri));
setResult(Activity.RESULT_OK,mResultIntent);
Log.d("********", "ok"+RESULT_OK);
}
}catch(IllegalArgumentExceptione){
e.printStackTrace();
mResultIntent.setDataAndType(null,"");
setResult(RESULT_CANCELED,mResultIntent);
}
finish();//结束当前activity
}
在Client一端,向服务端应用程序发送文件请求,需要调用startActivityForResult)方法,同时传递给这个方法一个Intent参数,它包含了客户端应用程序能处理的某个Action,比如ACTION_PICK;以及一个MIME类型。示例程序如下:
Intent mRequestFileIntent = new Intent(Intent.ACTION_PICK);
mRequestFileIntent.setType("image/jpg");
startActivityForResult(mRequestFileIntent, 0);
在Client端的Activity中需要重写onActivityResult())方法,接收Server端返回的数据。Client端选择文件之后, 服务端应用程序向客户端应用程序发回包含所选文件的Content URI的Intent,客户端应用程序从这个intent后的文件的Content URI,就可以通过获取其FileDescriptor来访问文件了。
@Override
protected void onActivityResult(int requestCode, int resultCode,Intent data) {
Log.d("******"," 222"+resultCode);
if( resultCode !=RESULT_OK ){
return;
}else{
Uri fileUri = data.getData();//获得Uri
Cursor returnCursor = getContentResolver().query(fileUri,null, null, null, null);
//获得文件名
int nameIndex =returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
//获得文件大小
int sizeIndex =returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
info.setText("\t文件名:"+returnCursor.getString(nameIndex)+"\n\t文件大小:"+returnCursor.getString(sizeIndex));
Log.d("*****",returnCursor.getString(nameIndex));
Log.d("*****", returnCursor.getString(sizeIndex));
returnCursor.close();
Bitmap bm = null;
//获得图片并显示
try {
InputStreamis = getContentResolver().openInputStream(fileUri);
bm= BitmapFactory.decodeStream(is);
img.setImageBitmap(bm);
} catch(FileNotFoundException e) {
e.printStackTrace();
}
}
}