极简开发 -- 进程间通讯框架非AIDL(IPCCommunication)

IPCCommunication github地址

进程间通信

说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写。

什么是IPCCommunication

IPCCommunication是通过两种方式实现进程间通信,一种是手写binder实现进程间通信,互相传递使用Bunder,可以在Bunder中自定义协议,另外一种是基于Message,客户端与服务端之间使用Message进行通信;

为什么要写这个框架

曾经听过一个人说过,程序中尽量使用通用组件或者框架,这样可以尽量的少写代码,这样有两个好处,一是少写代码就意味着少犯错误,二是可以多出喝咖啡的时间 ;

使用

手写Binder方式有两种方式
1.使用通用的IPCService服务库,包括与Activity通信
服务端
服务端可以通过Activity 启动也可以不启动,若不启动可以通过继承BaseIPCService的方式,具体实现查看SimpleService

public class MainActivity extends AppCompatActivity {

    private ServiceUtils mServiceUtils;
    private static final String TAG = "jcy_service";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mServiceUtils = new ServiceUtils(this);
    }


    @Override
    protected void onResume() {
        super.onResume();
        //调用默认IPC 服务
        mServiceUtils.setConnectListener(mConnectListener);
        mServiceUtils.bindService();
    }


    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mServiceUtils.unBindService();
    }

    ConnectListener mConnectListener = new ConnectListener() {

        @Override
        public void onBind() {
        }

        @Override
        public void onDisconnected() {

        }

        @Override
        public void onConnected(boolean success) {
            mServiceUtils.setReceiverListener(mReceiverListener);
        }

        @Override
        public void onUnbind() {
            mServiceUtils.setReceiverListener(null);
        }

        ;
    };

    private ReceiverListener mReceiverListener = new ReceiverListener() {
        @Override
        public Bundle receiveInfo(int code, Bundle msg) {
            String msgStr = msg.getString("msgStr");
            int msgInt = msg.getInt("msgInt");
            Log.d(TAG, "receiveInfo: msgStr " + msgStr + "   ,msgInt : " + msgInt);
            Bundle ret = new Bundle();
            ret.putString("return", "服务端返回  code " + code + " ,msgStr " + msgStr + "   ,msgInt : " + msgInt);
            return ret;
        }
    };
}

客户端

public class MainActivity extends AppCompatActivity  {

    private static final String TAG = "jcy_client_one";
    private TextView mTextView;
    private TextView tv_state;
    private ClientUtils mClientUtils;
    private Button btnGet, btn_bind, btn_unbind;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv_service);
        tv_state = (TextView) findViewById(R.id.tv_state);
        btnGet = (Button) findViewById(R.id.btn_getService);
        btn_bind = (Button) findViewById(R.id.btn_bind);
        btn_unbind = (Button) findViewById(R.id.btn_unbind);
        btnGet.setEnabled(false);
        tv_state.setText("  服务未绑定 ");
        mClientUtils = new ClientUtils(this);
        mClientUtils.setConnectListener(mConnectListener);
        btn_bind.setEnabled(true);
        btn_unbind.setEnabled(false);
    }

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mClientUtils!=null){
            mClientUtils.unBindService();
        }
    }

    /**
     * 获取数据
     *
     * @param v
     */
    public void onGetServie(View v) {
        if (mClientUtils.isConnect()) {
            Bundle send = new Bundle();
            send.putString("msgStr", "ClientOneApp发送消息");
            send.putInt("msgInt", 110);
            try {
                Bundle ret = mClientUtils.send(110, send);
                if (ret == null) {
                    Log.e(TAG, "onGetServie:  ret==null");
                } else {
                    String retStr = ret.getString("return");
                    mTextView.append("\n"+retStr);
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "onGetServie:Exception   " + e.toString());
            }
        } else {
            Toast.makeText(this, "未连接服务,无法获取消息", Toast.LENGTH_SHORT).show();
        }
    }


    /**
     * 绑定服务
     * @param v
     */
    public void onBindServie(View v) {
        mClientUtils.bindService();
    }

    /**
     * 解除绑定
     * @param v
     */
    public void onUnBindServie(View v) {
        mClientUtils.unBindService();
    }



    /**
     * 连接状态回调
     */
    private ConnectListener mConnectListener = new ConnectListener() {
        @Override
        public void onBind() {
            tv_state.setText("  服务正在绑定中…… ");
        }

        @Override
        public void onDisconnected() {
            btnGet.setEnabled(false);
            tv_state.setText("  服务断开连接 ");
            btn_bind.setEnabled(true);
            btn_unbind.setEnabled(false);
        }

        @Override
        public void onConnected(boolean success) {
            if (success) {
                tv_state.setText("  服务绑定成功 ");
                btn_bind.setEnabled(false);
                btn_unbind.setEnabled(true);
                btnGet.setEnabled(true);
            } else {
                btnGet.setEnabled(false);
                tv_state.setText("  服务绑定失败 ");
                mClientUtils.unBindService();
                btn_bind.setEnabled(true);
                btn_unbind.setEnabled(false);
            }
        }

        @Override
        public void onUnbind() {
            btn_bind.setEnabled(true);
            btn_unbind.setEnabled(false);
            tv_state.setText("  服务已解除绑定 ");
            mTextView.setText("");
        }
    };

}

