钱诺登时大喜,双手接过了这方倒离的宝塔一样的东西。钱诺仔细一看这件法宝,这件法宝上大下小,攻击有着十八层,每一层之上都传出了一种钱诺极为熟悉的气息,正是十八层地狱的气息。钱诺已经接触这件法宝,就有着一种明悟涌上了钱诺的心头。

再见天上罗汉仙官见佛祖举手投足间凭空造了一座山峰出来,皆拜服称赞。喧嚣一阵,他们个个驾云远去,正是:鹤影杳杳九霄云,落羽纷纷叹零丁。

董卿为什么没有被狐狸狗催眠?

李豫端着茶杯,打量着身后的这架白玉屏风,他是堂堂帝王,当然不是由他来谈生意,旁边李亨道:“是这样,我们打算买一万匹上绢,想来问问价钱。”
“这是我龙鳌岛的规矩!”钱诺对着楚不识笑着说道:“除去老夫人郑掌院、舅老爷赵掌教,只有四海龙王、化乐岛摩罗天王尹圆峤、不夜城商城主、琴剑老人、圆音洞三位神僧之外其余无论是谁,都要在此接下武器!”

所有人都相信,李庆安的心中已经为江南的发展画上了一幅未来之图。

Android查缺补漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四种进程间通讯介绍


本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8387752.html

进程间通讯篇系列文章目录:

  • Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身
  • Android查缺补漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四种进程间通讯介绍
  • Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解
  • Android查缺补漏(IPC篇)-- 跨进程通讯之Socket简介及示例

在Android中进程间通信的实现方式有多种,包括:Bundle、文件共享、ContentProvider、Messenger、AIDL、Socket等等,其各有各的优缺点,接下来就分别介绍一下上述各种进程间的通信方式及实现。

一、Bundle

Activity、Service、Receiver都支持Intent中传递Bundle数据,由于Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间传输。

传输的数据必须能够被序列化:

  • 基本类型
  • String类型
  • CharSequence
  • 实现了Parcelable接口的对象
  • 实现了Serializable接口的对象
  • List
  • IBinder类型
  • SparseArray

二、文件共享

文件共享作为进程间通讯时,无法解决并发读写时所带来的问题,所以只适合在对数据同步要求不高的进程间通讯。

其实SharedPreferences也属于文件共享方式的一种,sp是android中提供的一种轻量级存储方案,通过键值对的方式来存储数据,底层用xml文件来存储键值对。每个应用的sp文件放在当前包所在的data目录下,位于/data/data/package_name/shared_prefs目录下。由于系统对它的读写有一定的缓存策略,即在内存中会有一份sp文件的缓存,因此在多进程模式下,它变得不可靠。

三、ContentProvider

ContentProvider在前面介绍四大组件时就已经介绍过了,这里就不多说了,详见《Android查缺补漏--ContentProvider的使用》

四、Messenger

1、Messenger是什么?

通过Messenger可以很方便的在不同的进程之间传递Messager对象,在Messager对象中就可以放入我们需要传递的数据。我们知道,跨进程传输数据有很多种方式,其中AIDL最为强大也最为常用,而Messenger即相当于AIDL的简化版,其底层也是采用AIDL实现,是一种轻量级的跨进程传输方案。

所谓简化版常常功能有限,Messenger也不例外,相对于AIDL它的功能确实弱化了不少,在方便使用的同时它一次只能处理一个请求。

2、实现一个简单的Messenger通讯

实现一个Messenger需要在两个进程中做以下操作:

为了避免歧义,我们提前约定,以下所说的服务端若不做特别说明默认指Service所在的进程。
  • 在服务端:首先,创建一个Service充当服务端进程,然后在Service中创建一个Messenger,因为创建Messenger时需要传入一个Handler,所以还要创建一个Handler,接收数据的操作就在Handler中,最后在Service的onBind中返回这个Messenger的Binder即可。

