從源碼出發(fā)深入理解 Android Service

原文鏈接:http://www.woaitqs.cc/2016/09/20/android-service-usage.html

本文是 Android 系統(tǒng)學(xué)習(xí)系列文章中的第三章節(jié)的內(nèi)容烹卒,介紹了 Android Service 相關(guān)的基礎(chǔ)知識,然后從源碼的角度上分析 Service 的一些實現(xiàn)原理踩叭。對此系列感興趣的同學(xué)拳缠,可以收藏這個鏈接 Android 系統(tǒng)學(xué)習(xí)渔彰,也可以點擊 RSS訂閱 進行訂閱


0X00 Service 基礎(chǔ)知識

Service 作為 Android 提供的四大組件之一欧芽,主要負責(zé)一些沒有前臺顯示的后臺任務(wù)症脂。即使應(yīng)用本身不再可見织鲸,Service 的屬性也能使得其在后臺運行句占。除此之外沪摄,Service 也可以通過 Binder 機制,與界面甚至其他應(yīng)用進行進程間通信纱烘,以實現(xiàn)相應(yīng)的交互杨拐。這里需要簡單說明的是,既然是后臺任務(wù)擂啥,為什么不選用 Thread 了哄陶?選用 Service 和 Thread 的主要區(qū)別在于需不需要在應(yīng)用不可見的時候依然保留。舉例來說哺壶,新聞詳情頁面的數(shù)據(jù)請求屋吨,只用在當前頁面生效,而音樂播放這些后臺任務(wù)就可以通過 Service 的方式來實現(xiàn)山宾。

關(guān)于如何使用 Service离赫,官方教程已經(jīng)說明得足夠詳細了,如果對這些用法塌碌,還有不清晰的地方渊胸,請戳這里進行查看,-> 官方教程台妆。官方教程里面包括翎猛,startService 和 bindService 的區(qū)別,在不同場景下應(yīng)該選用哪種 Service 實現(xiàn)方式接剩。

Service 生命周期

0X01 startService 調(diào)用流程

從前面的教程里面切厘,可以知道 Service 的啟動一般有兩種方式,分別是 bindService 和 startService懊缺。這里主要說明 startService疫稿, 具體的實現(xiàn)邏輯在 ContextImpl 中,我們看看源碼是怎么實現(xiàn)的鹃两。

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

接下來遗座,看看方法內(nèi)部具體是怎么實現(xiàn)的。

private ComponentName startServiceCommon(Intent service, UserHandle user) {
    try {
        validateServiceIntent(service);
        service.prepareToLeaveProcess();
        ComponentName cn = ActivityManagerNative.getDefault().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), getOpPackageName(), user.getIdentifier());
        // ignore some codes...
        return cn;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

從上面的代碼可以看到俊扳,這里是通過 ActivityManagerNative 來執(zhí)行的途蒋。如果看過我的另一篇文章,Android Activity 生命周期是如何實現(xiàn)的, 可能會覺得很熟悉馋记。事實上這里采用的機制就是同樣的号坡。

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

ActivityManagerNative 的 getDefault 方法是這么實現(xiàn)的懊烤。可以看到宽堆,gDefault 是類型為 IActivityManager 的 Binder 對象腌紧。而這個 Binder 對象可以看做是在 System Server 中的 ActivityManagerService 的句柄,通過這個 Binder 對象畜隶,可以跨進程調(diào)用 ActivityManagerService寄啼。

如果上述內(nèi)容不容易理解的話,我們可以類比地來看這個問題代箭。我們遙控電視的時候墩划,例如進行增加音量的操作,這個操作實際不是由遙控器完成的嗡综,而是電視中的電子元件完成的乙帮。遙控器會和電視進行協(xié)商,先聲明有哪些操作可以執(zhí)行极景,然后將這些協(xié)商后的操作在遙控器端和電視端 ?? 都實現(xiàn)察净,區(qū)別在于電視機是真的實現(xiàn),而遙控器只是發(fā)送操作指令而已盼樟。前面代碼中提及的 gDefault 就是 ActivityManagerService 的遙控器氢卡。