另外还支持自定义Service 的方式 具体查看SimpleService类
Message的使用方式与自定义Binder的形式类似,大部分使用方式几乎相同

分析

手写Binder

Binder.png

这个图是盗用别人的嘿嘿;
首先Activity通过调用bindService去绑定一个远程的服务(Service),绑定成功后返回一个IBinder对象。这时候双方就算是建立了连接了。
建立连接之后,双方就可以通过持有的IBinder进行通信。Activity使用IBinder的transact方法去给底层的Binder Driver(Linux层)发送消息间接调用底层的IBinder的execTransact方法。
而execTransact导致的结果就是调用onTransact方法。那么这时候事件的处理就可以在该环节进行了。
上层主要使用 这个方法进行通信 处理消息;

public boolean onTransact(int code, android.os.Parcel data, 
android.os.Parcel reply, int flags) 

这个方法 一般情况下 都是返回true的,也只有返回true的时候才有意义,如果返回false了 就代表这个方法执行失败,onTransact 这个方法 就是运行在Binder线程池中的,一般就是客户端发起请求,然后android底层代码把这个客户端发起的,请求 封装成3个参数 来调用这个onTransact方法,第一个参数code 就代表通信的标志位,data就是方法参数(客户端传递过来的数据),reply就是方法返回值(通过这个方法可以向客户端中返回数据)。

自定义Binder为

/**
     * description : 自定义Binder实现消息传递
     * autour      : 姜春雨@沈阳
     * date        : 17-3-24 上午10:33
     * mail        : 1055655886@qq.com
     */
    public class IPCBinder extends Binder {

        /**
         *  处理消息
         * @param code 识别码
         * @param data 调用transact的对象传送过去的参数
         * @param reply 调用onTransact的对象返回的参数
         * @param flags Java里面默认的native方法都是阻塞的,当不需要阻塞的时候设置为IBinder.FLAG_ONEWAY,否则设置为0
         * @return
         * @throws RemoteException
         */
        @Override
        protected boolean onTransact(final int code, final Parcel data, final Parcel reply, final int flags) throws RemoteException {
            data.enforceInterface(Contant.DESCRIPTOR);
            Bundle bundle = dealMessage(code,data.readBundle());
            reply.writeNoException();
            reply.writeBundle(bundle);
            return true;
        }

        /**
         * 获取当前Service,用于与Service通信
         * @return
         */
        public BaseIPCService getService(){
            return BaseIPCService.this;
        }
    }

暂时还未找到如何实现异步通信

客户端发送消息

private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            Log.d(TAG, "---  onServiceDisconnected   --- ");
            connect = false;
            if (mConnectListener != null) {
                mConnectListener.onDisconnected();
            }
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            if (service == null) {
                connect = false;
                Log.d(TAG, "---  onServiceConnected  failed service == null   ---");
                if (mConnectListener != null) {
                    mConnectListener.onConnected(false);
                }
            } else {
                Log.d(TAG, "---  onServiceConnected  sucessed   ---");
                connect = true;
                if (mConnectListener != null) {
                    mConnectListener.onConnected(true);
                }
                mIBinder = service;
            }
        }
    };