示例代码如下:

/**
 * Created by liuwei on 18/1/29.
 */
public class MessengerService extends Service {

    private static final String TAG = MessengerHandler.class.getSimpleName();
    
    // 创建一个Handler,处理消息用
    private class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case AppConstants.MSG_FROM_CLIENT:
                    // 接收客户端发来的消息
                    Log.i(TAG, "handleMessage: MSG_FROM_CLIENT:" + msg.getData().getString("msg"));
                    break;
                default: super.handleMessage(msg);
            }
        }
    }
    
    // 创建Messenger
    private Messenger messenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // 返回Messenger的Binder
        return messenger.getBinder();
    }
}

这里要想让Service跑在另外一个进程中需要在AndroidManifest文件中添加process节点:

<service
    android:name=".messager.MessengerService"
    android:process=":remote" />
  • 在客户端:就像绑定一个普通的Service一样,通过bindService方法链接Service即可,不同点是,需要在ServiceConnection接口中的onServiceConnected方法中通过参数中的IBinder创建一个Messenger对象,通过这个Messenger对象即可向服务端发送message消息。

示例代码如下:

public class MessengerActivity extends AppCompatActivity {

    private final static String TAG = MessengerActivity.class.getSimpleName();

    Button btn_bind_messenger;
    Intent intent;
    Messenger messenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        btn_bind_messenger = ViewUtils.findAndOnClick(this, R.id.btn_bind_messenger, mOnClickListener);
        intent = new Intent(this, MessengerService.class);
    }

    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btn_bind_messenger:
                    // 绑定Service
                    bindService(intent, connection, BIND_AUTO_CREATE);
                    break;
            }
        }
    };

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 链接messenger成功,利用service创建一个Messenger
            messenger = new Messenger(service);

            // 向服务端发送一条消息
            Message message = Message.obtain(null, AppConstants.MSG_FROM_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("msg", "client bind messenger succeed!");
            message.setData(bundle);
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            messenger = null;
        }
    };

    @Override
    protected void onDestroy() {
        unbindService(connection);
        super.onDestroy();
    }
}

上面代码中ViewUtils.findAndOnClick()是博主封装的一个方法,功能只是绑定控件并为控件添加onClick监听器,无需重点关注,接下来运行工程点击按钮,在Android Monitor中将切换到remote进程,查看log如下:

.../cn.codingblock.ipc:remote I/MessengerHandler: handleMessage: MSG_FROM_CLIENT:client bind messenger succeed!

3、服务端如何回应客户端?

到上面这一步一个简单的Messenger通讯就完成了,接下在MessengerActivity中我们就可以使用Messenger对象向服务端发送数据了,但是如何才能得到服务端的回应呢,或者服务端想向客户端发送数据怎么办?

这个其实也很简单,我们只需要在客户端这里也创建一个Messenger,然后再向服务端发送数据时在Message的replyTo指向客户单的Messenger对象即可,如下:

message.replyTo = clientMessenger;

然后再服务端通过获取Message.replyTo,就可以获取到客户端的Messenger:

Messenger client = msg.replyTo;

具体实现如下:

  • 在上面的MessengerActivity中增加一个Handler,并通过Handler创建一个Messenger,在向服务端发送一条消息时告诉服务器接收回复的messenger:
// 新增一个MessengerHandler
private class MessengerHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case AppConstants.MSG_FROM_SERVICE:
                Log.i(TAG, "handleMessage: MSG_FROM_SERVICE:" + msg.getData().getString("msg"));
                break;
            default:super.handleMessage(msg);
        }
    }
}
// 创建一个clientMessenger
private Messenger clientMessenger = new Messenger(new MessengerHandler());

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 链接messenger成功,利用service创建一个Messenger
        messenger = new Messenger(service);

        // 向服务端发送一条消息
        Message message = Message.obtain(null, AppConstants.MSG_FROM_CLIENT);
        Bundle bundle = new Bundle();
        bundle.putString("msg", "client bind messenger succeed!");
        message.setData(bundle);

        // 关键点,告诉服务器接收回复的messenger
        message.replyTo = clientMessenger;

        try {
            messenger.send(message);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        messenger = null;
    }
};
  • 在上面的MessengerService中获取客户端的Messenger,并通过获取的这个Messenger对象向客户端回应消息:
