美文网首页Android开发经验谈Android开发半栈工程师
Android进程间(IPC机制)通信(Bundler,Mess

Android进程间(IPC机制)通信(Bundler,Mess

作者: Ch3r1sh | 来源:发表于2017-09-08 17:19 被阅读77次

    在Android中每个app都由Activity和Service组成的,这而Activity和Service可能运行在同一个进程中,也可能在不同的进程中。

    那么,不在同一个进程的Activity或者Service是如何通信的呢?这时候就要用到Binder进程间通信机制了。

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

    Bundle

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

    Bundle已经实现了Parcelable接口,在它的序列化与反序列化特性下,他可以在不同的进程中进行数据的传输.

    常规的是Intent又可以对Bundle进行传递数据,因此我们经常在一个activity,service中利用intent传输数据就是Bundle的利用之一

    • ProcessActivity数据传输方
    Bundle bundle = new Bundle();
            bundle.putString("ipc_bundle", "BundleName");
            Intent intent=new Intent(ProcessActivity.this,ProcessActivity2.class);
            intent.putExtras(bundle);
            startActivity(intent);
    
    
    • ProcessActivity2数据接收方
      Bundle bundle=getIntent().getExtras();
            String ipc=bundle.getString("ipc_bundle");
    

    Messenger

    Messenger属于轻量化的IPC通信,他的底层是基于AIDL实现.可以在不同的进程中传输Message对象,客户端发送一个Message到服务端,服务端借到消息后对消息进行处理在包装成Message再次反馈到客户端.

    • 服务端
    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, "进程Id:" + pid + "");
    
            //HandlerThread,使用Looper处理队列消息
            handlerThread = new HandlerThread("messenger_server");
            handlerThread.start();
            //获取Looper
            Looper looper = handlerThread.getLooper();
            //让Handler 运行在HandlerThread中
    
            handler = new Handler(looper) {
                @Override
                public void handleMessage(Message msg) {
                    replyToClientMsg(msg);//回复客户端消息
                    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://接受消息处理
                    //模拟器服务器响应过程
                    Log.d(TAG, "msg what= [" + msg.what + "" + "]");
                    Log.d(TAG, "msg arg1= [" + msg.arg1 + "" + "]");
                    Log.d(TAG, "msg arg2= [" + msg.arg2 + "" + "]");
                    Log.d(TAG,"客户端发送的消息:"+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;//客户端回调
                        Message replyMsgToClient = Message.obtain(null, Constants.MSG_FROM_SERVICE);//回复给客户端的消息
                        Bundle bundle = new Bundle();
                        bundle.putString(Constants.MESSENGER_KEY, "我也爱你");
                        replyMsgToClient.setData(bundle);
    
                        msgFromClient.send(replyMsgToClient);//发送Message消息体给客户端
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
    
            }
        }
    
    }
    

    服务端通过Service来回应客户端的请求,通过一个Handler来实例化一个Messenger对象,在onBind中返回底层的Binder对象.

    因为客户端可能一次性或者多次发送消息,在服务端需要一个消息队列处理数据信息,因此使用HandlerThread,实现使用Looper来维护消息队列.以此保证不会出现并发的情况。

    • 客户端
    public class MessengerActivity extends AppCompatActivity {
        private static final String TAG = MessengerActivity.class.getSimpleName();
    
        private Button button;
    
        private Messenger messengerService;//服务端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, "进程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();
                }
            });
    
        }
    
        //绑定服务
        public void bindMessengerService() {
            Intent mIntent = new Intent(this, MessengerService.class);
            bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
            Log.e(TAG, "bindService invoked !");
        }
    
        //发送消息
        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,"服务端回复消息:"+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: 客户端发送的消息:我爱你,你爱我吗?
    09-08 03:12:19.056 15181-15181/com.allure.study D/MessengerActivity: 服务端回复消息:我也爱你
    
    android_state_layout.gif

    客户端主要用于绑定服务端,绑定之后生成一个Messenger,通过这个Messenger可以实现想服务端的消息发送.

    服务端收到消息之后的反馈需要客户端使用Message.reply回调给服务端,服务端在同样使用Message.reply即可实现双向的通信.

    特别注意在客户端的2个Messenger,一个是通过绑定服务端的获取的,一个是自身创建给服务端的回调.

    AIDL

    ADIL可以处理并发和跨进程.
    前面的Messenger的底层也是基于AIDL,但是在客户端有多个消息同时发送到客户端,如果按照Messenger的方式只能单独的在消息队列一个个处理,在处理这种场景时就更推荐使用AIDL进行解决.

    • 服务端
    QQ20170908-150237@2x.png

    创建一个AIDL文件,结尾为.aidl

    AIDL的数据类型

    • 基本的数据类型
    • HashMap,ArrayList,实现Parcelable的对象
    • AIDL接口

    如果AIDL中使用了自定义的Parcelable,必须建一个同名的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服务
            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类似,绑定服务,获取aidl对象,接口回调得到相应的服务响应信息.

    源码中可以看到AIDL.Stub属于Binder子类,所以AIDL也是基于Binder来完成的

    ContentProvider

    ContentProvider和Messenger一样底层也是基于AIDL Binder,和Messenger一样在系统都做了一定程度封装,便于上层的调用.最常见的便是我们获取通讯录使用ContentProvider

    简单实现一个ContentProvider的联系人增删改查的例子.

    • 数据库
    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进程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错误: " + 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属于四大组件因此我们在清单文件中需要对其进行注册

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

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

    • Activity 调用
    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进程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, "获取到联系人 " + 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: 获取到联系人 13333333333  Jacye
    

    总结

    到此,Android中常用的IPC方式就已经实现了,当然还有其他的实现方式,比如说文件共享,Scoket通信等,在这里只讲了Android 常用的.

    • Messenger适用于单一无并发的场景,也就是说同一个app的不同进程
    • AIDL多用于并发跨进程的场景(比如说2个app之间的交互),稳定快速高效是他的优点,但是在通信约束上需要客户端服务端都把AIDL加入自身项目。
    • contentprovider多用于跨进程和数据共享方面,相比较于AIDL来说更轻便

    相关文章

      网友评论

        本文标题:Android进程间(IPC机制)通信(Bundler,Mess

        本文链接:https://www.haomeiwen.com/subject/qmogjxtx.html