Android IPC进程间通信之AIDL和Messenger的使用

IPC简介

Android IPC机制是 Interprocess-communication的简称,android系统默认情况下是一个程序默认一个进程实例,不同进程间的数据都是互相独立,但是google提供了可以对不同进程间通信的机制,这就是IPC机制。android平台提供了 AIDL和Messenger来针对不同进程间通信的方式。

使用场景不同的进程间的通信

比如一个应用可能一些特殊原因,一个进程的内存不够用这个时候可能就会多开一个进程来获取系统更大的内存,四大组件都支持独立进程执行,通过在清单文件中配置 process来实现。这个时候不同进程要通信就要通过aidl来实现了。 或者是不同应用间的通信也需要用到IPC机制。

AIDL实现方式

aidl是(Android Interface Definition Language)的简称是一种接口描述语言,用来定义进程间通信的接口。使用aidl首先需要知道那个程序需要暴露什么接口和数据给别人使用,可以把定义的一端叫做服务端就像web服务器端一样提供给别人访问。有了服务端后,其他应用就可以根据提供的接口来访问服务器了。

服务端的AIDL定义

android studio ide提供了生成aidl的快捷方式,这里创建了IMyService.aidl和Student.aidl。IMyService就是提供接口给其他应用访问,Student是映射Student实体类。

IMyService.aidl

  /
  package com.jw.code;

  // Declare any non-default types here with import statements

  import com.jw.code.Student;
  interface IMyService {
      /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */
      List<Student> getStudent();
      void addStudent(in Student student);
  }

aidl中支持的参数类型为:
1.基本类型(int,long,char,boolean等),String,CharSequence,List,Map,
2.另外,接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示,上面的代码我们用in标记,因为它是输入型参数

Student.aidl

 
  package com.jw.code;

  // Declare any non-default types here with import statements
  parcelable Student;

build 一下可以看到ide自动生成了对应的.java类

public interface IMyService extends android.os.IInterface {
   /** Local-side IPC implementation stub class. */
   public static abstract class Stub extends android.os.Binder implements com.jw.code.IMyService {
       private static final java.lang.String DESCRIPTOR = "com.jw.code.IMyService";


       /** Construct the stub at attach it to the interface. */
       public Stub() {
           this.attachInterface(this, DESCRIPTOR);
       }


       /**
        * Cast an IBinder object into an com.jw.code.IMyService interface,
        * generating a proxy if needed.
        */
       public static com.jw.code.IMyService asInterface(android.os.IBinder obj) {
           if ((obj == null)) {
               return null;
           }
           android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
           if (((iin != null) && (iin instanceof com.jw.code.IMyService))) {
               return ((com.jw.code.IMyService) iin);
           }
           return new com.jw.code.IMyService.Stub.Proxy(obj);
       }


       @Override
       public android.os.IBinder asBinder() {
           return this;
       }


       @Override
       public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
           switch (code) {
               case INTERFACE_TRANSACTION: {
                   reply.writeString(DESCRIPTOR);
                   return true;
               }
               case TRANSACTION_getStudent: {
                   data.enforceInterface(DESCRIPTOR);
                   java.util.List<com.jw.code.Student> _result = this.getStudent();
                   reply.writeNoException();
                   reply.writeTypedList(_result);
                   return true;
               }
               case TRANSACTION_addStudent: {
                   data.enforceInterface(DESCRIPTOR);
                   com.jw.code.Student _arg0;
                   if ((0 != data.readInt())) {
                       _arg0 = com.jw.code.Student.CREATOR.createFromParcel(data);
                   } else {
                       _arg0 = null;
                   }
                   this.addStudent(_arg0);
                   reply.writeNoException();
                   return true;
               }
           }
           return super.onTransact(code, data, reply, flags);
       }


       private static class Proxy implements com.jw.code.IMyService {
           private android.os.IBinder mRemote;


           Proxy(android.os.IBinder remote) {
               mRemote = remote;
           }


           @Override
           public android.os.IBinder asBinder() {
               return mRemote;
           }


           public java.lang.String getInterfaceDescriptor() {
               return DESCRIPTOR;
           }


