将应用设置为使用内容 URI 共享文件后,您便可以响应其他应用对这些文件的请求。有一种方法可以响应这些请求,那就是从服务器应用提供一个其他应用可以调用的文件选择界面。通过这种方法,客户端应用可让用户从服务器应用选择文件,然后获得所选文件的内容 URI。
本课程介绍了如何在应用中创建响应文件请求的文件选择
接收文件请求
如需接收来自客户端应用的文件请求并使用内容 URI 进行响应,您的应用应提供一个文件选择
如需了解如何在客户端应用中实现文件请求,请参阅请求共享文件一课。
创建文件选择 Activity
如需设置文件选择
...
...
android:name=".FileSelectActivity"
android:label="@File Selector" >
android:name="android.intent.action.PICK"/>
android:name="android.intent.category.DEFAULT"/>
android:name="android.intent.category.OPENABLE"/>
在代码中定义文件选择 Activity
接下来,定义一个 files/images/ 目录中的可用文件,并允许用户选取所需的文件。以下代码段说明了如何定义此
Kotlin
class MainActivity : Activity() {
// The path to the root of this app's internal storage
private lateinit var privateRootDir: File
// The path to the "images" subdirectory
private lateinit var imagesDir: File
// Array of files in the images subdirectory
private lateinit var imageFiles: Array
// Array of filenames corresponding to imageFiles
private lateinit var imageFilenames: Array
// Initialize the Activity
override fun onCreate(savedInstanceState: Bundle?) {
...
// Set up an Intent to send back to apps that request a file
resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE")
// Get the files/ subdirectory of internal storage
privateRootDir = filesDir
// Get the files/images subdirectory;
imagesDir = File(privateRootDir, "images")
// Get the files in the images subdirectory
imageFiles = imagesDir.listFiles()
// Set the Activity's result to null to begin with
setResult(Activity.RESULT_CANCELED, null)
/*
* Display the file names in the ListView fileListView.
* Back the ListView with the array imageFilenames, which
* you can create by iterating through imageFiles and
* calling File.getAbsolutePath() for each File
*/
...
}
...
}Java
public class MainActivity extends Activity {
// The path to the root of this app's internal storage
private File privateRootDir;
// The path to the "images" subdirectory
private File imagesDir;
// Array of files in the images subdirectory
File[] imageFiles;
// Array of filenames corresponding to imageFiles
String[] imageFilenames;
// Initialize the Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Set up an Intent to send back to apps that request a file
resultIntent =
new Intent("com.example.myapp.ACTION_RETURN_FILE");
// Get the files/ subdirectory of internal storage
privateRootDir = getFilesDir();
// Get the files/images subdirectory;
imagesDir = new File(privateRootDir, "images");
// Get the files in the images subdirectory
imageFiles = imagesDir.listFiles();
// Set the Activity's result to null to begin with
setResult(Activity.RESULT_CANCELED, null);
/*
* Display the file names in the ListView fileListView.
* Back the ListView with the array imageFilenames, which
* you can create by iterating through imageFiles and
* calling File.getAbsolutePath() for each File
*/
...
}
...
}
响应文件选择
用户选择共享文件后,您的应用必须确定用户选择了哪个文件,然后为该文件生成内容 URI。
在使用 intent 将文件的 URI 从一个应用发送到另一个应用时,您必须谨慎获取其他应用可以读取的 URI。在搭载 Android 6.0(API 级别 23)及更高版本的设备上执行此操作需要特别小心,因为此 Android 版本中的权限模型发生了变化,特别是 危险的权限,接收方应用可能没有该权限。
考虑到这些因素,我们建议您避免使用
不允许跨配置文件共享文件。
要求您的应用在搭载 Android 4.4(API 级别 19)或更低版本的设备上拥有
要求接收方应用拥有
您可以不使用 URI 权限来授予其他应用对特定 URI 的访问权限。虽然 URI 权限不适用于file:// URI,但它们适用于与内容提供程序关联的 URI。
在 指定可共享目录一节。
以下代码段展示了如何检测所选文件并为其生成内容 URI:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
...
// Define a listener that responds to clicks on a file in the ListView
fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
/*
* Get a File for the selected file name.
* Assume that the file names are in the
* imageFilename array.
*/
val requestFile = File(imageFilenames[position])
/*
* Most file-related method calls need to be in
* try-catch blocks.
*/
// Use the FileProvider to get a content URI
val fileUri: Uri? = try {
FileProvider.getUriForFile(
this@MainActivity,
"com.example.myapp.fileprovider",
requestFile)
} catch (e: IllegalArgumentException) {
Log.e("File Selector",
"The selected file can't be shared: $requestFile")
null
}
...
}
...
}Java
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks on a file in the ListView
fileListView.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
* imageFilename array.
*/
File requestFile = new File(imageFilename[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: " + requestFile.toString());
}
...
}
});
...
}
请记住,您只能为特定的文件生成内容 URI,该文件需要位于包含 元素的元数据文件所指定的目录中。如需了解如何指定该目录,请参阅指定可共享目录一节。如果您针对未指定的路径中的
授予文件访问权限
您已经获得了要与其他应用共享的文件的内容 URI,现在要做的就是允许客户端应用访问该文件。如需允许访问,您需要向客户端应用授予访问权限,方法是将该内容 URI 添加到
以下代码段展示了如何设置文件的读取权限:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
...
// Define a listener that responds to clicks on a file in the ListView
fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
...
if (fileUri != null) {
// Grant temporary read permission to the content URI
resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
...
}
...
}
...
}Java
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks in the ListView
fileListView.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
resultIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
...
}
...
});
...
}
注意:调用
请勿使用 文件共享中所述),而不是仅针对此实现。
与请求方应用共享文件
如需与请求文件的应用共享文件,请将包含内容 URI 和权限的
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
...
// Define a listener that responds to clicks on a file in the ListView
fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
...
if (fileUri != null) {
...
// Put the Uri and MIME type in the result Intent
resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri))
// Set the result
setResult(Activity.RESULT_OK, resultIntent)
} else {
resultIntent.setDataAndType(null, "")
setResult(RESULT_CANCELED, resultIntent)
}
}
}Java
protected void onCreate(Bundle savedInstanceState) {
...
// Define a listener that responds to clicks on a file in the ListView
fileListView.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
resultIntent.setDataAndType(
fileUri,
getContentResolver().getType(fileUri));
// Set the result
MainActivity.this.setResult(Activity.RESULT_OK,
resultIntent);
} else {
resultIntent.setDataAndType(null, "");
MainActivity.this.setResult(RESULT_CANCELED,
resultIntent);
}
}
});
为用户提供在选择文件后立即返回到客户端应用的方法。一种方式是提供复选标记或完成按钮。使用按钮的
Kotlin
fun onDoneClick(v: View) {
// Associate a method with the Done button
finish()
}Java
public void onDoneClick(View v) {
// Associate a method with the Done button
finish();
}
如需了解其他相关信息,请参阅: