Android系统开发入门-13.Binder服务死亡讣告

前面我们花了几小节来实现了各种类型的服务及客户端调用验证,这些服务本质上都是 Binder 通信,而 Binder 通信本质上也是 C/S 通信。既然是 C/S 通信,那就会存在 client 或者 server 异常挂掉的情况。如果 server 挂掉了, client 就没法再调用 server 的接口了。同样,client 挂掉的话,server 也没法再调用 client 的回调接口。如果继续调用一个已经挂掉的 Binder 接口, Android 8.0 之后的 hidl 机制会导致调用进程也跟着挂掉,而 Android 8.0 之前的 Binder 通信模式只是会返回一个错误状态。无论何种情况,我们都需要处理好 Binder 挂掉的问题。Binder实现了一套 死亡讣告 的功能,即:服务端挂了,或者正常退出,Binder驱动会向客户端发送一份讣告,告诉客户端Binder服务挂了。下面我们以 hidl 服务为例来学习一下死亡讣告的使用方法。

直接在之前的 hidl TVServer 基础上进行修改。实现双向挂掉监听。

1. client 监听 server 是否挂掉

TVServerTest.cpp 修改如下:

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
#include <device/qiushao/pure/tvserver/1.0/ITVServer.h>
#include <device/qiushao/pure/tvserver/1.0/ITVServerListener.h>
#include <hidl/Status.h>
#include <utils/misc.h>
#include <hidl/HidlSupport.h>
#include <stdio.h>
#include <string>

using namespace std;

using ::android::hardware::hidl_string;
using ::android::hardware::hidl_death_recipient;
using ::android::sp;
using device::qiushao::pure::tvserver::V1_0::ITVServer;
using device::qiushao::pure::tvserver::V1_0::ITVServerListener;

android::sp<ITVServer> service = nullptr;

class TVServerListener : public ITVServerListener {
public:
android::hardware::Return<void> onMessage(const hidl_string& message) override {
printf("onMessage:%s\n", message.c_str());
return android::hardware::Return<void>();
}
};

class ServerDeathRecipient : public hidl_death_recipient {
virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) {
printf("serviceDied cookie = %d\n", cookie);
service = nullptr;
}
};

int main(){

sp<ITVServerListener> listener = new TVServerListener();
sp<ServerDeathRecipient> deathRecipient = new ServerDeathRecipient();

service = ITVServer::getService();
if (service == nullptr){
printf("Failed to get service\n");
return -1;
}

service->linkToDeath(deathRecipient, 0);
service->registerListener(getpid(), listener);
service->hello("qiushao", [&](hidl_string result){
printf("%s\n", result.c_str());
});

sleep(10);
//service->unregisterListener(getpid());

return 0;
}

增加了一个 ServerDeathRecipient 类作为一个监听器,通过 service->linkToDeath(deathRecipient, 0); 来注册监听器。当service 挂掉的时候, ServerDeathRecipient 监听器的 serviceDied 方法会被调用。我们可以在这个方法里面做些善后工作。

完整编译,启动虚拟机,查看 TVServer 的进程号,然后启动 TVServiceTest, 我们在代码里面 sleep 了 10 秒钟, 在 TVServiceTest 退出之前, 手动 kill 掉 TVServer , 看 log serviceDied 方法的确被调用了。

1
2
3
4
5
130|pure:/ # TVServerTest 
hello qiushao from TVServer
onMessage:message from server
serviceDied cookie = 0
pure:/ #

2. server 监听 client 是否挂掉

TVServer.h 修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
using ::android::hardware::hidl_death_recipient;

class ClientDeathRecipient;
class TVServer : public ITVServer {
public:
TVServer();
// Methods from ::device::qiushao::pure::tvserver::V1_0::ITVServer follow.
Return<void> hello(const hidl_string& name, hello_cb _hidl_cb) override;
Return<void> registerListener(const uint32_t pid, const sp<ITVServerListener>& listener) override;
Return<void> unregisterListener(const uint32_t pid) override;
private:
map<uint32_t, sp<ITVServerListener> > mListeners;
sp<ClientDeathRecipient> mDeathRecipient;
};

