您的当前位置:首页正文

内容分享之分享简单数据

来源:花图问答

Android应用的一个重要的功能就是应用间可以相互交流和互相调用,自己的app只要做好自己核心的功能,其他非核心的功能如果存在可以直接调用的app就调用它们的,没必要重复造轮子.

1. 发送简单的数据给其他app

1.1 发送文本内容

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来接收,具体可以参考之前的).
  • 视情况选择是否使用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,其他的也有,等等,具体Intent的各种标准的属性可以看;
  • intent的putExtra()中的第二个参数有的时候可能是string[],比如 EXTRA_EMAIL, EXTRA_CC等.

1.2 发送二进制的内容

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的数据,推荐的方法两种:
    1. 将数据存在,并保证其他的app有正确的访问权限.
      对于访问权限的保证,首选的机制是给接收的应用提供,这样就可以有效减少permission的滥用.
      对于ContentProvider的创建,可以使用,这样会比较简单.
    2. 使用系统的. MediaStore主要针对视频,音频和图片类型,但是从Android 3.0(API 11)开始,它也可以储存non-media类型(详情看). 文件也可以通过方法插入到MediaStore中,有一点要注意的是一旦文件被添加到系统的MediaStore中该文件就可以被设备上所有的app访问了.

1.3 发送多段内容

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

<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. 添加一个简单的分享功能

3.1 更新Menu的声明

在menu的xml中,要使用ShareActionProvider,需要在item中定义一个属性:android:actionProviderClass,如下示例:

<menu 
    <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. 具体步骤如下:

  1. menu的xml文件中:
 <item
        android:id="@+id/menu_share"
        android:title="@string/menu_share"
        support:actionProviderClass="android.support.v7.widget.ShareActionProvider"
        support:showAsAction="always" />
  1. 代码中:
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);
    }

Reference