首頁 > 軟體

Android Service完整實現流程分析

2023-01-04 14:00:11

前言

一開始的目標是解決各種各樣的ANR問題的,我們知道,ANR總體上分有四種型別,這四種型別有三種是和四大元件相對應的,所以,如果想了解ANR發生的根因,對安卓四大元件的實現流程是必須要了解的,都不明白ANR如何觸發的,怎麼能完美的解決ANR的問題呢?

所以會寫一系列的文章,來分析四大組建的實現原理,同時也順帶講解四種型別的ANR是如何發生的。

本篇主要介紹service的完整實現流程,下一篇文章介紹Service中的ANR是如何產生的。

一.APP側啟動Service

其實啟動service和啟動Activity是很相似的,都是APP通知系統側,由系統側完成的整個流程。

1.1前臺和後臺啟動

無論是Activity,還是service,還是Application,都繼承自Context的抽象類,所以可以使用Context的各種功能,就比如這了要介紹的啟動前臺/後臺service。

Context在安卓中,使用了一種典型的代理模式,我們呼叫的startService或者startForegroundService方法,最終都會委託給ContextImpl中的startService和startForegroundService來處理的。我們就來看下ContextImpl中的這兩個方法:

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

果然和我猜測的差不多,無論前臺還是後臺啟動,其實最終都會走到一個方法中,只是設定引數的區別而已。最終都會走執行startServiceCommon方法。

1.2startServiceCommon

該方法中,通過binder通知系統的AMS完成對應的service的啟動操作:

 ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());

接下來,我們就看下系統側是如何處理Service啟動流程的。

二.系統側分發處理Service的啟動邏輯

系統側的處理我主要分為3塊來講:

1.系統接受APP側的通知並轉發

2.系統側委託ActiveServices負責完成的處理流程

3.收到APP側執行完成的回撥,進行收尾操作

2.1AMS接受啟動service的通知

APP側持有system_server程序的binder,上面講到,它會通過binder方法startService完成對系統側的通知。所以AMS的startService會收到這個通知。

我們看下程式碼,發現AMS會把整個service的邏輯全部交由ActiveServices來處理,程式碼如下:

 try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }

系統程式碼startServiceLocked方法中,程式碼雖然很長,但是卻遵循著一個不變的宗旨:位語句,即前面處理各種異常的分支邏輯,把核心流程留到方法的最終來處理。

所以我們直接看startServiceLocked方法的最後一部分即可:

final ComponentName realResult =
                startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
                allowBackgroundActivityStarts, backgroundActivityStartsToken);

startServiceInnerLocked方法中,處理邏輯也是比較簡單的,最終會交給bringUpServiceLocked方法來進行處理。而bringUpServiceLocked方法中則最終會交給realStartServiceLocked完成整個流程。好像系統程式碼都喜喜歡用realStart,Activity啟動的流程中也有一個方法叫realStartActivity。

2.2realStartServiceLocked流程

realStartServiceLocked方法中,我們總結為三個流程:

1.bumpServiceExecutingLocked,啟動超時檢查。

2.thread.scheduleCreateService通知APP一側去建立Service。

3.sendServiceArgsLocked通知APP執行Service的生命流程。

private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {
        //1.啟動超時檢查
        bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
        ...
        //2.通知APP建立service
            thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());
            r.postNotification();
            created = true;
        ...
        //3.通知執行service生命流程
        sendServiceArgsLocked(r, execInFg, true);
       ...
    }

三.系統側通知APP啟動Service

一般情況下,APP側會收到系統側發過來兩種型別的通知,

第一種:建立Service的任務通知

第二種:執行Service生命流程的通知,通知Service執行onStartCommand方法。

ApplicationThread接受通知並建立Service

系統側持有APP側的binder,會通過scheduleCreateService這個binder方法通知APP一側進行相應的操作。而APP側,完成這個工作接收的就是ApplicationThread中的scheduleCreateService方法。該方法收到通知後,通過handler切換到主執行緒處理:

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

handle中,會切換到主執行緒執行ActivityThread的handleCreateService方法。

主要執行了如下的幾段邏輯:

1.如果是首次建立App程序的話,則需要重新建立Application;

2.建立Service物件;

3.呼叫service的attach方法進行關聯;

4.呼叫service的onCreate生命週期方法;

5.建立完成後,通過serviceDoneExecuting通知系統側建立完成。

try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            final java.lang.ClassLoader cl;
            if (data.info.splitName != null) {
                cl = packageInfo.getSplitClassLoader(data.info.splitName);
            } else {
                cl = packageInfo.getClassLoader();
            }
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));
            if (data.info.splitName != null) {
                context = (ContextImpl) context.createContextForSplit(data.info.splitName);
            }
            if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
                final String attributionTag = data.info.attributionTags[0];
                context = (ContextImpl) context.createAttributionContext(attributionTag);
            }
            // 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();
            mServicesData.put(data.token, data);
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

ApplicationThread接受通知並執行Service的生命流程

同樣的,這裡完成接受的是,仍然是ApplicationThread中的方法。這個流程中的接受方法是scheduleServiceArgs方法。

ApplicationThread中,收到通知後,通過handler把任務轉交到主執行緒。

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

接下來handler中切換到主執行緒會執行ActivityThread的handleServiceArgs方法。

handleServiceArgs方法主要會完成以下幾件事:

1.找到對應的service,呼叫起onStartCommand方法;

2.通知系統側回撥完成。

private void handleServiceArgs(ServiceArgsData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess(isProtectedComponent(createData.info),
                            s.getAttributionSource());
                }
                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);
                }
            }
        }
    }

發我們發現,不論是建立service,還是通知執行service的生命流程,最終都執行了一個完成的通知,這有何意圖呢?是的,這個意圖就是和ANR相關的,我們下一章來講了。

四.總結

前面一一講了實現的原理,我們最後再來做一個總結,儘量用一張圖+幾句話的方式來概括。

1.無論前臺啟動還是後臺啟動,最終都會走到ContextImpl這個最終實現類中的方法,完成和AMS的互動。

2.AMS中主要是ActiveServices完成的整個流程。其核心方法是realStartServiceLocked。

他首先啟動一個延時訊息,通過延時訊息進行超時的監測。

然後通知APP去生成Service。

通知APP側去完成Service的生命週期流程onStartCommand。

3.收到APP側執行完成的通知後,則取消註冊延時訊息。

到此這篇關於Android Service完整實現流程分析的文章就介紹到這了,更多相關Android Service內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com