分享文件
△概述:如果你要对外提供少量数据,你可以用Intent带着少量数据出去,当你需要对外提供大批量的数据(比如一个图片编辑软件向你请求一个图片资源,一个文档编辑器向你请求一个文档)你将需要分享文件。
△客户端与服务器:我们将请求文件的应用称为客户端,将对外提供数据的应用称为服务器。
服务器
△概述:
→这里所谓的服务器,就是一个能够将自己的文件分享出去的应用程序。
→文件是通过URL形式分享,通过文件的URL分享文件是安全的
→FileProvider组件提供方法:getUriForFile()为一个文件生成content URL。
△为了完成服务器的工作,你需要做如下事情:
→配置FileProvider,这个组件提供方法将文件以URL的形式分享出去。
→设置一个文件展示以及选择用的窗体,当其他的应用请求访问一些文件,如果你的应用包含这些文件,你需要将这些文件展现在窗体上以供用户选择。
→当用户选择了某个文件之后,你还要能响应用户选择(其实就是将用户选择的文件以URl形式,返回 给请求文件的那个应用)。
定义FileProvider
△想将文件分享出去,首先需要定义一个文件分享者:FileProvider,步骤如下:在配置文件里面增加一个字段<provider></provider>,这个字段需要指定如下信息:
→指定FileProvider的类。
→你的文件的URl的“authority”属性。
→一个xml文件,你的应用只能将指定的某些路径下的文件分享出去,这些路径由你通过这个XML文件指定。
注:→authority属性的内容应该是:包名.fileprovider
→xml文件由meta-data子元素来指定。
<provider
<!--指定fileProvider-->
android:name="android.support.v4.content.FileProvider"
<!--指定authroity属性值-->
android:authorities="com.test.fileprovider.fileprovider"
<!--允许授权-->
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
<!--指定xml文件的路径-->
android:resource="@xml/filepaths"/>
</provider>
△创建上一步所说的XML文件
→在res目录下面新建文件夹,取名xml,在文件夹新建一个文件,取名:filepaths,
这是按照上图当中resource内容创建。
→xml文件内容:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!—path表示你要分享哪个路径下的文件-->
<files-path path="images/" name="myimages"/>
</paths>
解析:
→到此为止,你已经拥有了一个完整的FileProvider。他配置了authority,你想要分享的文件所在路径 (或者说是你的应用程序能够分享出去的文件的路径)。
→现在,xml文件指定了如下路径:/data/data/包名/files/images。其实就是你的应用调用方法: getFilesDir()所返回的路径下的images子路径:files/images/。
→name属性内容与文件的路径没有关系,他是你想要在你的URL里面插入的一个字段,如果你在 files/images/路径下面有个文件名叫:myImage.jpg。那么fileprovider将会返回:
content://com.test.fileprovider.fileprovider/myimages/myImage.jpg。我们看到,最终的URL里面没有 包含images子路径名,而且包含了一个你想插入的name。name属性其实就是为了隐藏真实的路径 的。
→所以最终URL是:content://你在清单文件里面所指定的“authority”属性/你的xml文件里指定name属性/ 你的文件的全名。
→注:在<paths></paths>标签里面你还可以添加多个元素:<cache-path>(对应路径:/data/data/包 名/cache)或者<external-path>(对应你的应用程序在SD卡上的默认路径)。
设置一个文件展示以及选择用的窗体
△当你配置了FileProvider,且当有应用向你请求文件时,你需提供一个窗体,该窗体能实现如下功能:
→可以被请求文件的应用唤起(就是能被隐式Intent唤起)。
→将自己能够分享的文件显示在窗体里(你可以用ListView实现,安卓官方教程也以这个控件作为例子实现)。
→当用户点击了某个文件,该窗体能提供这个文件的URL(就是将URL返回给调用他的窗体)。
△通过配置清单文件的过滤器实现第一步
<intent-filter>
<!--你需要将action设置为:android.intent.ACTION_PICK,除了默认category,还要添加一个叫做:android.intent.CATEGORY.OPENABLE-->
<action android:name=”android.intent.ACTION_PICK”/>
<category android:name=”android.intent.CATEGORY_DEFAULT”/>
<category android:name=”android.intent.CATEGROY.OPENABLE”/>
<!—还要设置你能够分享的数据类型-->
<!—实际开发中你应该根据实际情况设置-->
<data mimeType=”text/plain” />
<data mimeType=”image/*”>
</intent-filter>
△通过设置ListView实现第二步(我的代码将分享的路径:手机内存/files/images/,至于如何在内存里创建一个images文件夹,我在这里不再重复)
public classFileSelectActivityextendsActivity {
//他将指向内存路径
private FilemyFilesDi;
//指向images子路径的
private FilemyImageDir;
//images子路径的所有文件
privateFile[] myImageFiles;
//images子路径的所有文件名字
privateString[] myImageFilenames;
//用ListView展示文件给用户
private ListView mFileListView = null;
private Uri fileUri =null;
Intent mResultIntent =null;
@Override
protected voidonCreate(Bundle savedInstanceState) {
// TODOAuto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.file_select_activity);
//先将成员变量给实例化
myFilesDir = getFilesDir();
myImageDir = new File(myFilesDir,”images”);
myImageFiles = myImageDir.listFiles();
//将文件的名字装进数组里面
for(int i = 0; i<myImageFiles.length; i++)
{
myImageFilenames[i] = myImageFiles[i].getName();
}
//根据ID的到控件
mFileListView =(ListView)findViewById(R.id.lv_file);
//设置Intent
mResultIntent = new Intent("com.test.fileprovider.ACTION_RETURN_FILE");
newIntent("com.test.fileprovider.ACTION_RETURN_FILE");
//为ListView添加适配器
mFileListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,myImageFileNames);
android.R.layout.simple_list_item_1, myImageFilenames));
/*到这一步你的路径下的文件已经可以显示出来,我的例程只是显示图片文件的文件名*/
/*接下来要进行如下工作:当有用户点击你的某个文件,你要获取该文件URL,并且将该URL返回给启用你的那个应用*/
//设置点击事件响应函数
mFileListView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view,int position, long id){
// TODOAuto-generated method stub
//获取被点击的那个文件
FilerequestFile = new File(myImageDir,myImageFilenames[position]);
try {
//获取文件的URL
fileUri=
FileProvider.getUriForFile(FileSelectActivity.this, "com.test.fileprovider.fileprovider",requestFile);
} catch(Exception e) {
// TODO:handle exception
//打印出现的错误的信息
Toast.makeText(FileSelectActivity.this,e+"",Toast.LENGTH_SHORT).show();
}
//如果成功获取了URL
if (fileUri!=null) {
//为URI添加访问权限mResultIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION);
//为Intent设置返回的数据
mResultIntent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
//设置成功返回标志
FileSelectActivity.this.setResult(RESULT_OK, mResultIntent);
}
//如果没有获取文件的URL,也要设置相关信息
else {
mResultIntent.setDataAndType(null, "");
FileSelectActivity.this.setResult(RESULT_CANCELED, mResultIntent);
}
}//onItemClick方法体
});
}
/*程序到此,你已经根据用户的选择获取文件的URL,而且设置了返回的intent,不过千万别忘记了还有一步,你得设置一个返回按钮,在里面调用结束的方法,将你这个应用窗体结束:finish();这样才能将所有的数据返回客户端的应用*/
客户端
△概述:客户端即请求获取某些文件的应用程序。
△客户端要做些什么:
→配置Intent,指定客户端想请求访问什么文件
(1) intent.setAction(Intent.ACTION_PICK);
(2) intent.setType(“image/*”);
→启动访问:当用户想访问某些文件,一般情况下,应当是在某个按钮点击事件响应函数里面执行:执行方法: startActivityForResult(intent,0);
→当服务器提供了想要的文件,客户端要处理自己所得到的文件。
复写:onActivityResult(int requestCode, int resultCode, Intent returnIntent);
方法里面,returnIntent参数,他带着从服务器里返回来的文件URL,通过他来打开我们想访问的文件
△配置Intent
public classMainActivityextendsActivity {
private Intent myRequestIntent = null;
private ParcelFileDescriptor mInputPFD = null;
//待会利用一个控件显示我们所得到的URL
privateTextView tv = null;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//配置Intent
myRequestIntent =newIntent(Intent.ACTION_PICK);
myRequestIntent.setType("image/jpg");
tv =(TextView)findViewById(R.id.tv_info);
}
}
△启动访问
//点击事件响应函数
public void requestFiles(View view){
//启动服务端的程序
startActivityForResult(myRequestIntent, 0);
}
//客户端通过URl处理所得到的文件
@Override
protected void onActivityResult(intrequestCode,intresultCode, Intent returnIntent) {
// TODOAuto-generated method stub
super.onActivityResult(requestCode,resultCode, returnIntent);
//如果没有得到数据直接返回当前函数
if(resultCode!=RESULT_OK) {
return;
//如果成功得到数据
}else {
Uri returnUri = returnIntent.getData();
tv.setText(returnUri+"");//通过控件显示文件的URl
try {
mInputPFD =getContentResolver().openFileDescriptor(returnUri, "r");
} catch(FileNotFoundException e) {
//TODO:handle exception
e.printStackTrace();
return;
}
FileDescriptorfd = mInputPFD.getFileDescriptor();
//fd是个流对象你可以通过它去得到文件
}//if
}
/*
到此为止你已经得到了一个客户端的程序
*/