W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
編寫(xiě):jdneo - 原文:http://developer.android.com/training/secure-file-sharing/sharing-file.html
對(duì)應(yīng)用程序進(jìn)行配置,使得它可以使用Content URI來(lái)共享文件后,其就可以響應(yīng)其他應(yīng)用程序的獲取文件的請(qǐng)求了。一種響應(yīng)這些請(qǐng)求的方法是在服務(wù)端應(yīng)用程序提供一個(gè)可以由其他應(yīng)用激活的文件選擇接口。該方法可以允許客戶端應(yīng)用程序讓用戶從服務(wù)端應(yīng)用程序選擇一個(gè)文件,然后接收這個(gè)文件的Content URI。
本課將會(huì)展示如何在應(yīng)用中創(chuàng)建一個(gè)用于選擇文件的Activity,來(lái)響應(yīng)這些獲取文件的請(qǐng)求。
為了從客戶端應(yīng)用程序接收一個(gè)文件獲取請(qǐng)求并以Content URI的形式進(jìn)行響應(yīng),我們的應(yīng)用程序應(yīng)該提供一個(gè)選擇文件的Activity。客戶端應(yīng)用程序通過(guò)調(diào)用startActivityForResult()方法啟動(dòng)這一Activity。該方法包含了一個(gè)具有ACTION_PICKAction的Intent參數(shù)。當(dāng)客戶端應(yīng)用程序調(diào)用了startActivityForResult(),我們的應(yīng)用可以向客戶端應(yīng)用程序返回一個(gè)結(jié)果,該結(jié)果即用戶所選擇的文件所對(duì)應(yīng)的Content URI。
關(guān)于如何在客戶端應(yīng)用程序?qū)崿F(xiàn)文件獲取請(qǐng)求,請(qǐng)參考:請(qǐng)求分享一個(gè)文件。
為建立一個(gè)選擇文件的Activity,首先需要在Manifest清單文件中定義Activity,在其Intent過(guò)濾器中,匹配ACTION_PICKAction及CATEGORY_DEFAULT和CATEGORY_OPENABLE這兩種Category。另外,還需要為應(yīng)用程序設(shè)置MIME類(lèi)型過(guò)濾器,來(lái)表明我們的應(yīng)用程序可以向其他應(yīng)用程序提供哪種類(lèi)型的文件。下面這段代碼展示了如何在清單文件中定義新的Activity和Intent過(guò)濾器:
<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>
下面,定義一個(gè)Activity子類(lèi),用于顯示在內(nèi)部存儲(chǔ)的“files/images/”目錄下可以獲得的文件,然后允許用戶選擇期望的文件。下面代碼展示了如何定義該Activity,并令其響應(yīng)用戶的選擇:
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
*/
...
}
...
}
一旦用戶選擇了一個(gè)被共享的文件,我們的應(yīng)用程序必須明確哪個(gè)文件被選擇了,并為該文件生成一個(gè)對(duì)應(yīng)的Content URI。如果我們的Activity在ListView中顯示了可獲得文件的清單,那么當(dāng)用戶點(diǎn)擊了一個(gè)文件名時(shí),系統(tǒng)會(huì)調(diào)用方法onItemClick(),在該方法中可以獲取被選擇的文件。
在onItemClick()中,根據(jù)被選中文件的文件名獲取一個(gè)File對(duì)象,然后將其作為參數(shù)傳遞給getUriForFile(),另外還需傳入的參數(shù)是在<provider>
標(biāo)簽中為FileProvider所指定的Authority,函數(shù)返回的Content URI包含了相應(yīng)的Authority,一個(gè)對(duì)應(yīng)于文件目錄的路徑標(biāo)記(如在XML meta-data中定義的),以及包含擴(kuò)展名的文件名。有關(guān)FileProvider如何基于XML meta-data將目錄路徑與路徑標(biāo)記進(jìn)行匹配的知識(shí),可以閱讀:指定可共享目錄路徑。
下例展示了如何檢測(cè)選中的文件并且獲得它的Content 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);
}
...
}
});
...
}
記住,我們能生成的那些Content URI所對(duì)應(yīng)的文件,必須是那些在meta-data文件中包含<paths>
標(biāo)簽的目錄內(nèi)的文件,這方面知識(shí)在Specify Sharable Directories中已經(jīng)討論過(guò)。如果調(diào)用getUriForFile()方法所要獲取的文件不在我們指定的目錄中,會(huì)收到一個(gè)IllegalArgumentException。
現(xiàn)在已經(jīng)有了想要共享給其他應(yīng)用程序的文件所對(duì)應(yīng)的Content URI,我們需要允許客戶端應(yīng)用程序訪問(wèn)這個(gè)文件。為了達(dá)到這一目的,可以通過(guò)將Content URI添加至一個(gè)Intent中,然后為該Intent設(shè)置權(quán)限標(biāo)記。所授予的權(quán)限是臨時(shí)的,并且當(dāng)接收文件的應(yīng)用程序的任務(wù)棧終止后,會(huì)自動(dòng)過(guò)期。
下例展示了如何為文件設(shè)置讀權(quán)限:
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);
}
...
}
...
});
...
}
Caution:調(diào)用setFlags()來(lái)為文件授予臨時(shí)被訪問(wèn)權(quán)限是唯一的安全的方法。盡量避免對(duì)文件的Content URI調(diào)用Context.grantUriPermission(),因?yàn)橥ㄟ^(guò)該方法授予的權(quán)限,只能通過(guò)調(diào)用Context.revokeUriPermission()來(lái)撤銷(xiāo)。
為了向請(qǐng)求文件的應(yīng)用程序提供其需要的文件,我們將包含了Content URI和相應(yīng)權(quán)限的Intent傳遞給setResult()。當(dāng)定義的Activity結(jié)束后,系統(tǒng)會(huì)把這個(gè)包含了Content URI的Intent傳遞給客戶端應(yīng)用程序。下例展示了其中的核心步驟:
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);
}
}
});
當(dāng)用戶選擇好文件后,我們應(yīng)該向用戶提供一個(gè)能夠立即回到客戶端應(yīng)用程序的方法。一種實(shí)現(xiàn)的方法是向用戶提供一個(gè)勾選框或者一個(gè)完成按鈕??梢允褂冒粹o的android:onClick屬性字段為它關(guān)聯(lián)一個(gè)方法。在該方法中,調(diào)用finish()。例如:
public void onDoneClick(View v) {
// Associate a method with the Done button
finish();
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: