• startService过程源码分析


    startService过程源码分析

    一、简介

    Service是Android四大组件之一,service有两种使用方式分别是startService和bindService,startService方式启动service之后我们无法控制service,即使调用service的组件死亡也不会导致启动的service死亡。startService方式启动service后至service死亡锁经历的生命周期如下:oncreate—>onstartcommon—>ondestory。本篇文章会分析startService过程源码,下篇文章会分析bindService过程源码。

    二、源码分析

    我们一般是调用ContextWrapper的startService,然后它又调用mBase.startService,此处mBase是一个ContextImpl对象。在ContextImpl类的startService方法中又调用了startServiceCommon

     //android.content.ContextWrapper
     @Override
     public ComponentName startService(Intent service) {
            return mBase.startService(service);
     }
    
    //ContextImpl.java
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    
    

    startServiceCommon如下所示:

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //1调用了AMS的startService方法
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    
    
    

    在注释1处通过ActivityManager.getService()获取AMS的本地代理,然后通过该代理调用AMS的startService方法。
    下面是获取ActivityManager.getService()获取AMS的本地代理的流程,可以看出是通过这是一个单例模式。获取AMS的方式是通过ServiceManager.getService来获取一个AMS的binder代理,然后转化为一个IActivityManager返回。因为AMS和调用client在不同进程所以返回的am是一个远端binder代理。

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }
    
    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;
                }
            };
    
    
    //IActivityManager.java
    private static class Proxy implements android.app.IActivityManager
    {
    @Override public android.content.ComponentName startService(android.app.IApplicationThread caller, android.content.Intent service, java.lang.String resolvedType, boolean requireForeground, java.lang.String callingPackage, int userId) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    android.content.ComponentName _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((caller!=null))?(caller.asBinder()):(null)));s
    if ((service!=null)) {
    _data.writeInt(1);
    service.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    _data.writeString(resolvedType);
    _data.writeInt(((requireForeground)?(1):(0)));
    _data.writeString(callingPackage);
    _data.writeInt(userId);
    mRemote.transact(Stub.TRANSACTION_startService, _data, _reply, 0);
    _reply.readException();
    if ((0!=_reply.readInt())) {
    _result = android.content.ComponentName.CREATOR.createFromParcel(_reply);
    }
    else {
    _result = null;
    }
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    }
    
    

    AMS的startService方法如下所示:

    //ActivityManagerService.java
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, 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 + " fg=" + requireForeground);
        synchronized(this) {
           //1获取调用者的pid和uid
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
               //2、调用ActiveServices的startServiceLocked方法
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }
    

    AMS的startService方法在注释1处获取调用者的pid和uid,然后在注释2处调用ActiveServices的startServiceLocked方法并传入在注释1处获取调用者的pid和uid。

    ActiveServices的startServiceLocked方法如下所示:

    //ActiveServices.java
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
            //1、获取调用进程ProcessRecord,如果为null则抛出异常
              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;
            }
    //2、通过retrieveServiceLocked解析启动service的intent,并将解析结果放在res.record中
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false);
       //解析的serviceRecord为null且permission不为null则判断返回缺少权限
      if (res.record == null) {
                return new ComponentName("!", res.permission != null
                        ? res.permission : "private to package");
            }
    
            ServiceRecord r = res.record;
    
    //3、调用startServiceInnerLocked
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
    
    }
    

    在注释1处获取调用进程的ProcessRecord,如果为null则抛出异常,在注释2处调用retrieveServiceLocked解析启动service的intent,解析结果包含serviceRecord和启动该service所需权限,在注释3处调用startServiceInnerLocked。

    startServiceInnerLocked如下所示:

    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    //1、调用bringUpServiceLocked,注意bringUpServiceLocked函数如果返回非null则表示启动出错。
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }
    }
    

    在bringUpServiceLocked中我们来分析service与调用者不在同一进程且service所在进程尚未启动的情况,这是最复杂的情况了。此时会调用startProcessLocked启动
    service进程。

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
            //1、如果service所在进程和主线程均为非空那么表示该service已经存在了,直接调用其onStartCommand方法
           if (r.app != null && r.app.thread != null) {
                sendServiceArgsLocked(r, execInFg, false);
                return null;
            }
           //...
            ProcessRecord app;
            if (!isolated) {
                //2、service所在进程已经启动则直接调用realStartServiceLocked启动service
                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 (TransactionTooLargeException e) {
                        throw e;
                    } 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.
                }
            }
           if (app == null && !permissionsReviewRequired) {
            //3、service与调用者不在同一进程且service所在进程尚未启动则调用startProcessLocked启动service进程
             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                hostingType, 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;
        }
        
        //4、service所在进程还未启动,将先将这个serviceRecord存储到mPendingServices中表示等待启动的service
            if (!mPendingServices.contains(r)) {
                mPendingServices.add(r);
            }
    }
    }
    

    在注释1处会先判断service是否已经启动过,如果启动过则直接调用其onStartCommand方法,在注释2处判断service所在进程是否启动,如果进程启动但是service尚未启动那么会调用realStartServiceLocked启动service。在注释3处则是service所在进程未启动那么会先启动service进程,因为进程启动是异步的所以在注释4处会先把service存放到mPendingServices中表示等待启动的service。

    下面我们先来看下service所在进程未启动的情况,该情况是调用startProcessLocked,如下所示:

    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
            //调用startProcessLocked
        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                null /* crashHandler */);
    }
    
    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
    //调用startProcessLocked
    startProcessLocked(
            app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
    
    }
    

    经过调用最终是通过startProcessLocked来启动新的进程

    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    if (entryPoint == null) entryPoint = “android.app.ActivityThread”;
    //1、Process.start创建新的进程
    startResult = Process.start(entryPoint,
            app.processName, uid, uid, gids, debugFlags, mountExternal,
            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
            app.info.dataDir, invokeWith, entryPointArgs);
    
    }
    

    创建进程过程之前在Android应用进程启动分析一文中说过,详细过程请参考 Android应用进程启动过程.md

    创建完新进程之后会调用android.app.ActivityThread的main函数,在Android程序中每个进程都有自己的android.app.ActivityThread。至此下面的这些操作都是在新启动的进程中,调用者所在进程已完成它的命令返回了。

    //ActivityThread.java
    public static void main(String[] args) {
    
    Looper.prepareMainLooper();
    
    ActivityThread thread = new ActivityThread();
    //1、看下attach
    thread.attach(false);
    Looper.loop();
    }
    
    
    private void attach(boolean system) {
    final IActivityManager mgr = ActivityManager.getService();
    try {
        //1、调用了AMS的attachApplication
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    }
    

    在ActivityThread的main函数中会调用attach函数,而attach又调用了AMS的attachApplication。

    AMS的attachApplication函数如下所示:

    @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            //1、调用attachApplicationLocked
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
    
    

    在注释1处调用attachApplicationLocked,如下所示:

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
    // Find any services that should be running in this process…
    if (!badApp) {
        try {
          //1、调用ActiveServices的attachApplicationLocked查看是否有需要启动的service
            didSomething |= mServices.attachApplicationLocked(app, processName);
            checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
            badApp = true;
        }
    }
    }
    
    

    attachApplicationLocked中我们只关注跟service启动有关的内容,就是代用attachApplicationLocked查看是否有需要启动的service

    ActiveServices的attachApplicationLocked如下所示:

    boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException {
        boolean didSomething = false;
        // Collect any services that are waiting for this process to come up.
        if (mPendingServices.size() > 0) {
            ServiceRecord sr = null;
            try {
                //1、mPendingServices中存储了需要在该进程启动的service,这里依次取出并启动
                for (int i=0; i<mPendingServices.size(); i++) {
                    sr = mPendingServices.get(i);
                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                            || !processName.equals(sr.processName))) {
                        continue;
                    }
    
                    mPendingServices.remove(i);
                    i--;
                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
                            mAm.mProcessStats);
                    //2、调用realStartServiceLocked启动service
                    realStartServiceLocked(sr, proc, sr.createdFromFg);
                    didSomething = true;
                    if (!isServiceNeededLocked(sr, false, false)) {
                        // We were waiting for this service to start, but it is actually no
                        // longer needed.  This could happen because bringDownServiceIfNeeded
                        // won't bring down a service that is pending...  so now the pending
                        // is done, so let's drop it.
                        bringDownServiceLocked(sr);
                    }
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception in new application when starting service "
                        + sr.shortName, e);
                throw e;
            }
        }
    }
    

    在注释1处取出要启动的service并在注释2处调用realStartServiceLocked启动service

    realStartServiceLocked如下所示:

    private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {
     //1、发送delay消息
     bumpServiceExecutingLocked(r, execInFg, "create");
      ...
    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.notifyPackageUse(r.serviceInfo.packageName,
                             PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
         //2、调用ApplicationThread的scheduleCreateService方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
        ...
        //3、service已经start,调用service的onStartCommand
        sendServiceArgsLocked(r, execInFg, true);
    
    }
    }
    

    在注释1处会发送一个延迟处理的消息SERVICE_TIMEOUT_MSG。在方法scheduleCreateService执行完成,也就是onCreate回调执行完成之后,便会remove掉该消息。但是如果没能在延时时间之内remove该消息,则会进入执行service timeout流程。注释2处scheduleCreateService会创建service并回调其onCreate生命周期函数。在注释3处则回调用service的onStartCommand。

    先来看下bumpServiceExecutingLocked,如下所示

        private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
            long now = SystemClock.uptimeMillis();
            if (r.executeNesting == 0) {
                r.executeFg = fg;
                ServiceState stracker = r.getTracker();
                if (stracker != null) {
                    stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
                }
                if (r.app != null) {
                    r.app.executingServices.add(r);
                    r.app.execServicesFg |= fg;
                    if (r.app.executingServices.size() == 1) {
                        //后台启动的服务
                        scheduleServiceTimeoutLocked(r.app);
                    }
                }
            } else if (r.app != null && fg && !r.app.execServicesFg) {
                r.app.execServicesFg = true;
                 //前台启动的服务
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    

    调用scheduleServiceTimeoutLocked,如下所示:

        void scheduleServiceTimeoutLocked(ProcessRecord proc) {
            if (proc.executingServices.size() == 0 || proc.thread == null) {
                return;
            }
            Message msg = mAm.mHandler.obtainMessage(
                    ActivityManagerService.SERVICE_TIMEOUT_MSG);
            msg.obj = proc;
            //发送延迟消息SERVICE_TIMEOUT_MSG
            mAm.mHandler.sendMessageDelayed(msg,
                    proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
        }
    

    scheduleServiceTimeoutLocked主要任务是发送延迟消息SERVICE_TIMEOUT_MSG,针对前后台service延迟时间会有所不同:

    • 对于前台服务,则超时为SERVICE_TIMEOUT,即timeout=20s;
    • 对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT,即timeout=200s;

    延迟消息发送后就会调用scheduleCreateService创建service,scheduleCreateService如下所示

    //ApplicationThread.java
    
    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;
        //给H发送了一个message
        sendMessage(H.CREATE_SERVICE, s);
    }
    
    //ActivityThread.java
    H是ActivityThread的一个内部类,继承自Handler
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case CREATE_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, (“serviceCreate: " + String.valueOf(msg.obj)));
        //1、H接收到CREATE_SERVICE的message之后调用handleCreateService
        handleCreateService((CreateServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    }
    }
    

    scheduleCreateService通过handler发送了一个CREATE_SERVICE的message,H类收到该消息后会调用handleCreateService进行处理,handleCreateService如下所示:

    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.
         //当应用处于后台即将进行GC,而此时被调回到活动状态,则跳过本次gc
        unscheduleGcIdler();
    
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            //1、调用ClassLoader创建service实例
            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对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //如果Application不存在则创建Application对象
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //2、回调service.attach
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            //3、回调onCreate()
            service.onCreate();
            mServices.put(data.token, service);
            try {
            //4、
         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);
            }
        }
    }
    
    

    可以看出handleCreateService是真正创建service的地方。在注释1处通过反射生成service实例在注释2处调用attach,在注释3处回调onCreate。在注释4处调用AMS的serviceDoneExecuting方法取消之前发送service启动超时的延迟消息。

    之后便是onStartCommand方法的回调,上面我们在AS的realStartServiceLocked函数中注释3处调用了sendServiceArgsLocked,sendServiceArgsLocked如下所示:

        private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
                boolean oomAdjusted) throws TransactionTooLargeException {
                ...
                 try {
                 //调用ApplicationThread的scheduleServiceArgs
                r.app.thread.scheduleServiceArgs(r, slice);
                }
        }
            
    

    ApplicationThread的scheduleServiceArgs如下所示:

       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;
                    //1、发送SERVICE_ARGS消息
                    sendMessage(H.SERVICE_ARGS, s);
                }
            }
    

    在注释1处发送SERVICE_ARGS消息,该消息是在handleServiceArgs函数中处理的,如下所示:

     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) {
                       //调用service的onStartCommand
                        res = s.onStartCommand(data.args, data.flags, data.startId);
                    }
         }
    

    至此service启动完毕。

    启动流程:

    1. Process A进程采用Binder IPC向system_server进程发起startService请求;
    2. system_server进程接收到请求后,向zygote进程发送创建进程的请求;
    3. zygote进程fork出新的子进程Remote Service进程;
    4. Remote Service进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
    5. system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向remote Service进程发送scheduleCreateService请求;
    6. Remote Service进程的binder线程在收到请求后,通过handler向主线程发送CREATE_SERVICE消息;
    7. 主线程在收到Message后,通过发射机制创建目标Service,并回调Service.onCreate()方法。

    到此,服务便正式启动完成。当创建的是本地服务或者服务所属进程已创建时,则无需经过上述步骤2、3,直接创建服务即可。

    参考:
    startService源码分析

  • 相关阅读:
    Kali Linux Web 渗透测试视频教程— 第八课 nessus
    互联网上那些excel文件
    Kali Linux Web 渗透测试视频教程— 第七课 OpenVas
    Kali linux系列之 zmap 安装
    kali Linux Web 渗透测试视频教程— 第六课 网络扫描-nmap与zmap
    kali linux 渗透测试视频教程 第五课 社会工程学工具集
    NLog
    使用ActionFilterAttribute 记录 WebApi Action 请求和返回结果记录
    日志记录
    webapi获取请求地址的IP
  • 原文地址:https://www.cnblogs.com/Robin132929/p/13785631.html
Copyright © 2020-2023  润新知