展开说说:Android服务之startService源码解析

通过上一篇文章我们掌握了Android四种的基本使用,本篇从源码层面总结一下startService的执行过程。

本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。

1、是什么

上一篇总结了“Service是Android系统中的四大组件之一,它是一种没有可视化界面,运行于后台的一种服务程序属于计算型组件,用来在后台执行持续性的计算任务,重要性仅次于Activity活动”。

2、有什么

Service和Activity一样也有自己的生命周期,也需要在AndroidManifest.xml中注册。

2.1 在AndroidManifest.xml中注册

 <service android:name="com.example.testdemo.service.ServiceJia" />

2.2 Service生命周期

Service的生命周期有很多,本文只谈startService和stopService涉及到的。

onCreate

它只在Service刚被创建的时刻被调用,Service在运行中,这个方法将不会被调用。也就是只有经历过onDestroy生命周期以后再次startService(intent) 才会执行。

onStartCommand

OnStartCommand方法是最重要的方法,因为它在我们需要启动Service的时候被调用。形参时当初startService时传递进来的Intent,这样就可以Service传值。在这个方法中,可以做服务启动后要执行的任务,但是切记这里也是在主线程运行,耗时的操作必须创建一个线程来执行,否则可能引发ANR导致程序闪退

关于onStartCommand的返回值介绍

START_STICKY:此时Service被杀死以后将会重新创建。但是不会重新传入原来的Intent对象,而是传入intent为null

START_NOT_STICKY:此时Service被杀死以后不重新创建

START_REDELIVER_INTENT:功能与START_STICKY类似在Service被杀死以后将会重新创建。厉害的一点时该返回值时Intent会重新传递给Service。

onDestroy

onDestory是在Service即将被销毁时执行的生命名周期,Service和Activity生命周期不一样,Service没有onStop生命周期。

日志打印:

调用startService后的生命周期:
2024-07-01 10:20:59.756 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onCreate:
2024-07-01 10:20:59.757 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onStartCommand:


调用stopService后的生命周期:
2024-07-01 10:21:06.861 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onDestroy:

  1. 怎么用

关于使用方法上一篇已经总结,这里不在赘述。

4、啥原理

Service的启动方法是调用

Intent serviceIntent = new Intent(ServiceActivity.this, ServiceJia.class);
startService(serviceIntent);

然后我们顺着startService方法开始解析源码,SDK版本API 30:

4.1 从ContexWrapper的startService开始:

@Override
public ComponentName startService(Intent service) {
    return mBase.startService(service);
}

4.2 ContextImpl类startService

mBase的类型是Context,但实际代码逻辑是在它的实现类ContextImpl类。

 @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
}
//
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), 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());
                } else if (cn.getPackageName().equals("?")) {
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

在ContextImpl类的startService中调用了startServiceCommon方法,而其中的关键代码是ActivityManager.getService().startService方法调用。

4.3 来到ActivityManager 

  /**
     * @hide
     */
    @UnsupportedAppUsage
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    @UnsupportedAppUsage
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
具体实现是在ActivityManagerService.java
    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage,
            String callingFeatureId, 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 + " fg=" + requireForeground);
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
}

4.4 进入ActiveServices类,用来辅助ActivityServiceManager管理Service的启动和停止等。

mServices = new ActiveServices(this);

上面先调用ActiveServicesstartServiceLocked方法;

res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);

然后startServiceLocked又调用本类的startServiceInnerLocked方法:

ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); 

然后startServiceInnerLocked又调用本类的bringUpServiceLocked方法:

String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);

然后在调用bringUpServiceLocked方法realStartServiceLocked

realStartServiceLocked(r, app, execInFg); 

4.5调用ApplicationThread的scheduleCreateService方法

app.thread.scheduleCreateService(r, r.serviceInfo,

        mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),

        app.getReportedProcState());

之后调用了sendServiceArgsLocked(r, execInFg, true);他就是onStartCommand生命周期,此处伏笔,下面4.8中详聊。

4.6 进入ApplicationThread类:

        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);
        }

这里sendMessage方法不是Handler的哈,是封装以后的,继续看:

 void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
}

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) {
            Slog.v(TAG,
                    "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
        }
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

4.7 这里发送了消息,然后去找对应what = H.CREATE_SERVICE的处理:

在handleMessage方法中处理:
                case CREATE_SERVICE:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceCreate: " + String.valueOf(msg.obj)));
                    }
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

关键时刻来咯:

