Android开发艺术探索-第二章-IPC机制


layout: post
date: 2015-12-28
title: Android开发艺术探索-第二章-IPC机制
categories: blog
tags: [Activity,Android]
category: Android
description:


本文首发于KuTear,转载请注明

2.1 Android IPC

Android常用进程间通信

  1. Binder
  2. Socket
  3. ContentProvider
  4. 文件共享(SharePreference/普通文件/数据库) 对同步要求不是很高才使用,不建议使用SharePreference.

2.2 Android多进程

  1. 开启方式

    在AndroidMenifest中为四大组件指定android:process=""属性 <br />
    默认进程名字为包名,指定 android:process=":##" 的进程名为 包名:##,该进程为当前App的私有进程,其他应用的组件不可以和他跑在同一进程中.其他类型的进程可以通过ShareUID共享(签名相同)

  2. 多进程的运行

    在不同进程中的四大组件,不能通过内存来共享数据.静态字段在不同的进程有不同的副本,且互不影响.不同进程含有不同的Application引用(开启进程会为该进程创建Application).

2.3 IPC基础介绍

  1. 序列化

    • Serializable

      静态成员变量属于类而不属于对象,不会参与序列化过程,其次使用transient(短暂)标志的成员变量也不会参与序列化过程.

      序列化到本地

             User user = new User(1, "hello world", false);
             File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
             ObjectOutputStream objectOutputStream = null;
             try {
                 objectOutputStream = new ObjectOutputStream(
                         new FileOutputStream(cachedFile));
                 objectOutputStream.writeObject(user);
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {
                 //数据流关闭
             }
      

      从本地恢复序列化

             User user = null;
             File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
             if (cachedFile.exists()) {
                 ObjectInputStream objectInputStream = null;
                 try {
                     objectInputStream = new ObjectInputStream(
                             new FileInputStream(cachedFile));
                     user = (User) objectInputStream.readObject();
                 } catch (IOException e) {
                     e.printStackTrace();
                 } catch (ClassNotFoundException e) {
                     e.printStackTrace();
                 } finally {
                     //数据流关闭
                 }
             }
      
    • Parcelable

      在Android Studio中可以利用插件自动生成.注意boolean转化为int/byte存储

        @Override
        public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(this.id);
             dest.writeByte(isFirst ? (byte) 1 : (byte) 0);
        }
        protected Demo(Parcel in) {
             this.id = in.readInt();
             this.isFirst = in.readByte() != 0;
        }
      

      相比Serializable,Parcelable的性能更好,Serializable需要大量的I/O操作

    • Binder

