美文网首页
Android 中的IPC机制

Android 中的IPC机制

作者: bogerLiu | 来源:发表于2019-03-01 16:09 被阅读1次
    • IPC是跨进程,这并非Android系统独有的,像一般的跨进程通信有Socket,文件读写,管道等等。而Android独有的跨进程则是Binder机制。

    1. 进程和线程

    1. 线程
      1.1 什么是线程呢?
      是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。(网上搜的)

    1.2 如何创建新的线程

    • 继承Thread
        private class MyThread extends Thread{
            @Override
            public void run() {
                super.run();
                //run;
            }
        }
        
        MyThread myThread= new MyThread();
        myThread.start();
    
    • 实现Runnable接口
      class MyThread implements Runnable {
    
            @Override
            public void run() {
    
            }
        }
    
        public void test() {
            Thread t = new Thread(new MyThread());
            t.start();
        }
    
    • FutureTask方式
      Thread t = new Thread();
            FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return 10;
                }
            });
            try {
                Integer integer = futureTask.get(); //返回call()返回的值,这里会阻塞,知道线程结束了才会执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            new Thread(futureTask).start();
    
    
    

    说明 这里的FutureTask是Java5提供的一种新的线程实现方式,它的特点是有返回值,而且call()方法会抛出异常,FutureTask主要是内部的Callable来实现线程的这里可以在它的源码run()方法中发现

    • 线程池方法

    线程创建的方法就是以上四种方式,就先说到这,稍后会专门写一篇文章来总结线程,毕竟本文的重点是Binder。


    1. 进程
      2.1 什么是进程?
      系统正在运行的程序就是一个进程,进程里可以有多个线程。
      2.2 Android中创建进程的方式
      就是四大组件在AndroidManifest文件中指定android:process属性即可。
      例如以下2种方式
     <activity
                android:name=".ui.activity.WebActivity"
                android:process=":process" />
            <activity
                android:name=".ui.activity.WebActivity"
                android:process="com.test.tb.remote" />
    

    第一种 :结尾的 系统会在前面拼接上包名,并且属于当前应用独立进程,第二种则是全局进程,其他应用则可以通过ShareUID以及包名相同的方式跑在同一进程


    2.多进程的优缺点

    • 优点
    1. 对于同一个APP内如果所有模块都跑在同一进程则可能导致该进程内存分配过大,容易被系统回收。
      2.多模块进程,可以保证该模块崩溃不会导致主进程崩溃从而导致APP退出。
    • 缺点
    1. 静态成员和单例模式失效(多进程会有多个内存模块,放在内存中唯一的效果都会失效)
    2. Application会被创建多次
      3.线程同步失效
      4.SharePreference失效(底层通过读写XML文件实现,对于并发读写可能会出现问题)

    3. Android中的多进程数据交互方式

    1. 使用Bundle,通过Intent传递过去
    2. 使用文件共享
    3. 使用Messenger
    4. 使用AIDL
    5. 使用Socket通信
    6. 使用ContentProvider

    对于2,5 这里就不做详细介绍了,这个是万能的实现方式。


    接下来详细解释每一种实现方式

    第1种:使用Intent传递Bundle实现

    因为对于Activity,Service,Receiver,都可以通过Intent启动,在启动的时候直接将值传过去。


    第3种:使用Messenger信使实现

    这里Messenger是Android替我们实现好的AIDL方式,具体的Messenger用法如下

    • 客户端
    public class ClientActivity extends Activity {
        private Messenger mClient;
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mClient = new Messenger(iBinder);
                Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
                Bundle bundle = new Bundle();
                bundle.putString("key", "value from client");
                message.setData(bundle);
                try {
                    mClient.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
    
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Intent intent = new Intent(this, ServerService.class);
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
       public void sendMessage(String str) {
            if (mClient != null) {
                Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
                Bundle bundle = new Bundle();
                bundle.putString("key", str);
                message.setData(bundle);
                try {
                    mClient.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                } 
            }
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(serviceConnection);
        }
    }
    
    • 服务端
    //记得把service放在新的进程里
    public class ServerService extends Service {
    
        public static final int TAG_MESSAGE = 1000;
        private Handler mServerHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case TAG_MESSAGE:
                        Bundle data = msg.getData();
                        String key = data.getString("key");
                        Log.e("tag", key);
                        break;
                    default:
                        super.handleMessage(msg);
                        break;
                }
            }
        };
        private Messenger mMessenger = new Messenger(mServerHandler);
    
        @Override
        public IBinder onBind(Intent intent) {
            return mMessenger.getBinder();
        }
    }
    
    

    以上就是Messenger的使用实例:客户端向服务器发送数据。


    这里如果要服务器向客户端发送数据呢?就需要如下

    • 客户端
      增加了Messenger和Handler用于接收服务器发来的消息
    public class ClientActivity extends Activity {
        Messenger mClientMessenger;
    
        private static class ClientHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case ServerService.TAG_MESSAGE_SERVER:
                        String serverKey = msg.getData().getString("serverKey");
                        Log.e("ServerService", serverKey);
                        break;
                }
            }
        }
    
        private Messenger mClient;
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mClient = new Messenger(iBinder);
                Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
                Bundle bundle = new Bundle();
                bundle.putString("key", "value from client");
                message.setData(bundle);
                if (mClientMessenger == null) {
                    mClientMessenger = new Messenger(new ClientHandler());
                    message.replyTo = mClientMessenger; //这里很重要,这里就是将client的Messenger传递给了Server,建立了连接。
                }
                try {
                    mClient.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
    
            }
        };
    
        public void sendMessage(String str) {
            if (mClient != null) {
                Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
                Bundle bundle = new Bundle();
                bundle.putString("key", str);
                message.setData(bundle);
                try {
                    mClient.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            Intent intent = new Intent(this, ServerService.class);
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(serviceConnection);
        }
    }
    
    
    • 服务端
      服务端通过客户端传过来的replyMsg来向客户端发送消息
    public class ServerService extends Service {
    
        private static final String TAG = "ServerService";
        public static final int TAG_MESSAGE_CLIENT = 1000;
        public static final int TAG_MESSAGE_SERVER = 1001;
    
        private static class ServerHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case TAG_MESSAGE_CLIENT:
                        Bundle data = msg.getData();
                        String key = data.getString("key");
                        Log.e(TAG, key);
                        Messenger replyTo = msg.replyTo;
                        Message message = Message.obtain(null, TAG_MESSAGE_SERVER);
                        Bundle bundle = new Bundle();
                        bundle.putString("serverKey", "hello, I got message & send you a new message");
                        message.setData(bundle);
                        try {
                            replyTo.send(message);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                        break;
                }
            }
        }
    
        ;
        private Messenger mMessenger = new Messenger(new ServerHandler());
    
        @Override
        public IBinder onBind(Intent intent) {
            return mMessenger.getBinder();
        }
    }
    
    

    以上就是Messenger具体使用,接下来就看看Messenger源码上的实现。


    先从服务端开始

    Messenger msger = new Messenger(new Handler()); // 1
     public IBinder onBind(Intent intent) { //2
            return mMessenger.getBinder();
        }
    

    这里看下Messenger的源码

      private final IMessenger mTarget;
    
        /**
         * Create a new Messenger pointing to the given Handler.  Any Message
         * objects sent through this Messenger will appear in the Handler as if
         * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
         * been called directly.
         * 
         * @param target The Handler that will receive sent messages.
         */
        public Messenger(Handler target) {
            mTarget = target.getIMessenger();
        }
        public IBinder getBinder() {
            return mTarget.asBinder();
        }
       public Messenger(IBinder target) {
            mTarget = IMessenger.Stub.asInterface(target);
        }
    

    这里是Handler的源码

      final IMessenger getIMessenger() {
            synchronized (mQueue) {
                if (mMessenger != null) {
                    return mMessenger;
                }
                mMessenger = new MessengerImpl();
                return mMessenger;
            }
        }
    
        private final class MessengerImpl extends IMessenger.Stub { //这里可以发现 实现是用AIDL方式
            public void send(Message msg) {
                msg.sendingUid = Binder.getCallingUid();
                Handler.this.sendMessage(msg);
            }
        }
    

    根据源码可以知道
    步骤1根据Messenger构造函数知道,则是将target初始化,来自Handler中MessengerImpl类,对于当客户端调用Messenger的send()方法就是调用MessengerImpl内的send方法,这里然后在通过Handler的sendMessage将消息回调出来,这里可以看出来 Messenger中的handler还是Handler的本职工作,消息的回调
    步骤2这里是对AIDL的返回Binder操作

    对于回调,其实就是通过传递Messenger构建连接操作

    第4种AIDL

    可以移步我的这篇文章aidl


    第6种 ContentProvider实现跨进程
    1. 首先是ContentProvider为什么支持跨进程呢?
      ContentProvider:内容提供者,是Android专门为了不同应用间数据共享的一种方式。其实你有没有注意到,平常我们获取系统联系人,相册中的照片等时候 就是一种跨进程获取数据的方式,APP本身是一个进程,联系人,相册是在另个进程中,通过ContentProvider可以获取到。

    2. 自定义ContentProvider实现通信
      ContentProvider主要是以表格方式存储,它暴露出来给我们的实现方法,而底层我们可以通过SQLite数据库,其他别的数据库,XML,文件,哪怕是内存中的类也可以,只要是满足它的格式即可。
      接下来实现一个自己的ContentProvider

    public class BookProvider extends ContentProvider {
        private static final String TAG = "BookProvider";
        @Override
        public boolean onCreate() {
            Log.d(TAG, "onCreate() called");
            return false;
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
            Log.d(TAG, "query() called ");
            return null;
        }
    
    
        @Override
        public String getType(Uri uri) {
            Log.d(TAG, "getType() called ");
            return null;
        }
    
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            Log.d(TAG, "insert() called ");
            return null;
        }
    
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            Log.d(TAG, "delete() called ");
            return 0;
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
            Log.d(TAG, "update() called ");
            return 0;
        }
    }
    
    

    manifest文件生命如下

      <provider
                android:name=".ipc.BookProvider"
                android:authorities="com.test.ipc.authorities"  //这个是唯一标识
                android:process=":providerpro" />
    

    其实以上就完成了一个自定义的Provider,你可以在自己的Provider方法内进行数据存储查询
    为了实现数据存储,这里可以用数据进行简单的存储

    public class BookSQLite extends SQLiteOpenHelper {
        private static final String DB_NAME = "book_provider.db";
        public static final String BOOK_TABLE_NAME = "book";
        public static final String USER_TABLE_NAME = "user";
        private static final int DB_VERSION = 1;
    
    
        static final String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS" 
                + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
        static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS" 
                + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,sex INT)";
    
        public BookSQLite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, DB_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(CREATE_BOOK_TABLE);
            db.execSQL(CREATE_USER_TABLE);
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    

    provider改后如下

    public class BookProvider extends ContentProvider {
        private static final String TAG = "BookProvider";
        public static final String AUTHORITY = "com.test.ipc.authorities";
        public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
        public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
    
        public static final int BOOK_URI_CODE = 0;
        public static final int USER_URI_CODE = 1;
    
        private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        private SQLiteDatabase mSQLite;
        private Context mContext;
    
        static {
            sMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE); //这里就将URI与对应的Code绑定,这样方便我们以后对不同的URI进行判断
            sMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
        }
    
        private String getTBName(Uri uri) {
            String name = null;
            switch (sMatcher.match(uri)) {
                case BOOK_URI_CODE:
                    name = BookSQLite.BOOK_TABLE_NAME;
                    break;
                case USER_URI_CODE:
                    name = BookSQLite.USER_TABLE_NAME;
                    break;
                default:
                    break;
    
            }
            return name;
        }
    
        @Override
        public boolean onCreate() {
            Log.d(TAG, "onCreate() called");
            mContext = getContext();
            mSQLite = new BookSQLite(getContext()).getWritableDatabase();
            return false;
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
            Log.d(TAG, "query() called ");
            String name = getTBName(uri);
            if (name == null) {
                return null;
            }
            return mSQLite.query(name, projection, selection, selectionArgs, null, null, sortOrder, null);
    
        }
    
    
        @Override
        public String getType(Uri uri) {
            Log.d(TAG, "getType() called ");
            return null;
        }
    
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            Log.d(TAG, "insert() called ");
            String name = getTBName(uri);
            if (name == null) {
                return null;
            }
            mSQLite.insert(name, null, values);
            mContext.getContentResolver().notifyChange(uri, null);
            return uri;
    
        }
    
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            Log.d(TAG, "delete() called ");
            return 0;
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
            Log.d(TAG, "update() called ");
            return 0;
        }
    }
    

    这样就是实现了数据的存储与查找

    以上就是Android中的IPC机制实现

    总结

    这篇文章主要是一个总章,对进程与线程进行一个总的介绍,稍后会逐一介绍线程,Binder的具体实现细节,ContentProvider的底层实现。

    参考《Android开发艺术探索》&Android源码

    相关文章

      网友评论

          本文标题:Android 中的IPC机制

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