分享文件
△概述:如果你要对外提供少量数据,你可以用Intent带着少量数据出去,当你需要对外提供大批量的数据(比如一个图片编辑软件向你请求一个图片资源,一个文档编辑器向你请求一个文档)你将需要分享文件。
△服务器与客户端:我们将请求文件的应用称为客户端,将对外提供数据的应用称为服务器。
△这里所说的文件分享,不是一个应用程序主动分享文件出去(这个功能有其他的实现方式),而是当有应用想要请求文件,你的应用可以将相应的文件提供出去。比如一个社交软件准备发送一张图片,社交软件就相当于是客户端,系统内置那个图库就相当于是服务器,社交软件向图库请求一个图片文件。
服务器的具体步骤如下
配置FileProvider
△想将文件分享出去,首先需要定义一个文件分享者:FileProvider,步骤如下:在配置文件里面增加provider标签:<provider></provider>,这个标签需要指定如下信息:
→指定FileProvider的类。
→指定“authority”属性,通常该属性值一般会是:”包名.fileProvider”。,当FileProvider为你的文件生成URIs时,authorities就是URIs的URI字段内容。
→一个xml文件,你的应用只能将指定的某些路径下的文件分享出去,这些路径由你通过这个XML文件指定。
注:
→xml文件由meta-data子元素来指定。
<provider
android:name="android.support.v4.content.FileProvider"
android.authorities="com.test.fileprovider.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:name="@xml/filepaths"/>
</provider>
我当前的包名是:com.test.fileprovider。
△创建上一步所说的XML文件
→在res目录下面新建文件夹,取名xml,在文件夹新建一个文件,取名:filepaths(你可以取你喜欢的名字),
这是按照上图当中resource内容创建。
→xml文件内容:
<?xml version="1.0" encoding="utf-8"?>
<path>
<!--paths表示你要分享哪个路径下的文件-->
<files-path path="images/" name="myimages"/>
</path>
解析:
→到此为止,你已经拥有了一个完整的FileProvider。他配置了authority,你想要分享的文件所在路径(或者说是你的应用程序能够分享出去的文件的路径)。
→现在,xml文件指定了如下路径:/data/data/包名/files/images。其实就是你的应用调用方法:getFilesDir()所返回的路径下的images子路径:files/images/。
→name属性内容与文件的路径没有关系,他是你想要在你的URI里面插入的一个字段,如果你在files/images/路径下面有个文件名叫:myImage.jpg。那么fileprovider将会返回:
content://com.test.fileprovider.fileprovider/myimages/myImage.jpg。我们看到,最终的URI里面没有包含images子路径名,而且包含了一个你想插入的name。name属性其实就是为了隐藏真实的路径的。
→所以最终URI是:content://你在清单文件里面所指定的“authority”属性/你的xml文件里指定name属性/你的文件的全名。
→注:在<paths></paths>标签里面你还可以添加多个元素:<cache-path>(对应路径:/data/data/包名/cache)或者<external-path>(对应你的应用程序在SD卡上的默认路径)。
设置一个文件展示以及选择用的窗体
△当你配置了FileProvider,且当有应用向你请求文件时,你需提供一个窗体,该窗体能实现如下功能:(三步)
→可以被请求文件的应用唤起(就是能被隐式Intent唤起)。
→将自己能够分享的文件显示在窗体里(你可以用ListView实现,安卓官方教程也以这个控件作为例子实现)。
→当用户点击了某个文件,该窗体能提供这个文件的URL(就是将URL返回给调用他的窗体)。
△第一步:配置清单文件的过滤器
<activity
android:name="com.test.fileprovider.ShowFile">
<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>
△第二步:通过配置ListView实现(我的代码将分享的路径:手机内存/files/images/,至于如何在内存里创建一个images文件夹,我在这里不再重复)
package com.test.fileprovider;
import java.io.File;
public class ShowFile extends Activity
{
//创建将用到得变量
//这些变量主要用于显示一个将分享的文件列表
private File filesDir;//他将指向内存路径files
private File imageDir;//指向images子路径的:files/images
private File[] imageFiles;//images子路径的所有文件
private String[] imageFileNames;//images子路径的所有文件名字
private ListView myFileListView = null;
private Uri fileUri = null;
Intent resultIntent = null;
//对变量进行初始化
//将文件的名字装填
@Override
protected void onCreate(bundle savedInstanceState){
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.file_select_activity);
//获取ListView
myFileListView = (ListView)findViewById(R.id.lv_file);
//设置Intent
resultIntent = new Intent("com.test.fileprovider.ACTION_RETURN_FILE");
//获取文件路径以及名字
filesDir = getFilesDir();
imageDir = new File(filesDir,"images");
imageFiles = imageDir.listFiles();//获取images子路径下所有文件
//将文件的名字装填
imageFileNames = new String[imageFiles.length];
for(int i = 0; i<imageFiles.length; i++){
imageFileNames[i] = imageFiles[i].getName();
}
setResult(Activity.RESULT_CANCELED, null);//设置一个默认的返回值
/*
通过ListView将文件的名字显示出来
同时设置ListView响应用户对文件的选择
*/
//将文件名显示出来
myFileListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,imageFileNames));
//监听
myFileListView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent,View view, int position, long id){
// TODO Auto-generated method stub
//记录哪个文件被选中了
File requestFile = new File(imageDir,imageFileNames[position]);
try{
//获取文件的URI
fileUri = FileProvider.getUriForFile(ShowFile.this,"com.test.fileprovider.fileprovider",requestFile);
}catch(Exception e){
// TODO: handle exception
}
/*
第三步:当用户选择了某个文件,而且服务器成功地得到文件的URI,将URI返回给客户端。
*/
//如果成功得到了URI
if(fileUri!=null){
//为RUI添加访问权限
resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//设置数据以及数据类型
resultIntent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
ShowFile.this,setResult(RESULT_OK,resultIntent);
}else {
resultIntent.setDataAndtype(null, "");
ShowFile.this.setResult(RESULT_CANCELED,)
}
}//OnItemClick方法体
});//setOnItemClickListener
}//onCreate方法
//这是一个点击事件响应函数
//当用户已选择完毕
//提供一个按钮按下那时退出窗体
public void finished(View view){
finish();
}
}
现在,服务器的设置已经完成,当有应用需要请求文件,你将可以将你自己的文件给提供出去
△总结:到底服务器都做了什么
→配置FileProvider,指定authorities,指定可分享文件的那个路径(通过配置xml文件)。
→配置<intent-filter>,使他能被客户端的隐式Intent唤起。
→配置ListView,当服务器被唤起时,提供一个可被分享的文件的列表。
→配置点击事件响应,当客户端选择文件,生成文件的URI,同时提供相关权限,并且将该文件的URI给客户端(setResult()方法)。
客户端
△概述:客户端即请求获取某些文件的应用程序。
△客户端要做些什么:
→配置Intent,指定客户端想请求访问什么文件
(1)intent.setAction(Intent.ACTION_PICK);
(2)intent.setType(“image/*”);
→启动访问:当用户想访问某些文件,一般情况下,应当是在某个按钮点击事件响应函数里面执行:执行方法: startActivityForResult(intent, 0);
→当服务器提供了想要的文件,客户端要处理自己所得到的文件。
onActivityResult(int requestCode, int resultCode, Intent returnIntent);
方法里面,returnIntent参数,他带着从服务器里返回来的文件URL,通过他来打开我们想访问的文件
△第一步:配置Intent
//下面哪些TextView用来显示文件信息
//只是方便我调试时使用
import java.io.FileDescriptor;
public class MainActivity extends Activity
{
private Intent requestIntent = null;
private ParcelFileDescriptor mInputPFD = null;
//这些TextView都是用来显示文件相关信息
private TextView filesUriTextView = null;
private TextView fileNameTextView = null;
private TextView fileSizeTextView = null;
private TextView fileTypeTextView = null;
@Override
protected void onCreate(Bundle saveInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.main);
requestIntent = new Intent(Intent.ACTION_PICK);
requestIntent.setType("image/jpg");
//当获取到文件的URI
//使用TextView将文件的相关信息显示出来
filesUriTextView = (TextView)findViewById(R.id.tv_files_uri);
fileNameTextView = (TextView)findViewById(R.id.tv_file_name);
fileSizeTextView = (TextView)findViewById(R.id.tv_files_size);
fileTypeTextView = (TextView)findViewById(R.id.tv_files_type);
}
/*第二部:启动访问*/
//点击事件响应函数
public void getsFile(View view){
startActivityForResult(requestIntent, 0);
}
/*第三步:客户端通过RUI处理所得到的文件,以及获取文件相关信息*/
@override
protected void onActivityResult(int requestCode,int resultCode,Intent returnIntent){
// TODO Auto-generated method stub
super.onActivityResult(requestCode,resultCode,returnIntent);
//如果不是正常返回
if(resultCode!=RESULT_OK){
return;
}
else{
Uri returnUri = returnIntent.getData();
//这个方法是自己写来的
//用它来获取文件的相关信息
getFileInfos(returnUri);
try{
}catch(FileNotFoundException e){
e.printStackTrace();
return;
}
FileDescriptor fd = mInputPFD.getFileDescriptor();
}//if
}
//getFileInfos方法
private void getFileInfos(Uri uri){
filesUriTextView.setText("uri:"+uri);
fileTypeTextView.setText("文件类型:"+getContentResolver.getType(uri));
Cursor returnCursor = getContentResolver().query(uri,null,null,null,null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
fileNameTextView.setText("文件名字:"+returnCursor.getString(nameIndex));
fileSizeTextView.setText("尺寸:"+returnCursor.getLong(sizeIndex));
}
}
解析:当服务器窗体退出之后,客户端的onActivityResult()将被调用。returnIntent参数,就是服务器返回的Intent。Intent包含了文件URI,URI里包含文件各种信息。openFileDescriptor()方法返回ParcelFileDesctiptor,通过ParcelFileDescriptor实例,可以得到FileDescriptor对象,客户端可以通过”FileDescriptor”对象读取那个文件。
/*
到此为止你已经得到了一个客户端的程序
*/
△总结:客户端到底做了什么事
→配置一个启动服务器的Intent
→调用方法启动访问
→当得到了服务器的返回信息,处理信息。