2.4 Android中的IPC

  • Bundle

    在Service,Activity,Receiver中使用Intent传递Bundle对象,在日常开发的过程中,对同一线程的通信也使用的比较多.前面说过,支持基本的类型和实现序列化的类

  • 文件共享

    在A进程向文件xxx写入,在B进程从文件xxx读出内容,需要注意的就是同步问题,前面说过不要使用SharePreference.

  • Messenger

    Messenger实质上还是AIDL,不过使用变得相当的简单.
    下面是一个栗子:
    创建一个Service(MessengerService.java),并指定进程,其主要代码为:

           //接收客户端的数据并做出反馈
           private static class MessengerHandler extends Handler {
               @Override
               public void handleMessage(Message msg) {
                   super.handleMessage(msg);
                   Log.v(TAG, msg.what + "");
                   Messenger reply = msg.replyTo;
                   Message replymsg = Message.obtain();
                   replymsg.what = msg.what * 2;
                   try {
                       reply.send(replymsg);
                   } catch (RemoteException e) {
                       e.printStackTrace();
                   }
               }
           }
       
           private Messenger messenger = new Messenger(new MessengerHandler());
       
           @Override
           public IBinder onBind(Intent intent) {
               return messenger.getBinder();
           }
    

    在Activity中链接该服务

           //处理服务端返回的数据
           private static class MessagerHandler extends Handler {
               @Override
               public void handleMessage(Message msg) {
                   super.handleMessage(msg);
                   Log.v(TAG, msg.what + "");
               }
           }
       
           private ServiceConnection connection = new ServiceConnection() {
               @Override
               public void onServiceConnected(ComponentName name, IBinder service) {
                   Messenger messenger = new Messenger(service);
                   Message msg = Message.obtain();
                   msg.what = 1024;
                   //指定接收服务端返回的数据的处理者
                   msg.replyTo = new Messenger(new MessagerHandler());
                   try {
                   //向服务端发送数据    
                       messenger.send(msg);
                   } catch (RemoteException e) {
                       e.printStackTrace();
                   }
               }
       
               @Override
               public void onServiceDisconnected(ComponentName name) {
       
               }
           };
       
           @Override
           protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.activity_messager);
       
               Intent intent = new Intent(this, MessengerService.class);
               bindService(intent, connection, BIND_AUTO_CREATE);
           }
    

    Messenger串行处理客户端的消息,Message的object不能在跨进程中来传递非系统自定义的Parcelable数据,只能在Bundle中传递.

  • AIDL

    注意点:需要在AIDl中使用的已经序列化好了的对象必须编写对应的aidl文件声明(Book.java-->Book.aidl)

       // Book.aidl
       package com.kutear.studydemo.android.art.aidl;
       
       // Declare any non-default types here with import statements
       
       parcelable Book;
    

    aidl中使用到的类必须按全路径导入.即使在同包下

       // IBookManager.aidl
       package com.kutear.studydemo.android.art.aidl;
       //同路径任导入
       import com.kutear.studydemo.android.art.aidl.Book;
       import com.kutear.studydemo.android.art.aidl.IListener;
       
       // Declare any non-default types here with import statements
       
       interface IBookManager {
           void addBook(in Book book);
           List<Book> getBookList();
           void registerListener(IListener listener);
           void unregisterListener(IListener listener);
       }
    

    创建Service(服务端)

       public class AidlService extends Service {
    
           private static final String TAG = "AidlService";
           
           private CopyOnWriteArrayList<Book> books = new CopyOnWriteArrayList<>();
           private ArrayList<IListener> mListeners = new ArrayList<>();
           private Binder mBinder = new IBookManager.Stub() {
           
                   @Override
                   public void addBook(Book book) throws RemoteException {
                       books.add(book);
                       displayListener(book);
                   }
           
                   @Override
                   public List<Book> getBookList() throws RemoteException {
                       return books;
                   }
           
                   @Override
                   public void registerListener(IListener listener) throws RemoteException {
                       //listener-->com.kutear.studydemo.android.art.aidl.IListener$Stub$Proxy@41895598
                       //同加进来的对象类型都变了
                       if (listener != null) {
                               Log.v(TAG, listener.toString());
                               mListeners.add(listener);
                       }
                   }
           
                   @Override
                   public void unregisterListener(IListener listener) throws RemoteException {
                       if (listener != null) {
                               mListeners.remove(listener);
                       }
                   }
           };
           
           public AidlService() {
           }
           
           //观察者模式分发
           private void displayListener(Book book) {
               for (IListener listener : mListeners) {
                   try {
                           listener.newBookArrived(book);
                   } catch (RemoteException e) {
                           e.printStackTrace();
                   }
               }
           }
           
           @Override
           public IBinder onBind(Intent intent) {
                   return mBinder;
           }
           
           @Override
           public void onCreate() {
                   super.onCreate();
                   books.add(new Book(1, "HTTP"));
                   books.add(new Book(2, "TCP"));
           }
       }
    

    在Activity中进行绑定(客户端)

       //注意这里不能直接new IListener(),因为跨进程过程中对象的还原是基于序列化
       //,也就是说还原后的对象已经不是原本的对象
       //在AidlService#mBinder#registerListener(IListener listener)中的
       //listener实际是不等于直接创建的mListener的
       //导致在分发的时候本地无法回调
       private IListener mListener = new IListener.Stub() {
    
           @Override
           public void newBookArrived(Book book) throws RemoteException {
               Log.v(TAG,"NewBook"+book.toString());
               //运行在客户端的Binder线程池中,非UI线程,不能操作UI,
               //使用Hanlder切换至主线程
           }
       };
    
       private ServiceConnection connection = new ServiceConnection() {
           @Override
           public void onServiceConnected(ComponentName name, IBinder service) {
               IBookManager bookManager = IBookManager.Stub.asInterface(service);
               try {
                   Log.v(TAG, bookManager.getBookList().toString());
                   //mListener = com.kutear.studydemo.android.art.demo2.AidlActivity$1@41900c48
                   bookManager.registerListener(mListener);
                   bookManager.addBook(new Book(100, "Android"));
               } catch (RemoteException e) {
                   e.printStackTrace();
               }
           }
    
           @Override
           public void onServiceDisconnected(ComponentName name) {
    
           }
       };
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_aidl);
    
           Intent intent = new Intent(this, AidlService.class);
           bindService(intent, connection, BIND_AUTO_CREATE);
       }
    

    在客户端调用服务端的函数,运行在服务端的Binder线程池中,若该函数是耗时操作,则需要在客户端开启异步线程来操作.否则ANR

    注意使用类RemoteCallbackList来实现注册与解除注册,原因就是代码中的注释部分,对象重新生成过

    <b>权限控制</b>

           @Override
           public IBinder onBind(Intent intent) {
               int check = checkCallingOrSelfPermission("permission_name");
               if(check == PackageManager.PERMISSION_DENIED){
                   //授权失败
                   //TODO
               }else{
                   //授权成功
                   //TODO
               }
               return mBinder;
           }
    
  • ContentProvider

    主要的类就是ContentProvider(提供内容),ContentResolver(解析内容).自定义类继承至ContentProvider,实现基本的CURL方法.注意SQLiteDatabase本身是处理了同步的.
    ContentProvider的onCreate在主线程执行,其余函数在Binder线程池中执行,需要注意的是同步问题.
    除了基本的增删改之外,还可以调用自定义的方法.下面是个栗子.

           //ContentProvider
           public String customMethod(){
               Log.v(TAG,"function customMethod");
               return "Hello";
           }
       
           @Nullable
           @Override
           public Bundle call(String method, String arg, Bundle extras) {
               if(method.equals("customMethod")){
                   customMethod();
                   //TODO 封装Bundle返回 
                   //returm bundle;
               }
               return super.call(method, arg, extras);
           }
    

    在需要的地方调用

        Bundle bundle =    getContentResolver().call(uri,"customMethod",null/*arg*/,null/*extras*/);
    
  • Socket

    利用Socket来进程间的通信同在java中利用Socket通信是一模一样的,只是需要注意的就是一定不要在主线程通信.

