关于IPC的一点愚见

IPC是Inter-Process-Communication的简写,就是指进程间通讯。

Linux的进程间通讯,不必多说,就是指管道,信号和跟踪,信号量,套字节,共享内存等。

在这里主要讨论的是Android开发中常用的IPC方案。

目前为止,我们常用的IPC方案有以下几种:
1.使用Bundle
2.使用文件共享
3.使用Messenger(信使)
4.AIDL
5.ContentProvider组件
6.socket套字节
7.Binder线程池

现在让我一点一点总结,但是不会像前几篇打开源码一点点说,这是个总结:

1.Bundle 这是每一个每一个初学者都会接触到的一个类:
我们写Activity的onCreate():

 protected void onCreate(Bundle savedInstanceState)

当时我初学的时候,查了一下文档,上面的说法大概是Bundle是作用存储Activity里面的信息。

正如上面创建Activity一样,四大组件中的三大组件都支持Intent中传递Bundle数据。Bundle实现了Parcelable接口,所以它可以很轻松在不同的进程之间传递。

public final class Bundle extends BaseBundle implements Cloneable, Parcelable 

前面也说过,Binder作为进程间的核心机制来说,就是使用序列化的Parcelable进行传递的。也是因此Bundle只支持基本类型,实现了Parcelable接口的类,或者说也实现了Serializable的数据类型,还有一些Android支持的特殊的对象啊。

有一个特殊场景需要注意一下:A进程要进行一个计算,并把计算完成后,这个结果要启动进程B的一个组件并把结果发给B进程的一个组件。可是这个结算结果不支持放入Bundle怎么办。

可以有一个解决方案:我们通过Intent启动B进程的Service,将数据传到B的Service进行计算,再让Service器启动B进程中真正要启动的目标组件。这样就避免了自己去做进程间通讯。

2.文件共享
这里面也包含了SharePreference的方案,其实就是我们学Java里面的用过的知识,BufferedRead和PrintWriter一起运用序列化写入一个约定的好文件,在通过反序列化读出来。要么就用Android封装好的SharePreference,去解析里面的内容。

3.使用信使也就是Messenger
Messenger是用AIDL实现的轻量化的IPC方案。
让我们粗略的看看下面的怎么样:

public final class Messenger implements Parcelable

首先,Messenger已经实现了序列化,为了进程间通讯做准备。

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

很熟悉吧,就是之前分析过AIDL,将所有的工作丢到IMessenger这个Binder类中工作,并且实现进程间通讯。

由于Messenger一次只做一个请求,因此,我们不需要考虑线程同步问题。那么Messenger实现步骤分两步:实现服务端和客户端:
1)实现服务端进程创建一个Service来处理来自客户端的请求,接着在Service的onBind返回的Binder进行处理。(为了证明能够完成进程通信,请在AndroidManifest.xml注册Service增加一个属性android:process=”:remote”)

MessengerService.java

public class MessengerService extends Service{
    private static String Tag = "Messenger";

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg){
            switch (msg.what) {
            case 1:
                Log.e(Tag, "receive msg from Client:" + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;//此处是当从客户端发送消息过来是后,声明的新Messenger
                Message replyMessage = Message.obtain(null,0);
                Bundle bundle = new Bundle();//Bundle作为数据传输的容器
                bundle.putString("reply","received");
                replyMessage.setData(bundle);
                try {
                    client.send(replyMessage);//发送到客户端
                } catch (RemoteException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                break;

            default:
                super.handleMessage(msg);
                break;
            }
        }
    }

    private final Messenger messenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return messenger.getBinder();
    }

MainActivity.java