           /**
            * Demonstrates some basic types that you can use as parameters
            * and return values in AIDL.
            */
           @Override
           public java.util.List<com.jw.code.Student> getStudent() throws android.os.RemoteException {
               android.os.Parcel _data = android.os.Parcel.obtain();
               android.os.Parcel _reply = android.os.Parcel.obtain();
               java.util.List<com.jw.code.Student> _result;
               try {
                   _data.writeInterfaceToken(DESCRIPTOR);
                   mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
                   _reply.readException();
                   _result = _reply.createTypedArrayList(com.jw.code.Student.CREATOR);
               } finally {
                   _reply.recycle();
                   _data.recycle();
               }
               return _result;
           }


           @Override
           public void addStudent(com.jw.code.Student student) throws android.os.RemoteException {
               android.os.Parcel _data = android.os.Parcel.obtain();
               android.os.Parcel _reply = android.os.Parcel.obtain();
               try {
                   _data.writeInterfaceToken(DESCRIPTOR);
                   if ((student != null)) {
                       _data.writeInt(1);
                       student.writeToParcel(_data, 0);
                   } else {
                       _data.writeInt(0);
                   }
                   mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                   _reply.readException();
               } finally {
                   _reply.recycle();
                   _data.recycle();
               }
           }
       }

       static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
       static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
   }

   /**
    * Demonstrates some basic types that you can use as parameters
    * and return values in AIDL.
    */
   public java.util.List<com.jw.code.Student> getStudent() throws android.os.RemoteException;

   public void addStudent(com.jw.code.Student student) throws android.os.RemoteException;
}

可以看到自动实现了定义的接口

在java包下面需要创建一个实现Parcelable接口的实体类,IPC通信传递的class必须是可以序列化的。


 public class Student implements Parcelable {

     public static final int SEX_MALE = 1;
     public static final int SEX_FEMALE = 2;

     public int sno;
     public String name;
     public int sex;
     public int age;

     public Student() {
     }

     public static final Parcelable.Creator<Student> CREATOR = new
             Parcelable.Creator<Student>() {

                 public Student createFromParcel(Parcel in) {
                     return new Student(in);
                 }

                 public Student[] newArray(int size) {
                     return new Student[size];
                 }

             };

     private Student(Parcel in) {
         readFromParcel(in);
     }

     @Override
     public int describeContents() {
         return 0;
     }

     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(sno);
         dest.writeString(name);
         dest.writeInt(sex);
         dest.writeInt(age);
     }

     public void readFromParcel(Parcel in) {
         sno = in.readInt();
         name = in.readString();
         sex = in.readInt();
         age = in.readInt();
     }

     @Override
     public String toString() {
         return String.format(Locale.ENGLISH, "Student[ %d, %s, %d, %d ]", sno, name, sex, age);
     }

 }

