通过 FileProvider 来设置文件共享, FileProvider 是在 v4 的支持库中的。
指定 FileProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
在 manifest 清单文件中添加 provider 节点元素,指定 FileProvider 类。
android:authorities 属性:指定权威的 URI 通过 FileProvider 来生成你想要的 URIs 的内容。
一般情况 android:authorities 的组成: 包名 + fileprovider 来组成的
< meta-data > 里的 android:name 属性:指向指定要共享的目录的XML文件
< meta-data > 里的 android:resource 属性:是文件的路径和名称
指定可共享目录
在 res/xml/ 目录下创建 filepaths.xml 文件
<paths>
<files-path path="images/" name="myimages" />
</paths>
< files-path > 标签分享的目录是在应用程序的内部存储的 files/ 目录
path 属性:共享 files/ 下的 images/ 子目录
name 属性:告诉 FileProvider 在 files/images/ 子目录下添加路径段 myimages 内容的URI中的文件
注意: XML 文件是唯一可以指定要共享的目录,不能以编程方式添加目录。
content://com.example.myapp.fileprovider/myimages/default_image.jpg
通过 startActivityForResult() 中的 intent 包含着 ACTION_PICK 来传递数据。
设置一个可以选择的 Activity,意图过滤器要匹配 ACTION_PICK,类别是 CATEGORY_DEFAULT 和 CATEGORY_OPENABLE,也可以选择指定 MIME Type。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<application>
...
<activity
android:name=".FileSelectActivity"
android:label="@"File Selector" > <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>
定义选择 Activity 的返回 code
public class MainActivity extends Activity {
// The path to the root of this app's internal storage
private File mPrivateRootDir;
// The path to the "images" subdirectory
private File mImagesDir;
// Array of files in the images subdirectory
File[] mImageFiles;
// Array of filenames corresponding to mImageFiles
String[] mImageFilenames;
// Initialize the Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Set up an Intent to send back to apps that request a file
mResultIntent =
new Intent("com.example.myapp.ACTION_RETURN_FILE");
// Get the files/ subdirectory of internal storage
mPrivateRootDir = getFilesDir();
// Get the files/images subdirectory;
mImagesDir = new File(mPrivateRootDir, "images");
// Get the files in the images subdirectory
mImageFiles = mImagesDir.listFiles();
// Set the Activity's result to null to begin with
setResult(Activity.RESULT_CANCELED, null);
/* * Display the file names in the ListView mFileListView. * Back the ListView with the array mImageFilenames, which * you can create by iterating through mImageFiles and * calling File.getAbsolutePath() for each File */
...
}
...
}
当你访问客户端程序的共享文件时,需要授予权限。这个权限是暂时的,当接收应用程序的任务堆栈完成后自动失效。
// 临时授予读取权限到内容URI
mResultIntent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION );
注意:调用setFlags()是安全地访问权限授予使用临时访问权限文件的唯一途径。避免调用 Context.grantUriPermission()的文件的内容URI的方法,因为这种方法授予访问,你只能通过调用撤销Context.revokeUriPermission() 。
通过 startActivityForResult() 来请求,然后通过 onActivityResult()来处理请求结果。
public class MainActivity extends Activity {
private Intent mRequestFileIntent ;
private ParcelFileDescriptor mInputPFD ;
...
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState );
setContentView ( R . layout . activity_main );
mRequestFileIntent = new Intent ( Intent . ACTION_PICK );
mRequestFileIntent . setType ( "image/jpg" );
...
}
...
protected void requestFile () {
/**
*当用户请求一个文件时,发送意向到
*服务器应用程序。
*文件。
* /
startActivityForResult (mRequestFileIntent , 0 );
...
}
...
}
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent returnIntent) {
// If the selection didn't work if (resultCode != RESULT_OK) { // Exit without doing anything else return; } else { // Get the file's content URI from the incoming Intent
Uri returnUri = returnIntent.getData();
/*
* Try to open the file for "read" access using the
* returned URI. If the file isn't found, write to the * error log and return. */ try { /* * Get the content resolver instance for this context, and use it * to get a ParcelFileDescriptor for the file. */ mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r"); } catch (FileNotFoundException e) { e.printStackTrace(); Log.e("MainActivity", "File not found."); return; } // Get a regular file descriptor for the file FileDescriptor fd = mInputPFD.getFileDescriptor(); ... } }
该方法openFileDescriptor() 返回一个ParcelFileDescriptor该文件。从这个对象,客户端应用程序获取一个的FileDescriptor对象,它可以再使用读取文件。
判断文件的 MIME Type 类型
/*
* Get the file's content URI from the incoming Intent, then
* get the file's MIME type
*/
Uri returnUri = returnIntent.getData();
String mimeType = getContentResolver().getType(returnUri);
查询文件的名称和大小, FileProvider 有个 query() 可以查询出文件的相关信息。
DISPLAY_NAME:返回 String 类型。文件名称。
SIZE:返回 Long 类型。文件大小。
/*
* Get the file's content URI from the incoming Intent,
* then query the server app to get the file's display name
* and size.
*/
Uri returnUri = returnIntent.getData();
Cursor returnCursor =
getContentResolver().query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* move to the first row in the Cursor, get the data,
* and display it.
*/
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
TextView nameView = (TextView) findViewById(R.id.filename_text);
TextView sizeView = (TextView) findViewById(R.id.filesize_text);
nameView.setText(returnCursor.getString(nameIndex));
sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex)));