@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 {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            // Service resources must be initialized with the same loaders as the application
            // context.
            context.getResources().addLoaders(
                    app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

            context.setOuterContext(service);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().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);
            }
        }
    }

这里先创建了一个ContextImpl实例,然后创建了Application(这里是个工厂模式等于空了才创建);创建Service实例并与ContextImpl关联;然后调用onCreate的生命周期方法。最后有个mServices.put,后面其他场景会从里面取来使用。

4.8再看onStartCommand生命周期

先生是否记得上面4.4小节买过伏笔sendServiceArgsLocked(r, execInFg, true);

sendServiceArgsLockedr.app.thread.scheduleServiceArgs(r, slice);这行代码很熟悉了,和调用onCreate时候是一样的模式。

进入ApplicationThread类:

public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
            List<ServiceStartArgs> list = args.getList();

            for (int i = 0; i < list.size(); i++) {
                ServiceStartArgs ssa = list.get(i);
                ServiceArgsData s = new ServiceArgsData();
                s.token = token;
                s.taskRemoved = ssa.taskRemoved;
                s.startId = ssa.startId;
                s.flags = ssa.flags;
                s.args = ssa.args;

                sendMessage(H.SERVICE_ARGS, s);
            }
        }

二次封装的发消息:

sendMessage(H.SERVICE_ARGS, s);和onCreate一样。

handleMessage处理消息:

