Android Studio使用AIDL初步实现进程间通信的步骤以及相关注意事项
这里记录一下使用Android Studio实现AIDL通信的操作步骤
说明一下:这一篇仅仅记录了初步使用步骤,传输的数据是基本类型。
一·服务端
1.首先创建一个Application,在此Application中创建一个Service,姑且叫RemoteService吧
在Manifest文件中给Service添加一个action,这是为了方便调用方找到这个服务,这两个箭头所指向的内容后面会用到
2.定义一个私有方法,返回值为一个String。
public class RemoteService extends Service {
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
private String getTime() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss sss").format(new Date());
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
3.创建.aidl文件,project视图下,选中src-->main,new-->AIDL-->AIDL File,新建一个AIDL文件,取名IRemoteServiceAidl
4.build一下工程,可以看到编译器帮助我们生成了一个对应的AIDL的java文件
4.大体看一下文件内容,Stub是一个抽象类,继承了Binder类,实现了IRemoteServiceAidl接口,也就是说,在服务中,需要继承Stub类,然后实现IRemoteServiceAidl接口方法,这样调用端就可以访问service的方法了
4.service里面添加Binder成员变量,并在onBind回调方法中返回实例对象,此时的Binder类似于中介,可通过它调用service的公有或者私有方法。
public class RemoteService extends Service {
private RemoteServiceBinder binder;
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
binder = new RemoteServiceBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
private String getTime() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss sss").format(new Date());
}
@Override
public void onDestroy() {
System.out.println("remote service has been destory");
super.onDestroy();
}
/**
* 实现aidl文件中暴露的接口方法
*/
private class RemoteServiceBinder extends IRemoteServiceAidl.Stub {
@Override
public String callGetTime() throws RemoteException {
//调用service中的私有方法
return getTime();
}
}
}
至此,AIDL服务端完成。
二·客户端
1.同样,新建一个application,复制服务端的aidl文件夹到相同层级的目录,然后build一下项目
2.定义RemoteServiceBinder接口对象,并在serviceConnection回调中获取Binder的实例,代码如下
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnRemoteService;
//定义接口对象
private IRemoteServiceAidl binder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//绑定服务时回调,然后通过返回回来的Binder实例化成员binder
binder = IRemoteServiceAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnRemoteService = findViewById(R.id.btn_remote_service);
btnRemoteService.setOnClickListener(this);
Intent intent2 = new Intent();
intent2.setAction("com.aoto.remote.service");
intent2.setPackage("com.aoto.remoteservice");
bindService(intent2,serviceConnection,BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_remote_service:
try {
//此时按钮点击事件就可以调用远程接口的方法了
System.out.println("localService: "+binder.callGetTime());
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
此时客户端完成
关于非基本数据类型的数据传输会在后续博客中继续,文章中有误的地方欢迎您提供宝贵意见们也可以留言讨论,谢谢。
注意事项说一下吧(主要是我自己在写的时候遇到的坑):
1.bindService()绑定到远程服务后报错:
Service Intent must be explicit
这是由于安全机制的问题,不能使用隐式Intent启动服务,必须明确指定到具体的service组件
解决方案:
①.使用 Intent.setPackage("**.**.remoteservice");设置服务所在的应用的包名。就是在第一张图中的箭头所指的内容。
②.通过PackageManager获取符合action的具体的Intent和ComponentName,将隐式Intent转换为显示Intent,方法如下:
/**
*get an explicit intent in order to start or bind service
* @param context the context that need to start or bind a service component
* @param implicitIntent the intent that not so explicit,such as an intent created from an non-parameter constructor
* or with an extra action but no idea which package it belongs to
* @return Intent that can start or bind an service component directly
*/
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
2.调用Binder对象的方法时报空指针异常 ( 不走onServiceConnected()回调方法 )
在出发按钮点击事件或其他时间调用binder对象的接口方法时,binder为空,经调试发现没有回调onServiceConnected(),查阅了一番文档没有找到令人信服的解释,有的网友说在执行bindService()后,并没有立即执行onServiceConnected()回调并返回binder对象,所以执行时报错,官方文档还没有提到时间差的问题,所以,仅在这里记录一下解决方案。
将一下代码放在onCreate方法中执行,而不是事件回调方法中执行即可解决
Intent intent2 = new Intent();
intent2.setAction("com.aoto.remote.service");
intent2.setPackage("com.aoto.remoteservice");
bindService(intent2,serviceConnection,BIND_AUTO_CREATE);