Activity组件之一Service--- startService

关于Activity的四大组件之一Service, 这里面有几样东西可以分析呢?

1. 关于Service的启动,根据不同的需求可以分为有两种:一种是通过调用StartService,另一种就是BindService。

    第一个话题,怎么去使用,然后这两种方法分别是怎么启动的,也就是说启动流程是怎么样的。(代码,UML图)

     有什么区别呢?

2. 关于这两种 不同的方式,Service处理完成事务之后 怎么销毁?

3. 关于Service的ANR会在什么时候产生呢?

4. Service是时候被加载进AMS的,也就是说我要去start一个Service的时候,系统是怎么知道有没有这个Service呢?



第一,关于Service的声明还有如何加载到AMS中。

1. 首先Service必须要在应用的AndroidManifest.xml中申明


  ...
  
      
      ...
  

2. PackageManagerService如何去解析呢?

    1) 当我们安装一个apk的时候,PMS会去解析新装的apk的package的一系列的信息。具体的过程需要debug查看代码,我们这里先指关注PackageParser.parsePackage()

    private Package parsePackage(
        Resources res, XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
        ... ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
          
            if (tagName.equals("application")) {
                if (foundApp) {
                ... ...
                foundApp = true;
                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
                    return null;
                }
            } else if (tagName.equals("permission")) {
               ... ...
            } else if (tagName.equals("permission-tree")) {
               ... ...
            } else if (tagName.equals("uses-permission")) {
               ... ...

            }
        return pkg;
    }


   
    

    2) PackageParser.parseApplication()

        在parse Manifest中的Application的标签时,如果我们在里面声明了Service就会通过 owner.services.add(s);把这个service添加到对应package的services数组中,然后PMS会去遍历所有的packages中的service,通过mServices.addService(s);把它加到PMS中的mServices的数组中。这样我们的Service便在PMS中有了记录。

在PMS中一个Package就对应一个应用程序,里面记录着apk相关的所有东西,包括package的名字,进程名,里面的Activity,service,receive等等。

    private boolean parseApplication(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
        throws XmlPullParserException, IOException {
     

    
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                ... ...
                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                ... ...
                owner.receivers.add(a);

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, attrs, flags, outError);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
                owner.services.add(s);


第二,通过StartService()去启动一个Service

1. ContextImpl.StartService(Intent service)

   StartService会直接去调用startServiceAsUser,而startServiceAsUser则会通过ActivityManagerNative通过binder去调用AMS的startService()

    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceAsUser(service, mUser);
    }

    @Override
    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
        try {
            service.setAllowFds(false);
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service,
                service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
             ......
            return cn;
        } catch (RemoteException e) {
            return null;
        }
    }

2  AMS.startService(IApplicationThread caller, Intent service, String resolvedType, int userId) 

    这个函数中caller就是一个binder对象,

                         servcie就是传过来的Intent

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, int userId) {

        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            checkValidCaller(callingUid, userId);
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

3. ActiveServices.startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, int userId)

  1) 首先根据caller去查找出与之对应的ProcessRecord对象 callerApp.    

  2)  retrieveServiceLocked(service, resolvedType, callingPid, callingUid, userId, true);   // 

  3) bringUpServiceLocked(r, service.getFlags(), false)  // 

   ComponentName startServiceLocked(IApplicationThread caller,
            Intent service, String resolvedType,
            int callingPid, int callingUid, int userId) {
        if (caller != null) {
            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);

        }

        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType,
                    callingPid, callingUid, userId, true);
        if (res == null) {
            return null;
        }
        ... ...
        ServiceRecord r = res.record;
        NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
                callingUid, r.packageName, service, service.getFlags(), null);
        if (unscheduleServiceRestartLocked(r)) {
            if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
        }
        r.startRequested = true;
        r.callStart = false;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants));
        r.lastActivity = SystemClock.uptimeMillis();
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        String error = bringUpServiceLocked(r, service.getFlags(), false);
        if (error != null) {
            return new ComponentName("!!", error);
        }
        return r.name;
    }