case SERVICE_ARGS:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceStart: " + String.valueOf(msg.obj)));
                    }
                    handleServiceArgs((ServiceArgsData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

mServices取出onCreate是存的service,然后调用onStartCommand生命周期。

private void handleServiceArgs(ServiceArgsData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
                }
                int res;
                if (!data.taskRemoved) {
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }

                QueuedWork.waitToFinish();

                try {
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }

至此StartService的启动该流程分析完毕!

才疏学浅,如有错误,欢迎指正,多谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/777703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高级计算机体系结构--期末教材复习

Chap2 性能评测和并行编程性能评测并行编程为什么需要三次 barrier改进方法 Chap3 互连网络交换和路由二维网格中 XY 路由 死锁、活锁及饿死死锁避免的方法&#xff1a;虚通道、转弯模型二维网格中最小 西向优先、北向最后和负向优先算法转弯模型&#xff1a;超立方体的部分自适…

原生JavaScript实现录屏功能

1. 前言 使用JavaScript实现浏览器中打开系统录屏功能 示例图: 2. 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><…

深度学习——卷积神经网络(convolutional neural network)CNN详解(一)——概述. 步骤清晰0基础可看

在CNN的学习过程中我会提供相应的手算例子帮助理解训练过程。 其他关于神经网络的学习链接如下&#xff1a; 一、了解卷积神经网络 卷积神经网络的作用 总的来说&#xff0c;卷积神经网络的第一个主要作用是对图像进行特征提取&#xff0c;所谓特征提取&#xff0c;就是明白…

7.6第三天作业

一、在数据库中创建一个表student&#xff0c;用于存储学生信息 CREATE TABLE student( id INT PRIMARY KEY, name VARCHAR(20) NOT NULL, grade FLOAT ); &#xff08;1.&#xff09;先创建一个数据库 &#xff08;2.&#xff09;创建student表 查看是否创建成功 1、向studen…

QT c++函数模板与类模板的使用

QT c类模板的使用 #pragma once#include <QtWidgets/QMainWindow> #include "ui_QtWidgetsApplication5.h"class QtWidgetsApplication5 : public QMainWindow {Q_OBJECTpublic:QtWidgetsApplication5(QWidget *parent nullptr);~QtWidgetsApplication5();te…

代码随想录算法训练营第13天|二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法、102.二叉树的层序遍历

打卡Day13 1.理论基础2.二叉树的递归遍历3.二叉树的迭代遍历3.二叉树的统一迭代法4.102.二叉树的层序遍历扩展107. 二叉树的层序遍历 II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针117. 填充每个…

嵌入式C语言面试相关知识——关键字(不定期更新)

嵌入式C语言面试相关知识——关键字 一、博客声明二、C语言关键字1、sizeof关键字2、static关键字3、const关键字4、volatile关键字5、extern关键字 一、博客声明 又是一年一度的秋招&#xff0c;怎么能只刷笔试题目呢&#xff0c;面试题目也得看&#xff0c;想当好厂的牛马其实…

数据可视化之智慧城市的脉动与洞察

在数字化转型的浪潮中,城市作为社会经济发展的核心单元,正经历着前所未有的变革。城市数据可视化大屏看板作为这一变革中的重要工具,不仅极大地提升了城市管理效率,还为公众提供了直观、全面的城市运行状态视图,成为智慧城市建设不可或缺的一部分。本文将深入探讨以“城市…

一文理解 Treelite,Treelite 为决策树集成模型的部署和推理提供了高效、灵活的解决方案

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、什么是 Treelite&#xff1f; Treelite 是一个专门用于将决策树集成模型高效部署到生产环境中的机器学习模型编译器&#xff0c;特别适合处理大批量数据的推理任务&#xff0c;能够显著提升推理性能…

Java之网络面试经典题(一)

目录 ​编辑 一.Session和cookie Cookie Session 二.HTTP和HTTPS的区别 三.浅谈HTTPS为什么是安全的&#xff1f; 四.TCP和UDP 五.GET和Post的区别 六.forward 和 redirect 的区别&#xff1f; 本专栏全是博主自己收集的面试题&#xff0c;仅可参考&#xff0c;不能相…

嵌入式Linux系统编程 — 7.2 进程的环境变量

目录 1 什么是进程的环境变量 2 环境变量的作用 3 应用程序中获取环境变量 3.1 environ全局变量 3.2 获取指定环境变量 getenv 4 添加/删除/修改环境变量 4.1 putenv()函数添加环境变量 4.2 setenv()函数 4.3 unsetenv()函数 1 什么是进程的环境变量 每一个进程都有一…

Android - Json/Gson

Json数据解析 json对象&#xff1a;花括号开头和结尾&#xff0c;中间是键值对形式————”属性”:属性值”” json数组&#xff1a;中括号里放置 json 数组&#xff0c;里面是多个json对象或者数字等 JSONObject 利用 JSONObject 解析 1.创建 JSONObject 对象&#xff0c;传…

快手大模型首次集体亮相,用AI重塑内容与商业生态

7月6日&#xff0c;在2024世界人工智能大会期间&#xff0c;快手举办了以“新AI新应用新生态”为主题的大模型论坛&#xff0c;会上&#xff0c;快手大模型首次集体亮相&#xff0c;视频生成大模型可灵、图像生成大模型可图等产品的多项新功能正式发布。 继图生视频、视频续写…

Appium启动APP时报错Security exception: Permission Denial

报错内容Security exception: Permission Denial: starting Intent 直接通过am命令尝试也是同样的报错 查阅资料了解到&#xff1a;android:exported | App quality | Android Developers exported属性默认false&#xff0c;所以android:exported"false"修改为t…

QT学习积累——如何提高Qt遍历list的效率

目录 引出Qt遍历list提高效率显示函数的调用使用&与不使用&除法的一个坑 总结自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重载带参数的按钮触发信号触发信号拓展 lambda表达式返回值mutable修饰案例 引出 QT学习积累——如何提高Qt遍历list…

Springboot学习之用EasyExcel4导入导出数据(基于MyBatisPlus)

一、POM依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><m…

Kotlin中的数据类型

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

使用WinSCP工具连接Windows电脑与Ubuntu虚拟机实现文件共享传输

一。环境配置 1.首先你的Windows电脑上安装了VMware虚拟机&#xff0c;虚拟机装有Ubuntu系统&#xff1b; 2.在你的windows电脑安装了WinSCP工具&#xff1b; 3.打开WinSCP工具默认是这样 二。设置WinSCP连接 打开WinSCP&#xff0c;点击新标签页&#xff0c;进入到如下图的…

Ubuntu20.04配置TurtleBot3 Waffle Pi远程控制

这里写目录标题 0. 机器人配置1. Ubuntu20.04配置TurtleBot3 Waffle Pi远程控制1.1 TurtleBot3 Waffle Pi端配置1.2 PC端配置1.2.1 安装turtlebot3的环境配置1.2.2 创建项目并安装Turtlebot31.2.3 配置环境变量 1.3 PC端与TurtleBot3进行通信1.3.1 PC端与机器人端互PING和SSH连…

用C#调用Windows API向指定窗口发送按键消息详解与示例

文章目录 1. 按键消息的定义及功能2. 引入所需的命名空间3. 定义Windows API函数4. 定义发送消息的方法5. 获取窗口句柄6. 调用API发送按键消息7. 使用示例注意事项总结 在C#中调用Windows API向指定窗口发送按键消息是一种常见的操作&#xff0c;这通常用于自动化脚本、游戏辅…