Android 開發(fā)藝術探索筆記(五) 之 Android 中的 IPC 方式

Android 中進行 IPC 的方式有很多枝恋,比如 在 Activity 或者廣播中用 Intent 傳遞小數據段标、共享文件沃但、Binder拐辽、ContentProvider拣挪、Socket。下面詳細介紹一下各種方式俱诸。

1.使用 Bundle

Android 的 Activity 菠劝、Service 、Recriver 支持在 Intent 中傳遞 Bundle 數據睁搭,由于 Bundle 實現了 Parcelable 接口赶诊,所以當我們在一個進程啟動另一個進程的 Activity 、Service 园骆、Receiver 后舔痪,我們可以在 Bundle 中附加我們需要傳遞的數據,其中包括基本數據類型锌唾、實現了 Parcelable 或者 Serializable 接口的對象還有一些 Android 支持的特殊對象锄码。

2.使用文件共享

兩個進程通過讀/寫同一個文件來交換數據也可以進行 IPC 。但是注意從文件中反序列化得到的對象和原來序列化的對象不是同一個對象,他們只是內容相同而已巍耗。這種方式適合在對數據同步要求不高的進程之間進行通信秋麸,并且要妥善處理并發(fā)的讀/寫問題。

PS: 使用 SharePrefrences 在面對高并發(fā)的讀/寫訪問的情況下炬太,會有很大幾率會丟失數據灸蟆,因此 IPC 不建議使用 SharePrefrences 。

3.使用 Messenger

Messenger 其實是封裝好的 AIDL 亲族,而且由于它一次只處理一個請求炒考,因此在服務端不需要考慮線程同步的問題。

下面我們來使用一下 Messenger 霎迫。

  • 1)建立服務端進程

    我們在服務端創(chuàng)建一個 Service 來處理客戶端的連接請求锭部,并且通過創(chuàng)建一個 Handler 來構建服務端的 Messenger 對象妒貌,然后在 Service 的 onBind()中返回服務端 Messenger 的 Binder 對象,可以理解成:我(服務端)將可以和我通信的工具(Binder)經過我的 Messenger 包裝后,發(fā)給你(客戶端)觉痛,你通過這個工具包裝成一個 Messenger 對象才可以發(fā)消息給我躺酒。

    如果服務端想要回復客戶端消息啸箫,就要客戶端在發(fā)送過來的 Message 中包含一個用于回復客戶端的 Messenger 對象馆里,服務端收到這個對象后用這個對象發(fā)送消息返回給客戶端。

    服務端的源碼:https://github.com/innovatorCL/IPCServer

    show my code :

    /**
     *
     * 使用 Messenger 方式的服務端
     * Created by innovator on 2018/1/24.
     */
    
    public class MessengerService extends Service{
    
    
    private static final String TAG = "TAG";
    
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.i(TAG,"服務端收到客戶端發(fā)來的消息:"+msg.getData().getString("msg"));
    
                    //接收 客戶端 傳過來的 Messenger筒扒,用來回復 客戶端
                    Messenger clientMessenger = msg.replyTo;
                    Message message = Message.obtain(null,MyConstants.MSG_REPLY_TO_CLIENT);
                    Bundle data = new Bundle();
                    data.putString("msg","你好怯邪,我是服務器,很高興能和你對話");
                    message.setData(data);
    
                    try{
                        clientMessenger.send(message);
                    }catch (RemoteException e){
                        Log.i(TAG,"服務端回應客戶端異常:"+e.getMessage());
                    }
                    break;
    
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }
    
    //構造服務器的 Messenger
    private final Messenger messenger = new Messenger(new MessengerHandler());
    
    
    //連接客戶端的時候返回服務器的Binder對象給客戶端
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
    }
    
  • 2)建立客戶端進程

    我們在客戶端中首先要綁定服務端的 Service花墩,綁定成功后悬秉,通過服務端返回的 Binder 對象構造客戶端的 Messenger, 這樣就可以通過這個 Messenger 來給服務端發(fā)送消息了冰蘑,發(fā)送的消息類型是 Message 對象和泌。可以看到,用 Messenger 方式進行 IPC 的靈魂人物是 Binder懂缕,這個其實就是 Messenger 和 AIDL 底層的東西允跑。

    上面只是客戶端發(fā)送消息給服務端需要做的事王凑,如果服務端需要回應客戶端搪柑,就要在客戶端創(chuàng)建一個新的 Handler,然后通過這個 Handler 來創(chuàng)建一個新的 Messenger對象索烹,然后將這個新的 Messenger 對象放在上面客戶端給服務端發(fā)送的 Message 里面(將這個新的 Messenger 作為 Message 的 replyTo()方法的參數就可以了)工碾。 當服務端拿到這個客戶端傳過來的 Messenger 對象,就可以給客戶端發(fā)送消息了百姓。

    客戶端的源碼:https://github.com/innovatorCL/IPCClient

show my code

public class MessengerActivity extends AppCompatActivity {

  private static final String TAG = "MessengerActivity";

