使用AIDL實現兩個APP之間跨進程通訊

昨天咱們主管說準備把項目拆分一下,如今項目依賴了好幾個負責串口通信Library,準備把這些Library變成獨立的APP,經過Android跨進程機制進行數據交互。而後讓我寫一個跨進程通訊的Demo進行測試。java

跨進程通訊的方式有好幾種,我這裏用的是AIDL的方式。android

1、同一個APP內Service和Activity通訊

首先實現同一應用內跨進程通訊,而後在實現APP間通訊。由於AIDL是c/s模式,因此咱們先建立一個服務端應用。git

一、建立一個服務端的APP,而後在建立一個Service服務。

服務端包名:com.aidl.service github

建立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,容許其餘應用調用。服務器

二、建立傳送消息的消息對象

AIDL是不支持傳遞普通的Java對象的,不過支持Parcelable對象,因此咱們的消息對象要實現Parcelable。ide

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方法
}
複製代碼
三、建立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 輸入標記。this

③、建立消息管理的接口IMsgManager.aidlspa

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的完整路徑。3d

到這裏AIDL文件編寫完成。最後須要Make Project,編譯器生成對應的Binder文件

在這裏插入圖片描述

四、編寫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");
複製代碼

2、兩個或多個APP之間通訊

上面咱們已經完成了服務端的功能,而且實現activity和service的雙向通訊。如今只須要將activity的功能放到另外一個應用內實現就好了。

一、建立客戶端應用。

包名:com.aidl.client

二、將服務端的AIDL文件拷貝到客戶端

將服務端的AILD文件夾拷貝到客戶端,而且包名和服務端同樣,保持不變。服務端和客戶端AIDL目錄以下。

在這裏插入圖片描述

三、拷貝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上去了:github.com/Zhengyi66/A…

相關文章
相關標籤/搜索