AIDL文件编写及源码分析
AIDL编写
Binder被用来实现进程间的通信,基本使用步骤如下:
- 创建AIDL文件,编写接口方法;
- 如果有自定义的model对象,则在aidl下创建包名一致且名称与model名一致的aidl文件;
- make project
- 编写相应的代码,启动服务发送和接收数据。
具体操作:
以下例子来源于 FrameWork/BinderSample
创建model对象
Person
实现 Parcelable
接口 (如果不需要自定义model,则不需要)
package com.github.jxiaow.sample.model;
import android.os.Parcel;
import android.os.Parcelable;
public class Personal implements Parcelable {
public String name;
public int age;
public Personal(String name, int age) {
this.name = name;
this.age = age;
}
// .....
}
创建AIDL文件
如果有自定义model对象并且与AIDL接口文件的包名不一致时需要创建一个AIDL
文件表示
// IPersonal.aidl
// 包名必须与model的一致
package com.github.jxiaow.sample.model;
// Declare any non-default types here with import statements
// 自定义的model需要使用parcelable进行声明
parcelable Personal;
编写AIDL接口文件
// ILeoAidl.aidl
package com.github.jxiaow.sample;
// Declare any non-default types here with import statements
// 导入model对应的aidl文件
import com.github.jxiaow.sample.model.IPersonal;
interface IPersonalAidlInterface {
// 自己定义的model 需要添加in修饰
void addPersonal(in Personal person);
List<Personal> getPersonalList();
}
make project
通过Android studio 的 make project 后生成java版本的aidl接口类。代码结构:
生成的重要部分代码如下:
package com.github.jxiaow.sample;
public interface IPersonalAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* Stub 用于数据服务接收端,该抽象方法由服务接收端实现
* 通过asInterface 返回一个proxy给数据发送端
*/
public static abstract class Stub extends android.os.Binder
implements com.github.jxiaow.sample.IPersonalAidlInterface {
// 通过该字段区分binder
private static final java.lang.String DESCRIPTOR =
"com.github.jxiaow.sample.IPersonalAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.github.jxiaow.sample.IPersonalAidlInterface interface,
* generating a proxy if needed.
*/
public static com.github.jxiaow.sample.IPersonalAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 如果服务端和客户端不在同一进程,那么obj.queryLocalInterface(DESCRIPTOR)为null
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.github.jxiaow.sample.IPersonalAidlInterface))) {
return ((com.github.jxiaow.sample.IPersonalAidlInterface) iin);
}
// 返回一个服务端代理,用于客户端远程调用服务端的方法
return new com.github.jxiaow.sample.IPersonalAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
// onTransact 复写了Binder中的onTransact
// 客户端调用代理执行相应的方法后会调用到服务端的此方法上进行相应的方法调用
// 数据发送端proxy调用transact后通过binder会调用stub的onTransact
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addPersonal: {
data.enforceInterface(descriptor);
com.github.jxiaow.sample.model.Personal _arg0;
if ((0 != data.readInt())) {
_arg0 = com.github.jxiaow.sample.model.Personal.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPersonal(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPersonalList: {
data.enforceInterface(descriptor);
java.util.List<com.github.jxiaow.sample.model.Personal> _result = this.getPersonalList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
// 代理静态内部类,用于发送数据到服务端
private static class Proxy implements com.github.jxiaow.sample.IPersonalAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void addPersonal(com.github.jxiaow.sample.model.Personal person)
throws android.os.RemoteException {
// _data 是发送的数据
// _reply 接收的数据
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
// mRemote.transact方法会调用到服务端的onTransact,然后进行方法标记分发
boolean _status = mRemote.transact(Stub.TRANSACTION_addPersonal, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addPersonal(person);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<com.github.jxiaow.sample.model.Personal> getPersonalList()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.github.jxiaow.sample.model.Personal> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getPersonalList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getPersonalList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.github.jxiaow.sample.model.Personal.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.github.jxiaow.sample.IPersonalAidlInterface sDefaultImpl;
}
// 每个方法的标志
static final int TRANSACTION_addPersonal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPersonalList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
// 此方法有继承自Stub的子类实现
public void addPersonal(com.github.jxiaow.sample.model.Personal person)
throws android.os.RemoteException;
public java.util.List<com.github.jxiaow.sample.model.Personal> getPersonalList()
throws android.os.RemoteException;
}
客户端使用
// 8.0 以后,不允许service后台运行,所以如果将service至于后台,bindService会报错
// W/ActivityManager: Unable to start service Intent
// { cmp=com.github.jxiaow.binderservice/.MyService } U=0: not found
val intent = Intent()
intent.setClassName(
"com.github.jxiaow.binderservice",
"com.github.jxiaow.binderservice.MyService"
)
bindService(intent, conn, Context.BIND_AUTO_CREATE)
通过asInterface 获取使用。
private val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.d("Personal", "onServiceConnected: ")
personalInterface = IPersonalAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName) {
personalInterface = null
}
}
源码分析流程
源码基于API 30
Activity#bindService
在Acitivity
中绑定或启动Service
时,经常会使用下面的方式,我们就以下面的方式进行源码调用分析。
val intent = Intent(this, PersonalService::class.java)
bindService(intent, conn, Context.BIND_AUTO_CREATE)
ContextWrapper#bindService
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
return mBase.bindService(service, conn, flags);
}
以上方法中调用了mBase.bindService
,而我们可以知道mBase
就是ContextImpl
的实例。
ContextImpl#bindService
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null, getUser());
}
ContextImpl#bindServiceCommon
bindServiceCommon
通过ActivityManagerService
进行Service
的相关操作。
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
// ....
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(this);
// 重点, 此处调用了ActivityManagerService中的方法
// 用于service的相关生命周期处理
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
ActivityManager#getService
// 获取一个IActivityManager AIDL接口实现proxy实例
// 由于android 8.0 采用的AIDL模板生成代码的方式,无法在Andorid Studio中看到生成的代码Proxy
// 返回一个单例 ActivityMangerService的Binder代理类
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
// 通过ServiceManager获取IActivityManager服务端(ActivityManagerService)的代理类
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
// 通过Stub.asInterface 获取AIDL接口生成代码中的proxy实例
// 此处获取的就是AIDL中的proxy, 如果有疑问,去看一下前面的创建AIDL文件处的代码
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
具体的解析,请看上面代码中的注释,ActivityManager.getService()
获取到的是一个IActivityManagerService
的代理类。
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback{}
ActivityManagerService#bindIsolatedService
public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection,
int flags, String instanceName,
String callingPackage, int userId)
throws TransactionTooLargeException {
// ....
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, instanceName, callingPackage, userId);
}
}
ActivieService#bindServiceLocked
如果service已经存在则直接绑定,如果不存在则先创建然后再绑定
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
// ....
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
// 获取Service信息缓存(serivceInfo),如果缓存不存在则创建
// 如果启动一个未启动的service,那么此处是没有缓存的
// 如果启动的是一个已经启动了的service,那么缓存是存在的
ServiceLookupResult res =
retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
}
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
// ....
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
// 创建和绑定service
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
// 由于service的创建最终是通过handler转发的,那么创建的时间一般情况下会晚于下面的代码执行
// b.intent.received 未执行绑定操作时,默认情况下是 false, publishService中置为true
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
// 调用的是ServiceDispatcher中的相关方法间接调用onServiceConnection
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortInstanceName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
// b.intnet.doRebind 默认是false
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
// 检查是否需要重新绑定
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
}
// 默认是false,绑定成功后为true,
// 如果bringUpServiceLocked能执行到realStartService中,则b.intent.requested = true
else if (!b.intent.requested) {
// 请求绑定service
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
}
// ...
return 1;
}
ActivieService#bringUpServiceLocked
bringUpServiceLocked
中 先判断service
是否已经创建,如果创建调用sendServiceArgsLocked
方法进行相关操作,否则
调用realStartServiceLocked
进行service
创建和绑定。
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
// 如果service已经启动调用sendServiceArgsLocked方法通过远程服务执行onStartCommon
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
// ....
// 判断每次启动Service时是否需要独立开启一个进程,此属性是通过 android:isolatedProcess="false" 设置
// 默认false
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
// 启动服务的app是否为null
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
// 重点, 创建并绑定service
// 此方法
realStartServiceLocked(r, app, execInFg);
return null;
}
// ...
}
}
// ....
return null;
}
ActivieService#sendServiceArgsLocked
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
// r.pendingStarts在service执行完onStartCommon后为空
// 在startServiceLocked中添加service到pendingStarts中 (startService方式启动service)
// 在realStartServiceLocked中也会添加 (bindService 方式启动service)
final int N = r.pendingStarts.size();
// 此处可以判断可以说明 onStartCommon只会执行一次
if (N == 0) {
return;
}
// ...
ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
slice.setInlineCountLimit(4);
Exception caughtException = null;
try {
// 调用 applicationThread中的scheduleServiceArgs
r.app.thread.scheduleServiceArgs(r, slice);
}
// ...
}
AcitivieService#realStartServiceLocked
realStartServiceLocked
方法中主要完成了2件事:1. 在ApplicationThread
中调用scheduleCreateService
进行 Service的创建;2. 请求绑定Service。
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// ....
boolean created = false;
try {
//....
// app.thread ===> ApplicationThread
// 1. AplicationThread 创建Service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
r.postNotification();
created = true;
}
// ....
// 2. 请求绑定Service
requestServiceBindingsLocked(r, execInFg);
// 3. 如果是startService方式,service恢复时调用onStartCommon方法
// 正好验证了bindService() 不会调用onStartCommon方法
// If the service is in the started state, and there are no
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}
sendServiceArgsLocked(r, execInFg, true);
}
ActivityThread#handleCreateService
在AcitivieService#realStartServiceLocked()
中调用ApplicationThread#scheduleCreateService
后,scheduleCreateService
通过Handler发送what为CREATE_SERVICE
的事件,然后在会调用ActivityThread#handlerCreateService
进行Service 创建。
// 创建Service
@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
// 反射创建
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
}
try {
// ....
// 调用service的onCreate
service.onCreate();
mServices.put(data.token, service);
// ...
} catch (Exception e) {
// ....
}
}
ActivityThread#handleBindService
在AcitivieService#realStartServiceLocked
中Service创建成功后,会调用requestServiceBindingsLocked
进行Service的绑定,最终调用到ActivityThread#handleBindService
。
// 绑定Service
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
// ...
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
// onBind方法返回binder
IBinder binder = s.onBind(data.intent);
// 调用activityManagerService 的publishService
// 调用ServiceConnection的conn
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
// 重新绑定
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
// ....
}
}
}
ActivityManagerService#publishService
public void publishService(IBinder token, Intent intent, IBinder service) {
// ...
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
ActiveServices#publishServiceLocked
- 通过
intent
进行过滤获取IntentBindRecord
并对request
、received
赋值为true; - 遍历
connections
,调用c.conn.connected
。
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
+ " " + intent + ": " + service);
if (r != null) {
// 通过intent过滤获取IntentBindRecord
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
// ...
try {
c.conn.connected(r.name, service, false);
} catch (Exception e) {
// ...
}
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
ContextImpl#bindServiceCommon
前面对service
启动和绑定的分析完毕后,在来看看是如何回调ServiceConnection
的相关方法的。
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
....
if (mPackageInfo != null) {
if (executor != null) {
// IServiceConnection 来源
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
} else {
throw new RuntimeException("Not supported in system context");
}
....
}
LoadApk#getServiceDispatcherCommon
创建ServiceDispatcher
(对ServiceConnect实例进行包装和增强)。
private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
Context context, Handler handler,
Executor executor, int flags) {
synchronized (mServices) {
....
if (sd == null) {
if (executor != null) {
sd = new ServiceDispatcher(c, context, executor, flags);
} else {
sd = new ServiceDispatcher(c, context, handler, flags);
}
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler, executor);
}
return sd.getIServiceConnection();
}
}
@UnsupportedAppUsage
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
// connection的实际类型
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mActivityExecutor = null;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
InnerConnection
InnerConnection
继承自 AIDL
中生成的Stub
类。
private static class InnerConnection extends IServiceConnection.Stub {
@UnsupportedAppUsage
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service, dead);
}
}
}
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
// ....
// If there was an old service, it is now disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
if (dead) {
mConnection.onBindingDied(name);
}
// If there is a new viable service, it is now connected.
if (service != null) {
// 回调定义的onServiceConnected 方法
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
mConnection.onNullBinding(name);
}
}
本文链接:https://jxiaow.gitee.io/posts/40026fc8/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!