學習筆記(2)IPC機制

chensheng發表於2019-04-27

IPC簡介以及基本知識

1 IPC含義

程式間通訊或者跨程式通訊,是指兩個程式之間進行資料交換的過程。程式指的一般都為一個程式或者一個應用,一個程式可以包含多個執行緒,執行緒指的是一個執行單元(如UI執行緒等等)

2 IPC基本知識

要理解IPC機制,學習IPC的一些基本知識概念是必須的,主要包含三個內容:物件序列化,Binder。

2.1 序列化

2.1.1 Serializable序列化

類直接實現Serializable介面,VersionUID可寫可不寫,寫時有利於反序列化的進行,不寫時有時候會造成物件反序列化失敗。失敗的原因:序列化時會判斷要反序列化的物件是否還是和當前類是同一個類,若不是則反序列化失敗(增加資料,刪除資料等時類會改變)

2.1.2 Parcelable

實現Parcelable介面,對一個完整的物件進行分解,而分解後的每一個部分都是Intent所支援的資料型別

2.2 Binder

2.2.1 Binder是Android中一種跨程式通訊方式

  1. 從Android Framework來看,Binder是ServiceManager連線各種Manager(AcitvityManager,WindowManager等)和相應ServiceManager的橋樑。
  2. 從Android應用層來說,Binder是客戶端與服務端進行通訊的媒介(當客戶端bindService時,服務端就會返回一個Binder物件,通過對這個物件的操作,客戶端就可以獲取到服務端的資料等)

2.2.2 Binder底層實現

3 Android中的IPC方式

3.1 使用Bundle

Activity,Service,Receiver三大元件都支援在Intent中傳遞Bundle資料

往一個服務端中傳送資料:

 Intent intent = new Intent(MainActivity.this,MyDownloadService.class);
                    Bundle bundle = new Bundle();
                    bundle.putString("key","value");
                    intent.putExtras(bundle);
複製程式碼

在服務端中接收:

   Bundle mBundle = getIntent().getExtras();
                    if(mBundle!=null) {
                        String data = mBundle.getString("key");
                    }
複製程式碼

3.2 檔案共享

在一個程式中寫入(ObjectOutputStream)資料到檔案,在另一個程式中讀取(ObjectInputStream)檔案資料,從而也可實現多程式資料交流

特別:SharedPreferences,它通過鍵值對儲存資料,在底層採用XML檔案儲存鍵值對,但於系統對它的讀寫有一定的快取策略,,即記憶體中會有一份SharedPreferences檔案的快取,因此多程式模式下,系統對它的讀寫變得不可靠,容易丟失資料,不建議在多程式中採用

3.3 使用Messenger(信使)

通過Messenger可以在不同程式間傳遞Message物件,底層實現是AIDL,Messenger一次只處理一個請求,所以在客戶端我們不用考慮執行緒同步問題,因為它不存在併發的問題

3.3.1 服務端程式

  1. 建立Service來響應客戶端的請求,同時建立一個Handler,並從handleMessage()方法中獲取客戶端傳來的Messenger物件,通過這個Messenger傳送訊息message給客戶端從而實現服務端向客戶端發訊息,並通過此Handler來建立一個服務端的Messenger物件,
 /**
     * 處理客戶端發來的資訊
     */
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.d("chen", "從客戶端接收到資訊:" + msg.getData().getString("msg"));
                    //獲取客戶端的Messenger
                    Messenger client = msg.replyTo;
                    //建立一條訊息
                    Message replyMessage = Message.obtain(null,2);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","服務端已經收到資訊");
                    //往客戶端中傳送訊息
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    //通過Handler建立一個Messenger物件
    private final Messenger messenger = new Messenger(new MessengerHandler());
複製程式碼
  1. 然後在Service的onBind中返回這個服務端的Messenger物件底層的Binder給客戶端進行操作
 @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //給客戶端返回Binder,以便可以進行程式間的資料交流
        return messenger.getBinder();
    }
複製程式碼

3.3.2 客戶端程式

  1. 首先要建立一個ServiceConnection來繫結服務端的Service,繫結成功後用伺服器返回後的IBinder物件來建立一個Messenger物件
   private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //1獲取Messenger物件
            Messenger messenger = new Messenger(service);
            ......
        }

複製程式碼
  1. 通過這個物件就可以向伺服器傳送訊息了,傳送訊息的型別為Message物件,並建立一個ReplyMessenger傳給服務端,且傳送的訊息是到服務端中建立Messenger時所傳入的Handler中的handlerMessage()方法中獲取資訊
            //2建立一個Message物件,引數一:Handler 引數二:Message的傳送碼
            Message message = Message.obtain(null, 1);
            //3建立一個包裹進行資料的存放
            Bundle data = new Bundle();
            data.putString("msg", "這裡是客戶端");
            message.setData(data);
            //5返回一個接收伺服器資訊的Messenger物件給伺服器,讓伺服器進行傳送訊息,從而在客戶端中可以接收
            message.replyTo = getReplyMessenger;
            try {
                //開始傳送訊息
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
複製程式碼
  1. 如果需要服務端迴應客戶端,就和服務端一樣,我們還需要建立一個Handler並建立一個新的Messenger,並把這個Messenger物件通過客戶端傳遞給服務端的Messange的replyTo引數傳遞給服務端,服務端通過獲取這個replyTo引數就可以獲取到客戶端的Messenger物件,從而實現向客戶端進行傳送訊息
 /**
     * 獲取伺服器返回訊息的Messenger
     */
    private Messenger getReplyMessenger = new Messenger(new getMessengerHandler());

    private static class getMessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 2:
                    Log.d("chen", "從伺服器接收到訊息" + msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
複製程式碼

相關文章