2.5 Binder线程池

  • 多个AIDL绑定在一个Service上

    实现过程为在IBinderPool中绑定Service,获取到AIDL实现的一个IBinder(暂命名iquery,在service中返回的.包含一个查询其他AIDL的IBinder的方法)
    在IBinderPool中实现查询的方法,实现部分通过iquery的方法.

2.6 选择合适的IPC

选择合适的IPC

参考

Art of Android Development Reading Notes 2

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

推荐阅读更多精彩内容

  • Android开发艺术探索 第二章IPC机制 Linux中IPC通信方式?答:命名管道,共享内存,信号量(具体再细...
    方木Rudy阅读 1,083评论 0 2
  • 1. activiy的生命周期和启动模式 ActivityA到ActivityB的跳转生命周期的顺序是什么呢?Ac...
    super_shanks阅读 1,168评论 0 1
  • 1. 使用Bundle 由于Bundle实现了Parcelable接口,所以在四大组件中的三大组件(Activit...
    lemonCode阅读 1,073评论 0 2
  • ——写给B哥 走进超市里我一眼就看到绿色食品专柜 你常常在此流连 我却没有看见过你 当我来到绿色食品专柜我就感到一...
    大树怪阅读 331评论 1 2
  • 论遗憾 当终于明白 该怎样度过大学时 大学早已烟消云散 正当壮年 却不知 该如何度过这一生 而弥留之际 幡然醒悟 ...
    稻子未熟阅读 294评论 0 2