【Android源码系列】Service启动源码解析
来源:互联网 发布:用java编写冒泡排序 编辑:程序博客网 时间:2024/05/16 05:58
一、写在前面
上次说了Activity启动,我们接着来看Service。同为四大组件,两者的启动方式相差不多,有了上次的经验,分析起来得心应手。但是我们知道Service有两种方式:start&bind。
Intent intent = new Intent(MainActivity.this, StartService.class); startService(intent); bindService(intent,serviceConnection,BIND_AUTO_CREATE);
上面的代码想必大家很熟悉了,废话不多说,进入正题。
二、startService方式
直接进入startService方法,我们看到:
public ComponentName startService(Intent service) { return mBase.startService(service); }
是在contextWraper里面,并交给了mBase去执行。往上查找,发现这个mBase是Activity启动时attach方法里生成并传入的。
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
发现他是一个ContextImpl对象,继承了context而已。我继续看他的startService方法,发现他调用了自己的startServiceCommon。在startServiceCommon里面我们看到了一行熟悉的代码:
private ComponentName startServiceCommon(Intent service, UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); //省略部分代码 return cn; } catch (RemoteException e) { return null; } }
这里和Activity启动方式相同,都是通过binder对象进行进程通信,这里我就不多说了,还不清楚的可以先看我的上一篇文章。这里我们直接找到ActivityManegerService(简称AMS)里的startService方法。
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, int userId) { enforceNotIsolatedCaller("startService"); // Refuse possible leaked file descriptors if (service != null && service.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service + " type=" + resolvedType); synchronized(this) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; }
这个方法也很简单,发现他直接交给mServices处理了,这个mServices是一个ActiveServices的实例,这个类负责Service的很多工作,具体可以下来详细看,这里先不多说。
我们进入到ActiveServices这个类里,发现先调用了startServiceLocked,再调用startServiceInnerLocked,然后继续走到bringUpServiceLocked,最后进入了realStartServiceLocked方法,看名字感觉是真正启动Service的地方了
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { 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的启动:
app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
这句是不是很熟悉,和Activity的启动如出一辙,其实Service启动本来就和Activity相差不大。同样的,我们找到ActivityThread里的scheduleCreateService,同样的,还是通过H这个Handler调用了方法handleCreateService。在这个方法里调用了attach和onCreate,我们的Service正式启动了。
service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); service.onCreate();
不过很奇怪的是,我找了很久,往下再也没有出现Onstart之类的生命周期。无奈,只能回头看看,终于在刚刚提到的realStartServiceLocked方法了看到了
// 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);
看注释大概意思是“这里如果启动过了,将不再调用onStartCommand,如果没有则调用”,所以进去sendServiceArgsLocked方法里瞧瞧,同样的,我们找到了和onCreate启动相同的方式。
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
通过binder跨进程调用了onStartCommand方法,后续原理相同我就不一一贴代码了。这里就把startService启动方式分析完了,发现和Activity启动方式本质上没什么差别,那bind方式呢?
三、bind启动方式
一路查看bind启动的源码,发现TMD和start方式一毛一样,最后都走到了ActiveServices的realStartServiceLocked方法里,执行一遍onCreate。只是不会执行onStartCommand,而是执行了requestServiceBindingsLocked(r, execInFg)方法,同样的,也是跨进程调用了ActivityThread里的方法:
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
然后走到handleBindService方法里:
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) { }
可以看出,onbind方法之会调用一次。
那这样就完了吗?不对呀,bind启动还需要一个东西,ServiceConnection!通过onServiceConnected方法来获取Service传过来的binder对象。好吧,继续看。
我们看到onbind调用之后还会继续调用下面这句话:
ActivityManagerNative.getDefault().publishService( data.token, data.intent, binder);
很熟悉的binder通信,我们直接跳到AMS的publishService方法,经过一番辗转,我们看到了这句话:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { //省略无数代码 try { try { c.conn.connected(r.name, service); } catch (Exception e) { } } } } } } finally { Binder.restoreCallingIdentity(origId); } }
这里调用了conn的connected,好像离成功很近了。我们发现conn是一个IServiceConnection接口,又是一次IPC通信。那我们看看这个c是哪里来的。往上跟踪代码最后找到了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; } }
IServiceConnection 对象sd是通过getServiceDispatcher方法得到,继续跟进发现是一个LoadedApk的内部类ServiceDispatcher的内部类InnerConnection (有点绕),继承了IServiceConnection.Stub
private static class InnerConnection extends IServiceConnection.Stub { final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher; InnerConnection(LoadedApk.ServiceDispatcher sd) { mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd); } public void connected(ComponentName name, IBinder service) throws RemoteException { LoadedApk.ServiceDispatcher sd = mDispatcher.get(); if (sd != null) { sd.connected(name, service); } } }
可以看到,还是调用了sd的connected方法:
public void connected(ComponentName name, IBinder service) { if (mActivityThread != null) { mActivityThread.post(new RunConnection(name, service, 0)); } else { doConnected(name, service); } }
mActivityThread其实就是一个Handler对象,往上翻他其实就H,执行了一个runable,runable里面同样走到了doConnected里,我们来看看这个doConnected
public void doConnected(ComponentName name, IBinder service) { ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; synchronized (this) { if (mForgotten) { // We unbound before receiving the connection; ignore // any connection received. return; } old = mActiveConnections.get(name); if (old != null && old.binder == service) { // Huh, already have this one. Oh well! return; } if (service != null) { // A new service is being connected... set it all up. mDied = false; info = new ConnectionInfo(); info.binder = service; info.deathMonitor = new DeathMonitor(name, service); try { service.linkToDeath(info.deathMonitor, 0); mActiveConnections.put(name, info); } catch (RemoteException e) { // This service was dead before we got it... just // don't do anything with it. mActiveConnections.remove(name); return; } } else { // The named service is being disconnected... clean up. mActiveConnections.remove(name); } if (old != null) { old.binder.unlinkToDeath(old.deathMonitor, 0); } } // If there was an old service, it is not disconnected. if (old != null) { mConnection.onServiceDisconnected(name); } // If there is a new service, it is now connected. if (service != null) { mConnection.onServiceConnected(name, service); } }
很轻松的发现里面确实调用了
mConnection.onServiceConnected(name, service);
至此,bind绑定完成。
四、总结
可以发现,无论是start方式还是bind方式,甚至包括Activity启动方式,核心都是通过IPC通信完成的,可见binder在Android里的地位。
那是因为无论是Activity还是Service都涉及到了跨进程启动,比如从你的应用跳转到别的应用,包括跳到Home页面(其实Home界面也是一个进程)。
好的,下一篇将继续分析四大组件,让我们来看看Android这四大组件的原理,这会让我们对Android系统有更加深刻的认识。
最后还是老样子,画出UML图将对整个步骤更加清晰。
- 【Android源码系列】Service启动源码解析
- 【Android源码系列】Activity启动源码解析
- 【Android源码系列】BroadcastReceiver启动源码解析
- 【Android源码系列】ContentProvider启动源码解析
- Android源码解析四大组件系列(一)---Service的启动过程分析
- Service 启动流程源码解析
- Android源码解析系列
- Android源码解析系列
- android源码解析系列
- Android源码解析系列
- Android源码解析系列
- Android源码解析系列
- Android 进程启动源码解析
- android源码分析 Service的启动过程
- Android四大组件Service启动源码分析
- Service源码解析
- android源码分析系列启动篇
- Android 6.0 Framework源码解析系列[目录]
- Kotlin学习笔记--String比较和Map
- 关于XStream相关的常用方法;设置别名;忽略属性;设置xml字段属性
- 基于c++的运算符重载(一、复数非友元+友元)
- Text file busy(文本文件忙)
- 要想使用XStream的注解,必须开启注解扫描.
- 【Android源码系列】Service启动源码解析
- ros kinetic版编译error: ‘shared_ptr’ in namespace ‘std’ does not name a template type
- 、 Ajax跨域问题 1. 跨域介绍 1.1. 什么是跨域; 跨域解决方案;jQuery解决跨域操作
- Educational Codeforces Round 31 C. Bertown Subway dfs
- post请求和get请求的区别(文件上传)
- 个人笔记(随时更新)
- Oracle 11gR2 RAC集群服务启动与关闭总结
- linux(centos)常用命令部分整理(2)
- 正则表达式二匹配多个正则表达式模式