android中的几种IPC方式

1. 使用Bundle

  1. 由于Bundle实现了Parcelable接口,所以在四大组件中的三大组件(Activity, Service, Receiver)都支持在Intent中传递Bundle.就可以在Bundle中附加我们需要的信息通过Intent发送出去. 当然传递的类型必须是能够被序列化的,
  2. 这个地方有一个疑问,intent也可以传输数据啊,为啥还要用bundle呢?
    • 解释是:bundle的内部实现是一个hashMap.一个容器将要传输的数据装起来然后做传输,如果使用intent则需要一个一个传输,如果此数据需要从A传到B然后传入C,那么用bundle就很方便了,而对于intent则还需要在B取出,然后在转入..
  3. 由于Bundle使用很简单就不展示代码了;

2. 使用文件共享

  1. 原理:两个进程通过读/写同一个文件来交换数据. 例如进程A把数据写入文件中,而进程B从文件中读取出来数据.
  2. 需要注意的点:文件共享适合在对数据同步要求不高的进程之间进行通信,并且要妥善的处理并发读写的问题.
  3. SharePreferencess是Android提供的一个轻量级方案,通过键值对存储数据,底层采用XML文件来进行存储. 存储路径/data/data/package name/shared_prefs目录下. 也属于文件的一种,但是由于系统对SP的读写存在一定的缓存策略,内存中会有一份缓存,所以多进程下,系统对它的读写也就变得不可靠.
  4. 文件共享,使用sp就是比较好的例子,sp的使用也比较简单就不列出了