private class MessengerHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case AppConstants.MSG_FROM_CLIENT:
                // 接收客户端发来的消息
                Log.i(TAG, "handleMessage: MSG_FROM_CLIENT:" + msg.getData().getString("msg"));

                // 服务端收到消息后,给客户端发送回应
                Messenger client = msg.replyTo;
                Message replyMsg = Message.obtain(null, AppConstants.MSG_FROM_SERVICE);
                Bundle bundle = new Bundle();
                bundle.putString("msg", "ok,I will reply you soon!");
                replyMsg.setData(bundle);
                try {
                    client.send(replyMsg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default: super.handleMessage(msg);
        }
    }
}

再次运行工程,点击按钮,将进程切换到主进程后log如下:

.../cn.codingblock.ipc I/MessengerActivity: handleMessage: MSG_FROM_SERVICE:ok,I will reply you soon!

至此,通过Messenger实现的一个完整的两个进程之间的交互过程完成了,上面的两个进程虽然都在同一个App中,但其效果同在两个App中几乎一致,只是如果是两个App的话在需要稍微修改一下绑定Service时的Intent并将Service的export属性设为true。

4、通过Messenger实现两个App通讯

接下来试验一下将两个进程中放入两个App中:

  • 新建一个名为IpcClient的Module,将MessengerActivity拷贝过去,在bindService时,不要忘记将Intent稍作修改一下,因为现在MessengerService在另外一个工程中,无法直接传入到Intent中,需改为如下方式:
intent = new Intent();
intent.setComponent(new ComponentName("cn.codingblock.ipc", "cn.codingblock.ipc.messager.MessengerService"));

为了能够更加方便的区分是哪个工程传到Service端的消息,在发送消息时加入如下信息:

bundle.putString("msg", "client bind messenger succeed!(from ipc_client)");

其他代码不变。

  • 然后再原来的工程,将配置文件中的MessengerService的exported属性设置为true,否则外界将无法调用此Service。
<service
    android:name=".messager.MessengerService"
    android:exported="true"
    android:process=":remote" />

此时,工程结构如下:

运行IpcClient工程,点击按钮,首先在Android Monitor中切换到cn.codingblock.ipc:remote进程查看log如下:

.../cn.codingblock.ipc:remote I/MessengerHandler: handleMessage: MSG_FROM_CLIENT:client bind messenger succeed!(from ipc_client)

然后将进程切换到cn.codingblock.ipcclient中log如下:

.../cn.codingblock.ipcclient I/MessengerActivity: handleMessage: MSG_FROM_SERVICE:ok,I will reply you soon!

5、Messenger可以传输的数据类型包括:

简单来说,Messenger可以传输Message可承载的数据类型,而Message中能使用的载体有:what、arg1、arg2、Bundle和replyTo,其实Message中还有一个Object类型的载体,这个载体在同一个进程中非常使用,但是在Android2.2之前object字段不支持跨进程传输,在2.2之后也仅支持系统提供的实现Parcelable接口的对象。所以总结起来,Messenger在跨进程时可传递的类型如下:

  • Bundle类型
  • Messenger类型

小结

本篇介绍了四种比较简单的跨进程通信方式,这四种实现起来相对方便,但功能也非常有限,在后续的博文中将介绍AIDL和Socket的使用。下面的表格为以上四种跨进程通信方式的比较:

