Android 进程间通信AIDL(一)
AIDL是什么
AIDL,即Android Interface Definition Language(Android接口定义语音),提到AIDL,不得不先引入Android系统中的Binder,Binder是Android跨进程通信的一种方式,众所周知,Android系统是基于linux内核,系统会为每一个应用分配一个单独的虚拟机,每个应用拥有自己独立的进程,当然,一个应用也可以有多个进程,如果我们的应用需要实现进程间通信,Android并没有采用linux已有的通信方式,如Socket、管道等,而是采用一种了新的通信方式Binder,Binder在平时应用层开发可能用到的不多,但是如果查看Android系统源码就会发现,Android系统中很多地方用到了Binder,它是ServiceManager连接各种Manager(ActivityManager,WindowManager等)与其相对应的ManagerService的桥梁,对于应用层来说,通过Binder,客户端与服务端可以进行跨进程通信,客户端可以访问服务端提供的方法或数据,AIDL正是为了定义客户端与服务端进程间通信时相互都认可的接口,当我们创建AIDL接口时,Android SDK会为我们自动生成对应的binder类,通过这个binder可以实现进程间通信。
如何使用AIDL
AIDL基本使用有三个步骤:
- 创建一个Service和AIDL文件
- 实现接口
- 向客户端公开接口
需要注意的是,AIDL支持下列数据类型:
- 基本数据类型(int long double char 等等)
- CharSequence
- String
- List
- Map
- Parcelable
这里选择用自定义对象来作为数据传递,首先我们新建一个Person类并且实现Parcelable接口。需要注意的是,如果需要通过ADIL进行复杂对象传递的话,该对象必须实现Parcelable接口
package com.zx.aidl.demo;import android.os.Parcel;import android.os.Parcelable;public class Person implements Parcelable { private int id; private String 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; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.id); dest.writeString(this.name); } public Person() { } protected Person(Parcel in) { this.id = in.readInt(); this.name = in.readString(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public Person createFromParcel(Parcel source) { return new Person(source); } @Override public Person[] newArray(int size) { return new Person[size]; } };}
接下来分别创建Person.aidl、IRemoteService.aidl和Service
Person.aidl
package com.zx.aidl.demo;parcelable Person;
IRemoteService.aidl
package com.zx.aidl.demo;import com.zx.aidl.demo.Person;interface IRemoteService {void addPerson(in Person person);List getPersons();}
IRemoteService 这个接口中定义了俩个方法,需要注意的是AIDL接口中,只能定义方法,并不支持定义静态常量,当我们在AIDL文件中引用到其他自定义类型时,需要创建一个与该类同名的AIDL文件,如上面的Person.aidl ,并在文件中声明该类为parcelable,这里的parcelable首字母是小写,同时需要在引用该类的AIDL接口中import它的包名,接下来创建服务端的Service
package com.zx.aidl.demo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.support.annotation.Nullable;import android.util.Log;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;public class RemoteService extends Service { private static final String TAG = RemoteService.class.getSimpleName(); private CopyOnWriteArrayList persons = new CopyOnWriteArrayList<>(); @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate() executed"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand() executed"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy() executed"); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "onUnbind() executed"); return super.onUnbind(intent); } private IRemoteService.Stub binder = new IRemoteService.Stub() { @Override public void addPerson(Person person) throws RemoteException { persons.add(person); } @Override public List getPersons() throws RemoteException { return persons; } }; @Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind() executed"); return binder; }}
到这里,已经完成了上面所述的三个步骤;
1.创建AIDL和Service
2.实现接口
private IRemoteService.Stub binder = new IRemoteService.Stub() { @Override public void addPerson(Person person) throws RemoteException { persons.add(person); } @Override public List getPersons() throws RemoteException { return persons; } };
3.向客户端公开接口
@Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind() executed"); return binder; }
第二步实现接口的时候发现这么一个类IRemoteService.Stub,我们之前并没有创建过这个类,那这个类到底从哪来的?
IRemoteService其实是编译器根据IRemoteService.aidl这个文件为我们自动生成的一个Java类,Stub是IRemoteService的一个内部抽象类
,我们先不管这个类,接下来分析AIDL原理的时候,再去分析这个类,在RemoteService这个里,创建了一个CopyOnWriteArrayList来保存数据,为什么采用它呢,这里主要是提醒大家考虑线程并发的问题,也就是多个客户端跟服务端通信的问题,CopyOnWriteArrayList可以避免多线程并发引发的问题,这里我们不对这它进行分析,如果感兴趣,可自行去了解,我们在RemoteService里对它的生命周期方法进行了log打印,顺便查看下客户端服务端通信时,Service的生命周期。,不要忘了在AndroidManifest中注册Service
服务端工作已完成,接下来直接上客户端布局以及Activity代码
package com.zx.aidl.demo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.text.TextUtils;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import java.util.List;public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btnBind; private Button btnUnbind; private EditText etId; private EditText etName; private Button btnAdd; private Button btnGetPersons; private TextView tvContent; private IRemoteService iRemoteService; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { iRemoteService = IRemoteService.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnBind = (Button) findViewById(R.id.btn_bind); btnUnbind = (Button) findViewById(R.id.btn_unbind); btnAdd = (Button) findViewById(R.id.btn_add); etId = (EditText) findViewById(R.id.et_id); etName = (EditText) findViewById(R.id.et_name); btnGetPersons = (Button) findViewById(R.id.btn_get); tvContent = (TextView) findViewById(R.id.tv_content); btnBind.setOnClickListener(this); btnUnbind.setOnClickListener(this); btnGetPersons.setOnClickListener(this); btnAdd.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn_bind : bindService(new Intent(this,RemoteService.class),serviceConnection, Context.BIND_AUTO_CREATE); break; case R.id.btn_unbind: unbindService(serviceConnection); break; case R.id.btn_add: addPerson(); break; case R.id.btn_get : getPersons(); break; } } private void getPersons() { try { List personList = iRemoteService.getPersons(); if (personList != null) { StringBuilder sb = new StringBuilder(); for (Person person : personList) { sb.append("id :").append(person.getId()).append(" ").append("姓名:").append(person.getName()).append("\n"); } tvContent.setText(sb.toString()); } } catch (RemoteException e) { e.printStackTrace(); } } private void addPerson() { String id = etId.getText().toString(); String name = etName.getText().toString(); if (! TextUtils.isEmpty(id) && ! TextUtils.isEmpty(name)) { try { Person person = new Person(); person.setId(Integer.parseInt(id)); person.setName(name); iRemoteService.addPerson(person); } catch (RemoteException e) { e.printStackTrace(); } } }}
客户端实现其实很简单,首先通过bindService方法绑定服务端Service并建立连接,在ServiceConnection 的onServiceConnected方法回调内接受服务端Service onBind方法返回的binder对象并把它转换为我们需要的
IRemoteService 实例,然后通过IRemoteService实例去调用它本身的addPerson()与getPersons()方法,这样就达到了客户端调用服务端的方法功能,其实也就是客户端与服务端通信,接下来首先看下客户端bindService()与unBindService()方法时,Service会执行那些生命周期方法。
与startService()方法不同的是,bindService时,Service只执行了onCreate()与onBind()方法,同理,执行unBindService()时,Service执行了与之对应的onUnbind()与onDestroy()方法,所以当我们绑定解绑一个Service时,它的生命周期为:onCreate()--->onBind()--->onUnbind()--->onDestory()
回到正题,看下客户端与服务端数据传递,首先我们在之前已经在服务端RemoteService实现的接口的回调方法里加几行log日志打印
private IRemoteService.Stub binder = new IRemoteService.Stub() { @Override public void addPerson(Person person) throws RemoteException { Log.e(TAG, " addPerson() executed "); Log.e(TAG,person.getId()+""); Log.e(TAG,person.getName()); persons.add(person); } @Override public List getPersons() throws RemoteException { Log.e(TAG, " getPersons() executed "); if (persons.size() > 0) { for (Person person : persons) { Log.e(TAG,person.getId()+""); Log.e(TAG,person.getName()); } } return persons; } };
通过日志发现,每次在MainActivity中通过调用IRemoteService的addPerson和getPersons方法时,都会执行服务端binder的对应addPerson和getPersons回调方法,至于其中其中的原理,下篇分析AIDL原理时,再详细分析。。以上就是AIDL基本的使用,当然如果在项目中使用AIDL,业务肯定要复杂的多,本文只是抛砖引玉,真正使用的时候,根据项目相应业务去做处理。。。 AIDL原理分析
更多相关文章
- android 调用系统相机或者系统相册功能时,onActivityResult方法不
- andorid中Html.fromHtml方法
- Flutter学习笔记(六)Flutter与原生的互相通信
- Android禁止横屏竖屏切换的有效方法
- Android socket AsyncTask和linux服务端通讯
- 知识梳理:Android子线程中更新UI的3种方法
- android中判断当前运行activity名的方法