本例子将会介绍远程服务回调接口的用法以及注销回调接口的方法、权限校验、远程服务挂掉后重连的方法等。
直接上代码,远程服务aidl接口及服务代码:
package com.example.aidl4.aidl;
import java.util.List;
import com.example.aidl4.aidl.PersionParcel;
interface IAddPersionListener {
void addPersion(in PersionParcel newPersion);
}
package com.example.aidl4.aidl;
import java.util.List;
import com.example.aidl4.aidl.PersionParcel;
import com.example.aidl4.aidl.IAddPersionListener;
interface IPersionBinder {
String getName(in PersionParcel p);
List<PersionParcel> getAllPersions();
void registerListener(IAddPersionListener listener);
void unregisterListener(IAddPersionListener listener);
}
public class AidlService extends Service {
private static final String mAllowPackages[] = { "com.example.aidl5" };
private AtomicBoolean mIsServiceRunning = new AtomicBoolean(true);
private CopyOnWriteArrayList<PersionParcel> mPersionList = new CopyOnWriteArrayList<PersionParcel>();
private RemoteCallbackList<IAddPersionListener> mListenerLists = new RemoteCallbackList<IAddPersionListener>();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mPersionList.add(new PersionParcel("zhangsan", 20, "男"));
new Thread() {
@Override
public void run() {
// 每隔30秒添加一人并调用接口中定义的方法
while (mIsServiceRunning.get()) {
SystemClock.sleep(30 * 1000);
PersionParcel persionParcel = new PersionParcel("lili", 20,
"女");
onAddPersion(persionParcel);
}
}
}.start();
}
private void onAddPersion(PersionParcel persionParcel) {
mPersionList.add(persionParcel);
final int N = mListenerLists.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
mListenerLists.getBroadcastItem(i).addPersion(persionParcel);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mListenerLists.finishBroadcast();
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceRunning.set(false);
}
private Binder mBinder = new IPersionBinder.Stub() {
@Override
public String getName(PersionParcel p) throws RemoteException {
return p.getName();
}
@Override
public List<PersionParcel> getAllPersions() throws RemoteException {
return mPersionList;
}
@Override
public void registerListener(IAddPersionListener listener)
throws RemoteException {
mListenerLists.register(listener);
}
@Override
public void unregisterListener(IAddPersionListener listener)
throws RemoteException {
mListenerLists.unregister(listener);
};
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
String pkgName = null;
String[] packages = AidlService.this.getPackageManager()
.getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
pkgName = packages[0];
}
for (String item : mAllowPackages) {
if (!item.equals(pkgName)) {
System.out.println("pkgName:" + pkgName + "被拒绝访问服务");
return false;
}
}
return super.onTransact(code, data, reply, flags);
}
};
}
客户端调用代码:
public class MainActivity extends Activity {
private IPersionBinder mBinder;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
System.out.println("添加了新人");
};
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
System.out.println("线程名1:" + Thread.currentThread().getName());
if (mBinder == null) {
return;
}
mBinder.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBinder = null;
// 这里重新绑定远程服务
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindAidlService();
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mBinder != null) {
try {
List<PersionParcel> list = mBinder.getAllPersions();
for (PersionParcel item : list) {
System.out.println("姓名:" + item.getName() + ", 年龄:"
+ item.getAge() + ", 性别:" + item.getSex());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConn);
if (mBinder != null && mBinder.asBinder().isBinderAlive()) {
try {
mBinder.unregisterListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private void bindAidlService() {
Intent service = new Intent("com.example.aidl4.AidlService");
bindService(service, mConn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
System.out.println("线程名:" + Thread.currentThread().getName());
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinder = IPersionBinder.Stub.asInterface(service);
try {
mBinder.registerListener(mListener);
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
private IAddPersionListener mListener = new IAddPersionListener.Stub() {
@Override
public void addPersion(PersionParcel newPersion)
throws RemoteException {
mHandler.sendEmptyMessage(0);
}
};
}
注销自定义接口时,需要使用RemoteCallbackList,它是系统专门提供的用于删除跨进程listener接口的。
public class RemoteCallbackList<E extends IInterface>
RemoteCallbackList是一个泛型,支持任意的AIDL接口。
工作原理:它内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。
ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
其中Callback中封装了真正的远程listener,当客户端注册listener时,它会把这个listener的信息存入mCallbacks中,key和value分别通过下面的方式获得:
IBinder key = listener.asBinder();
Callback value = new Callback(listener, cookie);
虽然说跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象有一个共同点,那就是它们底层的Binder对象是同一个。RemoteCallbackList还有一个很有用的功能,那就是当客户端进程终止后,它能够自动移除客户端所注册的listener。
遍历RemoteCallbackList时必须按照代码中所示,其中beginBroadcast和finishBroadcast必须配对使用。
Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,此时客户端到服务端的Binder连接断裂,会导致远程调用失败。为了解决这个问题,BInder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理,当Binder死亡时就会收到通知,此时可以重新发起连接请求从而恢复连接。
使用方法如代码上所示。在onServiceDisconnected方法中也可以重连远程服务。另外通过Binder的isBinderAlive方法也可以判定Binder是否死亡。
区别:onServiceDisconnected在客户端的UI线程中被回调,而binderDied是在客户端的Binder线程池中被回调。也就是说,在binderDied方法中不能访问UI。
本文详细讲解了远程服务回调接口的使用,包括注销接口、权限校验和远程服务挂掉后的重连策略。重点介绍了RemoteCallbackList的工作原理,如何在客户端注册和注销接口,并展示了如何通过linkToDeath和unlinkToDeath来处理服务端异常终止的情况,确保服务的自动恢复。
1243

被折叠的 条评论
为什么被折叠?