[圖片上傳失敗...(image-9ead3a-1512375661501)]

接著往下看看電視端是具體怎么操作的,這里的電視端就是 ActivityManagerService.

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");

    // ignore some codes...

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

看起來具體的邏輯晨缴,都在類型為 ActiveServices 的 mServices 對象中译秦。ActiveServices 是 AMS 專門用來管理 Service 的類,大部分和 Services 相關(guān)的邏輯都在這里面击碗。

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, String callingPackage, int userId)
        throws TransactionTooLargeException {
    if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
            + " type=" + resolvedType + " args=" + service.getExtras());

    // 調(diào)用進程的優(yōu)先級是否足夠
    final boolean callerFg;
    if (caller != null) {
        // 能否找到相應(yīng)的 Process
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + Binder.getCallingPid()
                    + ") when starting service " + service);
        }
        callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
    } else {
        callerFg = true;
    }

    // ignore some codes.

    final ServiceMap smap = getServiceMap(r.userId);
    boolean addToStarting = false;
    if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
            // 避免同時有多個服務(wù)啟動時筑悴,造成的性能問題,如果超過閾值后稍途,就進行延遲
            if (r.delayed) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
                return r.name;
            }
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                // Something else is starting, delay!
                Slog.i(TAG_SERVICE, "Delaying start of: " + r);
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
            addToStarting = true;
        } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
            addToStarting = true;
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Not delaying, but counting as bg: " + r);
        }
    }

    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

可以看到 startServiceLocked 主要進行了權(quán)限校驗和為性能進行的調(diào)度阁吝,具體的邏輯,還在 startServiceInnerLocked 方法里面械拍。

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    ProcessStats.ServiceState stracker = r.getTracker();
    if (stracker != null) {
        // 跟蹤 Service 內(nèi)存試用狀況
        stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
    }
    r.callStart = false;
    synchronized (r.stats.getBatteryStats()) {
        // 跟蹤電池占用情況
        r.stats.startRunningLocked();
    }
    // 實際啟動 Service
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }

    // 設(shè)置超時時間
    if (r.startRequested && addToStarting) {
        boolean first = smap.mStartingBackground.size() == 0;
        smap.mStartingBackground.add(r);
        r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
        if (DEBUG_DELAYED_SERVICE) {
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
        } else if (DEBUG_DELAYED_STARTS) {
            Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
        }
        if (first) {
            smap.rescheduleDelayedStarts();
        }
    } else if (callerFg) {
        smap.ensureNotStartingBackground(r);
    }

    return r.name;
}

接下來看看 bringUpServiceLocked 是如何實現(xiàn)的突勇,這里就是實際啟動 Service 的地方。

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting) throws TransactionTooLargeException {

    if (r.app != null && r.app.thread != null) {
        // 目標Service已經(jīng)啟動
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }

    if (!whileRestarting && r.restartDelay > 0) {
        // 正在重啟坷虑,或者等待重啟甲馋,直接返回
        return null;
    }

    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);

    // 從重啟 Service 列表中移除
    if (mRestartingServices.remove(r)) {
        // 重試計數(shù)
        r.resetRestartCounter();
        clearRestartingIfNeededLocked(r);
    }

    // 移除 delayed 的標示
    if (r.delayed) {
        if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
        getServiceMap(r.userId).mDelayedStartList.remove(r);
        r.delayed = false;
    }

    // Service 即將被啟動,它所屬的Package不能被停止
    try {
        AppGlobals.getPackageManager().setPackageStoppedState(
                r.packageName, false, r.userId);
    } catch (RemoteException e) {
    } catch (IllegalArgumentException e) {
        Slog.w(TAG, "Failed trying to unstop package "
                + r.packageName + ": " + e);
    }

    // isolated 變量主要是用于如下問題
    // Service 可以通過 AndroidManifest 中的指令指定在單獨的進程上運行
    // 啟動這個單獨的進程猖吴,是一個耗時的過程摔刁,因而 isolated 標記起來后,
    // 可以避免在這個過程中又創(chuàng)建了一個進程
    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, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        // 如果應(yīng)用進程已經(jīng)創(chuàng)建海蔽,而且 Looper 線程已經(jīng)準備完畢
        // 那么就調(diào)用 realStartServiceLocked 實際啟動 Service
        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);
            }

        }
    } else {
        app = r.isolatedProc;
    }

    // 應(yīng)用進程還未創(chuàng)建共屈,這里需要先啟動應(yīng)用進程
    if (app == null) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", 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;
        }
    }


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

    if (r.delayedStop) {
        // 準備停止 Service
        r.delayedStop = false;
        if (r.startRequested) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Applying delayed stop (in bring up): " + r);
            stopServiceLocked(r);
        }
    }

    return null;
}

