Android進(jìn)程間(IPC機(jī)制)通信(Bundler,Messenger,AIDL,ContentProvider)


在Android中每個(gè)app都由Activity和Service組成的,這而Activity和Service可能運(yùn)行在同一個(gè)進(jìn)程中,也可能在不同的進(jìn)程中。

那么,不在同一個(gè)進(jìn)程的Activity或者Service是如何通信的呢?這時(shí)候就要用到Binder進(jìn)程間通信機(jī)制了。

而接下來要使用的一切都是基于Binder完成的,在Android中,無Binder不Andorid.

Bundle

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}

Bundle已經(jīng)實(shí)現(xiàn)了Parcelable接口,在它的序列化與反序列化特性下,他可以在不同的進(jìn)程中進(jìn)行數(shù)據(jù)的傳輸.

常規(guī)的是Intent又可以對Bundle進(jìn)行傳遞數(shù)據(jù),因此我們經(jīng)常在一個(gè)activity,service中利用intent傳輸數(shù)據(jù)就是Bundle的利用之一

  • ProcessActivity數(shù)據(jù)傳輸方
Bundle bundle = new Bundle();
        bundle.putString("ipc_bundle", "BundleName");
        Intent intent=new Intent(ProcessActivity.this,ProcessActivity2.class);
        intent.putExtras(bundle);
        startActivity(intent);

  • ProcessActivity2數(shù)據(jù)接收方
  Bundle bundle=getIntent().getExtras();
        String ipc=bundle.getString("ipc_bundle");

Messenger

Messenger屬于輕量化的IPC通信,他的底層是基于AIDL實(shí)現(xiàn).可以在不同的進(jìn)程中傳輸Message對象,客戶端發(fā)送一個(gè)Message到服務(wù)端,服務(wù)端借到消息后對消息進(jìn)行處理在包裝成Message再次反饋到客戶端.

  • 服務(wù)端
public class MessengerService extends Service {

    public static final String TAG = "MessengerService";

    private HandlerThread handlerThread;
    private Handler handler;
    //Messenger
    private Messenger messenger;

    @Override
    public void onCreate() {
        super.onCreate();
        int pid = android.os.Process.myPid();
        Log.d(TAG, "進(jìn)程Id:" + pid + "");

        //HandlerThread,使用Looper處理隊(duì)列消息
        handlerThread = new HandlerThread("messenger_server");
        handlerThread.start();
        //獲取Looper
        Looper looper = handlerThread.getLooper();
        //讓Handler 運(yùn)行在HandlerThread中

        handler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                replyToClientMsg(msg);//回復(fù)客戶端消息
                super.handleMessage(msg);
            }
        };

        messenger = new Messenger(handler);

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }


    private void replyToClientMsg(Message msg) {
        switch (msg.what) {
            case Constants.MSG_FROM_CLIENT://接受消息處理
                //模擬器服務(wù)器響應(yīng)過程
                Log.d(TAG, "msg what= [" + msg.what + "" + "]");
                Log.d(TAG, "msg arg1= [" + msg.arg1 + "" + "]");
                Log.d(TAG, "msg arg2= [" + msg.arg2 + "" + "]");
                Log.d(TAG,"客戶端發(fā)送的消息:"+msg.getData().getString(Constants.MESSENGER_KEY));

                Toast.makeText(MessengerService.this, msg.getData().getString(Constants.MESSENGER_KEY), Toast.LENGTH_SHORT).show();
                try {
                    Messenger msgFromClient = msg.replyTo;//客戶端回調(diào)
                    Message replyMsgToClient = Message.obtain(null, Constants.MSG_FROM_SERVICE);//回復(fù)給客戶端的消息
                    Bundle bundle = new Bundle();
                    bundle.putString(Constants.MESSENGER_KEY, "我也愛你");
                    replyMsgToClient.setData(bundle);

                    msgFromClient.send(replyMsgToClient);//發(fā)送Message消息體給客戶端
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

        }
    }

}

