Android IPC機制利用Messenger實現跨進程通訊_Android

來源:互聯網
上載者:User

寫作原因:跨進程通訊的實現和理解是Android進階中重要的一環。下面博主分享IPC一些相關知識、操作及自己在學習IPC過程中的一些理解。這一章使用Messenger實現跨進程通訊,其中bindService基礎部分參見Android IPC機制綁定Service實現本地通訊。

跨進程簡介

在介紹使用Messenger跨進程通訊之前先要瞭解以下問題:為什麼需要跨進程通訊?只有有了需求才有學習的價值。我個人將不同進程簡單的理解為不同的應用程式(當然也有例外,比如可以在同一個應用程式中開啟兩個或多個進程)。由於進程之間不能像線程一樣共用記憶體,所以資料通訊不能像線程一般進行。在Android中可以使用bundle,廣播,Messenger,AIDL和Socket進行跨進程通訊。本章利用Messenger分別進行單應用程式多進程單向通訊和多應用程式多進程雙向通訊的實現。

Messenger介紹

Messenger是通過使用Message來實現跨進程通訊,一次實現一個請求的方式,這是它的優點也是缺點。其底層實現為AIDL(下章我將闡述)。Messenger的優點是:基於Message,方便使用;支援回調的方式,也就是服務端處理完成長任務可以和用戶端互動;不需要編寫aidl檔案。

Messenger使用流程如下(轉載):

單應用程式多進程單向通訊

先介紹一下Android中單應用程式開啟多進程的方法,實際上只要在mainfests中的你想開啟新進程的組件的XML中添加<android:progress = ":remote(可以自訂)">或者<android:progress = "包.remote(可以自訂)">就行。如:

<service android:name=".CustomService" android:process=":remote"/>

這樣就把Service放在新的線程中運行了。

Service實現

 下面是服務端的代碼實現,具體思路是:建立一個Handler對象用來處理用戶端發送來的訊息,再建立一個Messenger對象將上面的Handler對象作為參數傳入,這樣我們就獲得了一個信使。下面就是通過getBinder()把這個信使建立的Binder對象返回給用戶端(一旦用戶端拿到這個Binder,又可以將它還原為Messenger)。Handler中處理資訊為:當得到的Message的what值為MSG_SAY_HELLO時輸出Toast。

public class CustomService extends Service{  static final int MSG_SAY_HELLO = 1;  //實現一個能夠處理接收資訊的Handler  class IncomingHandler extends Handler{    @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      switch(msg.what){        case MSG_SAY_HELLO:          Toast.makeText(getApplicationContext(),"hello!",Toast.LENGTH_SHORT).show();          break;        default:          super.handleMessage(msg);      }    }  }  //被用戶端接收的Messenger對象  final Messenger messenger = new Messenger(new IncomingHandler());  @Override  public IBinder onBind(Intent intent) {    Toast.makeText(getApplicationContext(),"binding!",Toast.LENGTH_SHORT).show();    return messenger.getBinder();  }}

 Activity實現

 在用戶端中我們應該做的是:拿到服務端傳來的Messenger對象(在ServiceConnection中取得,具體參見上一篇文章),然後建立一個Message對象,為Message寫入資料,注意Message中的what要與服務端中Handler對象中的what一致。使用該Messenger通過send()將Message發送給服務端,這樣就可以實現用戶端與服務端的單向通訊了。

具體代碼如下:

 

public class MainActivity extends AppCompatActivity {  private Button mBtStart;  private Messenger messenger = null;  private boolean mBound;  private TextView mTvMsg;  private ServiceConnection serviceConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {      messenger = new Messenger(service);      mBound = true;    }    @Override    public void onServiceDisconnected(ComponentName name) {      messenger = null;      mBound = false;    }  };  public void sayHello(View v){    if(!mBound) return;    Message msg = Message.obtain(null,CustomService.MSG_SAY_HELLO,0,0);    try{      messenger.send(msg);    }catch (RemoteException e){      e.printStackTrace();    }  }  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mBtStart = (Button) findViewById(R.id.bt_start);    mTvMsg = (TextView) findViewById(R.id.tv_msg);    mBtStart.setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        Intent intent = new Intent(MainActivity.this,CustomService.class);        bindService(new Intent(MainActivity.this,CustomService.class),serviceConnection, Context.BIND_AUTO_CREATE);      }    });  }  @Override  protected void onStop() {    super.onStop();    // Unbind from the service    if (mBound) {      unbindService(serviceConnection);      mBound = false;    }  }}

 以上過程一開始理解可能會有些抽象,多動手才能加深理解。

