本文译自:http://developer.android.com/training/beam-files/receive-files.html
Android的Beam文件传输器把文件复制到接收设备的制定目录中。它还会使用Android媒体扫描器来扫描复制的文件,并给MediaStore提供器添加媒体文件条目。本文介绍如何响应文件复制完成的动作,以及如何在接收的设备上定位被复制的文件。
响应请求显示数据
当Android的Beam文件传输器把文件复制到接收设备上时,它会发送一个通知,其中包含ACTION_VIEW类型操作的Intent对象、被传输的第一个文件的MIME类型,以及指向第一个文件的URI。当用户点击这个通知时,这个Intent对象就会发送给系统。要让你的应用程序响应这个Intent请求,就要在清单中响应该Intent请求的Activity的<activity>元素中添加<intent-filter>元素。在<intent-filter>元素中,添加以下子元素:
<actionandroid:name=”android.intent.action.VIEW” />
与发送的ACTION_VIEW类型的Intent通知相匹配。
<categoryandroid:name=”android.intent.category.CATEGORY_DEFAULT” />
与没有明确类别的Intent对象相匹配。
<dataandroid:mimeType=”mime-type” />
匹配一个MIME类型。指定你的应用程序能够处理的那些MIME类型。
例如,下例代码片段显示了如何添加一个触发com.example.android.nfctransfer.ViewActivity的Intent过滤器:
<activity
android:name="com.example.android.nfctransfer.ViewActivity"
android:label="Android Beam Viewer" >
...
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
...
</intent-filter>
</activity>
注意:Android的Beam文件传输器不仅限于从源设备上发送一个ACTION_VIEW类型操作的Intent对象。在接收设备上的其他应用程序也能够发送带有这种操作的Intent对象。在“从内容URI中获取目录”一节中,我们会讨论这种情况的处理。
申请文件权限
要读取Android的Beam文件传输器复制到设备上的文件,就要申请READ_EXTERNAL_STORAGE权限。例如:
<uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"/>
如果你想要把被传输的文件复制到你自己的存储区域,就要申请WRITE_EXTERNAL_STORAGE权限。这个权限包含了READ_EXTERNAL_STORAGE权限。
注意:在Android4.2.2(API Level 17)之前,用户可以选择是否执行READ_EXTERNAL_STORAGE权限。未来的平台版本可能要在任何情况中都要求这个权限,所以为了确保兼容性,在它成为必要之前就申请这个权限。
由于你的应用程序有控制内部存储区域的权限,因此当要在内部存储区域中复制被传输的文件时,不需要申请写权限。
获取被复制文件的目录
Android的Beam文件传输器用一个单独的传输器把所有的文件复制到接收设备上的一个目录中。由Android的Beam文件传输器发送的内容Intent通知中的URI指向了第一个被传输的文件。但是,你的应用程序也可能接收到来自其他源设备的Android的Beam文件传输器以外的ACTION_VIEW操作。要决定如何处理接收到的Intent,需要检查它的方案和权限。
调用Uri.getScheme()方法来获取URI方案。下列代码片段显示了如何判断方案,并处理相应的URI:
publicclassMainActivityextendsActivity{
...
// A File objectcontaining the path to the transferred files
private File mParentPath;
// Incoming Intent
private Intent mIntent;
...
/*
* Called from onNewIntent() for aSINGLE_TOP Activity
* or onCreate() for a new Activity. ForonNewIntent(),
* remember to call setIntent() to store themost
* current Intent
*
*/
private void handleViewIntent() {
...
// Get theIntent action
mIntent = getIntent();
String action = mIntent.getAction();
/*
* For ACTION_VIEW, theActivity is being asked to display data.
* Get the URI.
*/
if (TextUtils.equals(action, Intent.ACTION_VIEW)) {
// Get the URI from the Intent
Uri beamUri = mIntent.getData();
/*
* Test for thetype of URI, by getting its scheme value
*/
if (TextUtils.equals(beamUri.getScheme(), "file")) {
mParentPath = handleFileUri(beamUri);
} else if (TextUtils.equals(
beamUri.getScheme(), "content")) {
mParentPath = handleContentUri(beamUri);
}
}
...
}
...
}
从文件的URI中获取目录
如果输入的Intent中包含了一个文件的URI,那么该URI中包含了完整的文件路径和文件名称。对于Android的Beam文件传输器,文件路径也会指明其他的被传输文件的位置(如果有的话)。以下是获取被复制文件所在路径的方法:
...
public String handleFileUri(Uri beamUri) {
// Get thepath part of the URI
String fileName = beamUri.getPath();
// Create aFile object for this filename
File copiedFile = new File(fileName);
// Get astring containing the file's parent directory
return copiedFile.getParent();
}
...
从内容的URI中获取目录
如果收到的Intent对象包含一个内容URI,那么该Uri可能指向一个目录和保存在MediaStore的内容提供器中的文件名。你可以通过测试URI的权限值来检查用于MediaStore的内容URI。用于MediaStore的内容URI可以来自Android的Beam文件传输器,也可以来自另一个应用程序,在这两种情况中,都可以获取用于内容URI的目录和文件名称。
你还可以接收一个包含内容URI的ACTION_VIEW操作类型的Intent对象,它用于内容提供器而不是MediaStore。这种情况下,该内容URI不包含MediaStore的权限值,并且该内容URI通常不指向一个目录。
注意:对于Android的Beam文件传输器,你收到的ACTION_VIEW操作类型的Intent中的内容URI如果第一个输入文件有”audio/*”、”image/*”、或”video/*”的MIME类型,就说明该文件与媒体内容相关。Android的Beam文件传输器会通过在存储被传输文件的目录上运行媒体扫描器,来索引它要传输的媒体文件。媒体扫描器把扫描的结果写入MediaStore的内容提供器,然后它把第一个文件的内容URI返回给Android的Beam文件传输器。这个内容URI是你收到的Intent通知中的内容之一。要获取该目录中的第一个文件,就要从使用该内容URI的MediaStore中来获取。
判断内容提供器
要判断是否可以从内容提供器中获取文件目录,就要通过调用Uri.getAuthority()方法来获取与该内容提供器关联的URI权限。它的结果又两种可能:
MediaStore.AUTHORITY
这种类型的URI被MediaStore用来跟踪文件。从MediaStore中获取完整的文件名称和目录名称。
任何其他权限值
这种类型的URI来自另一个内容提供器。可以显示与该内容URI关联的数据,但不能获取该文件目录。
要获取用于MediaStore内容URI的目录,就要使用Uri参数和列映射(MediaColumns.DATA)在指定的输入的内容URI上运行一个查询。该查询会返回代表该URI的包含完整路径和文件名称的游标(Cursor)。返回的路径中还包含了Android的Beam文件传输器刚复制到该设备中的其他文件。
下例代码片段展示了如何检查内容URI的权限,以及如何获取被传输的文件的路径和文件名称:
...
public StringhandleContentUri(Uri beamUri) {
// Positionof the filename in the query Cursor
int filenameIndex;
// Fileobject for the filename
File copiedFile;
// Thefilename stored in MediaStore
String fileName;
// Test theauthority of the URI
if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) {
/*
* Handlecontent URIs for other content providers
*/
// For aMediaStore content URI
} else {
// Get the column that contains the file name
String[] projection = { MediaStore.MediaColumns.DATA };
Cursor pathCursor =
getContentResolver().query(beamUri, projection,
null, null, null);
// Check for a valid cursor
if (pathCursor != null &&
pathCursor.moveToFirst()) {
// Get the column index in the Cursor
filenameIndex = pathCursor.getColumnIndex(
MediaStore.MediaColumns.DATA);
// Get the full file name including path
fileName = pathCursor.getString(filenameIndex);
// Create a File object for the filename
copiedFile = new File(fileName);
// Return the parent directory of the file
return new File(copiedFile.getParent());
} else {
// The query didn't work; return null
return null;
}
}
}
...
要学习更多的有关从内容提供器中获取数据的方法,请看“从提供器中获取数据”。