当需要进程间通信的时候有时候需要用到aidl技术,此时就需要绑定远程的服务
具体如何使用aidl技术,在此处不做介绍,只是说说在开发中在绑定过程中遇到的坑
不多说直接上代码:
Intent intent = new Intent();
intent.setAction("wsls.MyService");//通过隐式意图启动远程服务
mConn = new MyServiceConnection();
bindService(intent, mConn, Context.BIND_AUTO_CREATE)
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//绑定成功回调,返回绑定的IBinder对象
}
@Override
public void onServiceDisconnected(ComponentName name) {
//远程服务断开连接(比如远程服务挂掉了)
}
}
次方法在4.4及以下没有问题,能够正常绑定,但运行在5.0之后会报以下异常
Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit
于是开始查看源码,点击bindService方法,跳转到
ContextWrapper类中的
然后会调用bindServiceCommon方法@Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { return mBase.bindService(service, conn, flags); }通过代码可以发现,绑定服务工作转换到mBase.bindService,mBase又是一个什么东西呢?点击查看@OverrideContext mBase;发现就是个上下文.context是一个抽象类,它的实现类是ContextImp于是乎我们查看ContextImp类中的代码
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess();
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
return false;
}
}
通过代码可以发现内部会调用 validateServiceIntent(service);他是干什么的呢?
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
通过代码可以发现在5.0以上的异常就是他抛出来的,他会通过传进去的intent进行判断包名(远程的包名)是否为空,并判断当前版本是否大于版本LOLLIPOP,如果是5.0以上的则直接抛出一个异常,低于5.0的未做处理,这就是为什么在4.0能正常启动远程服务,5.0不可以的原因.在此贴上正确代码
intent.setAction("wsls.MyService");
intent.setPackage("com.wsl.test");//这句不能少
mConn = new MyServiceConnection();
boolean b = bindService(intent, mConn, Context.BIND_AUTO_CREATE);
分析到此结束
另外补充一点,包名一定要以com开头,不然也无法启动远程的服务
初次写博客,请大家多多包涵,欢迎大家吐槽