上面的代碼較為復(fù)雜,這里進行下初步的總結(jié)党窜。首先判斷服務(wù)是否已經(jīng)啟動拗引,或者正在重啟中,則直接返回幌衣。其后矾削,當前 Service 進程正在啟動中,也直接返回豁护。最后判斷應(yīng)用進程是否啟動哼凯,如果沒有啟動進程,則先啟動進程楚里。

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    // 如果主進程不存在断部,拋出異常
    if (app.thread == null) {
        throw new RemoteException();
    }

    r.app = app;
    // 記錄重啟和最后活動時間
    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

    final boolean newService = app.services.add(r);
    bumpServiceExecutingLocked(r, execInFg, "create");
    mAm.updateLruProcessLocked(app, false, null);
    mAm.updateOomAdjLocked();

    boolean created = false;
    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();
        }
        // 確保 Package Opt 工作完成
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        // 通過 app.thread 的 binder 對象,通過創(chuàng)建目標 Service.
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        if (!created) {
            // Keep the executeNesting count accurate.
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);

            // Cleanup.
            if (newService) {
                app.services.remove(r);
                r.app = null;
            }

            // Retry.
            if (!inDestroying) {
                scheduleServiceRestartLocked(r, false);
            }
        }
    }

    // 如果是通過 bindService 來執(zhí)行的班缎,這里就會通知客戶端.
    requestServiceBindingsLocked(r, execInFg);

    updateServiceClientActivitiesLocked(app, null, true);

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

    // 通知調(diào)用 onStartCommand 接口
    sendServiceArgsLocked(r, execInFg, true);

    if (r.delayed) {
        if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
        getServiceMap(r.userId).mDelayedStartList.remove(r);
        r.delayed = false;
    }

    if (r.delayedStop) {
        // Oh and hey we've already been asked to stop!
        r.delayedStop = false;
        if (r.startRequested) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Applying delayed stop (from start): " + r);
            stopServiceLocked(r);
        }
    }
}

上面代碼的重點在于 app.thread, 這個 IApplicationThread 對象同樣也是一個 Bindle 接口蝴光,與前文提交的 gDefault 不同之處在于兩者的方向是相反的。前者是應(yīng)用進程操作AMS达址,而后者則是AMS操作應(yīng)用進程蔑祟。IApplicationThread 對應(yīng)的實現(xiàn)是 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);
}

原來還是通過 mH Handler 這種方式來執(zhí)行的呀疆虚,這與前面文章提及的知識是完全一樣的。如果大家想詳細地了解的話满葛,建議看看這些系列文章 Android 系統(tǒng)學(xué)習(xí)計劃装蓬。在 Handler 中的 handleMessage 是如何處理 CREATE_SERVICE 這個消息的?

case CREATE_SERVICE:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
    handleCreateService((CreateServiceData)msg.obj);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    break;

繼續(xù)看 handleCreateService 方法的實現(xiàn)纱扭。

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) {
            // nothing to do.
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to create service " + data.info.name
                + ": " + e.toString(), e);
        }
    }
}

