Bootstrap

使用AIDL实现两个APP之间跨进程通信

昨天我们主管说准备把项目拆分一下,现在项目依赖了好几个负责串口通讯Library,准备把这些Library变成独立的APP,通过Android跨进程机制进行数据交互。然后让我写一个跨进程通信的Demo进行测试。

跨进程通信的方式有好几种,我这里用的是AIDL的方式。

一、同一个APP内Service和Activity通信

首先实现同一应用内跨进程通信,然后在实现APP间通信。因为AIDL是c/s模式,所以我们先创建一个服务端应用。

1、创建一个服务端的APP,然后在创建一个Service服务。

服务端包名:com.aidl.service
创建service
自动生成MyService类和manifest注册文件。

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="com.aidl.service.MyService"></action>
            </intent-filter>
        </service>

当然你也可以手动创建。这里的enabled和exported属性要设置为true,允许其他应用调用。

2、创建传送消息的消息对象

AIDL是不支持传递普通的Java对象的,不过支持Parcelable对象,所以我们的消息对象要实现Parcelable。

public class Msg implements Parcelable {

    private String msg;
    private long time;
    public Msg(String msg){
        this.msg = msg;
    }
    public Msg(String msg, long time) {
        this.msg = msg;
        this.time = time;
    }
    protected Msg(Parcel in) {
        msg = in.readString();
    }
    public static final Creator<Msg> CREATOR = new Creator<Msg>() {
        @Override
        public Msg createFromParcel(Parcel in) {
            return new Msg(in);
        }
        @Override
        public Msg[] newArray(int size) {
            return new Msg[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(msg);
    }
  	//set,get方法
}
3、创建AIDL文件

① 、在项目的根目录下创建一个Msg的AIDL文件。包名和项目包名一致,并且Msg.aidl声明为parcelable类型,Msg.aidl路径和Msg.java路径一致。
msg.aidl
②、创建一个收到消息的监听接口IReceiveMsgListener.aidl

package com.aidl.service;
import com.aidl.service.Msg;

interface IReceiveMsgListener {
   void onReceive(in Msg msg);
}

导入Msg.aidl的完整路径import com.aidl.service.Msg。 onReceive()中Msg使用 in 输入标记。

③、创建消息管理的接口IMsgManager.aidl

package com.aidl.service;
import com.aidl.service.IReceiveMsgListener;
import com.aidl.service.Msg;

interface IMsgManager {
   void sendMsg(in Msg msg);
   void registerReceiveListener(IReceiveMsgListener receiveListener);
   void unregisterReceiveListener(IReceiveMsgListener receiveListener);
}

IMsgManager.aidl中提供了发送消息的方法、注册和解除消息监听的方法。同样要导入Msg.aidl 和IReceiveMsgListener.aidl的完整路径。

到这里AIDL文件编写完成。最后需要Make Project,编译器生成对应的Binder文件
在这里插入图片描述

4、编写MyService服务代码
public class MyService extends Service {
    //AIDL不支持正常的接口回调,使用RemoteCallbackList实现接口回调
    private RemoteCallbackList<IReceiveMsgListener> mReceiveListener = new RemoteCallbackList<IReceiveMsgListener>();

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends IMsgManager.Stub {
        
        //发送消息
        public void sendMsg(Msg msg) {
            receiveMsg(msg);
        }
        
        //注册
        @Override
        public void registerReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
            mReceiveListener.register(receiveListener);
        }
        
        //解除注册
        @Override
        public void unregisterReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
            boolean success = mReceiveListener.unregister(receiveListener);
            if (success){
                Log.d("tag","===  解除注册成功");
            }else {
                Log.d("tag","===  解除注册失败 ");
            }
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            return super.onTransact(code, data, reply, flags);
        }
    }
    