public class MainActivity extends Activity {
    private static final String TAG = "MessageActivity";
    private Messenger mService;
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }


    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // TODO Auto-generated method stub
            mService = new Messenger(service);//客户端发向服务端的数据
            Message msg = Message.obtain(null,1);//1是message的what,标志位
            Bundle data = new Bundle();
            data.putString("msg", "hello,this is client");
            msg.setData(data);
            msg.replyTo = mGetReplyMessenger;//对返回的Msg处理
            try{
                mService.send(msg);//客户端发送到服务端的数据
            }catch(RemoteException e){
                e.printStackTrace();
            }
        }
    };


    //此处是处理从服务器返回的数据
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg){
            Log.e("handler", "handle");
            switch (msg.what) {
            case 0:
                Log.e(TAG, "0");
                Log.e(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                break;

            default:
                super.handleMessage(msg);
                break;
            }
        }
    }

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


}

这里要注意,发送端发到接收端,那么接收端就必须有一个Handler来处理。一般的以客户端发向服务端的方向作为基准,要接受数据,客户端要有replyTo应答:msg.replyTo = Handler。服务端则是需要通过msg.replyTo来声明一个新的Messenger。

这样就能够完成进程间通讯。

以后空在慢慢研究Meesenger,整理出文章,现在只需要怎么用就行。

4.AIDL的使用:
AIDL的使用在前面说了一下,现在介绍怎么使用。
第一步:我们需要建立一个包,里面放的全是AIDL的文件以及AIDL相关的类。为什么这么做呢?这是因为我们要这个AIDL的文件放入客户端里面,来办到进程间通讯。如果不这么做,反序列和序列化的时候,Parcelable是根据包的结构做出像Serializable那样的序列码,如果包的结构不一样,序列会出现问题。

第二步:在这个包里面,创建aidl文件,在里面不是.java格式,因此我们只能手动的声明package,import类进来。注意在这里面,这能够处理基本数据类型,实现了Parcelable接口的类。
支持类型如下:
基本类型:int,long,char,boolean,double等
String,CharSequence;
List:支持ArrayList
Map:只支持HashMap
Parcelable:支持所有实现了Parcelable的接口
AIDL:AIDL本省也可以调用AIDL

这里我们做一个远程服务通知有新书的到来的一个过程

如下:
Book.aidl

package com.example.bindertest.aidl;

parcelable Book;

IBookManager.aidl

package com.example.bindertest.aidl;

import com.example.bindertest.aidl.Book;
import com.example.bindertest.aidl.IOnNewBookArrived;

interface IBookManager{
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrived listener);
    void unregisterListener(IOnNewBookArrived listener);
}

IOnNewBookArrived.aidl

package com.example.bindertest.aidl;

import com.example.bindertest.aidl.Book;
import com.example.bindertest.aidl.IOnNewBookArrived;

interface IBookManager{
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrived listener);
    void unregisterListener(IOnNewBookArrived listener);
}

我们要做的目的,就是为了实现进程两端的相互的通信,因此,在Service端实现了功能为:返回书本的list,添加书本,绑定观察者,解绑观察者的Binder,将参数发送到另一端处理。
而在Activity中实现了一旦新书到达就发送的通知Binder,由于我们需要一个Handler来处理消息,因此这个Binder声明在客户端。此时在这个Binder中,那么相对于Service组件来说,Activity组件此时作为服务端,Service组件作为客户端。这个思想我发现在我研究四大组件的时候经常使用。

说明就这么多,下面是源码:

BookManager.java

public class BookManagerService extends Service{
    private static final String TAG = "BMS";
    /*作为线程同步时候的容器,线程写入的时候不会写入内存,而是写入副本**/
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    /*判断Service是否存活的原子类**/
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    /*IInterface的callback对象存入了真正的Binder对象,不然移除对象的时候,移除不干净**/
    private RemoteCallbackList<IOnNewBookArrived> mListenerList = new RemoteCallbackList<IOnNewBookArrived>();