方法實現(xiàn)相對簡單牍帚,首先通過 ClassLoader 加載相關(guān)的 class 對象,然后將 Service 和 Application 綁定在一起乳蛾,最后調(diào)用 Service 的 onCreate 方法暗赶。到此為止,Service 就完全啟動了肃叶。

如果使用 Service 的方式是 startService蹂随,那么在 Service 啟動后,就可以執(zhí)行 sendServiceArgsLocked 方法因惭,從而在 Service 的 onStartCommand 里面可以執(zhí)行相應(yīng)的后臺代碼岳锁,需要特別說明的是,這個是執(zhí)行在 UI 線程上的,因而建議不要執(zhí)行耗時的任務(wù)等脂,如果是耗時的任務(wù),需要通過多線程的方式來避免主線程的阻塞茫船。即使應(yīng)用當前不在前臺乒躺,阻塞 UI 線程招盲,也是很不好的情況。

我們看看 sendServiceArgsLocked 是如何實現(xiàn)的嘉冒。

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

    while (r.pendingStarts.size() > 0) {
        Exception caughtException = null;
        ServiceRecord.StartItem si;
        try {
            r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
        } catch (TransactionTooLargeException e) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
                    + si.intent);
            caughtException = e;
        } catch (RemoteException e) {
            // Remote process gone...  we'll let the normal cleanup take care of this.
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
            caughtException = e;
        } catch (Exception e) {
            Slog.w(TAG, "Unexpected exception", e);
            caughtException = e;
        }

        if (caughtException != null) {
            // Keep nesting count correct
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            if (caughtException instanceof TransactionTooLargeException) {
                throw (TransactionTooLargeException)caughtException;
            }
            break;
        }
    }
}

這里通過循環(huán)的方式曹货,將在隊列中的消息,依次通過 app.thread 發(fā)布到應(yīng)用進程中去讳推,如果中途發(fā)生了 TransactionTooLargeException 異常顶籽,則會提前終止這個過程。app.thread 的 scheduleServiceArgs 方法银觅,也是通過 mH 這個 Handler 來執(zhí)行的礼饱,發(fā)送的消息為 SERVICE_ARGS

case SERVICE_ARGS:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
    handleServiceArgs((ServiceArgsData)msg.obj);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    break;
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 {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_START, 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);
            }
        }
    }
}

handleServiceArgs 方法中设拟,s.onStartCommand 就是我們書寫后臺代碼的地方慨仿,當這個方法執(zhí)行完成后,又通過 gDefault 這個遙控器通知 AMS 來任務(wù)完成了纳胧。

對于 startService 這種方式而言镰吆,是需要手動調(diào)用 stopSelf 方法來結(jié)束 Service 的,背后的原理與 startService 方法類似跑慕,這里就不再贅述万皿。


0X01 bindService 調(diào)用流程

bindService 相較于 startService 要復(fù)雜一些,通過這種方式實現(xiàn)的 Service核行,容易多個組件綁定到它牢硅,通過 ServiceConnection 的方式來進行通信。當沒有任何其他組件芝雪,連接到這個 Service 時减余,該 Service 會自動銷毀。

bindService 方法是這樣聲明的惩系。

@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        UserHandle user) {
    // ignore some codes ...
    validateServiceIntent(service);
    try {
        // ignore some codes ...
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

可以看到同樣是通過 gDefault 這個遙控器來通知 AMS 進行相應(yīng)的操作的位岔,原理與上面 startService 相同,接收到遙控器的指令后堡牡,ActiveServices 的 bindSericeLocked 方法開始執(zhí)行抒抬。bindSericeLocked 在進行一些校驗,確認進程創(chuàng)建成功等等步驟后晤柄,還是通過 app.thread 發(fā)送 BIND_SERVICE 消息擦剑,來執(zhí)行對應(yīng)的邏輯。

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) {
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to bind to service " + s
                        + " with " + data.intent + ": " + e.toString(), e);
            }
        }
    }
}

