将应用设置为使用内容URI共享文件后,您可以响应其他应用对这些文件的请求。响应这些请求的一种方式是从服务器应用提供其他应用可以调用的文件选择接口。这种方法允许客户端应用程序让用户从服务器应用程序中选择一个文件,然后接收所选文件的内容URI。
本课将向您展示如何在应用程序中创建响应文件请求的文件选择Activity。
接收文件请求
要从客户端应用程序接收文件请求并使用内容URI进行回复,应用程序应提供文件选择Activity。客户端应用程序通过使用包含操作ACTION_PICK的Intent调用startActivityForResult()来启动此Activity。当客户端应用程序调用startActivityForResult()时,您的应用程序可以以用户选择的文件的内容URI的形式向客户端应用程序返回结果。
创建文件选择Activity
要设置文件选择Activity,请先在清单中指定Activity,以及与操作ACTION_PICK和类别CATEGORY_DEFAULT和CATEGORY_OPENABLE匹配的intent过滤器。还要为您的应用为其他应用提供的文件添加MIME类型过滤器。以下代码段显示如何指定新的Activity和intent过滤器:
<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
接下来,定义一个Activity子类,显示应用程序在内部存储中的files / images /目录中可用的文件,并允许用户选择所需的文件。以下代码段演示了如何定义此Activity并响应用户的选择:
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。由于Activity显示ListView中的可用文件列表,因此当用户单击文件名时,系统将调用onItemClick()方法,您可以在其中获取所选文件。
在onItemClick()中,获取所选文件的文件名的File对象,并将其作为参数传递给getUriForFile(),以及在FileProvider的元素中指定的权限。结果内容URI包含权限,对应于文件目录(如XML元数据中指定的)的路径段以及包括其扩展名的文件的名称。 FileProvider如何将目录映射到基于XML元数据的路径段。
以下代码段显示了如何检测所选文件并获取其内容URI:
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks on a file in the ListView
mFileListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
/*
* When a filename in the ListView is clicked, get its
* content URI and send it to the requesting app
*/
public void onItemClick(AdapterView<?> adapterView,
View view,
int position,
long rowId) {
/*
* Get a File for the selected file name.
* Assume that the file names are in the
* mImageFilename array.
*/
File requestFile = new File(mImageFilename[position]);
/*
* Most file-related method calls need to be in
* try-catch blocks.
*/
// Use the FileProvider to get a content URI
try {
fileUri = FileProvider.getUriForFile(
MainActivity.this,
"com.example.myapp.fileprovider",
requestFile);
} catch (IllegalArgumentException e) {
Log.e("File Selector",
"The selected file can't be shared: " +
clickedFilename);
}
...
}
});
...
}
请记住,您只能为驻留在您在元数据文件中指定的包含
<paths>
元素的目录中的文件生成内容URI,如上一节中所述。如果对未指定的路径中的文件调用getUriForFile(),则会收到IllegalArgumentException。
授予文件权限
现在您要与其他应用程式共用的档案有内容URI,您必须允许客户端应用程式存取该档案。要允许访问,请通过将内容URI添加到Intent,然后在Intent上设置权限标志来向客户端应用程序授予权限。您授予的权限是临时的,并在接收应用程序的任务堆栈完成时自动过期。
以下代码段说明如何为文件设置读取权限:
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks in the ListView
mFileListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView,
View view,
int position,
long rowId) {
...
if (fileUri != null) {
// Grant temporary read permission to the content URI
mResultIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
...
}
...
});
...
}
警告:调用setFlags()是使用临时访问权限安全授予对文件的访问权限的唯一方法。避免为文件的内容URI调用Context.grantUriPermission()方法,因为此方法授予您只能通过调用Context.revokeUriPermission()撤消的访问。
与请求应用程序共享文件
要与请求它的应用程序共享文件,请将包含内容URI和权限的Intent传递给setResult()。当刚刚定义的Activity完成时,系统将包含内容URI的Intent发送到客户端应用程序。以下代码段显示了如何执行此操作:
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks on a file in the ListView
mFileListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView,
View view,
int position,
long rowId) {
...
if (fileUri != null) {
...
// Put the Uri and MIME type in the result Intent
mResultIntent.setDataAndType(
fileUri,
getContentResolver().getType(fileUri));
// Set the result
MainActivity.this.setResult(Activity.RESULT_OK,
mResultIntent);
} else {
mResultIntent.setDataAndType(null, "");
MainActivity.this.setResult(RESULT_CANCELED,
mResultIntent);
}
}
});
为用户提供一种在选择文件后立即返回到客户端应用程序的方法。一种方法是提供复选标记或完成按钮。使用按钮的android:onClick属性将方法与按钮相关联。在方法中,调用finish()。例如:
public void onDoneClick(View v) {
// Associate a method with the Done button
finish();
}