内容分享之分享简单数据

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

1.3 发送多段内容

要发送多段内容,就要使用ACTION_SEND_MULTIPLE这个action,然后将一系列的URI作为参数传到数据中,但是MIME类型就要看你的内容的类型而定.
如下示例:

ArrayList imageUris = new ArrayList();
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间的基本交互中有描述,下面看示例代码:


    
        
        
        
    
    
        
        
        
    
    
        
        
        
    

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 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,如下示例:


    
    ...

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文件中:
 
  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

  1. Sharing Simple Data
  2. Sending Simple Data to Other Apps
  3. Receiving Simple Data from Other Apps
  4. Adding an Easy Share Action
  5. MIME Media Types
  6. This is not supported, use MenuItemCompat.getActionProvider()

你可能感兴趣的:(内容分享之分享简单数据)