AIDL跨进程通信的使用
项目链接:https://github.com/liaozhoubei/AIDLService
AIDL:Android Interface Definition Language,即Android接口定义语言,这是一种用于Android的跨进程通信(IPC)的工具。
为什么有AIDL呢?这是因为在android中一个进程不允许直接访问另一个进程的内存。
进程是一个应用在手机上执行的过程,这是一个动态的概念,一个app的主进程可以从这个app打开算起,然后直接退出app,这时就算是一个进程的结束。
在android中允许有多进程,因此多进程通信可以在自己的应用中进行通信,也可以在不同应用中通信,而这便是AIDL的使用方向。
但是AIDL有个缺点,那就是比较耗资源,如非必要,就不适用AIDL的为好
AIDL的使用方法
我们知道了AIDL是用来干什么的,那么我们就开始学习如何使用AIDL吧!
我们先建立一个AIDLService项目,这个项目用来模拟远程服务端口的,即我们想通过AIDL调用这个项目中的方法,这就是跨进程通信。
由于只是一个小Demo,所以并没有做太复杂的东西,我们只想通过AIDLService这个进程进行一次基本数据类型的传递,即进行简单的计算,然后将计算结果传回去客户端中。
其次,我们还想通过AIDLService将一个Object对象传给客户端。
那么我们就开始吧。
AIDLService服务端: 创建一个AIDL文件
首先必须要有一个AIDL接口文件,即后缀名为aidl的文件,在这里我们是IService.aidl,代码如下
interface IService { // 传入两个正整数,返回他们的和 int add(int num1, int num2); // 传入一个Person对象,返回person集合 List addPerson(in Person p);}
看上去是不是很熟悉,没错,这其实就是一个interface接口而已,区别在于它并没有public或者private等修饰符。
其中addPerson(in Person p)中我们看到有个in Person p,这个in是什么呢?简单的说表示这个Person是从外面传进来的(可以自行搜索其中的意思)。
除此之外还需要创建一个Person.aidl,传递序列号之后的Person对象,代码很简单,如下:
parcelable Person;
就这么一行代码。
AIDLService服务端: 创建一个RemoteService远程服务
RemoteService远程服务中将实现IService.aidl中的两个方法,让后通过IBinder传出(IBinder可以让外界调用服务中的方法)。具体代码如下:
public class RemoteService extends Service { public final String TAG = this.getClass().getSimpleName(); private ArrayList persons; public RemoteService() { } @Nullable @Override public IBinder onBind(Intent intent) { persons = new ArrayList<>(); return iBinder; } private IBinder iBinder = new IService.Stub() { @Override public int add(int num1, int num2) throws RemoteException { return num1 + num2; } @Override public List addPerson(Person p) throws RemoteException { persons.add(p); return persons; } };}
当然,我们不要忘记建立Person.java,而且要对这个类实现序列号,因为Object必须实现序列号才能同传输,代码如下:
public class Person implements Parcelable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } protected Person(Parcel in) { // 从序列号中取出数据,要按照writeToParcel()写入的顺序一样取出 this.name = in.readString(); this.age = in.readInt(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int describeContents() { return 0; } /** * 将数据写入序列化中 * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { // 写入时的顺序决定取出数据时的顺序 dest.writeString(name); dest.writeInt(age); } /** * 必须实现的方法,写法基本固定 */ public static final Creator CREATOR = new Creator() { /** * 从序列化中取出一个个的字段 * @param in * @return */ @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } };}
OK,这样我们的AIDLService服务端就已经完成了,剩下的就是使用客户端调用了。
AIDLClient客户端
客户端用来调用AIDLService中的两个方法,那么如何调用呢?
首先必须要创建AIDLClient客户端。
然后我们直接把AIDLService当中的Person.aidl和IService.aidl两个文件拷贝到,需要注意的是这里两个文件所在的包名必须与在AIDLService时的包名保持一致。
还需要把Person.java拷贝过来,包名也需要保持一致。
AIDLClient客户端需要一个简单的界面,activity_main.xml代码如下:
非常简单,这里就不做解释了。
AIDLClient客户端:调用远程服务
MainActivity中的布局写好了,那么我们就开始进行远程吧。方法其实就使用本地服务基本一样。代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private EditText num1; private EditText num2; private EditText result; private EditText name; private EditText age; private EditText getPerson; private Button btn_1; private Button btn_2; IService iService; /** * 创建ServiceConnection与服务进行链接 */ private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iService = IService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iService = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); bindService(); } private void initView() { num1 = (EditText) findViewById(R.id.num1); num2 = (EditText) findViewById(R.id.num2); result = (EditText) findViewById(R.id.result); btn_1 = (Button) findViewById(R.id.btn_addNumber); btn_2 = (Button) findViewById(R.id.btn_addPerson); name = (EditText) findViewById(R.id.name); age = (EditText) findViewById(R.id.age); getPerson = (EditText) findViewById(R.id.getPerson); btn_1.setOnClickListener(this); btn_2.setOnClickListener(this); } /** * 与远程服务进行绑定 */ private void bindService() { Intent intent = new Intent(); // 传入AIDL的包名,以及调用远程服务的全类名 intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService")); bindService(intent, conn, Context.BIND_AUTO_CREATE); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_addNumber: int num1 = Integer.parseInt(this.num1.getText().toString()); int num2 = Integer.parseInt(this.num2.getText().toString()); try { int num = iService.add(num1, num2); result.setText(num + ""); Toast.makeText(getApplicationContext(), num + "来到了", Toast.LENGTH_LONG).show(); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.btn_addPerson: String p1 = name.getText().toString(); int num3 = Integer.parseInt(age.getText().toString()); Person person = new Person(p1, num3); try { ArrayList persons = (ArrayList) iService.addPerson(person); Toast.makeText(getApplicationContext(), persons.toString(), Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } break; } }}
在这里,我们看到基本与调用本地的服务的方法没多大区别,只是在使用bindService()方法时,我们要设置包名,即设置我们要访问的服务的信息,
intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService"));
总结
AIDL的使用并不难,而且在调用的时候也就象使用普通的服务一样,区别只在双方都必须有aidl文件,其中表明能够被调用的方法而已。
下面总结一下aidl多进程通信的使用步骤
- 首先服务都创建aidl接口文件,这个接口文件表示里面的方法能被外部调用。同时要注意的是如果有Object对象传输,必须实现Parcelable接口。
- 创建一个Service,通过IBinder实现aidl接口中的方法,然后再onBind()中返回这个IBinder.
- 在客户端复制服务端的aidl文件,同时保持包名一致,如果有Object对象,也一样要复制。
- 直接通过bindService()方法绑定服务,调用IBinder中的方法。
更多相关文章
- Android Binder机制(三) ServiceManager守护进程
- Android 中文设置成粗体的方法
- 极光推送(守护进程)
- Bitmap和Drawable相互转换方法
- android onSaveInstanceState方法 横坚屏切换
- Android P 以及之后版本不支持同时从多个进程使用具有相同数据目
- Android Studio 编译提示 aapt.exe finished with non-zero exit
- android中处理json最佳方法