名称 优点 缺点 场景
Bundle 使用简单 1、只能传输Bundle支持的类型 2、不支持RPC 四大组件间的通信
文件共享 使用简单 1、不适合并发 2、做不到即时通信 无并发访问、不要求实时通信的场景
ContentProvider 1、在数据源访问方面功能强大 2、支持一对多 3、可通过call方法扩展其他操作 受约束的AIDL、主要提供数据的CRUD操作 一对多的进程间数据共享
Messenger 1、支持一对多串行通信 2、支持实时通信 1、只能串行通信 2、只能传输Bundle支持的类型 3、不支持RPC 低并发一对多即时通信、无RPC需求

最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!

参考文献:

  • 《Android开发艺术探索》

源码地址:本系列文章所对应的全部源码已同步至github,感兴趣的同学可以下载查看,结合代码看文章会更好。源码传送门

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8387752.html

当前文章:http://hnhdqp.com/content/201903/06/content_73274.html

发布时间:2019-03-23 00:00:00

一喝酒就脸红的人能锻炼酒量吗? 全球9大正在消逝的旅游胜地 自卑与自欺 性感可以很聪明 别让长靴不高兴 五天结束的网恋 我们早已是故乡的异乡人 怎能没有光鲜好时候

成长营:优秀的孩子也需要成长 为了孩子,父母该不该守住破碎的婚姻 一只狗的绝望体验 女优卖胸与日本本土AV文化不符 因为我们值得 如果你已经20岁.... 男性国人学习爱 突破“离婚”或“凑合过”的婚姻困惑 怀孕日记——准妈妈的选择题 时代病的白描 为什么你的努力一文不值? 孩子入园不适怎么办? 步非烟之所托非人 亲爱的,因为你的努力,我才倍加珍惜 宝宝皮肤过敏的注意事项 魏则西事件让百度在失去了什么? 报复方舟子的肖传国被自己的欲望锤倒了! 五只猴子的故事 震惊!教育有一个大家没有意识到的陷阱

编辑:道陵扁

相关新闻

跟屁虫APP紧握大爱 不惧被山寨

2019-03-23 00:00:00

瓦房店诩期诜企业管理有限公司

府谷建立“三动机制”共同服务管理流动人口

2019-03-23 00:00:00

庄河当贤讼机械设备有限公司

元器件受阻 分析称iPhone5S发布将推迟至9月末

2019-03-23 00:00:00

重庆伺却妒有限公司

外交部提醒赴毛里求斯中国游客注意旅行安全

2019-03-23 00:00:00

儋州克魄电子有限公司

热门推荐

  • 冈本伦自曝黑历史:曾被参演自己作品的声优蔑视
  • 仓储(Repository)和工作单元模式(UnitOfWork)
  • 山东昌乐:打造齐国古城 展示古齐都文化
  • 三星申请可折叠双屏手机专利:主打玩游戏
  • 2017全国民族地区投资贸易洽谈会在京举办
  • 蚂蚁金服:天猫双11全天保险出单8.6亿单
  • IARC涉嫌操纵草甘膦评估结论
  • IDC:2017年Q3平板出货量连续12季度下滑,苹果逆势增长
  • 汽车圈的逆向工程,看谁狠?
  • 急需用钱怎么办?几大方式让你体面借钱
  • 河北新闻网版权所有 本站点信息未经允许不得复制或镜像 法律顾问:文字艺术优化(文字优化) 为什么我们要称呼他为芈月呢?
  • 请尊重一个姑娘的努力 copyright ? 2000 - 2016
  • 新闻热线:0311-67563366 广告热线:0311-67562966 新闻投诉:0311-67562994
  • 冀ICP备 09047539号-1 | 互联网新闻信息服务许可证编号:1312006002
  • 广播电视节目制作经营许可证(冀)字第101号|信息网络传播视听节目许可证0311618号
  • 怎样安慰情绪低落的朋友 让心理学社会学教你 护肤必备手册之(二)周期护理 一个没带手机的下午,我是这样度过的 恋物癖:难以启齿的诱惑