Android中的IPC方式

IPC是Inter-ProcessCommunication的縮寫,進程間通信、跨進程通信,是指兩個進程之間進行數據交換的過程。說起進程間通信,我們首先腦補下什么是進程,什么是線程,進程和線程是截然不同的概念。按照操作系統中的描述,線程是CPU調度的最小單元,同時線程是一種有限的系統資源。而進程一般指一個執行單元,在PC和移動設備上指一個程序或者一個應用。一個進程可以包含多個線程,因此進程和線程是包含與被包含的關系。最簡單的情況下,一個進程中可以只有一個線程,即主線程,在Android里面主線程也叫UI線程,在UI線程里才能操作界面元素。通常情況下一個進程中需要執行大量耗時的任務,如果這些任務放在主線程中去執行就會造成界面長時間無法響應,嚴重影響用戶體驗,這種情況在PC系統和移動系統中都存在,在Android中有一個特殊的名字叫做ANR(ApplicationNotResponding),即應用無響應。解決這個問題就需要用到線程,把一些耗時的任務放在線程中即可。

對于Android來說,它是一種基于Linux內核的移動操作系統,它的進程間通信方式并不能完全繼承自Linux,它有自己的進程間通信方式。在Android中最有特色的進程間通信方式就是Binder了,通過Binder可以輕松地實現進程間通信。除了Binder,Android還支持Socket,通過Socket也可以實現任意兩個終端之間的通信,當然同一個設備上的兩個進程通過Socket通信自然也是可以的。

具體IPC方式有很多,比如可以通過在Intent中附加extras來傳遞信息,或者通過共享文件的方式來共享數據,還可以采用Binder方式來跨進程通信,另外,ContentProvider天生就是支持跨進程訪問的,因此我們也可以采用它來進行IPC。此外,通過網絡通信也是可以實現數據傳遞的,所以Socket也可以實現IPC。下面就具體介紹幾種主要的IPC方式。

1.使用Bundle

四大組件中的三大組件(Activity、Service、BroadcastReceiver)都是支持在Intent中傳遞Bundle數據的,由于Bundle實現了Parcelable接口,所以它可以方便地在不同的進程間傳輸。這里傳輸的數據必須可以被序列化,比如基本數據類型和實現了Parcelable、Serializable接口的類都可以傳輸。

2.使用文件共享

這個看起來比較好理解,就是在SD卡中存儲個文件用來共享,但是這個明顯的有并發問題。眾所周知,SharedPreferences是Android提供的一個輕量級的存儲方案,在xml中以鍵值對的形式存儲。同樣的,在多進程的情況下,讀寫并不是可靠的。

3.使用Messenger

Messenger很多地方翻譯為信使,這個也算貼切。Messenger是一中輕量級的IPC方式,底層實現也是基于AIDL。

Messenger的使用方法很簡單,它對AIDL做了封裝,使得我們可以更簡便地進行進程間通信。同時,由于它一次處理一個請求,因此在服務端我們不用考慮線程同步的問題,這是因為服務端中不存在并發執行的情形。實現一個Messenger有如下幾個步驟,分為服務端和客戶端。

A.服務端進程首先,我們需要在服務端創建一個Service來處理客戶端的連接請求,同時創建一個Handler并通過它來創建一個Messenger對象,然后在Service的onBind中返回這個Messenger對象底層的Binder即可。

B.客戶端進程客戶端進程中,首先要綁定服務端的Service,綁定成功后用服務端返回的IBinder對象創建一個Messenger,通過這個Messenger就可以向服務端發送消息了,發消息類型為Message對象。如果需要服務端能夠回應客戶端,就和服務端一樣,我們還需要創建一個Handler并創建一個新的Messenger,并把這個Messenger對象通過Message的replyTo參數傳遞給服務端,服務端通過這個replyTo參數就可以回應客戶端。這聽起來可能還是有點抽象,不過看了下面的兩個例子,讀者肯定就都明白了。首先,我們來看一個簡單點的例子,在這個例子中服務端無法回應客戶端。

/**
 * Created by Stone on 2018/8/16.
 */
public class MessengerService extends Service {


    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler

    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}
public class MessengerActivity extends AppCompatActivity {

    private static final String TAG = " MessengerActivity";
    private TextView tvResult;
    private Messenger mService;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            Bundle data = new Bundle();
            data.putString("msg", " hello, this is client.");
            msg.setData(data);
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
        }
    };
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        tvResult = findViewById(R.id.tv_result);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

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

問題來了,實際使用中不可能只有客戶端訪問服務端,還有服務端響應客戶端,那我們怎么改進下呢?直接上代碼:

/**
 * Created by Stone on 2018/8/16.
 */