连接成功后通过可以获取到Binder对象
发送消息使用 mIBinder.transact(code, _data, _reply, 0);

 /**
     * 向服务端发送消息
     * @param code 消息类型
     * @param bundle 消息体
     * @return 返回值为服务器的返回
     * @throws Exception
     */
    public Bundle send(int code, Bundle bundle) throws Exception {
        if (mIBinder == null) {
            throw new NullPointerException("IBinder is Null");
        }
        Bundle result = null;
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeBundle(bundle);
            mIBinder.transact(code,
                    _data, _reply, 0);
            _reply.readException();
            result = _reply.readBundle();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        } finally {
            _reply.recycle();
            _data.recycle();
            return result;
        }
    }

Message通信

服务端的onBind是这么写的:

public IBinder onBind(Intent intent)
    {
        return mMessenger.getBinder();
    }

那么点进去:

public IBinder getBinder() {
        return mTarget.asBinder();
    }

可以看到返回的是mTarget.asBinder();

mTarget是哪来的呢?

HandlerThread mHandlerThread = new HandlerThread("BaseMsgIPCService");
            mHandlerThread.start();
            Handler mHandler= new Handler(mHandlerThread.getLooper()){
                @Override
                public void handleMessage(Message msgfromClient)
                {
                    super.handleMessage(msgfromClient);
                }
            };
            mMessenger= new Messenger(mHandler);

 public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

原来是Handler返回的,我们继续跟进去

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

     private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

mTarget是一个MessengerImpl对象,那么asBinder实际上是返回this,也就是MessengerImpl对象;
客户端
客户端首先通过onServiceConnected拿到sevice(Ibinder)对象,这里没什么特殊的,我们平时的写法也是这样的,只不过我们平时会这么写:
IMessenger.Stub.asInterface(service)拿到接口对象进行调用;
而,我们的代码中是
mService = new Messenger(service);
跟进去,你会发现:

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

和我们平时的写法一模一样!
到这里就可以明白,客户端与服务端通信,实际上和我们平时的写法没有任何区别,通过编写aidl文件,服务端onBind利用Stub编写接口实现返回;客户端利用回调得到的IBinder对象,使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用。
(2)服务端与客户端通信
那么,客户端与服务端通信的确没什么特殊的地方,我们完全也可以编写个类似的aidl文件实现;那么服务端是如何与客户端通信的呢?
还记得,客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个mMessenger,我们在Activity中初始化的。
那么将消息发送到服务端,肯定是通过序列化与反序列化拿到Message对象,我们看下Message的反序列化的代码:

# Message
private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
        sendingUid = source.readInt();
    }

主要看replyTo,调用的是Messenger.readMessengerOrNullFromParcel

public static Messenger readMessengerOrNullFromParcel(Parcel in) {
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;
    }

    public static void writeMessengerOrNullToParcel(Messenger messenger,
            Parcel out) {
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
                : null);
    }

通过上面的writeMessengerOrNullToParcel可以看到,它将客户端的messenger.mTarget.asBinder()对象进行了恢复,客户端的message.mTarget.asBinder()是什么?
客户端也是通过Handler创建的Messenger,于是asBinder返回的是:

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
 final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

   public IBinder getBinder() {
        return mTarget.asBinder();
    }

那么asBinder,实际上就是MessengerImpl extends IMessenger.Stub
中的asBinder了。

#IMessenger.Stub

@Override 
public android.os.IBinder asBinder()
{
return this;
}

那么其实返回的就是MessengerImpl对象自己。到这里可以看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。
最终,发送给客户端的代码是这么写的:

msgfromClient.replyTo.send(msgToClient);

public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

这个mTarget实际上就是对客户端的MessengerImpl对象的封装,那么send(message)(屏蔽了transact/onTransact的细节),这个message最终肯定传到客户端的handler的handleMessage方法中。
Message源码分析部分摘抄自鸿洋_的博客

欢迎大家follow,star,如果发现bug,希望您能够及时联系我

IPCCommunication github地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容