IPC机制系列之三 Android中的IPC方式

  Android中的跨进程通信地方式很多,比如通过在Intent中附加extras来传递信息,或者通过共享文件的方式来共享数据,还可以采用Binder方式来实现,另外ContentProvider天生就是支持跨进程通信的,此外通过网络通信也就是Socket也可以实现IPC。
  1.1 使用Bundle

  在Android中四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以他可以方便地在不同的进程间传输。当我们在一个进程中启动了另一个进程中的activity和service和Receiver,我们就可以在Bundle中附加我们需要传输给远程进程的信息并信息并通过Intent发送出去。当然我们传输的数据必须是能够序列化。

  使用Bundle实现进程间通信地另一种使用场景,A中正在进行计算,计算完成后需要启动B进程,并且将结果传递给B,然后结果的类型Bundle不支持,如果再加入其他的方式会显得麻烦,故而你需要通过Intent去启动B中的一个Service组件,开启一个IntentService去完成这个计算任务,由于Service组件也运行在B中,所以B可以直接获得结果。这样就成功避免了进程间通信的问题。

  1.2 使用文件共享

  使用文件共享来实现进程间通信的核心在于两个进程通过读/写同一个文件来交换数据,这里最大的问题就是Android基于Linux,使得其并发读写文件可以没有限制的进行,因此两个进程同时对一个文件进行操作是有可能出现的,这样的话会出现数据丢失。但是使用文件最大的好处就是简单实用,除了可以交换文本,我们也可以将一些序列化一个对象到文件中,然后另一个进程恢复这个对象。在下面我们可以实现一个demo,实现进程间的通信。