    //调用AIDL中Stub抽象类,在Service中实现本地方法
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            // TODO Auto-generated method stub
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            // TODO Auto-generated method stub
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrived listener)
                throws RemoteException {
            // TODO Auto-generated method stub
            mListenerList.register(listener);

        }

        @Override
        public void unregisterListener(IOnNewBookArrived listener)
                throws RemoteException {
            // TODO Auto-generated method stub
            mListenerList.unregister(listener);
        }
    };

    /*为了让我们的远程服务不让任何客户端连上,我们做一个permission权限的检查**/
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        int check = checkCallingOrSelfPermission("com.example.bindertest.permission.ACCESS_BOOK_SERVICE");
        if(check == PackageManager.PERMISSION_DENIED){
            return null;
        }
        return mBinder;
    }

    @Override
    public void onCreate(){
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
        new Thread(new ServiceWorker()).start();

    }

    @Override
    public void onDestroy(){
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }

    /*在这里我们从存了Binder的list中取出实例,这个list相当于观察者模式这通知后,被观察者做出相应的动作**/
    private void onNewBookArrived(Book book)throws RemoteException{
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for(int i=0;i<N;i++){
            IOnNewBookArrived l = mListenerList.getBroadcastItem(i);
            if(l != null){
                try{
                    /*这里调用调用另一端Notify通知方法**/
                    l.onNewBookArrived(book);
                }catch (RemoteException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    /*启用线程,为的是每5秒模拟发送一个通知的情景**/
    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(!mIsServiceDestroyed.get()){
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                int bookid = mBookList.size()+1;
                Book newBook = new Book(bookid, "new book#"+bookid);
                try {
                    onNewBookArrived(newBook);
                } catch (Exception e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }

    }

}

下面是MainActitivty.java

public class MainActivity extends Activity {
    private static final String TAG = "BookManagerActvity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    private IBookManager mRemoteBookManager;

    //这里是处理器,为了处理从服务端发过来的信息
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.e(TAG, "received a new book"+msg.obj);
                break;

            default:
                super.handleMessage(msg);
                break;
            }
        }
    };

    //声明ServiceConnect用于绑定服务
    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName classname) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName classname, IBinder service) {
            // TODO Auto-generated method stub
            //判断是本地还是远程的
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try{
                List<Book> list = bookManager.getBookList();
                Log.e(TAG, "query listType:"+list.getClass().getCanonicalName());
                Log.e(TAG, "query list element:"+list.toString());
                Book newbook = new Book(323, "a new book");
                bookManager.addBook(newbook);
                List<Book> list2 = bookManager.getBookList();
                Log.e(TAG, "query list2 element:"+list2.toString());
                mRemoteBookManager = bookManager;
                Book book2 = new Book(3, "Android up");
                mRemoteBookManager.addBook(book2);
                List<Book> list3=mRemoteBookManager.getBookList();
                Log.e(TAG, "query list2 element:"+list3.toString());
                bookManager.registerListener(listener);//绑定观察者监听器
            }catch(RemoteException e){
                e.printStackTrace();
            }
        }
    };

    /*在这里实现了加入观察者出现了变化,则发送一个Msg到Handler**/
    private IOnNewBookArrived listener = new IOnNewBookArrived.Stub() {

        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            // TODO Auto-generated method stub
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,book).sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this,BookManagerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy(){
        if((mRemoteBookManager != null)&&mRemoteBookManager.asBinder().isBinderAlive()){
            try {
                Log.e(TAG, "unregister listener:"+listener);
                mRemoteBookManager.unregisterListener(listener);//解除监听器的绑定
            } catch (RemoteException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
        unbindService(conn);
        super.onDestroy();
    }



}

5.内容提供器
因为Sqlite不允许多进程的访问,ContentProvider做出了允许同步的方案。
具体一般人都会,这就略。

6.socket套字节
本地是通过端口来访问,单独开下一章来总结

7.Binder连接池
Binder连接池,其实就是使用aidl。为什么分开来说呢,主要是因为在需求中有可能会出现需要控制多个aidl,难道我们一个个去实现每一个函数,这明显不可能,我需要的是BinderPool这种策略。
单独开一章来总结。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值