Service启动过程分析
Service是一种计算型组件,用于在后台执行一系列的计算任务。由于工作在后台,因此用户是无法直接感知到它的存在。Service组件和Activity组件略有不同,Activity组件只有一种运行模式,即Activity处于启动状态,但是Service组件却有两种状态:启动状态和绑定状态。当Service组件处于启动状态时,这个时候Service内部可以做一些后台计算,并且不需要和外界有直接的交互。
Service分为两种工作状态,一种是启动状态,主要用于执行后台计算,另一张是绑定状态,主要用于其他组件和Service的交互,需要注意的是Service的这两种状态是可以共存的,即Service既可以处于启动状态也可以同时处于绑定状态。
Service的启动过程
Service的启动过程从ContextWrapper的startService开始。
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
这里的mBase的类型是ContextImpl,其实说到这,就得说下Activity被创建时会通过attach方法将一个ContextImpl对象关联起来,这也就是mBase。从ContextWrapper的实现可以看出,其中大部分操作都是通过mBase来实现的,在设计模式中这是一种典型的桥接模式。
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
从startServiceCommon方法中可以看出,是ActivityManagerNative.getDefault()这个方法的类启动的,其实ActivityManagerNative.getDefault()就是AMS,是通过AMS来启动服务的行为是一个远程过程调用。
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"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, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
在上面的代码中,AMS会通过mServices这个对象来完成Service后续的启动过程,mServices对象的类型是ActiveService,ActiveService是一个辅助AMS进行Service管理的类,包括Service的启动、绑定和停止等。
最终是通过app.thread的scheduleCreateService方法来创建Service对象并调用onCreate,接着再通过sendServiceArgsLocked方法来调用Service的其他方法,比如onStartCommand,这两个过程均是进程间通信,到最后是通过ApplicationThread的scheduleCreateService方法来
public final void scheduleCreateService(IBinder token, ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
是通过发消息给Handler H,H会接收这个CREATE_SERVICE消息并通过handleCreateService方法来完成Service的最终启动。
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 = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
handleCreateService主要完成了如下几件事:
- 通过类加载器创建Service的实例
- 创建Application对象并调用其onCreate,当然Application的创建过程只会有一次。
- 接着创建ConTextImpl对象并通过Service的attach方法建立二者之间的关系。
- 最后调用onCreate方法,并将Service对象存储到ActivityThreadd的一个列表中。
Service的绑定过程
和Service的启动过程一样,Service绑定过程也是从ContextWrapper开始的。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
然后会调用bindServiceCommon方法,这个方法主要完成如下两件事情:
- 首先将客户端的ServiceConnection对象转化为ServiceDispatcher.InnerConnection对象。
- 接着bindServiceCommon会通过AMS来完成Service的具体绑定过程,对应于AMS的bindService方法。
最终也是调用handleBindService来处理。
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) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
最后,以借用一张流程图来说明:
阅读扩展
源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中可以看到技术积累的过程。
1,Android系统简介
2,ProGuard代码混淆
3,讲讲Handler+Looper+MessageQueue关系
4,Android图片加载库理解
5,谈谈Android运行时权限理解
6,EventBus初理解
7,Android 常见工具类
8,对于Fragment的一些理解
9,Android 四大组件之 " Activity "
10,Android 四大组件之" Service "
11,Android 四大组件之“ BroadcastReceiver "
12,Android 四大组件之" ContentProvider "
13,讲讲 Android 事件拦截机制
14,Android 动画的理解
15,Android 生命周期和启动模式
16,Android IPC 机制
17,View 的事件体系
18,View 的工作原理
19,理解 Window 和 WindowManager
20,Activity 启动过程分析
21,Service 启动过程分析
22,Android 性能优化
23,Android 消息机制
24,Android Bitmap相关
25,Android 线程和线程池
26,Android 中的 Drawable 和动画
27,RecylerView 中的装饰者模式
28,Android 触摸事件机制
29,Android 事件机制应用
30,Cordova 框架的一些理解
31,有关 Android 插件化思考
32,开发人员必备技能——单元测试
Service启动过程分析的更多相关文章
- 探索 OpenStack 之(11):cinder-api Service 启动过程分析 以及 WSGI / Paste deploy / Router 等介绍
OpenStack 中的每一个提供 REST API Service 的组件,比如 cinder-api,nova-api 等,其实是一个 WSGI App,其主要功能是接受客户端发来的 HTTP R ...
- Activity启动过程分析
Android的四大组件中除了BroadCastReceiver以外,其他三种组件都必须在AndroidManifest中注册,对于BroadCastReceiver来说,它既可以在AndroidMa ...
- ASP.Net Core MVC6 RC2 启动过程分析[偏源码分析]
入口程序 如果做过Web之外开发的人,应该记得这个是标准的Console或者Winform的入口.为什么会这样呢? .NET Web Development and Tools Blog ASP.NE ...
- 开机SystemServer到ActivityManagerService启动过程分析
开机SystemServer到ActivityManagerService启动过程 一 从Systemserver到AMS zygote-> systemserver:java入层口: /** ...
- Neutron分析(2)——neutron-server启动过程分析
neutron-server启动过程分析 1. /etc/init.d/neutron-server DAEMON=/usr/bin/neutron-server DAEMON_ARGS=" ...
- ActivityManagerService启动过程分析
之前讲Android的View的绘制原理和流程的时候,讲到过在Android调用setContentView之后,Android调用了一个prepreTravle的方法,这里面就提到了Activity ...
- Tomcat源码学习(2)——启动过程分析
Tomcat启动过程分析 启动 tomcat 时,Windows下执行 startup.bat :Linux下执行 startup.sh 文件,实际上最后都是调用 org.apache.catalin ...
- Net Core MVC6 RC2 启动过程分析
入口程序 如果做过Web之外开发的人,应该记得这个是标准的Console或者Winform的入口.为什么会这样呢?.NET Web Development and Tools Blog ASP.NET ...
- startActivity启动过程分析(转)
基于Android 6.0的源码剖析, 分析android Activity启动流程,相关源码: frameworks/base/services/core/java/com/android/serv ...
随机推荐
- input输入限制
1:只能输入两位小数点:function keepTwoPointNum(that){ var val=that.value; if(isNaN(val)){ $(that).val(''); ret ...
- Vue.js连接后台数据jsp页面  ̄▽ ̄
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- 性感天才黑客乔治·霍兹George Hotz 17岁打脸乔布斯20岁搞疯索尼
1.国内外著名黑客信息 1) 国外著名黑客 George Hotz 乔治·霍兹(George Hotz,1989年10月2日-),美国学生,2007年8月解锁苹果(Apple)iPhone手机,使得i ...
- linux下安装haproxy作为端口转发服务器,以及安装keepalived作为haproxy高可用方案
一.安装haproxy作为端口转发服务器(主服务器:172.28.5.4,备服务器:172.28.5.8,浮点IP为:172.28.5.6) 1.安装依赖包 yum -y install wget g ...
- cdnbest站点里设置防盗链
设置需求只允许 test.kangleweb.com 访问 ,其他网站链接全部拒绝 (注:如果test.kangleweb.com是用https访问,那引用的内容地址www447.yivpn.cn也 ...
- Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.2/gradle-3
参考 https://blog.csdn.net/verkery6/article/details/80797705
- LENGTH()和CHAR_LENGTH()区别
LENGTH()返回以字节为单位的字符串的长度.CHAR_LENGTH()返回以字符为单位的字符串的长度. 分别对应于java中的str.getBytes().length和str.length( ...
- hmtl div水平、垂直居中
最近写网页经常需要将div在屏幕中居中显示,遂记录下几个常用的方法,都比较简单.水平居中直接加上<center>标签即可,或者设置margin:auto;当然也可以用下面的方法 下面说两种 ...
- PHP错误日志记录:display_errors与log_errors的区别
我们所做的东西,无论在开发环境还是在生产环境都可能会出现一些问题. 开发环境下,我们会要求错误尽可能详细的呈现出来,错误提示信息越详细越好,越详细越能帮助开发人员确定问题所在并从根本上解决他们. 生产 ...
- [leetcode]380. Insert Delete GetRandom O(1)常数时间插入删除取随机值
Design a data structure that supports all following operations in average O(1) time. insert(val): In ...