创建一个项目,然后再MainActivity中将序列化后的对象通过流的方式写到文本中,然后在清单文件中SecondActivity后通过process实现开启另一进程。然后从文本中将user对象恢复。
   MainActivity

  @Override
    protected void onResume() {
        super.onResume();
        persistToFile();
    }
    private void persistToFile(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user=new User("1","james");
                File dir=new File(MyContstants.CACHE_File_PATH);
                if(!dir.exists()){
                    dir.mkdirs();
                }
                File cacheFile=new File(MyContstants.CHAPTER_2_PATH);
                ObjectOutputStream outputStream=null;
                try {
                    outputStream=new ObjectOutputStream(new FileOutputStream(cacheFile));
                    outputStream.writeObject(user);
                    Log.e(TAG,"persist user:"+user);
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        if(outputStream!=null){
                            outputStream.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

   SecondActivity

@Override
    protected void onResume() {
        super.onResume();
        removeFromFile();

    }

    private void removeFromFile(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user=null;
                File cacheFile=new File(MyContstants.CHAPTER_2_PATH);
                ObjectInputStream inputStream=null;
                try {
                    inputStream=new ObjectInputStream(new FileInputStream(cacheFile));
                    user= (User) inputStream.readObject();
                    Log.e(TAG,"removeFromFile user:"+user);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if(inputStream!=null)
                        {
                            inputStream.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

下面看一下log日志:

10-10 21:19:52.947 2549-2573/com.byd.text E/MainActivity: persist user:User{userId='1', usereName='james'}
10-10 21:28:48.032 3790-3812/com.byd.text.remote E/SecondActivity: removeFromFile user:User{userId='1', usereName='james'}

  可以看到反序列化之后得到的对象内容上和序列化后的对象是一致的,但是他们本质上还是两个对象。通过文件共享这种数据来共享数据对文件的格式是没有具体要求的,只要读写双方约定好数据的格式即可。但是这个方式最大的局限性就在于并发写时会丢失数据,因此使用这种方式来实现进程间通信适合再对数据同步要求不高的进程之间通信,并且能够处理好并发读写的问题。

特例:SharePreference

  SharePreference是Android中提供的轻量级存储方案,它是通过键值对的方式来存储数据,在底层实现上它采用Xml文件来存储键值对,每个应用的SharePreference文件都可以在当前包的所在的data目录下找到,从本质上说,它也属于文件共享的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份它的缓存,因此在多进程模式下,系统对它的读写就会变得不可靠,也就是说面对高并发的读写操作,它有可能会丢失数据。因此,不建议在进程间通信时使用SharePreference。

  1.3 使用Messenger实现进程间通信

  Messenger可以翻译为信使,通过它可以在不同的进程中传递Message对象,在Message中放入我们要传递的数据,Messenger是一种轻量级的进程间通信方案,他的底层是对AIDL进行了分装,由于其一次只能执行一个请求,因此也不需要在服务端开路进程同步的问题。

  1.3.1 使用Messenger实现进程间通信的步骤

  1.服务端进程

  首先,我们需要在服务端创建一个Service来处理客户端的请求,同时创建一个Handler并通过它来创建一个Message对象,然后在Service的onBind中返回这个Messenger对象那个底层的Binder。

  2.客户端进程

  客户端中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发送的消息类型是Message对象。如若要实现服务端也能回复客户端,我们还需要在创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过replyTo参数就可以回应客户端。

  3.服务端的代码

/**
 * 作者:byd666 on 2017/10/12 15:28
 * 邮箱:sdkjdxbyd@163.com
 */
public class MessengerService extends Service {
    private static final String TAG="MessengerService";
    /**
     * 用来处理客户端发来的信息,并从中取出文本信息打印。
     */
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyContstants.MSG_FROM_CLIENT:
                    Log.e(TAG,msg.getData().getString("msg"));
                    //接收message中传递过来的Messenger的对象,并通过其回复客户端
                    Messenger client=msg.replyTo;
                    Message message=Message.obtain(null,MyContstants.MSG_FROM_SERVICE);
                    Bundle bundle=new Bundle();
                    bundle.putString("reply","got it,i am service");
                    message.setData(bundle);
                    try {
                        client.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    /**
     * 将客户端发送来的消息传递给MessengerHandler处理
     */
    private final Messenger mMessenger=new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回的是Messenger中的Binder对象
        return mMessenger.getBinder();
    }
}

注册Service,让其运行在单独的进程中。

<service
            android:name=".service.MessengerService"
            android:process=":messenger"/>

  4.客户端的代码

public class MessengerActivity extends AppCompatActivity {
    private static final String TAG="MessengerActivity";
    private Messenger mService;
    /**
     * 处理服务端接回复过来的消息
     */
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyContstants.MSG_FROM_SERVICE:
                    Log.e(TAG,msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    /**
     * 将messenger传递过来的消息交给Handler处理
      */
    private Messenger messenger=new Messenger(new MessengerHandler());

    private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //根据服务端返回的binder对象创建Messenger对象。
            mService=new Messenger(service);
            Message message=Message.obtain(null, MyContstants.MSG_FROM_CLIENT);
            //一个可以类似Map集合的存储数据的容器
            Bundle bundle=new Bundle();
            bundle.putString("msg","hello my server,i am client.");
            message.setData(bundle);
            //当客户端发送消息的时候,需要将接收服务端回复的Messenger通过Message的replyTo参数传给服务端
            message.replyTo=messenger;
            try {
                //使用此对象向服务端发送消息
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        //绑定远程MessengerService
        Intent intent=new Intent(this, MessengerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //销毁时解绑
        unbindService(mConnection);
        super.onDestroy();
    }
}

log日志:

10-12 21:45:24.881 18542-18542/com.byd.text.messenger E/MessengerService: hello my server,i am client.
10-12 21:45:24.992 18524-18524/com.byd.text E/MessengerActivity: got it,i am service

注意:通过以上例子我们可以看出,在Messenger中进行数据传递必须将数据放入到Message中,而二者都实现了Parcelable接口,因此可以跨进程传输。Message所支持的数据类型就是Messenger所支持的传输类型。

  1.3.2 使用Messenger实现进程间通信的原理图

这里写图片描述


最后ICP机制系列完事之后会将之前的demo放到github上,大家可以clone下来自己实现看一下。附一下地址:项目在git上面的地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值