3.1 ActiveServices.retrieveServiceLocked(Intent service, String resolvedType, int callingPid, int callingUid, int userId,  boolean createIfNeeded)

     1)先在mServiceMap中去查找是否它所在的进程里面已经有这个ServiceRecord了,如果有就直接取出来付给r.

           如果传递过来的service是指定类名的,那么service.getComponent()就不等于null,于是就通过mServiceMap.getServiceByName(service.getComponent(), userId); 去寻找.

         通过IntentFilter到userId对应的进程里面去查找是否已经有了。

     2)如果mServiceMap中还不存在就去new一个新的ServiceRecord

           首先会去通过AppGlobals.getPackageManager().resolveService()(service, resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId) 到PMS里去查找是有符合Intent的已经在PMS加载过的Service的信息。这个函数会返回一个ResolveInfo。

          如果找到了对应的Service,就会new ServiceRecord(mAm, ss, name, filter, sInfo, res);并且把它放到mServiceMap说明当前已经有这个Service了,最后把找到的这个ServiceRecord封装到new ServiceLookupResult(r, null)中作为一个结果返回。

    private ServiceLookupResult retrieveServiceLocked(Intent service,
            String resolvedType, int callingPid, int callingUid, int userId,
            boolean createIfNeeded) {
        ServiceRecord r = null;

        if (service.getComponent() != null) {
            r = mServiceMap.getServiceByName(service.getComponent(), userId);
        }
        if (r == null) {
            Intent.FilterComparison filter = new Intent.FilterComparison(service);
            r = mServiceMap.getServiceByIntent(filter, userId);
        }
        if (r == null) {
            try {
                ResolveInfo rInfo =
                    AppGlobals.getPackageManager().resolveService(
                                service, resolvedType,
                                ActivityManagerService.STOCK_PM_FLAGS, userId);
                ServiceInfo sInfo =
                    rInfo != null ? rInfo.serviceInfo : null;
                ... ...
                ComponentName name = new ComponentName(
                        sInfo.applicationInfo.packageName, sInfo.name);
                if (userId > 0) {
                    if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                            sInfo.name, sInfo.flags)) {
                        userId = 0;
                    }
                    sInfo = new ServiceInfo(sInfo);
                    sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
                }
                r = mServiceMap.getServiceByName(name, userId);
                if (r == null && createIfNeeded) {
                    Intent.FilterComparison filter = new Intent.FilterComparison(
                            service.cloneFilter());
                    ServiceRestarter res = new ServiceRestarter();
                    BatteryStatsImpl.Uid.Pkg.Serv ss = null;
                    BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
                    synchronized (stats) {
                        ss = stats.getServiceStatsLocked(
                                sInfo.applicationInfo.uid, sInfo.packageName,
                                sInfo.name);
                    }
                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
                    res.setService(r);
                    mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
                    mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);

                    // Make sure this component isn't in the pending list.
                    int N = mPendingServices.size();
                    for (int i=0; i


3.3 ActiveServices.bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean whileRestarting)

     获取ServiceRecord所在的application是否已经启动,如果启动了就直接调用realStartServiceLocked(r, app); 如果没有启动就先去mAm.startProcessLocked启动对应的process,然后把需要start的service添加到mPendingServices中,等到进程起来之后会调用attachApplicationLocked去启动相应的service,当然还是调用realStartServiceLocked(sr, proc);

    private final String bringUpServiceLocked(ServiceRecord r,
            int intentFlags, boolean whileRestarting) {
        
        ... ...
        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
        ... ...
        // Make sure that the user who owns this service is started.  If not,
        // we don't want to allow it to run.
        if (mAm.mStartedUsers.get(r.userId) == null) {
            String msg = "Unable to launch app "
                    + r.appInfo.packageName + "/"
                    + r.appInfo.uid + " for service "
                    + r.intent.getIntent() + ": user " + r.userId + " is stopped";
            Slog.w(TAG, msg);
            bringDownServiceLocked(r, true);
            return msg;
        }

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
            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);
                    realStartServiceLocked(r, app);
                    return null;
                } 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.
            }
        } else {
            // If this service runs in an isolated process, then each time
            // we call startProcessLocked() we will get a new isolated
            // process, starting another process if we are currently waiting
            // for a previous process to come up.  To deal with this, we store
            // in the service any current isolated process it is running in or
            // waiting to have come up.
            app = r.isolatedProc;
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated)) == 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, true);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }

        if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }

        return null;
    }