3. 使用Messenger

  1. Messenger(信使). 不同的进程中可以传递Message对象, 在Message中放入我们需要传递的数据,就可实现进程间传递. Messenger是一种轻量级的IPC方案,它的底层实现AIDL.

  2. 具体实现,共分为四部:

    1. 客户端进程创建两个 Messenger,一个 Sender ,一个 Receiver;

           private Messenger mGetReplyMessenger =  new Messenger(new Handler(){
      
           @Override
           public void handleMessage(Message msg) {
               switch (msg.what){
                   case 0:
                       Log.d(TAG, "handleMessage: 这里是客户端:::"+msg.getData().getString("reply"));
                       break;
                   default:
                       super.handleMessage(msg);
               }
           }
       });
      
          private ServiceConnection mConnection = new ServiceConnection() {
       
             @Override
               public void onServiceConnected(ComponentName componentName,   IBinder iBinder) {
             mMessenger = new Messenger(iBinder);
             Message message = Message.obtain(null, 1);
      
             Bundle bundle = new Bundle();
             bundle.putString("msg","wo shi 客户端");
      
             message.setData(bundle);
            message.replyTo= mGetReplyMessenger;
      
             try {
                 mMessenger.send(message);
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
      
         }
      
         @Override
         public void onServiceDisconnected(ComponentName componentName) {
      
         }
          };
      
    2. 绑定服务,由于在不同的应用中所以采用隐士启动方式:

          Intent intent = new Intent();
       intent.setAction("com.lemon.service");
       intent.setComponent(new ComponentName("com.lemon.messgagertest","com.lemon.messgagertest.MessagerService"));
       bindService(intent, mConnection,BIND_AUTO_CREATE);
      

    注意:采用隐士启动的时候,需要设置 intent.setComponent(),不然会报错,找不到;

    1. 服务端,接受信息,然后处理,然后返回:

       private Messenger mMessenger = new Messenger(new Handler(){
           @Override
           public void handleMessage(Message msg) {
      
               switch (msg.what){
                   case 1:
                       Log.e(TAG,"receive msg from client "+msg.getData().getString("msg"));
                       Messenger client = msg.replyTo;
                       Message message = Message.obtain(null, 0);
                       Bundle bundle = new Bundle();
                       bundle.putString("reply","消息已收到,稍后回复");
                       message.setData(bundle);
      
                       try {
                           client.send(message);
                       } catch (RemoteException e) {
                           e.printStackTrace();
                       }
                       break;
                   default:
                       super.handleMessage(msg);
               }
      
           }
       });
      
    2. 配置xml文件:

        <service android:name=".MessagerService"
                android:process=":remote">
           <intent-filter>
               <action android:name="com.lemon.service"></action>
           </intent-filter>
           </service>
      
  3. Messenger使用很简单,但是有局限性,因为Messenger对AIDL进行了封装,使得在使用时更加简单,并且它的处理方式是一次处理一个请求,因此服务器端不用考虑线程同步因为服务端不存在并发执行的情形.

  4. 在使用Messenger进行数据传递必须将数据放入到Message中. 而Messenger和Message都实现了序列化接口. 所以可以在进程间通信.

4. 使用AIDL

  1. 虽然Messenger使用方便, 但是要清楚它是以串行的方式处理客户端发来的消息,如果有大量并发的请求. 或者需求是跨进程调用服务端的方法时. 就无法使用Messenger. 这个时候就该AIDL

  2. 对于使用AIDL的流程简单梳理一遍

    1. 服务端
      • 服务端创建一个Service用来监听客户端的连接请求, 然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口并在onBind()返回即可.
    2. 客户端
      • 绑定服务端的Service,绑定成功后,将服务端返回来的Binder对象转成AIDL接口所属的类型,接着就可以直接调用AIDL中的方法了.
  3. 具体的实现

    1. AIDL 文件

      Book.aidl

       package com.lemon.bookaidl;
       parcelable Book;
      

      IBookManger.aidl

       package com.lemon.bookaidl;
      
       // Declare any non-default types here with import statements
       import com.lemon.bookaidl.book;
       import com.lemon.bookaidl.IOnBookArrivedListener;
       interface IBookManger {
      
       List<Book> getBookList();
       void addBook(in Book book);
       void registerListener(IOnBookArrivedListener listener);
       void unregisterListener(IOnBookArrivedListener listener);
       }
      

      IOnBookArrivedListener.aidl

       package com.lemon.bookaidl;
       
       // Declare any non-default types here with import statements
       import com.lemon.bookaidl.book;
       interface IOnBookArrivedListener {
       
       void IOnBookArriverListener(in Book book);
       }
      
    2. 服务端:

             public class BookMangerService extends Service {  
               private static final String                                     TAG                 = "BookMangerService";
             private              AtomicBoolean                              mIsServiceDestoryed = new AtomicBoolean(false);
             private              CopyOnWriteArrayList<Book>                 mBookList           = new CopyOnWriteArrayList<Book>();
             private              RemoteCallbackList<IOnBookArrivedListener> mListeners          = new RemoteCallbackList<>();
               private Binder mBinder = new IBookManger.Stub() {
               @Override
                   public List<Book> getBookList() throws RemoteException {
                     return mBookList;
                   }
      
                 @Override
                   public void addBook(Book book) throws RemoteException {
                   mBookList.add(book);
                   }
      
                   @Override
                       public void registerListener(IOnBookArrivedListener listener) throws RemoteException {
                         mListeners.register(listener);
                       }
      
                     @Override
                     public void unregisterListener(IOnBookArrivedListener listener) throws RemoteException {
                       mListeners.unregister(listener);
                       }
                       };
      
                     @Nullable
                     @Override
                       public IBinder onBind(Intent intent) {
                       return mBinder;
                     }
      
                         @Override
                       public void onCreate() {
                           super.onCreate();
      
                           mBookList.add(new Book(1,"Android"));
                             mBookList.add(new Book(2, "Ios"));
                         new Thread(new serviceWork()).start();
                           }
      
                             private class serviceWork implements Runnable {
                           @Override
                               public void run() {
                                   while (!mIsServiceDestoryed.get()){
                                     try {
                                           Thread.sleep(5000);
                                           } catch (InterruptedException e) {
                                               e.printStackTrace();
                                                 }
      
                               int bookId = mBookList.size()+1;
                               Book newBook = new Book(bookId,"#new Book"+bookId);
      
                                       try {
                                         onNewBookArrived(newBook);
                                         } catch (RemoteException e) {
                                         e.printStackTrace();
                                             }
                                               }
                                           }
                                           }
      
                                         private void onNewBookArrived(Book newBook)                           throws RemoteException {
                     mBookList.add(newBook);
      
                     final int N = mListeners.beginBroadcast();
                     Log.e("onNewBookArrived","registener listener size:" + N);
      
                         for (int i = 0 ;i<N ;i++){
                          IOnBookArrivedListener l = mListeners.getBroadcastItem(i);
                            if (l!=null){
                               l.IOnBookArriverListener(newBook);
                                  }
                                }
      
                           mListeners.finishBroadcast();
                                              }
      
                            @Override
                            public void onDestroy() {
                                 mIsServiceDestoryed.set(true);
                              super.onDestroy();
                                         }
                                  }
      
  4. 客户端

             public class MainActivity extends AppCompatActivity {       
                   private static final String TAG = "MainActivity";
     
                 private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
                 private IBookManger mIBookManger;
     
                 private android.os.Handler mHandler = new android.os.Handler(){
             @Override
                   public void handleMessage(Message msg) {
     
                 switch (msg.what){
                     case MESSAGE_NEW_BOOK_ARRIVED:
                         Log.e(TAG, "received new book:" + msg.obj);
                         break;
                     default:
                         super.handleMessage(msg);
     
                 }
             }
         };
     
         private ServiceConnection mServiceConnection = new ServiceConnection() {        
             @Override
             public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                 mIBookManger = IBookManger.Stub.asInterface(iBinder);
                 try {
                     List<Book> bookList = mIBookManger.getBookList();
                     Log.e(TAG, "query book list,list type:" + bookList.getClass().getCanonicalName());
                     Log.e(TAG, "query book list:" + bookList.toString());
     
                     Book newBook = new Book(3, "android进阶");
                     mIBookManger.addBook(newBook);
     
                     Log.e(TAG, "add book:" + newBook);
                     List<Book> newList =  mIBookManger.getBookList();
                     Log.e(TAG, "query book list:" + newList.toString());
                     mIBookManger.registerListener(mIOnBookArrivedListener);
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
             }
     
             @Override
             public void onServiceDisconnected(ComponentName componentName) {
                 }
         };
     
         private IOnBookArrivedListener mIOnBookArrivedListener = new IOnBookArrivedListener.Stub() {
             @Override
             public void IOnBookArriverListener(Book book) throws RemoteException {
                 mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,book).sendToTarget();
             }
         };
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
     
             bindService();
     
         }
     
         private void bindService() {
             Intent intent = new Intent();
             intent.setAction("com.lemon.bookservice");
             intent.setComponent(new ComponentName("com.lemon.bookaidl","com.lemon.bookaidl.BookMangerService"));
             bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
         }
     
         @Override
         protected void onDestroy() {
             if (mIBookManger != null && mIBookManger.asBinder().isBinderAlive()){
                 Log.e(TAG, "unregister listener:" + mIOnBookArrivedListener);
                 try {
                     mIBookManger.unregisterListener(mIOnBookArrivedListener);
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
             }
             unbindService(mServiceConnection);
             super.onDestroy();
         }   
             }   
    
  5. 几个点得理解:

    1. CopyOnWriteArrayList:支持并发的读写,这里我们使用它来进行自动的线程同步;
    2. RemoteCallBackList:是系统专门提供的用于删除跨进程listener的接口:它的工作原理其实很简单:在它的内部有一个Map结构专门用来保存所有AIDL回调3. 这个例子涉及到的东西比较多,如果刚接触可以参考点简单的例子,就实现个远程服务进行计算,而client获取到结果的这种,实现特别简单,主要看你怎么理解

