本课程教你如何产生一个在app和设备间共享数据的app。
Sharing Simple Data
Sharing Files
Sharing Files with NFC
Sharing Simple Data
Android应用最伟大之处之一就是不同应用间的通信和整合的能力。如果一个功能在其他的app里已经实现了,而这些功能并不是你的app的“主业”,为什么自己再重新实现这些功能呢?
本文讲述几种方式用来在不同的应用之间使用Intent API和ActionProvider
对象发送和接收简单数据。
Lessons
Sending Simple Data to Other Apps
学习如何通过Intent从你的application向其他的application发送文本和二进制数据
Receiving Simple Data from Other Apps
学习如何在你的application里获取从intent里传入的文本和二进制数据
学习如何在你的Action Bar里添加“share” action 条目
Sending Simple Data to Other Apps
当你构建一个intent时,你必须指定你想要该intent触发的action。Android定义了许多action,例如ACTION_SEND,该action表明intent从一个activity向另一个activity传递数据,甚至在不同进程间传递数据。为了向另一个activity发送数据,你需要做的是指定数据和类型,系统会识别能处理该intent的所有activity,然后将这些activity展示给用户(当不止一个activity时)或者立即的开启一个activity(如果仅仅有一个能处理的activity时)。相似地,你能在你的manifest文件里定义数据类型以表明你的activity能接收和处理来自其他application的哪些intent。
在不同application之间使用intent来发送和接收数据是最常见和通用的内容共享方式。Intent能让用户很快很容易的共享信息。
注:在ActionBar里添加一个共享action item的最好的方式是使用ShareActionProvider,该类在API 14及以上版本中才可用。
Send Text Content
ACTION_SEND action最直接和最通用的方式是从一个activity到另一个activity发送文本内容。例如,内建浏览器app能共享当前展示页面的URL作为文本在不同app间传递和共享。这对于在朋友和社交网络间共享文章和站点是非常有效的。如下是实现这种类型共享的代码:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(sendIntent);
如果有一个已安装的应用,该应用有一个匹配ACTION_SEND和MIME类型为text/plain的过滤器,Android系统将运行和启动该应用。如果不止一个app匹配,系统展示多选对话框(“chooser”)来让用户选择一个app。
然而,如果你调用Intent.createChooser(),传递给你的intent对象,该调用将返回你的intent的版本,总是展示该chooser。这样有一些优点:
- 即使用户先前已选择了一个默认的ACTION来处理该intent,chooser仍然将会展示给用户。
- 如果没有匹配的app,Android展示系统消息。
- 你能为chooser对话框指定一个title。
下面是更新了的代码:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
结果对话框如下图一所示:
Figure 1. Screenshot of ACTION_SEND
intent chooser on a handset.
还有一些其他的标准的extras: EXTRA_EMAIL
, EXTRA_CC
, EXTRA_BCC
, EXTRA_SUBJECT,
如果接收application不使用它们,系统简单地忽略它们。
注:一些email应用,例如Gmail,需要一个字符串数组String[]
给extras,例如 EXTRA_EMAIL
和 EXTRA_CC
, 可以使用putExtra(String, String[])
加这些到你的intent。
Send Binary Content
共享二进制数据也是使用ACTION_SEND action,需要设置合适的MIME类型,和添加URI到extra的数据里,该URI名叫EXTRA_STREAM。
这通常用于共享图片数据,但也能共享任何类型的二进制内容。
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));
注意如下几点:
- 你能使用"*/*"作为MIME类型,但是这将仅仅匹配能处理一般数据流的activity。
- 响应app需要访问URi指向数据的权限。推荐如下方式实现:
- 在你自己的
ContentProvider
存储数据 ,确保其他的app有相应的权限访问你的provider。更好的提供的访问机制是使用per-URI permissions ,该权限是临时的,仅仅保证对响应app的访问。一个容易的方式是使用FileProvider帮助类产生一个ContentProvider。 - 使用系统
MediaStore.MediaStore
其主要目的是用于视频、音频和图片MIME类型,然而,从Android3.0(API11)开始,该类也用于存储非媒体类型(seeMediaStore.Files
for more info)的数据。文件。文件能通过scanFile()
插入MediaStore。在after which acontent://
styleUri
suitable for sharing is passed to the providedonScanCompleted()
callback。注意:一旦加入到系统MediaStore,设备上的任何app都能访问存储在MediaStore里的数据。
Send Multiple Pieces of Content
为了共享多部分内容,使用ACTION_SEND_MULTIPLE
action配合一组指向内容的URIs。MIME类型依据你共享的内容而定。例如,如果你共享3个JPEG图片,MIME类型仍然是”image/jpeg“.例如如果是各种格式的图片,类型应该是”image/*",如果你正共享的是各种类型的数据,你应该使用“*/*” 像先前描述的,这决定了哪些app解析和处理你的数据。下面是一个例子:
ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));
像先前所说的,确保提供的URIs指向的数据能被相应的app访问。
Receiving Simple Data from Other Apps
你的app能发送数据给其他的应用,当然,你的app也能接收来自其他app的数据。想想用户怎么和你的app交互,以及你想要从其他的app接收什么类型的数据。例如,一个社交app很可能需要接收文本内容,如来自于其他app的一个感兴趣的web URL数据。Google+ Android application既可以接收文本也可以接收图片数据。
Update Your Manifest
意图过滤器通知系统某个app的组件将接受什么意图。类似于Sending Simple Data to Other Apps这节讲的如何使用action ACTION_SEND
构造Intent,为了能接受intent,你能使用该action产生意图过滤器。你在你的manifest里使用<intent-filter>
元素定义意图过滤器。如果你的app想要处理文本类型、一种或者多种图片格式的数据,你的manifest应该如下:
<activity android:name=".ui.MyActivity" > <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND_MULTIPLE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> </activity>注:更多的关于意图过滤器和意图的介绍请看见 Intents and Intent Filters
当另一个app通过构造一个intent试图共享如上manfest定义的数据,然后传递该intent给startActivity()
时,你的app将在intent chooser里作为一个选项被列出。如果用户选择你的app,响应的activity(对应上面例子中的.ui.MyActivity)将被调起。然后由你确定在你的代码和UI里如何处理传递过来的数据。
Handle the Incoming Content
为了处理一个intent投递过来的数据,首页调用getIntent()
得到该Intent对象。一旦你有了该对象,你能检查它里面的内容以决定接下来如何做。记住,如果activity能被系统的其他部分(例如launcher)调起,那么当你检查intent时,你将需要考虑如下问题:
void onCreate (Bundle savedInstanceState) {
...
// Get intent, action and MIME type
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent); // Handle text being sent
} else if (type.startsWith("image/")) {
handleSendImage(intent); // Handle single image being sent
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
if (type.startsWith("image/")) {
handleSendMultipleImages(intent); // Handle multiple images being sent
}
} else {
// Handle other intents, such as being started from the home screen
}
...
}
void handleSendText(Intent intent) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
// Update UI to reflect text being shared
}
}
void handleSendImage(Intent intent) {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (imageUri != null) {
// Update UI to reflect image being shared
}
}
void handleSendMultipleImages(Intent intent) {
ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (imageUris != null) {
// Update UI to reflect multiple images being shared
}
}
注意:需要特别关注对传入的数据进行check。你不能知道一些其他的app将送你什么数据。例如,传递过来的intent里可能设置了错误的MIME类型,或者发送过来的image可能极其的大。还要记住,在单独的线程而不是主线程(UI线程)处理二进制数据。
有些UI更新可能很简单,像填充EditText一样,也有像更新图片这样的复杂的UI更新,收到数据后怎么处理和更新UI因app而异。
Adding an Easy Share Action
通过使用Android4.0(API 14)里介绍的ActionProvider能在ActionBar里容易的实现一个有效的、用户友好的共享action。ActionProvider,一旦附件到actionbar里的某天menu item,既能处理该item的展示也能该item的行为。以ShareActionProvider为例,你提供一个共享intent,剩下的事由它来做。
注:ShareActionProvider在API 14或者更高的版本才有效。
Update Menu Declarations
为了能使用ShareActionProvider,在你的menu resource 文件的相应<item>定义android: action ProviderClass属性:
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_item_share" android:showAsAction="ifRoom" android:title="Share" android:actionProviderClass= "android.widget.ShareActionProvider" /> ... </menu>这表示ShareActionProvider处理item的展现和功能。然而,你需要告诉provider你想要share什么。
Figure 1. TheShareActionProvider
in the Gallery app.
Set the Share Intent
为了ShareActionProvider有效,你必须提供share intent给它。这共享intent应该和Sending Simple Data to Other Apps节里描述的相似,action为ACTION_SEND,通过extras设置EXTRA_TEXT和EXTRA_STREAM额外的数据。为了指派共享intent,首先当在Activity或者Fragment里渲染你的menu资源时找到相应的MenuItem。接下来,调用MenuItem.getActionProvider()
方法检索ShareActionProvider实例。使用 setShareIntent()
更新相关联的action item的共享意图。如下是例子:
private ShareActionProvider mShareActionProvider;
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate menu resource file.
getMenuInflater().inflate(R.menu.share_menu, menu);
// Locate MenuItem with ShareActionProvider
MenuItem item = menu.findItem(R.id.menu_item_share);
// Fetch and store ShareActionProvider
mShareActionProvider = (ShareActionProvider) item.getActionProvider();
// Return true to display menu
return true;
}
// Call to update the share intent
private void setShareIntent(Intent shareIntent) {
if (mShareActionProvider != null) {
mShareActionProvider.setShareIntent(shareIntent);
}
}
你可能只需要在menu产生期间设置share action一次,或者你可能当UI改变时设置或者更新它。例如,当你在Gallery app里全屏浏览照片时,share action当你在照片间滑动时改变。
更多关于ShareActionProvider
对象的讨论,参见 Action Bar guide。