    //收到消息处理
    public void receiveMsg(Msg msg) {
        //通知Callback循环开始,返回N为实现mReceiveListener回调的个数
        final int N = mReceiveListener.beginBroadcast();
        msg.setMsg("我是服务器,我收到了:"+msg.getMsg());
        for (int i = 0; i < N; i++){
            IReceiveMsgListener listener = mReceiveListener.getBroadcastItem(i);
            if (listener != null){
                try {
                    listener.onReceive(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        //通知通知Callback循环结束
        mReceiveListener.finishBroadcast();
    }
}

Service中通过Binder机制实现注册,解除注册和发送的方法。

Activity代码:

public class MainActivity extends AppCompatActivity {

    MyService.MyBinder binder = null;
    ServiceConnection mConnection;
    private ListView mListView;
    private EditText mEditText;
    private List<Msg> mMsgs = new ArrayList<>();
    private ListAdapter mAdapter;
    private IMsgManager mIMsgManager;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mAdapter.notifyDataSetChanged();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        mEditText = (EditText) findViewById(R.id.edit_text);
        mAdapter = new ListAdapter(this, mMsgs);
        mListView.setAdapter(mAdapter);

        mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                binder = (MyService.MyBinder) iBinder;
                IMsgManager msgManager = IMsgManager.Stub.asInterface(iBinder);
                mIMsgManager = msgManager;
                try {
                    mIMsgManager.asBinder().linkToDeath(mDeathRecipient, 0);
                    mIMsgManager.registerReceiveListener(mReceiveMsgListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {

            }
        };
		//注意Activity和Service是同一进程才能使用Intent通信
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);//开启服务

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (TextUtils.isEmpty(mEditText.getText().toString())) {
                    Toast.makeText(MainActivity.this, "消息为空", Toast.LENGTH_SHORT).show();
                    return;
                }
                binder.sendMsg(new Msg(mEditText.getText().toString().trim()));
            }
        });
        findViewById(R.id.btn_exit).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MainActivity.this.finish();
            }
        });
    }

    private IReceiveMsgListener mReceiveMsgListener = new IReceiveMsgListener.Stub() {

        @Override
        public void onReceive(Msg msg) throws RemoteException {
            msg.setTime(System.currentTimeMillis());
            mMsgs.add(msg);
            mHandler.sendEmptyMessage(1);
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        //当承载IBinder的进程消失时接收回调的接口
        @Override
        public void binderDied() {
            if (null == mIMsgManager) {
                return;
            }
            mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mIMsgManager = null;
            //断线重来逻辑
        }
    };

    @Override
    protected void onDestroy() {
        //解除注册
        if (null != mIMsgManager && mIMsgManager.asBinder().isBinderAlive()) {
            try {
                mIMsgManager.unregisterReceiveListener(mReceiveMsgListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        //解除绑定服务
        unbindService(mConnection);
        super.onDestroy();
    }
}

运行截图如下:
在这里插入图片描述
如果想要实现同一应用内跨进程通信需要修改Service的进程:

android:process=":remote"

需要使用action进行启动:

 Intent intent = new Intent();
 intent.setAction("com.aidl.service.MyService");

二、两个或多个APP之间通信

上面我们已经完成了服务端的功能,并且实现activity和service的双向通信。现在只需要将activity的功能放到另一个应用内实现就行了。

1、创建客户端应用。

包名:com.aidl.client

2、将服务端的AIDL文件拷贝到客户端

将服务端的AILD文件夹拷贝到客户端,并且包名和服务端一样,保持不变。服务端和客户端AIDL目录如下。
在这里插入图片描述

3、拷贝Msg.Java对象

我们知道客户端的包名是com.aidl.client,而Msg.aidl路径是com.aidl.service,所以我们要在com.aidl.service目录下创建Msg.java。
在这里插入图片描述
编写Activity代码:

public class MainActivity extends AppCompatActivity {

    private IMsgManager myBinder;//定义AIDL
    private ListView mListView;
    EditText mEditText;
    private List<Msg> mMsgs = new ArrayList<>();
    private ListAdapter mAdapter;
    private IMsgManager mIMsgManager;
    private Msg mMsg;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    mAdapter.notifyDataSetChanged();
                    mListView.smoothScrollToPosition(mMsgs.size() - 1);
            }
        }
    };
    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myBinder = IMsgManager.Stub.asInterface(iBinder);
            IMsgManager msgManager = IMsgManager.Stub.asInterface(iBinder);
            mIMsgManager = msgManager;
            try {
                //链接到死亡代理,当IBinder死亡时收到回调
                mIMsgManager.asBinder().linkToDeath(mDeathRecipient, 0);
                //注册消息监听
                mIMsgManager.registerReceiveListener(mReceiveMsgListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        mEditText = (EditText) findViewById(R.id.edit_text);
        mSendCountTv = (TextView) findViewById(R.id.send_count_tv);
        mReceiveCountTv = (TextView) findViewById(R.id.receive_count_tv);
     	mMsg = new Msg("");
        mAdapter = new ListAdapter(this, mMsgs);
        mListView.setAdapter(mAdapter);
   
        Intent intent = new Intent();
        //跨进程通信需要使用action启动
        intent.setAction("com.aidl.service.MyService");
        //android5.0之后,如果servicer不在同一个App的包中,需要设置service所在程序的包名
        intent.setPackage("com.aidl.service");
        //开启Service
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String msg = mEditText.getText().toString().trim();
                    if (TextUtils.isEmpty(msg)) {
                        Toast.makeText(MainActivity.this, "消息不能为空", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    mMsg.setMsg(msg);
                    //通过binder将消息传递到service
                    myBinder.sendMsg(mMsg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    //消息回调监听
    private IReceiveMsgListener mReceiveMsgListener = new IReceiveMsgListener.Stub() {
		//收到服务端消息
        @Override
        public void onReceive(Msg msg) throws RemoteException {
            msg.setTime(System.currentTimeMillis());
            if (mMsgs.size() > 100) {
                mMsgs.clear();
            }
            mMsgs.add(msg);
            mHandler.sendEmptyMessage(1);
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        /**
         * 当承载IBinder的进程消失时接收回调的接口
         */
        @Override
        public void binderDied() {
            if (null == mIMsgManager) {
                return;
            }
            mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mIMsgManager = null;
            //在这里重新绑定远程服务
        }
    };

    @Override
    protected void onDestroy() {
   		//解绑
        super.onDestroy();
    }
}

客户端运行截图:

服务端截图
因为我们服务端Activity也实现了IReceiveMsgListener 的接口,所以服务端Activity也能收到回调截图如下:
在这里插入图片描述
参考:部分参考Android开发艺术探索。

完整项目已经上传到GitHub上去了:https://github.com/Zhengyi66/AIDL

;