3.3.1  ActiveService.realStartServiceLocked(ServiceRecord r, ProcessRecord app)

     1) app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); 通过app.thread.scheduleCreateService去调用ActivityThread的scheduleCreateService。在ActivityThread的中通过handle去发送和处理“CREATE_SERVICE”的消息。调用handleCreateService

         

     2)  requestServiceBindingLock()  //这个函数在BindService的时候才会起作用 

     3)  sendServiceArgsLocked(r, true); //处理传递给Service的Intent带来的参数

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {

        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        app.services.add(r);
        bumpServiceExecutingLocked(r, "create");
        mAm.updateLruProcessLocked(app, true);

        boolean created = false;
        try {
            mAm.mStringBuilder.setLength(0);
            r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false);
            EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
                    r.userId, System.identityHashCode(r), r.shortName,
                    mAm.mStringBuilder.toString(), r.app.pid);
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked();
            }
            mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
            r.postNotification();
            created = true;
        } finally {
          ... ...
        }

        requestServiceBindingsLocked(r);

        // 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, true);
    }

3.3.1.1 ActivityThread.handleCreateService()

      在Client端,实例化对应的Service并通过attach,把相关的属性给它。通过mServices.put(data.token, service);把自己加到当前进程的mServices中。

     此时就会调用service.onCreate(); 并且通过mServices.put(data.token, service); 把ActiveServices里面的ServiceRecord传过来的Token与本地Service对象放到mServices的Map中。

     最后会通过ActivityManagerNative.getDefault().serviceDoneExecuting(token, 0, 0, 0);去通知ActiveServices。

    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.

        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) {
            ... ...
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = new ContextImpl();
            context.init(packageInfo, null, this);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            context.setOuterContext(service);
            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, 0, 0, 0);
            } catch (RemoteException e) {
                // nothing to do.
            }
        } catch (Exception e) {
            ... ...
        }
    }


3.3.1.3 ActiveServices.sendServiceArgsLocked(ServiceRecord r,boolean oomAdjusted)

     在startServiceLocked函数中bringupService之前,已经调用了r.pendingStarts.add去添加了一个pendingStarts,所以这里的pendingStarts.size()是大于0的。

     1. 通过r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); 发送Message给Client端处理。

    private final void sendServiceArgsLocked(ServiceRecord r,
            boolean oomAdjusted) {
        final int N = r.pendingStarts.size();
        if (N == 0) {
            return;
        }

        while (r.pendingStarts.size() > 0) {
            try {
                ServiceRecord.StartItem si = r.pendingStarts.remove(0);

                si.deliveredTime = SystemClock.uptimeMillis();
                r.deliveredStarts.add(si);
                si.deliveryCount++;
                if (si.neededGrants != null) {
                    mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                            si.getUriPermissionsLocked());
                }

                int flags = 0;
                if (si.deliveryCount > 1) {
                    flags |= Service.START_FLAG_RETRY;
                }
                if (si.doneExecutingCount > 0) {
                    flags |= Service.START_FLAG_REDELIVERY;
                }
                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
            } catch (RemoteException e) {
               ....
        }
    }

3.3.1.3.1 ActivityThread.handleServiceArgs(ServiceArgsData data)

    1. 调用s.onStartCommand去做相关的操作,onStartCommand会返回一个integer. The integer is a value that describes how the system should continue the service in the event that the system kills it.  

     2. 告诉AMS已经完成了ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 1, data.startId, res); 在ActiveServices.serviceDoneExecutingLocked中会对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());
                }
                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 {
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, 1, data.startId, res);
                } catch (RemoteException e) {
                    // nothing to do.
                }
                ensureJitEnabled();
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }

