AIDL实现跨进程通信

一、什么是AIDL  
介绍AIDL之前,先简单了解相关的知识。
IPC:跨进程间通讯(Inter-Process Communication)
原因:
因为在Android系统中,  每应用程序都运行在自己的进程中,进程间一般是无法直接进行数据交换的,而为了实现跨进程 间的通信,Android给我们提供了AIDL技术,AIDL(Android Interface Definition Language)是一种 接口语言。语法十分的简单,这种接口语言并不是真正的编程语言,只是定义两个进程间的通信接口而已。
在Android SDK的下的aidl.exe(路径sdk\build-tools\21.1.2)工具自动地在gen  目录下生成一个对应的Xxx.java的接口,在该接口中包含一个Stub的内部类,在该类中实现了IBinder接口 。
与自定义的通信接口,这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service
的onBind( )方法的返回值!
二、AIDL的简单实现
1.)创建服务端工程
step 1:创建XXX.aidl文件
1.在包路径下new file创建一个xxx.aidl文件
2.因为aidl和接口类似,所以直接new interface,编写好内容后,来到对应java文件所在目录下修改文件后缀名;


编写ADIL文件时有以下注意事项:

1.接口名词需要与aidl文件名相同

2.接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final!

3.AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都需要import

声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口,后面会详细讲解。

4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。


IPerson.aidl文件内容如下:
package com.makk.aidlserver;
interface IPerson{
 void setValue(String name);
 String getValue();
}
gen目录下生成了一个接口对应的实现:

你可以打开仔细看看里面的代码,而我们一般只关注
asInterface(android.os.IBinder obj)
setValue(java.lang.String name)
getValue()

sept 2:自定义Service类
1.继承Service类,并实例化 IPerson.Stub person
重写Service的onBind方法,返回一个 IPerson.Stub person的实例化对象!

MyService
package com.makk.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {

 private IPerson.Stub person = new Person();
 
 @Override
 public IBinder onBind(Intent intent) {
 
  return person;
 }
}

step 3:在AndroidManifest.xml文件中注册Service
  <service android:name="com.makk.aidlserver.MyService" >
            <intent-filter>
                <action android:name="com.makk.aidlserver.MyService" />
            </intent-filter>
  </service>
step 4:在MainActivity中手动绑定MyService并进行赋值
MainActivity

 public void onClick(View view) {
  switch (view.getId()) {
  case R.id.button1:
   Intent i = new Intent(this, MyService.class);
   bindService(i, conn, Service.BIND_AUTO_CREATE);
   break;
  case R.id.button2:
   unbindService(conn);
   break;
  default:
   break;
  }
 }
 ServiceConnection conn = new ServiceConnection() {
  @Override
  public void onServiceDisconnected(ComponentName name) {
  }
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   iPerson = IPerson.Stub.asInterface(service);
   if (iPerson != null) {
    try {
     iPerson.setValue("makk is exist");
     Toast.makeText(MainActivity.this, "赋值成功", Toast.LENGTH_SHORT)
       .show();
    } catch (RemoteException e) {
     e.printStackTrace();
     Toast.makeText(MainActivity.this, "赋值失败", Toast.LENGTH_SHORT)
       .show();
    }
   }
  }
 };
}
2.)创建客户端工程
1.服务端创建的IPerson.aidl文件复制过来,放在与服务端相同的包目录下

2.绑定启动service,在实现ServiceConnection接口的对象中获取数据ServiceConnection

调用bindService绑定远程Service

bindService(service, conn, BIND_AUTO_CREATE);

ps:第三个参数是设置如果服务没有启动的话,自动创建


和本地Service不同,绑定远程Service的ServiceConnection不能直接获取Service的onBind( )方法

返回的IBinder对象,只能返回onBind( )方法所返回的代理对象,需要做如下处理:

iPerson = IPerson.Stub.asInterface(service);


  public void onClick(View view) {
  Intent intent = new Intent();
  intent.setAction("com.makk.aidlserver.MyService");
  bindService(intent, conn, Service.BIND_AUTO_CREATE);
 }
 ServiceConnection conn = new ServiceConnection() {
  @Override
  public void onServiceDisconnected(ComponentName name) {
  }
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   person = IPerson.Stub.asInterface(service);
   if (person != null) {
    try {
     String value = person.getValue();
     Toast.makeText(MainActivity.this, "获取成功:" + value,
       Toast.LENGTH_SHORT).show();
    } catch (RemoteException e) {
     e.printStackTrace();
     Toast.makeText(MainActivity.this, "获取失败!!!",
       Toast.LENGTH_SHORT).show();
    }
   }
  }
 };
}



三、AIDL传递复杂数据的实现

1.)先要了解的是Parcelable接口,对于自定义类型,Android规定调用远程Service的参数与返回值都必须实现

Parcelable接口,该接口告诉Android运行时在封送(marshalling)解封送(unmarshalling)过程中如何序列化。


与反序列化对象,这个时候你或许会有疑惑,为什么不直接用Java提供的Serializable接口呢?这是因为Android

团队觉得Java序列化太慢,难以满足Android的进程间的通信需求,所以他们构建了Parcelable方案!

---->也就是自定义的对象要实现Parcelable接口!实现接口要实现下述方法


实现Paecelable接口意味着要实现writeToParcelreadFromPacel方法;

写入方法将对象写入到包裹(parcel)中,而读取方法则从包裹中读取对象,请注意,写入属性顺序需与读取顺序相同

需要在该类中实现android.os.Parcelable.Creator<T>接口

该接口里面的两个方法:

createFromParcel(Parcel source)

实现从source创建出JavaBean实例的功能


newArray(int size):

创建一个类型为T,长度为size的数组,只有一个简单的return new T[size]; (这里的T是Person类)


