引言
service作为Android四大组件之一,在日常开发中会使用到,其中作为一个服务提供方向不同访问者提供访问功能是service很重要的一个能楼,今天在这里,重点讲解通过bind Service方式访问service 以一个服务提供者的视角,理解service
代码记录
package com.example.accounttest.service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.example.accounttest.CallBack;
import com.example.accounttest.ServiceAIDL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 总结:从服务端的角度,理解bindService
* 1、作为一个服务端,要明确自己的服务场景,主要的服务场景有两种(订阅类型、普通信息获取)
* --订阅类型场景:例如当登录状变化时,通知所有注册的客户端。此类场景需要通过RemoteCallbackList对当前订阅的客户端进行保存,在状态变化时,集中通知所有客户端,相当于一对多的数据传输场景
* --普通信息获取场景:例如客户端调用接口查询数据。此类场景不需要对callback进行额外的存储,直接使用callback进行回调就行,相当于一对一的数据传输
* 2、从服务端的角度,在订阅通知场景,如何维护当前已经连接的客户端有哪些(客户端主动解绑、客户端异常崩溃解绑)
* --客户端主动解绑:通过从callback中获取IBinder作为key的方式,保证在同一个客户端可以主动发起CallBack解绑,通知服务端客户端下线(因为跨进程通信导致每次获取到的都不是同一个CallBack对象)
* --客户端异常崩溃解绑:通过监听死亡回调linkToDeath的形式,保证客户端在异常崩溃场景,服务端可以识别到客户端的解绑
* 3、服务端的其他注意点
* --mBinder对象的多线程问题:因跨进程通信,返回的数据都在binder线程,不一定是主线程,需要做线程安全处理
* --RemoteCallbackList很好的封装了多线程、死亡监听、IBinder作为key的处理,是订阅类型服务的首选
* --RemoteCallbackList 提供了cookie的能力,有额外字段要保存的话可以使用,可以参看room 组件中MultiInstanceInvalidationService的实现
* 4、疑惑点
* --客户端bindService 和 unBindService 服务端如何感知?bindService 多次,也仅会触发一次 onBind,unBindService服务端更是无感知
*/
public class ServerService extends Service {
private static final String TAG = "whqq";
//不建议直接存储CallBack的原因,是因为Binder接口调用,即便客户端使用的是同一个CallBack,也会因为跨进程通信,每次获取到的都不是同一个CallBack,使用set存储,会导致无法进行CallBack的解绑,而发生内存泄露
private static final Set<CallBack> set = new HashSet<>();
//使用HashMap<IBinder, CallBack>的形式,是对上面set的一个优化,因为跨进程通信,每次获取到的都不是同一个CallBack,但是他们的IBinder是相同的,因此可以用IBinder做key,来保证CallBack的解绑
private static final HashMap<IBinder, CallBack> map = new HashMap<>();
//RemoteCallbackList 相当于是系统提供的进阶版本HashMap<IBinder, CallBack>,其内部实现了以IBinder做key的存储,且同时通过DeathRecipient监听了非用户主动解绑(客户端崩溃异常解绑)与线程安全的处理,是推荐的使用方式
private static final RemoteCallbackList<CallBack> reMoteList = new RemoteCallbackList<CallBack>() {
@Override
public void onCallbackDied(CallBack callback) {
super.onCallbackDied(callback);
Log.e(TAG, "有客户端解绑了" + callback.asBinder().toString());
}
};
//RemoteCallbackList注意事项
//1、RemoteCallbackList内部实现了DeathRecipient,并通过onCallbackDied提供给上层业务做额外的解绑操作,不需要自己实现linkToDeath去监听解绑
//2、经测试,若自己实现了linkToDeath也无问题,两个回调都会触发
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
private IBinder mBinder = new ServiceAIDL.Stub() {
@Override
public void getInfo(Bundle bundle, CallBack callBack) throws RemoteException {
Log.e(TAG, "getInfo触发" + "客户端" + "传输值" + bundle.toString());
Log.e(TAG, "binder对象" + callBack.asBinder().toString());
Log.e(TAG, "callBack对象" + callBack.toString());
String string = bundle.getString("type");
if (string == null) {
return;
}
switch (string) {
case "同步查询个人信息":
syncQuery(bundle, callBack);
break;
case "异步查询个人信息":
aSyncQuery(bundle, callBack);
break;
case "订阅":
set.add(callBack);
Log.e(TAG, "当前对象数量" + set.size());
reMoteList.register(callBack);
callBack.asBinder().linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
Log.e(TAG, "有客户asdasdasda端解绑了");
}
},0);
Log.e(TAG, "当前remote对象数量" + reMoteList.beginBroadcast());
reMoteList.finishBroadcast();
map.put(callBack.asBinder(), callBack);
Log.e(TAG, "当前map对象数量" + map.size());
break;
case "通知订阅":
notifyAllClient();
break;
case "客户端请求断开连接":
unBind(callBack);
break;
default:
}
}
private void syncQuery(Bundle bundle, CallBack callBack) throws RemoteException {
Bundle bundle1 = new Bundle();
bundle1.putString("result", "结果回调");
callBack.onResult(bundle1);
}
private void aSyncQuery(Bundle bundle, CallBack callBack) throws RemoteException {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Bundle bundle1 = new Bundle();
bundle1.putString("result", "结果回调");
try {
callBack.onResult(bundle1);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private void unBind(CallBack callBack) {
set.remove(callBack);
Log.e(TAG, "当前对象数量" + set.size());
reMoteList.unregister(callBack);
Log.e(TAG, "当前remote对象数量" + reMoteList.beginBroadcast());
reMoteList.finishBroadcast();
map.remove(callBack.asBinder());
Log.e(TAG, "当前map对象数量" + map.size());
}
private void notifyAllClient() {
Bundle bundle1 = new Bundle();
bundle1.putString("result", "订阅结果回调");
try {
for (CallBack callBack : set) {
callBack.onResult(bundle1);
}
int n = reMoteList.beginBroadcast();
for (int i = 0; i < n; i++) {
reMoteList.getBroadcastItem(i).onResult(bundle1);
}
reMoteList.finishBroadcast();
for (Map.Entry<IBinder, CallBack> entry : map.entrySet()) {
entry.getValue().onResult(bundle1);
// 使用key和value进行操作
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
};
}