【注意】最后更新于 June 15, 2020,文中内容可能已过时,请谨慎使用。
Service 使用 aidl 的与 Activity 相互交互
一、为什么要使用 aidl
原因是当我们的 Client 和 Service 不是同一个进程时是无法直接使用的,而在 android 中进程间通讯的方法有 Activity、Content Provider、Broadcast 和 Service。
其中 Activity 需要界面,隐式调用没有回调 Broadcast 的接收对象经常会重新被实例化,且以上两种都是通过 intent 传送。回调不能完成交互。
Content Provider 则只提供数据,Service 有 aidl 这门进程间调用函数的机制。
二、普通单向 aidl 使用
需要一个 Client 和 Service 以及 aidl 接口,其中 Client 和 Service 是可以不在同一个应用中的。下面是 aidl:IMyAidl.aidl
1
2
3
4
5
| package zero.aidldemo.aidl;
interface IMyAidl {
void show(String str);
}
|
以上写完会在 gen 下同包名中生成与 aidl 文件相同的 java 文件打开后这个类中有一个抽象内部类 Stub 实现 Binder 和自身我们在 Service 中写一个继承这个 Stub 的内部类
Service:ServiceMain.java 把当前 Service 的 class 全名作为 Action 注册以便于其他进程调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class ServiceMain extends Service{
public final static String SERVICE_CLASS_NAME = "zero.musicplay.service.ServiceMain";
//继承IMyAidl.Stub
private class MyAidl extends IMyAidl.Stub{
@Override
public void show(String str) throws RemoteException {
Log.i("zerolog","ServiceMain_MyAidl_show="+str);
}
}
@Override
public IBinder onBind(Intent intent) {
return new ServiceBinder();
}
}
|
Client 用一个 Activity 来实现:ActivityClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| public class ActivityClient extends Activity implements OnClickListener,ServiceConnection{
//bind后的接口实现对象。
private IMyAidl mAsInterface;
//Service的Intent
private Intent _intent;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//界面加载和一个按键监听。。。
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(this);
//绑定Service
Intent _intent=new Intent();
_intent.setAction(ServiceMain.SERVICE_CLASS_NAME);
//在4.多后具体哪个版本不记得了隐式bindService需要设置Service的包名。
_intent.setPackage(getPackageName());
//不知道是怎么回事现在我用api23编的如果不先startService就没法bindService不知道为什么。
//statrtService(_intent);
//第一个Intent,ServiceConnection对象,flags自己查api吧没懂什么用。
bindService(_intent, this, 0);
}
@Override
public void onClick(View v) {
//bind是异步的防止mAsInterface还未获得而FC
if (mAsInterface != null) {
try {
mAsInterface.show("MainActivity->show");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
//记得解绑
if(mAsInterface != null){
unbindService(this);
stopService(_intent);
}
super.onDestroy();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAsInterface = IMyAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
|
效果就是点击按钮触发 Service 里的实现接口函数。
三、双向 aidl 使用
单向的 aidl 接口中函数也可以有返回值,但是只适用于同步且时间较短。如果需要的函数执行的是一个异步任务就不好用了。
所以这里用双向 aidl 比较合适。
与上面相比多了一个 aidl 的回调先看回调的 aidl:IMyAidlCallBack.aidl
1
2
3
4
5
| package zero.aidldemo.aidl;
interface IMyAidlCallBack {
void callBackShow();
}
|
没什么变化但是在原来的调用 Service 的 aidl 中需要加入两个方法:IMyAidl.aidl 方法名不是固定的
1
2
3
4
5
6
7
8
9
10
| package zero.aidldemo.aidl;
//eclipse对aidl文件编辑支持比较差有时无法自动导包。
import zero.aidldemo.aidl.IMyAidlCallBack;
interface IMyAidl {
void show(String str);
//注册回调
void registerCallback(IMyAidlCallBack cb);
//解除回调
void unregisterCallback(IMyAidlCallBack cb);
}
|
Service:ServiceMain.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| public class ServiceMain extends Service {
public final static String SERVICE_CLASS_NAME = "zero.musicplay.service.ServiceMain";
// 回调列表对象
private RemoteCallbackList<IMyAidlCallBack> mCallbackList;
private class MyAidl extends IMyAidl.Stub {
@Override
public void show(String str) throws RemoteException {
Log.i("zerolog", "ServiceMain_MyAidl_show=" + str);
// 取出已绑定的回调对象个数并开始广播
int conut = mCallbackList.beginBroadcast();
for (int j = 0; j < conut; j++) {
// 遍历调用回调方法。
mCallbackList.getBroadcastItem(j).callBackShow();
}
// 解除回调广播。
mCallbackList.finishBroadcast();
}
@Override
public void registerCallback(IMyAidlCallBack cb) throws RemoteException {
// 添加到回调列表中
mCallbackList.register(cb);
}
@Override
public void unregisterCallback(IMyAidlCallBack cb)
throws RemoteException {
// 从回调列表中移除
mCallbackList.unregister(cb);
}
}
@Override
public void onCreate() {
// 在服务被创建时实例化一个为空的回调列表对象
mCallbackList = new RemoteCallbackList<IMyAidlCallBack>();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return new MyAidl();
}
}
|
和上面说的一样在原有的 aidl 中添加两个方法用于给回调列表对象添加删除。并需要一个回调列表对象。
所以我在 onCreate() 实例化。
然后是 Client:ActivityClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
| public class ActivityClient extends Activity implements OnClickListener,
ServiceConnection {
// bind后的接口实现对象。
private IMyAidl mAsInterface;
// Service的Intent
private Intent _intent;
// 回调实现对象
private MyCallBack mCallBack;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 界面加载和一个按键监听。。。
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(this);
// 实例化回调实现
mCallBack = new MyCallBack();
// 绑定Service
Intent _intent = new Intent();
_intent.setAction(ServiceMain.SERVICE_CLASS_NAME);
// 在4.+后具体哪个版本不记得了隐式bindService需要设置Service的包名。
_intent.setPackage(getPackageName());
// 不知道是怎么回事现在我用api23编的如果不先startService就没法bindService不知道为什么。
// statrtService(_intent);
// 第一个Intent,ServiceConnection对象,flags自己查api吧没懂什么用。
bindService(_intent, this, 0);
}
@Override
public void onClick(View v) {
// bind是异步的防止mAsInterface还未获得而FC
if (mAsInterface != null) {
try {
mAsInterface.show("MainActivity->show");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
// 记得解绑
if (mAsInterface != null) {
try {
mAsInterface.unregisterCallback(mCallBack);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
unbindService(this);
stopService(_intent);
}
super.onDestroy();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAsInterface = IMyAidl.Stub.asInterface(service);
// 在绑定完成后将通过定义好的方法添加到Service的回调列表中
try {
mAsInterface.registerCallback(mCallBack);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
// 实现回调接口中的方法。
private class MyCallBack extends IMyAidlCallBack.Stub {
@Override
public void callBackShow() throws RemoteException {
Log.i("zerolog", "callBackShow");
// 千万记住不能在这里再调用Service的aidl方法会报异常
// java.lang.IllegalStateException: beginBroadcast() called while
// already in a broadcast
// mAsInterface.show("");
}
}
}
|
好了以上就是一个交互式的 aidl 小例子,回调可以在 Service 的任意地方调用不一定要在 Service 的 aidl 被调用时调用。
以及这些内部类也都可以提取出来,但是就不好访问 Service 和视图了。
四、注意
1.回调对象的实现方法不能直接调用Service的aidl方法。可以用Handler来调用。
2.如果发觉只用bindSrevice无法启动Service可以先startService再bindSrevice
五、例子源码
度盘
文章作者
上次更新
2020-06-15 18:12:42 +08:00
(9c054d8)
许可协议
CC BY-NC-ND 4.0