只是声明了一个 mDeathRecipient。

TVServer.cpp 修改如下

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
#include "TVServer.h"
#include <android/log.h>
#define LOG_TAG "TVServer"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

namespace device {
namespace qiushao {
namespace pure {
namespace tvserver {
namespace V1_0 {
namespace implementation {

class ClientDeathRecipient : public hidl_death_recipient {
private:
sp<TVServer> mTVServer;
public:
ClientDeathRecipient(sp<TVServer> tvserver): mTVServer(tvserver) {}
virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) {
uint32_t pid = cookie;
LOGD("client died pid = %d", pid);
mTVServer->unregisterListener(pid);
}
};

TVServer::TVServer() {
LOGD("TVServer start...");
mDeathRecipient = new ClientDeathRecipient(this);
}

// Methods from ::device::qiushao::pure::tvserver::V1_0::ITVServer follow.
Return<void> TVServer::hello(const hidl_string& name, hello_cb _hidl_cb) {
char buf[100];
::memset(buf, 0, 100);
::snprintf(buf, 100, "hello %s from TVServer", name.c_str());
hidl_string result(buf);
_hidl_cb(result);

LOGD("listener size = %d", mListeners.size());
map<uint32_t, sp<ITVServerListener> >::iterator it;
for (it = mListeners.begin(); it != mListeners.end(); ++it) {
sp<ITVServerListener> listener = it->second;
listener->onMessage("message from server");
}

return Void();
}

Return<void> TVServer::registerListener(const uint32_t pid, const sp<ITVServerListener>& listener) {
mListeners[pid] = listener;
listener->linkToDeath(mDeathRecipient, pid);
LOGD("registerListener %d, %x", pid, listener.get());
return Void();
}

Return<void> TVServer::unregisterListener(const uint32_t pid) {
mListeners[pid]->unlinkToDeath(mDeathRecipient);
mListeners.erase(pid);
LOGD("unregisterListener %d", pid);
return Void();
}


// Methods from ::android::hidl::base::V1_0::IBase follow.

//ITVServer* HIDL_FETCH_ITVServer(const char* /* name */) {
//return new TVServer();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace tvserver
} // namespace pure
} // namespace qiushao
} // namespace device

在注册 listener 的时候顺便给 listener linkToDeath 到一个 hidl_death_recipient 死亡讣告上。
linkToDeath 的第二个参数为 cookie, 用来标志当前的 listener,若当前的 listener GG了, 系统会自动调用 ClientDeathRecipient 的 serviceDied 函数。其中的 cookie 值即为我们在 linkToDeath 传的第二个参数。收到 serviceDied 回调后,我们把对应 cookie 的 listener 从 mListener 中移除就行。这里我们使用了 client 的进程号作为 cookie。
当然上面的代码还没有考虑多线程的情况, 实际项目代码中我们对所有的临界区资源访问前都应该进行加锁。

完整编译,启动虚拟机,启动 TVServiceTest,等待 TVServiceTest 进程退出,其实也就是 client GG 的情况。看 log serviceDied 方法的确被调用了。

1
2
3
4
5
6
7
8
9
10
11
130|pure:/ # TVServerTest 
hello qiushao from TVServer
onMessage:message from server
pure:/ # logcat -s TVServer
--------- beginning of main
02-03 15:02:51.255 5789 5792 D TVServer: registerListener 8044, d9643000
02-03 15:02:51.256 5789 5792 D TVServer: listener size = 1
02-03 15:03:01.259 5789 5792 D TVServer: client died pid = 8044
02-03 15:03:01.259 5789 5792 D TVServer: unregisterListener 8044
^C
130|pure:/ #

3. 非 hidl binder 服务的死亡讣告

对于另外两种 binder 服务的死亡讣告其实流程都是一样的,差异只是需要继续的死亡讣告类不一样而已。
java 服务对应的是java层 IBinder.DeathRecipient。
native 服务对应的是c++层 IBinder::DeathRecipient。
具体实现这里就不再展开了,有兴趣的同学可以自己尝试一下。