在 onBind 方法,給調(diào)用者返回 Binder 對象惠勒,通過 publishService 方法通過到 AMS 內(nèi)部去赚抡,我們看看接下來發(fā)生了什么。

public void publishService(IBinder token, Intent intent, IBinder service) {
    // Refuse possible leaked file descriptors
    if (intent != null && intent.hasFileDescriptors() == true) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }

    synchronized(this) {
        if (!(token instanceof ServiceRecord)) {
            throw new IllegalArgumentException("Invalid service token");
        }
        mServices.publishServiceLocked((ServiceRecord)token, intent, service);
    }
}

ActiveServices 中的代碼也相對簡單, 遍歷建立起的 ServiceConnection捉撮,并調(diào)用它們的 connected 方法怕品,這也是我們需要編寫后臺代碼的地方妇垢。

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    final long origId = Binder.clearCallingIdentity();
    try {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                + " " + intent + ": " + service);
        if (r != null) {
            Intent.FilterComparison filter
                    = new Intent.FilterComparison(intent);
            IntentBindRecord b = r.bindings.get(filter);
            if (b != null && !b.received) {
                b.binder = service;
                b.requested = true;
                b.received = true;
                for (int conni=r.connections.size()-1; conni>=0; conni--) {
                    ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                    for (int i=0; i<clist.size(); i++) {
                        ConnectionRecord c = clist.get(i);
                        if (!filter.equals(c.binding.intent.intent)) {
                            continue;
                        }
                        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                        try {
                            c.conn.connected(r.name, service);
                        } catch (Exception e) {
                            Slog.w(TAG, "Failure sending service " + r.name +
                                  " to connection " + c.conn.asBinder() +
                                  " (in " + c.binding.client.processName + ")", e);
                        }
                    }
                }
            }

            serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

0X02 總結(jié)

Service 作為四大組件之一巾遭,提供了不需要前臺頁面情況下,在后臺繼續(xù)執(zhí)行任務(wù)的能力闯估。Service 一般有兩種使用方式灼舍,分別是通過 startService 和 bindService,前者適合執(zhí)行一次性的任務(wù)涨薪,而后者則具備一定交互的能力骑素,可以用作處理相對復(fù)雜的后臺邏輯。

關(guān)于更多詳細的用法刚夺,還是建議閱讀官方教程献丑,Services.


文檔信息


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侠姑,隨后出現(xiàn)的幾起案子创橄,更是在濱河造成了極大的恐慌,老刑警劉巖莽红,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妥畏,死亡現(xiàn)場離奇詭異,居然都是意外死亡安吁,警方通過查閱死者的電腦和手機醉蚁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鬼店,“玉大人网棍,你說我怎么就攤上這事「局牵” “怎么了滥玷?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俘陷。 經(jīng)常有香客問我罗捎,道長,這世上最難降的妖魔是什么拉盾? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任桨菜,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倒得。我一直安慰自己泻红,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布霞掺。 她就那樣靜靜地躺著谊路,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菩彬。 梳的紋絲不亂的頭發(fā)上缠劝,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音骗灶,去河邊找鬼惨恭。 笑死,一個胖子當著我的面吹牛耙旦,可吹牛的內(nèi)容都是我干的脱羡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼免都,長吁一口氣:“原來是場噩夢啊……” “哼锉罐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绕娘,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脓规,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后业舍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抖拦,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年舷暮,在試婚紗的時候發(fā)現(xiàn)自己被綠了态罪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡下面,死狀恐怖复颈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沥割,我是刑警寧澤耗啦,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站机杜,受9級特大地震影響帜讲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜椒拗,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一似将、第九天 我趴在偏房一處隱蔽的房頂上張望获黔。 院中可真熱鬧,春花似錦在验、人聲如沸玷氏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盏触。三九已至,卻和暖如春块饺,著一層夾襖步出監(jiān)牢的瞬間赞辩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工刨沦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诗宣,地道東北人膘怕。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓想诅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親岛心。 傳聞我的和親對象是個殘疾皇子来破,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容