看了一天了终于把bindservice大致梳理了一遍
和service启动过程一样,service绑定也是从contextWrapper开始,具体怎么开始可以看我另一篇博客--context
程序最终调用contextImpl中的bindServiceCommon方法
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;
}
}
首先将serviceConnection对象转化为一个实现了IServiceConnection对象,从getServiceDispatcher可以看出,实现了IServiceConnection接口的对象其实是LoaderApk的一个内部类
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();
}
}
我们进入内部类一探究竟
static final class ServiceDispatcher {
private final ServiceDispatcher.InnerConnection mIServiceConnection;
private final ServiceConnection mConnection;
private final Context mContext;
private final Handler mActivityThread;
private final ServiceConnectionLeaked mLocation;
private final int mFlags;
private RuntimeException mUnbindLocation;
private boolean mDied;
private boolean mForgotten;
private static class ConnectionInfo {
IBinder binder;
IBinder.DeathRecipient deathMonitor;
}
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(sd);
}
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
private final ArrayMap mActiveConnections
= new ArrayMap();
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
void validate(Context context, Handler activityThread) {
if (mContext != context) {
throw new RuntimeException(
"ServiceConnection " + mConnection +
" registered with differing Context (was " +
mContext + " now " + context + ")");
}
if (mActivityThread != activityThread) {
throw new RuntimeException(
"ServiceConnection " + mConnection +
" registered with differing handler (was " +
mActivityThread + " now " + activityThread + ")");
}
}
void doForget() {
synchronized(this) {
for (int i=0; i
发现原来实现了IServiceConnection接口的对象是InnerConnection类,这个类继承于IServiceConnection.stud。
之所以要转换成InnerConnection对象,是因为服务的绑定有可能是跨进程的,serviceconnection对象必须借助BInder才能让service端回调自己的方法,而InnerConnection充当了binder的角色,因为作者没找到IServiceConnection的源码,所以作者猜测IServiceConnection.stud应该继承binder类。
忘记分析getservicedispatcher源码了
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();
}
}
在上面的代码中,mservice是一个Arraymap
private final ArrayMap> mServices
就是说,我们可以通过context取出一个ArrayMap然后我们回到contextImpl中来,最后调用ActivityManagernative.getdefalut进入AMS
调用和Activity启动差不多
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service, resolvedType,
connection, flags, userId);
}
}
int bindServiceLocked(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}
ActivityRecord activity = null;
if (token != null) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
int clientLabel = 0;
PendingIntent clientIntent = null;
if (callerApp.info.uid == Process.SYSTEM_UID) {
// Hacky kind of thing -- allow system stuff to tell us
// what they are, so we can report this elsewhere for
// others to know why certain services are running.
try {
clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
} catch (RuntimeException e) {
}
if (clientIntent != null) {
clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
if (clientLabel != 0) {
// There are no useful extras in the intent, trash them.
// System code calling with this stuff just needs to know
// this will happen.
service = service.cloneFilter();
}
}
}
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"BIND_TREAT_LIKE_ACTIVITY");
}
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
if (res == null) {
return 0;
}
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
+ s);
}
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (!s.hasAutoCreateConnections()) {
// This is the first binding, let the tracker know.
ProcessStats.ServiceState stracker = s.getTracker();
if (stracker != null) {
stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
s.lastActivity);
}
}
}
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList();
s.connections.put(binder, clist);
}
clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet();
}
activity.connections.add(c);
}
b.client.connections.add(c);
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.hasAboveClient = true;
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList();
mServiceConnections.put(binder, clist);
}
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
if (s.app != null) {
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
s.app.treatLikeActivity = true;
}
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
mAm.updateOomAdjLocked(s.app);
}
if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b
+ ": received=" + b.intent.received
+ " apps=" + b.intent.apps.size()
+ " doRebind=" + b.intent.doRebind);
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " 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.
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
getServiceMap(s.userId).ensureNotStartingBackground(s);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
private final String bringUpServiceLocked(ServiceRecord r,
int intentFlags, boolean execInFg, boolean whileRestarting) {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
if (!whileRestarting && r.restartDelay > 0) {
// If waiting for a restart, then do nothing.
return null;
}
if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
// We are now bringing the service up, so no longer in the
// restarting state.
if (mRestartingServices.remove(r)) {
r.resetRestartCounter();
clearRestartingIfNeededLocked(r);
}
// Make sure this service is no longer considered delayed, we are starting it now.
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "REM FR DELAY LIST (bring up): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
if (mAm.mStartedUsers.get(r.userId) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
}
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
ProcessRecord app;
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);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
app = r.isolatedProc;
}
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Applying delayed stop (in bring up): " + r);
stopServiceLocked(r);
}
}
return null;
}
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
mAm.updateOomAdjLocked();
boolean created = false;
try {
if (LOG_SERVICE_START_STOP) {
String nameTerm;
int lastPeriod = r.shortName.lastIndexOf('.');
nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
mAm.appDiedLocked(app);
} finally {
if (!created) {
app.services.remove(r);
r.app = null;
scheduleServiceRestartLocked(r, false);
return;
}
}
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
// 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));
}
sendServiceArgsLocked(r, execInFg, true);
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "REM FR DELAY LIST (new proc): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Applying delayed stop (from start): " + r);
stopServiceLocked(r);
}
}
}
不同的是,service的绑定过程会在requestServiceBindingsLocked(r, execInFg);中实现
private final boolean requestServiceBindingLocked(ServiceRecord r,
IntentBindRecord i, boolean execInFg, boolean rebind) {
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (RemoteException e) {
if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
return false;
}
}
return true;
}
然后调用 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,r.app.repProcState);回调Activity中的方法
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
sendMessage(H.BIND_SERVICE, s);
}
把所有数据打包装到bindserviceData中,方便以后取出
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (DEBUG_SERVICE)
Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}