• 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 的时候须要做一下空指针保护

    • 签名校验

  • 相关阅读:
    计算机网络通信
    javap查看class文件
    JDK动态代理与CGLib动态代理
    error the @annotation pointcut expression is only supported at Java 5 compliance
    redis清空缓存
    利用HttpURLConnection发送请求
    linux下用命令导出mysql表数据
    adb push和pull使用
    mysqld.exe占比cpu 100% 解决方案
    养成好习惯
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7259093.html
Copyright © 2020-2023  润新知