安卓下的文件分享——FileProvider


分享文件

△概述:如果你要对外提供少量数据,你可以用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

    →调用方法启动访问

    →当得到了服务器的返回信息,处理信息。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值