  //將從服務器獲取到的 Binder 對象包裝成自己的 Messenger 對象渊额,從而給服務端發(fā)送消息
  private Messenger mMessenger = null;
  private static final int SAY_HELLO = 0;
  private boolean mBound = false;

  private ServiceConnection connection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          Log.i("TAG","通過 Messenger 連接服務器成功");
          mBound = true;
          //根據服務器返回的 Binder 對象創(chuàng)建 Messenger 對象,并使用這個對象向服務器發(fā)送數據
          mMessenger = new Messenger(service);
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
          Log.i("TAG","斷開與服務器的連接");
          mBound = false;
      }
  };

  //構造用于服務器回復客戶端的Handler
  private static class ReplyMessengerHandler extends Handler{
      @Override
      public void handleMessage(Message msg) {
          switch (msg.what){
              case MSG_REPLY_TO_CLIENT:
                  Log.i("TAG","客戶端接收到了服務端通過 Messenger 發(fā)送過來的消息:"+msg.getData().getString("msg"));
                  break;
              default:
                  super.handleMessage(msg);
                  break;
          }
      }
  };

  //構造用于服務器回復客戶端的 Messeenger
  private Messenger mReplyMessenger = new Messenger(new ReplyMessengerHandler());

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_messenger);
      attempToBind();

  }

  @Override
  protected void onStart() {
      super.onStart();
      attempToBind();
  }

  @Override
  protected void onDestroy() {
      unbindService(connection);
      super.onDestroy();
  }

  private void attempToBind(){
      if(!mBound){
          Intent i = new Intent();
          i.setAction("com.innovator.messenger");
          i.setPackage("com.innovator.ipcserver");
          bindService(i,connection, Context.BIND_AUTO_CREATE);
      }
  }

  public void sayHello(View v){
      if(mMessenger == null){
          attempToBind();
          return;
      }

      if(mMessenger != null){
          Message message = Message.obtain(null,SAY_HELLO);
          Bundle data = new Bundle();
          data.putString("msg","你好,這是在客戶端發(fā)送給你的消息");
          message.setData(data);

          //將可以回復自身(客戶端)的工具 mReplyMessenger 傳給 服務端
          message.replyTo = mReplyMessenger;

          try {
              mMessenger.send(message);
          }catch (RemoteException e){
              Log.i("TAG","客戶端發(fā)送信息異常:"+e.getMessage());
          }
      }
  }
}
  • 調試的結果:

1.當客戶端連接到服務端的 Service 旬迹,然后給服務端發(fā)送消息時火惊,服務端打印的 Log :

服務端 Log

2.客戶端打印的 Log :

客戶端 Log
  • 總結一下

Messenger 方式是以串行的方式處理客戶端發(fā)送的消息的,如果有大量消息同時發(fā)送到服務端奔垦,服務端仍然只能一個一個處理屹耐,所以當有大量的并發(fā)請求,使用 Messenger 就不太合適了椿猎。同時惶岭,Messenger 只能用來傳遞消息,不能跨進程調用服務端的方法犯眠,這種情況也不適合使用 Messenger按灶。

4.使用 ContentProvider

本次講的內容已經比較多了,相信看到這里的同學應該已經疲勞了筐咧,ContentProvider 和 Socket 的內容比較多鸯旁,我們放在下一節(jié)來看吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末量蕊,一起剝皮案震驚了整個濱河市羡亩,隨后出現的幾起案子,更是在濱河造成了極大的恐慌危融,老刑警劉巖畏铆,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異吉殃,居然都是意外死亡辞居,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門蛋勺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓦灶,“玉大人,你說我怎么就攤上這事抱完≡籼眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵巧娱,是天一觀的道長碉怔。 經常有香客問我,道長禁添,這世上最難降的妖魔是什么撮胧? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮老翘,結果婚禮上芹啥,老公的妹妹穿的比我還像新娘锻离。我一直安慰自己,他們只是感情好墓怀,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布汽纠。 她就那樣靜靜地躺著,像睡著了一般傀履。 火紅的嫁衣襯著肌膚如雪疏虫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天啤呼,我揣著相機與錄音卧秘,去河邊找鬼。 笑死官扣,一個胖子當著我的面吹牛翅敌,可吹牛的內容都是我干的。 我是一名探鬼主播惕蹄,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚯涮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卖陵?” 一聲冷哼從身側響起遭顶,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泪蔫,沒想到半個月后棒旗,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡撩荣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年铣揉,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片餐曹。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逛拱,死狀恐怖,靈堂內的尸體忽然破棺而出台猴,到底是詐尸還是另有隱情朽合,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布饱狂,位于F島的核電站曹步,受9級特大地震影響,放射性物質發(fā)生泄漏嗡官。R本人自食惡果不足惜箭窜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衍腥。 院中可真熱鬧磺樱,春花似錦、人聲如沸婆咸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尚骄。三九已至块差,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倔丈,已是汗流浹背憨闰。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留需五,地道東北人鹉动。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像宏邮,于是被迫代替她去往敵國和親泽示。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容