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

原文同时发表在我的博客

点我进入还能看到更多

需求背景

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

寻找解决方案

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

  1. AIDL

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

  2. ContentProvider

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

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

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

  3. Broadcast

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

    1. 虽然广播可以通过指定包名来进行发送指向性消息,但是却不能验证消息去向 App 的签名。
    2. 系统重启之后,在系统的广播队列里边的消息就丢失了。

实现

为了简要,主要讲讲 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 的时候需要做一下空指针保护
  • 签名校验
时间: 2024-10-18 06:28:03

Android中使用ContentProvider进行跨进程方法调用的相关文章

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

Android四大组件应用系列——使用ContentProvider实现跨进程通讯

一.问题描述 如何在Android中实现不同应用之间的通讯(既跨进程进行调用)?Android提供了多种实现方式,使我们可以实现跨进程访问Activity.通过ContentProvider跨进程访问其他应用的数据.通过Broadcast可以向android系统中所有应用程序发送广播.使用AIDL实现跨进程的Service.下面我们就使用ContentProvider实现跨进程访问数据,并可对数据进行增.删.改.查 二.应用实现 使用ContentProvider实现数据共享,主要是共享应用的S

Android中常用的三种存储方法浅析

Android中常用的三种存储方法浅析 Android中数据存储有5种方式: [1]使用SharedPreferences存储数据 [2]文件存储数据 [3]SQLite数据库存储数据 [4]使用ContentProvider存储数据 [5]网络存储数据 在这里我只总结了三种我用到过的或即将可能用到的三种存储方法. 一.使用SharedPreferences存储数据 SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置信息比如窗口状态,它的本质是基

Wayland中的跨进程过程调用浅析

原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449 Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个参考实现.Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的Binder.与Binder不同的是,在Wayland中Client和Server底层通过domain socke

Wayland (三) Wayland中的跨进程过程调用浅析 [FW]

原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449 Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个參考实现.Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的Binder.与Binder不同的是,在Wayland中Client和Server底层通过domain socke

android中bitmap压缩的几种方法的解读

最近在研究微信的sdk,在缩略图这遇到了一点问题. 微信的缩略图要求是不大于32k,这就需要对我的图片进行压缩.试了几种方法,一一道来. 1.质量压缩法: 代码如下 ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 100; while ( baos.toByteArray().length /

【转】Android中引入第三方Jar包的方法(java.lang.NoClassDefFoundError解决办法)

原文网址:http://www.blogjava.net/anchor110/articles/355699.html 1.在工程下新建lib文件夹,将需要的第三方包拷贝进来.2.将引用的第三方包,添加进工作的build path.3.(关键的一步)将lib设为源文件夹.如果不设置,则程序编译可以通过,但运行的时候,会报: java.lang.NoClassDefFoundError # re: Android中引入第三方Jar包的方法(java.lang.NoClassDefFoundErro

Android 中对于图片的内存优化方法

Android 中对于图片的内存优化方法,需要的朋友可以参考一下 1. 对图片本身进行操作 尽量不要使用 setImageBitmap.setImageResource. BitmapFactory.decodeResource 来设置一张大图,因为这些方法在完成 decode 后,最终都是通过 Java 层的 createBitmap 来完成的,需要消耗更多内存.因此,改用先通过 BitmapFactory.decodeStream 方法,创建出一个 bitmap,再将其设为 ImageVie

Android中使用代码截图的各种方法总结

1,基于Android SDK的截屏方法 (1)主要就是利用SDK提供的View.getDrawingCache()方法.网上已经有很多的实例了.首先创建一个android project,然后进行Layout,画一个按键(res/layout/main.xml): <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android