Android Service原理分析之startService(一)

目录

  • Service概述
  • Service分类
  • startService时序图
  • 源码解析
    • ActivityManagerService中的处理
    • ActiveServices中的处理
    • Service.onCreate的执行
    • Service.onStartCommand的执行
  • 总结

1. Service概述

Service作为Android四大组件之一,在开发过程中非常常用,它虽然没有ui,但是同样可以在后台做很多重要的事情,我们平时使用启动service主要通过startService以及bindService来启动service以便已经相关操作。本文将介绍startService的原理,后续将分别介绍bindService的原理,以及在Android O上对service的新增限制管控。
:本文基于Android 8.1

2. Service分类

  1. 从启动方式上,可以分别通过startService以及bindService启动,两者之间最重要的区别在于bindService可以建立binder连接,更加方便通信。
  2. 从运行方式上,可以分为前台service(foregroundService,下面简称fg-service)与后台service,两者的区别在于有前台service的进程对应的优先级会更高,不容易被系统清理掉,但是前台service需要在通知栏里面显示一个常驻的通知。

3. startService时序图

startService
startService的启动大致分为三种情况:

  1. 对应进程不存在(先启动进程然后启动service并执行onCreate和onStartCommand)
  2. 进程存在但是service不存在(启动service并执行onCreate和onStartCommand)
  3. 进程和service都存在(只执行onStartCommand)

上面的时序图主要针对进程存在,但是service不存在的情况,其他两种在主要流程上没有太大区别,关键在于其中有一些特殊判断,在文章过程中也会提到。

4. 源码解析

4.1 ContextImpl.startService

    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    
        public ComponentName startForegroundService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, true, mUser);
    }
    // 最终的入口都在这个方法中
    // foreground service只是传参requireForeground为true,普通service为false
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            // binder call至ams
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            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());
                } else if (cn.getPackageName().equals("?")) {
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

最终通过startServiceCommon调用至ams,注意这里返回值有进行特殊处理,然后抛出了一些异常,主要返回值的component的packageName有"!","!!","?"三种,后面在介绍源码过程中可以看到具体是哪里会返回这些异常。
"!":权限校验没通过,不允许启动
"!!":无法启动
"?":不允许启动

4.2 ActivityManagerService.startService

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // 进行一些简单的校验
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        // callingPackage不能为空
        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                // mService正是ActiveServices对象
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

这块只是简单校验之后,就调用到了ActiveServices.startServiceLocked
参数解析:
caller:发起startService的进程对应的IApplicationThread对象
service:要启动的service的intent
resolvedType:service类型,启动的时候可以设置data已经schema等
requireForeground:是否发起启动foregroundService
callingPackage:发起startService的进程的packageName
userId:当前用户id

4.3 ActiveServices.startServiceLocked

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        final boolean callerFg;
        // 获取发起binder call的caller进程,如果不存在则抛异常
        if (caller != null) {
            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + callingPid
                        + ") when starting service " + service);
            }
            // 根据当前这个进程所处的调度环境判断是前台还是后台
            callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
        } else {
   
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值