describeContents():这个我也不知道是拿来干嘛的,直接返回0即可!不用理他



2.)非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。方向指示符包括

in、out、和inoutin表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。






1.创建服务端工程
step1.先建立Worker.aidl与Salary.aidl文件,同时需要实现Parcelable 接口
Worker.aidl:     parcelable Worker;
Salary.aidl:     parcelable Salary;  
step2.创建Worker和Salary类,都需要实现Parcelable接口.!
另外因为我们后面是根据Worker对象来获取Map集合中的数据,
所以Worker.java中我们重写了hashcode和equals 的方法;而Salary类则不需要!
Worker.java
package com.makk.aidlserver;
import android.os.Parcel;
import android.os.Parcelable;
public class Worker implements Parcelable {
 private int id;
 private String name;
 public Worker() {
 }
 public Worker(int id, String name) {
  super();
  this.id = id;
  this.name = name;
 }
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 // 实现Parcelable必须实现的方法,不知道拿来干嘛的,直接返回0就行了
 @Override
 public int describeContents() {
  return 0;
 }
 // 写入数据到Parcel中的方法
 @Override
 public void writeToParcel(Parcel dest, int flags) {
  dest.writeInt(id);
  dest.writeString(name);
 }
 // 必须提供一个名为CREATOR的static final属性 该属性需要实现
 // android.os.Parcelable.Creator<T>接口
 public static final Parcelable.Creator<Worker> CREATOR = new Creator<Worker>() {
  @Override
  public Worker[] newArray(int size) {
   return new Worker[size];
  }
  @Override
  public Worker createFromParcel(Parcel source) {
   return new Worker(source.readInt(), source.readString());
  }
 };
 // 因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦,
 // 需要我们重写hashCode()和equals()方法
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Worker other = (Worker) obj;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }
}

Salary.java
package com.makk.aidlserver;
import android.os.Parcel;
import android.os.Parcelable;
public class Salary implements Parcelable {
 private String type;
 private int salary;
 public Salary() {
 }
 public Salary(String type, int salary) {
  super();
  this.type = type;
  this.salary = salary;
 }
 public String getType() {
  return type;
 }
 public void setType(String type) {
  this.type = type;
 }
 public int getSalary() {
  return salary;
 }
 public void setSalary(int salary) {
  this.salary = salary;
 }
 @Override
 public int describeContents() {
  return 0;
 }
 @Override
 public void writeToParcel(Parcel dest, int flags) {
  dest.writeString(type);
  dest.writeInt(salary);
 }
 public static final Parcelable.Creator<Salary> CREATOR = new Creator<Salary>() {
  @Override
  public Salary[] newArray(int size) {
   return new Salary[size];
  }
  @Override
  public Salary createFromParcel(Parcel source) {
   return new Salary(source.readString(), source.readInt());
  }
 };
 public String toString() {
  return "工作:" + type + "    薪水" + salary;
 };
}

step3. 创建 ISalary.aidl文件,写一个简单的获取工资的方法。
ISalary.aidl
package com.makk.aidlserver;
import com.makk.aidlserver.Worker;
import com.makk.aidlserver.Salary;
interface ISalary {
 //定义一个Person对象作为传入参数  
    //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in  
    Salary getMsg(in Worker owner);  
}
step4. 创建一个继承Service的类,在类中实例化ISalary.Stub,在ISalary.Stub的getMsg()方法下初始化数据。
package com.makk.aidlserver;
import java.util.HashMap;
import java.util.Map;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class SalaryService extends Service {
 ISalary.Stub salary = new ISalary.Stub() {
  @Override
  public Salary getMsg(Worker owner) throws RemoteException {
   Map<Worker, Salary> ss = new HashMap<Worker, Salary>();
   // 初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化
   ss.put(new Worker(1, "Jay"), new Salary("码农", 2000));
   ss.put(new Worker(2, "GEM"), new Salary("歌手", 20000));
   ss.put(new Worker(3, "XM"), new Salary("学生", 20));
   ss.put(new Worker(4, "MrWang"), new Salary("老师", 2000));
   return ss.get(owner);
  }
 };
 @Override
 public IBinder onBind(Intent intent) {
  return salary;
 }
 @Override
 public void onCreate() {
  super.onCreate();
 }
 
 @Override
 public void onDestroy() {
  super.onDestroy();
 }
}

别忘记在AndroidManfiest.xml中配置Service
<service android:name="com.makk.aidlserver.SalaryService" >
            <intent-filter>
                <action android:name="com.makk.aidlserver.SalaryService" />
            </intent-filter>
        </service>
step5.启动该app时就启动该Service
Intent intent = new Intent(this, SalaryService.class);
  bindService(intent, connSalary, Service.BIND_AUTO_CREATE);
2.创建客户端工程
step1.将服务端的AIDL文件拷贝过来(包含Salary.java、Worker.java)

step2.调用Service
intent.setAction("com.makk.aidlserver.MyService");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
在ServiceConnection中获取数据
ServiceConnection connSalary  = new ServiceConnection() {
 
  @Override
  public void onServiceDisconnected(ComponentName name) {
   
  }
 
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   salary = ISalary.Stub.asInterface(service);
   if (salary!=null) {
    try {
     Salary s = salary.getMsg(new Worker(1, "Jay"));
     Toast.makeText(MainActivity.this, "获取成功:" + s.getType()+"salary:"+s.getSalary(),
       Toast.LENGTH_SHORT).show();
    } catch (RemoteException e) {
     e.printStackTrace();
     Toast.makeText(MainActivity.this, "获取失败!!!",
       Toast.LENGTH_SHORT).show();
    }
   }
  }
 };
最后 Android中的跨进程IPC的代表AIDL就讲解到这里,后续有什么新的内容会陆续补充!!!

全部的代码:下载地址

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值