创建服务端的Service类

    public class MyServer extends Service {

     private final static String TAG = "MyService";
     private static final String PACKAGE_SAYHI = "com.example.test";

     private NotificationManager mNotificationManager;
     private boolean mCanRun = true;
     private List<Student> mStudents = new ArrayList<Student>();


     @Override
     public void onCreate() {
         Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
         thr.start();

         synchronized (mStudents) {
             for (int i = 1; i < 6; i++) {
                 Student student = new Student();
                 student.name = "student#" + i;
                 student.age = i * 5;
                 mStudents.add(student);
             }
         }

         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
         super.onCreate();
     }


     @Override
     public IBinder onBind(Intent intent) {
         Log.d(TAG, String.format("on bind,intent = %s", intent.toString()));
         displayNotificationMessage("服务已启动");
         return mBinder;
     }


     //这里实现了aidl中的抽象函数
     private final IMyService.Stub mBinder = new IMyService.Stub() {

         @Override
         public List<Student> getStudent() throws RemoteException {
             synchronized (mStudents) {
                 return mStudents;
             }
         }


         @Override
         public void addStudent(Student student) throws RemoteException {
             synchronized (mStudents) {
                 if (!mStudents.contains(student)) {
                     mStudents.add(student);
                 }
             }
         }


         //在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,只允许包名为com.example.test的客户端通过,
         //其他apk将无法完成调用过程
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
             String packageName = null;
             String[] packages = MyServer.this.getPackageManager().
                     getPackagesForUid(getCallingUid());
             if (packages != null && packages.length > 0) {
                 packageName = packages[0];
             }
             Log.d(TAG, "onTransact: " + packageName);
             if (!PACKAGE_SAYHI.equals(packageName)) {
                 return false;
             }

             return super.onTransact(code, data, reply, flags);
         }
     };


     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         return super.onStartCommand(intent, flags, startId);
     }


     @Override
     public void onDestroy() {
         mCanRun = false;
         super.onDestroy();
     }


     private void displayNotificationMessage(String message) {
         //Notification notification = new Notification(R.drawable.icon, message, System
         //        .currentTimeMillis());
         //notification.flags = Notification.FLAG_AUTO_CANCEL;
         //notification.defaults |= Notification.DEFAULT_ALL;
         //PendingIntent contentIntent = PendingIntent
         //        .getActivity(this, 0, new Intent(this, MyActivity.class), 0);
         //notification.setLatestEventInfo(this, "我的通知", message, contentIntent);
         //mNotificationManager.notify(R.id.app_notification_id + 1, notification);
     }


     class ServiceWorker implements Runnable {
         long counter = 0;


         @Override
         public void run() {
             // do background processing here.....
             while (mCanRun) {
                 Log.d("scott", "" + counter);
                 counter++;
                 try {
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 }

onTransact在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,只允许包名为com.example.test的客户端通过,其他apk将无法完成调用过程onBind提供定义的Stub,返回给远端应用使用就是靠这个bind来操作接口。

xml注册service


 <service android:name=".MyServer">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="com.test.aidl"></action>
            </intent-filter>

        </service>

远端应用调用

如果其他应用想使用这套接口,就必须把定义好的aidl和实体类复制一份到自己项目中。

绑定服务setAction setPackage设置目标参数

    @Override
            public void onClick(View v) {
                Intent intentService = new Intent();
                intentService.setAction(ACTION_BIND_SERVICE);
                intentService.setPackage("com.example.administrator.myapplication");
                MainActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
            }

获取连接&调用接口,正确的话可以看到成功的调用了远程应用的接口。

 private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mIMyService = null;
        }


        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通过服务端onBind方法返回的binder对象得到IMyService的实例,得到实例就可以调用它的方法了
            mIMyService = IMyService.Stub.asInterface(service);
            try {
                Student student = mIMyService.getStudent().get(0);
                showDialog(student.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };



 public void showDialog(String message) {
        new AlertDialog.Builder(MainActivity.this).setTitle("scott").setMessage(message)
                                                  .setPositiveButton("确定", null).show();
    }

Messenager调用

Messenager是一个简化版的通信方式客户端不需要复制服务端的那一套aidl文件,但是它内部也是通过Hander+Message+aidl来实现通信的。

服务端返回Messenger

 private Handler mHandler = new Handler() {
        @Override
        public void dispatchMessage(Message msg) {
            String string = (String) msg.obj;
            Log.i("jinweiaaa", "msg " + string);
        }
    };

把创建的Messenger返回给绑定者

 @Override
    public IBinder onBind(Intent intent) {
         mMessenger = new Messenger(mHandler);
        return mMessenger.getBinder();
    }

客户端定义Messenager,根据远程Binder创建一个Messenger。

 Messenger mService = null;

  @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             mService = new Messenger(service);
         }

和服务器进行通信,发送一个msg给服务端。

  public void onClick(View v) {
                  Message obtain = Message.obtain(null, 1, 0, 0);
                  //obtain.obj = "IPC msg  send succ";
                  try {
                      mService.send(obtain);
                  } catch (RemoteException e) {
                      e.printStackTrace();
                  }
              }

Messenger与AIDL的比较

首先,在实现的难度上,肯定是Messenger要简单的多——至少不需要写AIDL文件了(虽然如果认真的究其本质,会发现它的底层实现还是AIDL)。另外,使用Messenger还有一个显著的好处是它会把所有的请求排入队列,因此你几乎可以不用担心多线程可能会带来的问题。

但是这样说来,难道AIDL进行IPC就一无是处了么?当然不是,如果是那样的话它早就被淘汰了。一方面是如果项目中有并发处理问题的需求,或者会有大量的并发请求,这个时候Messenger就不适用了——它的特性让它只能串行的解决请求。另外,我们在使用Messenger的时候只能通过Message来传递信息实现交互,但是在有些时候也许我们需要直接跨进程调用服务端的方法,这个时候又怎么办呢?只能使用AIDL。

所以,这两种IPC方式各有各的优点和缺点,具体使用哪种就看具体的需要了——当然,能使用简单的就尽量使用简单的吧。

原文地址

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

推荐阅读更多精彩内容