 多應用程式多進程雙向通訊

仍然以Activity和Service通訊為例,不過這次我們需要建立兩個Module(ClientApp和ServiceApp),ClientApp中就一個Activity,ServiceApp中一個Service(關於沒有Activity的App的啟動方式自行百度)。這樣就是兩個應用程式兩個進程的情況了。

下面開始分析這種情況下的Messenger的用法:

Service實現

依然是先從Service開始。大致思路與上面的Service的建立類似,但又一點不同的是,這次我們需要建立一個clientMessenger來實現Service向用戶端發送Message操作。在Service的Handler中有這樣一段代碼,

clientMessenger = msg.replyTo;//這個Message是在用戶端中建立的        if(clientMessenger!=null){          Message msgToClient = Message.obtain();          msgToClient.what = SEND_MESSAGE_CODE;          Bundle bundle = new Bundle();          bundle.putString("msg","用戶端,我接收到你的訊息了,這是我回應給你的,看到了嗎?");          msgToClient.setData(bundle);          try {            clientMessenger.send(msgToClient);          } catch (RemoteException e) {            e.printStackTrace();          }        }

上面代碼用來接收到用戶端發送來的資料後向用戶端發送資料作出回應。注意這裡的clientMessenger = msg.replyTo;,是指從用戶端中取出與msg一道捆綁過來的clientMessenger對象。利用clientMessenger就可以實現向Activity中返回資料了。

詳細代碼如下:

public class MyService extends Service {  private static final int RECEIVE_MESSAGE_CODE = 0x0001;  private static final int SEND_MESSAGE_CODE = 0x0002;  private Messenger clientMessenger = null;  private Messenger serviceMessenger = new Messenger(new ServiceHandler());  @Override  public IBinder onBind(Intent intent) {    return serviceMessenger.getBinder();  }  @Override  public void onCreate() {    super.onCreate();  }  @Override  public void onDestroy() {    super.onDestroy();    clientMessenger = null;  }  private class ServiceHandler extends Handler{    @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      if(msg.what == RECEIVE_MESSAGE_CODE){        Bundle data = msg.getData();        if(data != null){          String str = data.getString("msg");          Toast.makeText(getApplicationContext(),"Service:I received the message:"+str,Toast.LENGTH_SHORT).show();        }        clientMessenger = msg.replyTo;//這個Message是在用戶端中建立的        if(clientMessenger!=null){          Message msgToClient = Message.obtain();          msgToClient.what = SEND_MESSAGE_CODE;          Bundle bundle = new Bundle();          bundle.putString("msg","用戶端,我接收到你的訊息了,這是我回應給你的,看到了嗎?");          msgToClient.setData(bundle);          try {            clientMessenger.send(msgToClient);          } catch (RemoteException e) {            e.printStackTrace();          }        }      }    }  }}

Activity實現:

Activity中主要是實現綁定發送資料和解除綁定兩大塊功能。綁定先發送顯式Intent(5.0以上不支援隱式啟動Service,具體操作見下面的啟動過程)綁定Service,當綁定成功後擷取Messenger對象並使用該對象發送Message對象msg給Service,具體操作與上面的一樣。這裡有一點不一樣的是,為了能夠使得Service能獲得clientMessenger,我們必須手動將msg與clientMessenger關聯,即:msg.replyTo = clientMessenger;。這樣Service在獲得Activity發送過來的Message的同時也可以取到clientMessenger。而clientMessenger必須先建立出來,方法與單向擷取時一致。

詳細代碼如下:

public class MainActivity extends AppCompatActivity{  private Button mBtBind;  private Button mBtUnBind;  private TextView mTvMsg;  private static final int SEND_MESSAGE_CODE = 0x0001;  private static final int RECEIVE_MESSAGE_CODE = 0x0002;  private boolean isBound = false;  private String SERVICE_ACTION = "com.example.serviceapp.MyService";  private Messenger serviceMessenger = null;  private Messenger clientMessenger = new Messenger(new ClientHandler());  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mBtBind = (Button) findViewById(R.id.bt_bind);    mBtUnBind = (Button) findViewById(R.id.bt_unbind);    mTvMsg = (TextView) findViewById(R.id.tv_msg);    mBtUnBind.setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        if(isBound){          unbindService(serviceConnection);        }      }    });    mBtBind.setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        if(!isBound){          Intent intent = new Intent();          intent.setAction(SERVICE_ACTION);          intent.addCategory(Intent.CATEGORY_DEFAULT);          PackageManager pm = getPackageManager();          ResolveInfo info = pm.resolveService(intent,0);          if(info != null){            String packageName = info.serviceInfo.packageName;            String serviceName = info.serviceInfo.name;            ComponentName componentName = new ComponentName(packageName,serviceName);            intent.setComponent(componentName);            bindService(intent,serviceConnection,BIND_AUTO_CREATE);          }        }      }    });  }  private ServiceConnection serviceConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {      serviceMessenger = new Messenger(service);      isBound = true;      Message msg = Message.obtain();      msg.what = SEND_MESSAGE_CODE;      Bundle data = new Bundle();      data.putString("msg","你好,MyService,我是用戶端");      msg.setData(data);      msg.replyTo = clientMessenger;      try {        serviceMessenger.send(msg);      } catch (RemoteException e) {        e.printStackTrace();      }    }    @Override    public void onServiceDisconnected(ComponentName name) {      serviceMessenger = null;      isBound = false;    }  };  private class ClientHandler extends Handler{    @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      if(msg.what == RECEIVE_MESSAGE_CODE){        Bundle data = msg.getData();        if(data != null){          String str = data.getString("msg");          mTvMsg.setText(str);        }      }    }  }}

總結:

這一塊理解起來可能比較吃力,所以希望讀者多加嘗試,為後面的AIDL跨通訊方式學習做準備。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.