5. 使用ContentProvider

  1. ContentProvider是Android提供专门用于不用应用进行数据共享的方式. 它的底层同样也是Binder. 因为系统封装, 所以它的使用比起AIDL要简单很多.
  2. 要实现一个内容提供者, 只需要写一个类继承ContentProvider,并复写六个抽象方法. 其中有四个是CURD操作方法. 一个onCreate()用来做初始化. 一个getType()用来返回一个Uri请求所对应的MIME类型,比如图片还是视频等. 如果我们不关心那么可是直接返回NULL或者

6. 使用Socket

  1. Socket也称为套接字. 是网络通信中的概念, 它分为流式套接字和用户数据包套接字两种. 分别对应于网络的传输控制层中TCP和UDP协议.
socket实现IPC.png
客户端:

            public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG="MainActivity";
        private Button mSend;
        private EditText mInput;
        private TextView mMessage;
        private Socket mClientSocket;
        private PrintWriter mPrintWriter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
    
    
    
        }
    
        private void initView() {
    
            mMessage = (TextView) findViewById(R.id.tv_message);
            mInput = (EditText) findViewById(R.id.et_input);
            mSend = (Button) findViewById(R.id.bt_send);
    
            mSend.setOnClickListener(this);
    
            Intent service = new Intent(this, SocketService.class);
            startService(service);
            new Thread(){
                @Override
                public void run() {
                    connectTCPserver();
                }
            }.start();
        }
    
        private void connectTCPserver() {
    
            Socket socket=null;
            while (socket==null) {
                try {
                    socket = new Socket("localhost", 8888);
                    mClientSocket = socket;
                    mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                } catch (IOException e) {
                    SystemClock.sleep(1000);
                }
            }
    
    
    
    
                try {
                    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    while (!isFinishing()){
                        String msg = br.readLine();
                        if (msg!=null){
                            String time = formateDateTime(System.currentTimeMillis());
                            Log.e(TAG,"客户端收到信息:"+msg+"  at:"+time);
                        }
                    }
    
                    mPrintWriter.close();
                    br.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
        }
    
        private String formateDateTime(long l) {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(l));
        }
    
        @Override
        public void onClick(View view) {
            if (view== mSend) {
                final String msg = mInput.getText().toString();
                if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                    //像服务器发送信息
                    Log.e(TAG,"client has send '" + msg + "' at " + formateDateTime(System.currentTimeMillis()));
                    mPrintWriter.println(msg);
                    mInput.setText("");
                }
            }
        }
    
    
        @Override
        protected void onDestroy() {
            if (mClientSocket != null) {
                try {
                    mClientSocket.shutdownInput();
                    mClientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            super.onDestroy();
        }
    }
服务端:

    public class SocketService extends Service {
        private static final String TAG= "SocketService";
        private boolean mIsSocketServiceDestory=false;
    
    
        @Override
        public void onCreate() {
    
            new Thread(new TcpServer()).start();
            super.onCreate();
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
    
        @Override
        public void onDestroy() {
            mIsSocketServiceDestory=true;
            super.onDestroy();
        }
    
        private class TcpServer implements Runnable{
    
            @Override
            public void run() {
                 ServerSocket mServerSocket;
                try {
                    mServerSocket = new ServerSocket(8888);
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
    
                while (!mIsSocketServiceDestory){
    
                    try {
                        final Socket accept = mServerSocket.accept();
    
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    replyClient(accept);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
    
    
                        }).start();
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        private void replyClient(Socket accept) throws IOException {
            BufferedReader input = new BufferedReader(new InputStreamReader(accept.getInputStream()));
    
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())),true);
    
            while (!mIsSocketServiceDestory){
                String line = input.readLine();
                if (line==null)break;
    
                Log.e(TAG,"## reveive msg :"+line);
    
                String message = "server has received your message:"+line;
                out.println(message);
            }
    
            out.close();
            input.close();
            accept.close();
    
        }
    }

7. sharedUserId

sharedUserId的作用是让两个应用程序共享一个user id,我们都知道Linux进程给每一个应用程序分配了一个独立的user id,所以如果两个或多个应用程序的签名相同并且设置了一样的sharedUserId,他们将会共享一个user id,相同user id的应用程序可以访问对方的数据(也就是说如果应用程序中的一个文件的权限是600,相同uid可以直接访问,反之则无法访问),并且设置成一个Android:process就能够运行在一个进程中了。

  1. sharedUserId方式主要就是使用createPackageContext (String packageName, int flags)函数,该函数用来返回指定包名应用的上下文,注意是application的context。
  2. 注意里面的两个参数,尤其是第二个:这个flag,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项,CONTEXT_INCLUDE_CODE的作用就是在调用者的进程执行该应用的代码,也就是说可以使用getClassLoader()函数来初始化该application的相关类,如果需要加载的application不能被安全的加载进进程的话,将会抛出一个SecurityException,如果这个标示没有被设置,那么将不会在被加载的类上面施加任何约束,getClassLoader()将会返回默认的系统类加载器;CONTEXT_IGNORE_SECURITY的意思是忽略任何安全警告,和CONTEXT_INCLUDE_CODE标识一起使用可能会将不安全的代码加载进进程,所以谨慎使用。

1. 获取client应用的context

            context=createPackageContext("com.lemon.shareuidclient",CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY);
        tv_context.setText(context.toString());

2. 获取client应用的drawable图片

     int id = context.getResources().getIdentifier("android_boot","drawable","com.lemon.shareuidclient");
            iv_pic.setImageDrawable(ContextCompat.getDrawable(context,id));

3. 获取client应用的string中得值

     int ie = context.getResources().getIdentifier("lemon","string","com.lemon.shareuidclient");
     tv_string.setText(context.getString(ie));

4. 打开client应用的activity

     Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.lemon.shareuidclient","com.lemon.shareuidclient.MainActivity"));
            startActivity(intent);

5. 调用client中得方法

 try {
                Class clazz = context.getClassLoader().loadClass("com.lemon.shareuidclient.Method");
                Object instance    = clazz.newInstance();
                int [] arr= new int []{3,4,5,6};

                    int sum = (int) clazz.getMethod("add",int[].class).invoke(instance,arr);
                    tv_sum.setText(" sum: ="+sum);
                } catch (Exception e) {
                    e.printStackTrace();
                }

6. 获得client中得sharepreference

       SharedPreferences lemon = context.getSharedPreferences("lemon", MODE_MULTI_PROCESS);
       String string = lemon.getString("adress", "china");
       tv_shared_preference.setText(string );

由于SharedPreferences是有缓存机制的,所以如果在B应用中修改了该SharedPreferences文件,接着A应用去读取该文件中修改的那个值,这时你会发现还是修改前的值,这就是缓存机制导致的问题,不过有一个flag可以解决这个问题:MODE_MULTI_PROCESS,但是非常不幸的是api23已经将该标识deprecated了,原因是在一些版本上不可靠,有兴趣的可以去了解一下;

8.对比

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,565评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,021评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,003评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,015评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,020评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,856评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,178评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,824评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,264评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,788评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,913评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,535评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,130评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,102评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,334评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,298评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,622评论 2 343

推荐阅读更多精彩内容