ActiveServices.serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) 


    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
        boolean inStopping = mStoppingServices.contains(r);
        if (r != null) {
            if (type == 1) {
                // This is a call from a service start...  take care of
                // book-keeping.
                r.callStart = true;
                switch (res) {
                    case Service.START_STICKY_COMPATIBILITY:
                    case Service.START_STICKY: {
                        // We are done with the associated start arguments.
                        r.findDeliveredStart(startId, true);
                        // Don't stop if killed.
                        r.stopIfKilled = false;
                        break;
                    }
                    case Service.START_NOT_STICKY: {
                        // We are done with the associated start arguments.
                        r.findDeliveredStart(startId, true);
                        if (r.getLastStartId() == startId) {
                            // There is no more work, and this service
                            // doesn't want to hang around if killed.
                            r.stopIfKilled = true;
                        }
                        break;
                    }
                    case Service.START_REDELIVER_INTENT: {
                        // We'll keep this item until they explicitly
                        // call stop for it, but keep track of the fact
                        // that it was delivered.
                        ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                        if (si != null) {
                            si.deliveryCount = 0;
                            si.doneExecutingCount++;
                            // Don't stop if killed.
                            r.stopIfKilled = true;
                        }
                        break;
                    }
                    case Service.START_TASK_REMOVED_COMPLETE: {
                        // Special processing for onTaskRemoved().  Don't
                        // impact normal onStartCommand() processing.
                        r.findDeliveredStart(startId, true);
                        break;
                    }
                    default:
                        throw new IllegalArgumentException(
                                "Unknown service start result: " + res);
                }
                if (res == Service.START_STICKY_COMPATIBILITY) {
                    r.callStart = false;
                }
            }
            final long origId = Binder.clearCallingIdentity();
            serviceDoneExecutingLocked(r, inStopping);
            Binder.restoreCallingIdentity(origId);
        } else {
            Slog.w(TAG, "Done executing unknown service from pid "
                    + Binder.getCallingPid());
        }
    }

serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) 

    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
        if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
                + ": nesting=" + r.executeNesting
                + ", inStopping=" + inStopping + ", app=" + r.app);
        else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
        r.executeNesting--;
        if (r.executeNesting <= 0 && r.app != null) {
            if (DEBUG_SERVICE) Slog.v(TAG,
                    "Nesting at 0 of " + r.shortName);
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
                        "No more executingServices of " + r.shortName);
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            }
            if (inStopping) {
                if (DEBUG_SERVICE) Slog.v(TAG,
                        "doneExecuting remove stopping " + r);
                mStoppingServices.remove(r);
                r.bindings.clear();
            }
            mAm.updateOomAdjLocked(r.app);
        }
    }


到此StartService()启动结束,startService会返回一个ComponentName,而这个实际上就是ServiceRecord.name。


4. stopService

   如果一个service通过start开始,想要destroy它只有两个方法,一个是通过调用自己的stopSelf(), 另一个就是通过ContextImpl.stopService(Intent service).

   而这两个方法最终会走到ActiveServices的stopServiceTokenLocked 和 stopServiceLocked, 这两个方法都是去查找在ActivieServices中对应的ServiceRecord,找到之后最终调用bringDownServiceLocked去destroy相关的工作。

  1. r.app.thread.scheduleStopService(r);  通过Binder调用Client端的销毁工作。

    private final void bringDownServiceLocked(ServiceRecord r, boolean force) {
        ... ...

        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
        EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
                r.userId, System.identityHashCode(r), r.shortName,
                (r.app != null) ? r.app.pid : -1);

        mServiceMap.removeServiceByName(r.name, r.userId);
        mServiceMap.removeServiceByIntent(r.intent, r.userId);
        r.totalRestartCount = 0;
        unscheduleServiceRestartLocked(r);

        // Also make sure it is not on the pending list.
        int N = mPendingServices.size();
        for (int i=0; i

4.1 ActivityThread.handleStopService()

      调用s.onDestroy() 

    private void handleStopService(IBinder token) {
        Service s = mServices.remove(token);
        if (s != null) {
            try {
                if (localLOGV) Slog.v(TAG, "Destroying service " + s);
                s.onDestroy();
                Context context = s.getBaseContext();
                if (context instanceof ContextImpl) {
                    final String who = s.getClassName();
                    ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
                }

                try {
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            token, 0, 0, 0);
                } catch (RemoteException e) {
                    // nothing to do.
                }
            } catch (Exception e) {
                ... ...
            }
        }
        //Slog.i(TAG, "Running services: " + mServices);
    }



 


你可能感兴趣的:(Android,Service)