public class MessengerService extends Service {


    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler

    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message relpyMessage = Message.obtain(null, 2);
                    Bundle bundle = new Bundle();
                    bundle.putString(" reply", "消息已收到");
                    relpyMessage.setData(bundle);
                    try {
                        client.send(relpyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}
public class MessengerActivity extends AppCompatActivity {

    private static final String TAG = " MessengerActivity";
    private TextView tvResult;
    private Messenger mService;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            Bundle data = new Bundle();
            data.putString("msg", " hello, this is client.");
            msg.setData(data);
            //添加處理回復的Messenger
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
        }
    };
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
    private  class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 2:
                    Log.i(TAG, " receive msg from Service:" + msg.getData().getString(" reply"));
                    tvResult.append("\n receive msg from Service:" + msg.getData().getString(" reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        tvResult = findViewById(R.id.tv_result);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

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

從上面代碼可以看到,在收到客戶端的消息后,服務端把返回的消息放到了msg.replyTo中,這也是一個Messenger,具體接受的Messenger在MessengerActivity中定義,當然也是一組完整的Messenger跟Handler,具體可以看代碼。這里IPC的類型必須是Message支持的類型。最后給出Messenger的工作原理:

Messenger原理

4.使用AIDL

簡單的使用可參考Android Binder---AIDL ,這里接著Android Binder---AIDL介紹的AIDL繼續往下寫??紤]到用戶每次都是訪問getStudent方法比較費勁,如果StudentList有更新再通知用戶去獲取豈不是很省力?這里就要用到觀察者模式了。我們創建一個新的AIDL接口,這個用來監聽StudentList的變化。

// IOnNewStudentArrivedListener.aidl
package com.stone.demoandroid.entity;
import com.stone.demoandroid.entity.Student;
// Declare any non-default types here with import statements

interface IOnNewStudentArrivedListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onNewStudentArrived(in Student newStudent);
}

同樣的,之前的Manager也要有所變化添加兩個方法:registerListener、unregisterListener

// IStudentManager.aidl
package com.stone.demoandroid.entity;

// Declare any non-default types here with import statements
import com.stone.demoandroid.entity.Student;
import com.stone.demoandroid.entity.IOnNewStudentArrivedListener;
interface IStudentManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

    List<Student> getStudentList();
    void addStudent(in Student student);
    void registerListener( IOnNewStudentArrivedListener listener);
    void unregisterListener( IOnNewStudentArrivedListener listener);
}

Service跟Activity的代碼太長了就不全部貼出來了,有興趣的同學可以直接去Demo下載查看。這里有一點需要提一下,CopyOnWriteArrayList不能處理跨進程的Listener,需要用RemoteCallbackList,因為CopyOnWriteArrayList在服務端使用的Listener并不是客戶端定義的那個,看名字也知道只是一個Copy。還有就是并沒有處理ANR和Binder意外死亡的問題,有興趣額同學可以處理下。

5.使用ContentProvider

ContentProvider是Android中提供的專門用于不同應用間進行數據共享的方式,從這一點來看,它天生就適合進程間通信。和Messenger一樣,ContentProvider的底層實現同樣也是Binder,由此可見,Binder在Android系統中是何等的重要。雖然ContentProvider的底層實現是Binder,但是它的使用過程要比AIDL簡單許多,這是因為系統已經為我們做了封裝,使得我們無須關心底層細節即可輕松實現IPC。具體的實現這里就不贅述了,四大組件之一,用法一搜一堆,有興趣的可以看下Demo

6.使用Socket

Socket也稱為“套接字”,是網絡通信中的概念,它分為流式套接字和用戶數據報套接字兩種,分別對應于網絡的傳輸控制層中的TCP和UDP協議。本科的時候選修過JavaSocket,作業有實現TCP和UDP協議,考試竟然是上機,自己實現一個局域網的聊天工具。

具體的實現,大家可以看下Demo

選擇合適的IPC(任玉剛. Android開發藝術探索. 電子工業出版社. ),這里借大佬總結的帖在這里希望對大家有幫助:

image

主要的幾個IPC的方式就簡單介紹到這里,有什么問題可以留言。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,076評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,862評論 2 59
  • 使用Bundle 四大組件中的三大組件(Activity、Service、Receiver)都是支持在Intent...
    最最最最醉人閱讀 642評論 0 3
  • 灰蒙蒙的天空,灰蒙蒙的心,我以為也就如此吧! 似風,似云,似雨,似雪,你模糊的背影! 三千六百五十個輪回,我終于等...
    煙雨香茗閱讀 191評論 2 4
  • 國慶長假,我在糾結中踏上了前往“有福之州”——福州的列車。 離開牙牙學語的稚子,...
    汐_3dd8閱讀 310評論 0 0