服務(wù)端通過Service來回應(yīng)客戶端的請求,通過一個(gè)Handler來實(shí)例化一個(gè)Messenger對象,在onBind中返回底層的Binder對象.

因?yàn)榭蛻舳丝赡芤淮涡曰蛘叨啻伟l(fā)送消息,在服務(wù)端需要一個(gè)消息隊(duì)列處理數(shù)據(jù)信息,因此使用HandlerThread,實(shí)現(xiàn)使用Looper來維護(hù)消息隊(duì)列.以此保證不會出現(xiàn)并發(fā)的情況。

  • 客戶端
public class MessengerActivity extends AppCompatActivity {
    private static final String TAG = MessengerActivity.class.getSimpleName();

    private Button button;

    private Messenger messengerService;//服務(wù)端Service

    private Messenger messengerClient = new Messenger(new MessageHandler());//客戶端Messenger


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        int pid = android.os.Process.myPid();
        Log.d(TAG, "進(jìn)程Id:" + pid + "");
        bindMessengerService();


        button = (Button) findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        sendMessage();

                    }
                }).start();
            }
        });

    }

    //綁定服務(wù)
    public void bindMessengerService() {
        Intent mIntent = new Intent(this, MessengerService.class);
        bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
        Log.e(TAG, "bindService invoked !");
    }

    //發(fā)送消息
    public void sendMessage() {
        Message msgFromClient = Message.obtain(null, Constants.MSG_FROM_CLIENT, 1, 2);
        Bundle data = new Bundle();
        data.putString(Constants.MESSENGER_KEY, "我愛你,你愛我嗎?");
        msgFromClient.setData(data);
        msgFromClient.replyTo = messengerClient;
        try {
            messengerService.send(msgFromClient);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

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


    private class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constants.MSG_FROM_SERVICE:
                                    Log.d(TAG,"服務(wù)端回復(fù)消息:"+msg.getData().getString(Constants.MESSENGER_KEY));

                    Toast.makeText(MessengerActivity.this,  msg.getData().getString(Constants.MESSENGER_KEY) , Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }


    private ServiceConnection mServiceConnection = new ServiceConnection() {
        /**
         * @param name
         * @param service
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messengerService = new Messenger(service);

        }

        /**
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {
            messengerService = null;
        }
    };
}
  • 圖示
09-08 03:12:19.052 15181-15228/com.allure.study D/MessengerService: 客戶端發(fā)送的消息:我愛你,你愛我嗎?
09-08 03:12:19.056 15181-15181/com.allure.study D/MessengerActivity: 服務(wù)端回復(fù)消息:我也愛你
android_state_layout.gif

客戶端主要用于綁定服務(wù)端,綁定之后生成一個(gè)Messenger,通過這個(gè)Messenger可以實(shí)現(xiàn)想服務(wù)端的消息發(fā)送.

服務(wù)端收到消息之后的反饋需要客戶端使用Message.reply回調(diào)給服務(wù)端,服務(wù)端在同樣使用Message.reply即可實(shí)現(xiàn)雙向的通信.

特別注意在客戶端的2個(gè)Messenger,一個(gè)是通過綁定服務(wù)端的獲取的,一個(gè)是自身創(chuàng)建給服務(wù)端的回調(diào).

AIDL

ADIL可以處理并發(fā)和跨進(jìn)程.
前面的Messenger的底層也是基于AIDL,但是在客戶端有多個(gè)消息同時(shí)發(fā)送到客戶端,如果按照Messenger的方式只能單獨(dú)的在消息隊(duì)列一個(gè)個(gè)處理,在處理這種場景時(shí)就更推薦使用AIDL進(jìn)行解決.

  • 服務(wù)端
QQ20170908-150237@2x.png

創(chuàng)建一個(gè)AIDL文件,結(jié)尾為.aidl

AIDL的數(shù)據(jù)類型

  • 基本的數(shù)據(jù)類型
  • HashMap,ArrayList,實(shí)現(xiàn)Parcelable的對象
  • AIDL接口

如果AIDL中使用了自定義的Parcelable,必須建一個(gè)同名的AIDL文件

public class AIDLService extends Service {

    private static final String TAG = "AIDLService";


    public AIDLService() {
    }

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");

        return super.onStartCommand(intent, flags, startId);

    }


    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");

        super.onDestroy();

    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind");

        return super.onUnbind(intent);

    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.e(TAG, "onRebind");

    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.e(TAG, "onTaskRemoved");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind");


        return binder;
    }

    private Binder binder = new TestAIDL.Stub() {


        @Override
        public String reply(String s) throws RemoteException {
            if(s.equals("你愛我嗎")){
                return  "我愛你";
            }else {
                return "沒有你愛我,我怎么愛你";
            }
        }
    };

}
  • 客戶端
public class AIDLActivity extends AppCompatActivity {

    private static final String TAG = AIDLActivity.class.getSimpleName();

    private Button button;
    
    private TestAIDL testAIDL;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        //綁定AIDL服務(wù)
        bindService();


        button = (Button) findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                   String s = testAIDL.reply("你愛我嗎");
                    Log.d(TAG,"你愛我嗎");
                    Log.d(TAG,s);
                    Toast.makeText(AIDLActivity.this, "你愛我嗎", Toast.LENGTH_SHORT).show();
                    Toast.makeText(AIDLActivity.this, s, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unBindServcie();

    }

    private void unBindServcie() {
        unbindService(mServiceConnection);
    }

    private void bindService() {
        Intent mIntent = new Intent(this, AIDLService.class);

        bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
        Log.e(TAG, "bindService invoked !");
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            testAIDL = TestAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            testAIDL = null;
        }
    };
}
  • 圖示
android_state_layout.gif

和Messenger類似,綁定服務(wù),獲取aidl對象,接口回調(diào)得到相應(yīng)的服務(wù)響應(yīng)信息.

源碼中可以看到AIDL.Stub屬于Binder子類,所以AIDL也是基于Binder來完成的

ContentProvider

ContentProvider和Messenger一樣底層也是基于AIDL Binder,和Messenger一樣在系統(tǒng)都做了一定程度封裝,便于上層的調(diào)用.最常見的便是我們獲取通訊錄使用ContentProvider

簡單實(shí)現(xiàn)一個(gè)ContentProvider的聯(lián)系人增刪改查的例子.

  • 數(shù)據(jù)庫
public class DBOpenHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "contacts.db";
    public static final String CONTACTS_TABLE_NAME = "contacts";

    private static final String CREATE_CONTACTS_TABLE = "CREATE TABLE IF NOT EXISTS "
            + CONTACTS_TABLE_NAME + " (phonenumber CHAR(11) PRIMARY KEY, name TEXT)";

    public static final int DB_VESION = 1;

    public DBOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VESION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_CONTACTS_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

  • ContentProvider
public class TestContentProvider extends ContentProvider {
    private static final String TAG = TestContentProvider.class.getSimpleName();

    private static final String AUTHORITY = "com.allure.study.interprocesscommunication.contentprovider";
    public static final String CONTACTS_URI = "content://" + AUTHORITY + "/" + DBOpenHelper.CONTACTS_TABLE_NAME;

    private static final int CONTACTS_TABLE_CODE = 0;

    private UriMatcher uriMatcher;
    private SQLiteDatabase sqLiteDatabase;

    @Override
    public boolean onCreate() {

        Log.v(TAG, "ContactsProvider進(jìn)程Id " + android.os.Process.myPid());

        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, DBOpenHelper.CONTACTS_TABLE_NAME, CONTACTS_TABLE_CODE);
        sqLiteDatabase = new DBOpenHelper(getContext()).getWritableDatabase();
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
        String tableName = getTableName(uri);
        if(tableName == null) {
            throw new IllegalArgumentException("Uri錯(cuò)誤: " + uri);
        }
        Cursor cursor = sqLiteDatabase.query(tableName, strings, s, strings1, s1, null, null);
        return cursor;
    }

    /**
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        String tableName = getTableName(uri);
        if(tableName == null) {
            throw new IllegalArgumentException("Erro Uri: " + uri);
        }
        sqLiteDatabase.insert(tableName, null, contentValues);
        getContext().getContentResolver().notifyChange(uri, null);
        return null;
    }

    @Override
    public int delete(Uri uri, String s, String[] strings) {
        String tableName = getTableName(uri);
        if(tableName == null) {
            throw new IllegalArgumentException("Erro Uri: " + uri);
        }
        int count = sqLiteDatabase.delete(tableName, s, strings);
        if(count > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
        return 0;
    }

    
    private String getTableName(Uri uri) {
        String tableName = null;
        switch (uriMatcher.match(uri)) {
            case CONTACTS_TABLE_CODE:
                tableName = DBOpenHelper.CONTACTS_TABLE_NAME;
                break;
        }
        return tableName;
    }
}

由于ContentProvider屬于四大組件因此我們在清單文件中需要對其進(jìn)行注冊

  <!--ContentProvider-->
        <provider
            android:authorities="com.allure.study.interprocesscommunication.contentprovider"
            android:name=".interprocesscommunication.contentprovider.TestContentProvider" />

** authorities和TestContentProvider類里的AUTHORITY徐亞保持一致**
** name代表ContentProvider類**

  • Activity 調(diào)用
ublic class ContentProviderActivity extends AppCompatActivity {

    private static final String TAG = ContentProviderActivity.class.getSimpleName();
    private Uri uri;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_provider);
        Log.v(TAG, "Activity進(jìn)程Id " + android.os.Process.myPid());
        uri = Uri.parse(TestContentProvider.CONTACTS_URI);
    }

    public void insert(View v) {
        ContentValues values = new ContentValues();
        values.put("phoneNumber", "15000000001");
        values.put("name", "Jacye");
        getContentResolver().insert(uri, values);
        Log.v(TAG, "插入了 " + "Jacye " + "電話號碼: 15000000001");
    }

    public void delete(View v) {
        getContentResolver().delete(uri, "name=?", new String[]{"Jacye"});
    }

    public void update(View v) {
    }

    public void query(View v) {
        String[] colum = new String[]{"phoneNumber", "name"};
        Cursor cursor = getContentResolver().query(uri, colum, null, null, null);
        while (cursor.moveToNext()) {
            String phoneNumber = cursor.getString(0);
            String name = cursor.getString(1);
            Log.v(TAG, "獲取到聯(lián)系人 " + phoneNumber + "  " + name);
        }
        cursor.close();
    }
}

顯示

09-08 05:04:40.978 31726-31726/com.allure.study V/ContentProviderActivity: 插入了 Jacye 電話號碼: 13333333333
09-08 05:04:42.870 31726-31726/com.allure.study V/ContentProviderActivity: 獲取到聯(lián)系人 13333333333  Jacye

總結(jié)

到此,Android中常用的IPC方式就已經(jīng)實(shí)現(xiàn)了,當(dāng)然還有其他的實(shí)現(xiàn)方式,比如說文件共享,Scoket通信等,在這里只講了Android 常用的.

  • Messenger適用于單一無并發(fā)的場景,也就是說同一個(gè)app的不同進(jìn)程
  • AIDL多用于并發(fā)跨進(jìn)程的場景(比如說2個(gè)app之間的交互),穩(wěn)定快速高效是他的優(yōu)點(diǎn),但是在通信約束上需要客戶端服務(wù)端都把AIDL加入自身項(xiàng)目。
  • contentprovider多用于跨進(jìn)程和數(shù)據(jù)共享方面,相比較于AIDL來說更輕便
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容