您的位置:首页 > 移动开发 > Android开发

Android中使用ContentProvider进行跨进程方法调用

2015-06-26 08:30 656 查看
原文同时发表在我的博客

点我进入还能看到更多

需求背景

最近接到这样一个需求,需要和别的 App 进行联动交互,比如下载器 App 和桌面 App 进行联动,桌面的 App 能直接显示下载器 App 内的下载任务进度和状态。

寻找解决方案

从需求上知道了,主要问题在如何解决跨进程的通信上边。

AIDL

AIDL 即 Android Interface Definition Language的缩写,是专为 Android 中跨进程通信接口的描述语言。优缺点很明显,优点是稳定,快,Android 专门用于跨进程通信设计的。缺点是比较麻烦,AIDL 是通信的约定,参加通信的双方都需要把这个 AIDL 文件都加入自己的代码中,然后创建 Service 来实现访问和被访问。

ContentProvider

作为 Android 四大基础组件之一的 ContentProvider 本来它的作用只是提供内容性质的跨进程访问。但是在 API 11 (Android 3.0) 中,ContentProvider 加入了一个新的方法,可以用来进行跨进程的方法调用,ContentProvider 中这个方法的定义如下:

Bundle call(String method, String arg, Bundle extras)


从易用性来讲,这个没有 AIDL 那么麻烦,而且扩展性更强,也没有 Broadcast 过于依赖系统,API 11 应该就是主要是缺点了,别的缺点暂时没发现,欢迎补充。

Broadcast

广播是最简单的:优点是把分发消息的任务全部交给 Android 系统了;缺点也是因为全交给系统了,很多地方不受控制。缺点:

虽然广播可以通过指定包名来进行发送指向性消息,但是却不能验证消息去向 App 的签名。

系统重启之后,在系统的广播队列里边的消息就丢失了。

实现

为了简要,主要讲讲 ContentProvider 吧。

ContentProvider

首先是下载器 App 的 ContentProvider 代码实现

package cn.hiroz.downloader.realname;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.util.Log;

public class DownloaderContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}

@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings2, String s2) {
return null;
}

@Override
public String getType(Uri uri) {
return null;
}

@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}

@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}

@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}

@Override
public Bundle call(String method, String arg, Bundle extras) {
if ("DOWNLOAD".equals(method)) {     // 当调用我下载的时候
Log.e("Downloader", "download: " + arg);
// 调用桌面 App 的方法来更新状态
updateStatus("download");
} else ("PAUSE".equals(method)) {    // 当调用我暂停的时候
Log.e("Downloader", "pause: " + arg);
// 调用桌面 App 的方法来更新状态
updateStatus("pause");
}
return null;
}

// 我们要调用的对方的 ContentProvider 的 URI
private final Uri LAUNCHERCONTENTPROVIDER_URI = Uri.parse("content://cn.hiroz.launcher.LauncherContentProvider");
}

private void updateStatus(String status) {
getContext().getContentResolver().call(LAUNCHERCONTENTPROVIDER_URI, "UPDATE_STATUS", status, new Bundle());
}


在下载器 App 的 AndroidManifest.xml 中还需要添加 ContentProvider 的定义:

<provider
android:name="cn.hiroz.downloader.realname.DownloaderContentProvider"
android:authorities="cn.hiroz.downloader.DownloaderContentProvider"
android:exported="true"/>


我特地加了authorities设置,这样在交互时候访问的 ContentProvider 的 URI 会看起来不一样,也不会暴露我真实的 ContentProvider 类

然后是桌面 App 的 ContentProvider 代码实现

package cn.hiroz.launcher.realname;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.util.Log;

public class LauncherContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}

@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings2, String s2) {
return null;
}

@Override
public String getType(Uri uri) {
return null;
}

@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}

@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}

@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}

@Override
public Bundle call(String method, String arg, Bundle extras) {
// 当被调用“更新状态”的时候
if ("UPDATE_STATUS".equals(method)) {
Log.e("Launcher", "update status: " + arg);
}
return null;
}

// 我们要调用的对方的 ContentProvider 的 URI
private final Uri DOWNLOADERCONTENTPROVIDER_URI = Uri.parse("content://cn.hiroz.downloader.DownloaderContentProvider");
}

public void download(String arg) {
getContext().getContentResolver().call(DOWNLOADERCONTENTPROVIDER_URI, "DOWNLOAD", status, new Bundle());
}

public void pause(String arg) {
getContext().getContentResolver().call(DOWNLOADERCONTENTPROVIDER_URI, "PAUSE", status, new Bundle());
}

}


在桌面 App 的 AndroidManifest.xml 中还需要添加 ContentProvider 的定义:

<provider
android:name="cn.hiroz.launcher.realname.LauncherContentProvider"
android:authorities="cn.hiroz.launcher.LauncherContentProvider"
android:exported="true"/>


然后在桌面 App 中,就可以通过 LauncherContentProvider 的
download
方法和
pause
方法来调用下载器 App 的功能了(这两个方法写在这里不太合适,不过我仅仅是为了节省篇幅放一起了)。下载器 App 中被调用了方法,就会调用桌面 App 的更新状态。

这里只是演示了一个交互的过程,有更多问题欢迎大家一起讨论学习~~

引申

找不到 ContentProvider 的时候需要做一下空指针保护

签名校验
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: