Android应用的一个重要的功能就是应用间可以相互交流和互相调用,自己的app只要做好自己核心的功能,其他非核心的功能如果存在可以直接调用的app就调用它们的,没必要重复造轮子.
本文将会告诉你一些通用的方法通过Intent和ActionProvider来进行应用间简单数据的发送和接收.(PS: 想了解更多关于Intent的介绍可以看之前的使用Intent来进行App间的基本交互).
1. 发送简单的数据给其他app
Intent中必须要带有action,在Android系统中定义了很多种action,其中有一种action可以用来在不同的activity之间甚至不同的进程之间进行数据传输,这个action就是ACTION_SEND.
1.1 发送文本内容
ACTION_SEND最直接和最常见的用法就是从一个activity发送文本内容给另外一个activity.例如内置的浏览器可以将当前页面的URL以文本的形式分享给任何的应用(可以匹配Intent的),这种方法对通过邮件或社交网络来分享文章或网页来说非常有用.如下实例:
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);
上面的代码有两点需要注意的:
- 启动隐式的intent的时候,记得要先验证是否有activity来接收,具体可以参考之前的使用Intent来进行App间的基本交互).
- 视情况选择是否使用chooser(候选框),时候候选框的好处是可以不用验证是否有activity来处理,没有会自动提醒;还有一个好处是可以自定义标题.
改进版的代码如下:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
// with chooser
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
注意:
- 上面intent的 putExtra() 中的第一个参数用的是Intent.EXTRA_TEXT,这些Intent中定义的一些标准的extra,其他的也有EXTRA_EMAIL,EXTRA_CC等等,具体Intent的各种标准的属性可以看Intent;
- intent的putExtra()中的第二个参数有的时候可能是string[],比如 EXTRA_EMAIL, EXTRA_CC等.
1.2 发送二进制的内容
二进制的数据可以用ACTION_SEND的action,合适的MIME类型以及EXTRA_STREAM和URI作为putExtra的两个参数来配合完成,这可以用来分享任何类型的二进制内容,下面看下常用的图片的分享:
Intent shareIntent = new Intent();
// 1. action
shareIntent.setAction(Intent.ACTION_SEND);
// 2. Intent.EXTRA_STREAM和uri类型的uriToImage
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
// 3. 合适的MIME类型
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));
上面有几点需要注意:
- You can use a MIME type of "*/*", but this will only match activities that are able to handle generic data streams.(对于这句话我不理解。。。。真在求证中..)
- 接收该Intent的应用需要权限来访问Intent中的Uri的数据,推荐的方法两种:
- 将数据存在ContentProvider,并保证其他的app有正确的访问权限.
对于访问权限的保证,首选的机制是给接收的应用提供per-URI permissions,这样就可以有效减少permission的滥用.
对于ContentProvider的创建,可以使用FileProvider,这样会比较简单. - 使用系统的MediaStore. MediaStore主要针对视频,音频和图片类型,但是从Android 3.0(API 11)开始,它也可以储存non-media类型(详情看MediaStore.Files). 文件也可以通过scanFile()方法插入到MediaStore中,有一点要注意的是一旦文件被添加到系统的MediaStore中该文件就可以被设备上所有的app访问了.
- 将数据存在ContentProvider,并保证其他的app有正确的访问权限.
1.3 发送多段内容
要发送多段内容,就要使用ACTION_SEND_MULTIPLE这个action,然后将一系列的URI作为参数传到数据中,但是MIME类型就要看你的内容的类型而定.
如下示例:
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.."));
还是提醒下,要保证接收这些URI的应用有访问它们的权限.
2. 接收其他app发送的简单数据
正如app可以给其他的app发送数据一样,app也可以接收其他的app发送过来的数据,比如社交应用就会接收文本,图片或网址的数据。
2.1 更新Manifest
具体的在使用Intent来进行App间的基本交互中有描述,下面看示例代码:
<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>
2.2 处理接收的内容
当你用getIntent()方法拿到Intent对象后,就可检查intent中携带的内容然后做相应的处理,如下:
oid 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
}
}
上述有两点需要注意:
- 对intent的action和type的准确判断是非常重要的,特别是如上支持多种类型,因为你永远不知道其他app给你发送的数据是什么类型的.
- 对于二进制数据的处理,记得要用异步.
3. 添加一个简单的分享功能
在Android 4.0(API 14)开始引入的ActionProvider之后,在ActionBar上面添加一个有效并友好的分享功能变得更简单了. 对于ActionProvider,一旦将它添加到ActionBar的menu的item中,它就会负责去处理试图的显示和事件的处理,比如ShareActionProvider,你只需要给它一个intent然后其他的它就会帮你完成.
3.1 更新Menu的声明
在menu的xml中,要使用ShareActionProvider,需要在item中定义一个属性:android:actionProviderClass,如下示例:
<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>
3.2 设置要分享的Intent
上述设置了ShareActionProvider之后,还需要给它提供一个Intent,具体如下:
private mShareActionProvider 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);
}
}
通过上述代码之后,你还要在合适的地方调用setShareIntent()方法,并把构建的Intent对象传入,然后其他的事情就交给mShareActionProvider去处理就好了.
上述代码在运行时候可能会抛出一个异常:
java.lang.UnsupportedOperationException: This is not supported, use MenuItemCompat.getActionProvider()
...
原因:
这是因为用到的Activity继承的父类是v7包中的AppCompatActivity,从而在调用onCreateOptionsMenu()创建Menu时返回的也会是v7包中的MenuBuilder对象的引用,然后用该menu来调用findItem()方法返回的也会v7包中的MenuItemImpl对象的引用,而在MenuItemImpl中,getActionProvider()这个方法只会抛出一个上述异常(如下),其他什么都不做,这就是上述异常的原因.
package android.support.v7.view.menu;
...
public final class MenuItemImpl implements SupportMenuItem {
...
@Override
public android.view.ActionProvider getActionProvider() {
throw new UnsupportedOperationException(
"This is not supported, use MenuItemCompat.getActionProvider()");
}
...
}
解决方法:
使用v7包中的ShareActionProvider. 具体步骤如下:
- menu的xml文件中:
<item
android:id="@+id/menu_share"
android:title="@string/menu_share"
support:actionProviderClass="android.support.v7.widget.ShareActionProvider"
support:showAsAction="always" />
- 代码中:
import android.support.v7.widget.ShareActionProvider;
...
// BEGIN_INCLUDE(get_sap)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu resource
getMenuInflater().inflate(R.menu.main_menu, menu);
// Retrieve the share menu item
MenuItem shareItem = menu.findItem(R.id.menu_share);
// Now get the ShareActionProvider from the item
mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
...
return super.onCreateOptionsMenu(menu);
}
网友评论