zoukankan      html  css  js  c++  java
  • Android中ANR的触发机制-Service篇

    个人博客

    http://www.milovetingting.cn

    Android中ANR的触发机制-Service篇

    概述

    ANR,即Application Not Responding,应用程序不响应。在Android系统中,对于事件的处理,都需要在一定的时间内完成,如果处理超时的话,就会触发ANR,弹出不响应的界面,让用户选择等待或是立即结束应用。ANR机制的简单流程:在事件发给应用处理前,会先发一个延时消息到系统的Looper中,如果应用在规定的时间内执行完成,则会移除掉延时消息。如果没有在规定时间内执行完,就会在处理延时消息中,触发ANR。

    ANR主要场景:

    1. Service

    2. BroadcastReceiver

    3. ContentProvider

    4. Input:包括输入和触摸

    触发机制分析

    下面对Service进行源码分析,源码为Android9.0。

    首先从Service的启动来分析。这里只分析startService的模式,bindService模式暂时不分析。

    附一张时序图

    Service的ANR机制

    不管是通过Activity的startService还是非Activity的Context中的startService,最终都是调用ContextWrapper的startService方法:

    //ContextWrapper
    @Override
        public ComponentName startService(Intent service) {
            return mBase.startService(service);
        }
    

    mBase对应的具体Context类为ContextImpl。ContextImpl的startService方法:

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

    startService方法调用startServiceCommon方法

    //ContextImpl
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
                UserHandle user) {
            try {
                validateServiceIntent(service);
                service.prepareToLeaveProcess(this);
                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();
            }
        }
    

    在这个方法里,跨进程调用AMS的startService方法

    //AMS
    @Override
        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) {
                final int callingPid = Binder.getCallingPid();
                final int callingUid = Binder.getCallingUid();
                final long origId = Binder.clearCallingIdentity();
                ComponentName res;
                try {
                    //调用ActiveServices的startServiceLocked方法
                    res = mServices.startServiceLocked(caller, service,
                            resolvedType, callingPid, callingUid,
                            requireForeground, callingPackage, userId);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
                return res;
            }
        }
    

    AMS中调用ActiveServices的startServiceLocked方法:

    //ActiveServices
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
                int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
                throws TransactionTooLargeException {
            //...
            ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
            return cmp;
        }
    

    这个方法又调用了startServiceInnerLocked方法:

    //ActiveServices
    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
                boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
            //...
            String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
            if (error != null) {
                return new ComponentName("!!", error);
            }
            //...
        }
    

    这个方法调用bringUpServiceLocked方法:

    //ActiveServices
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
                boolean whileRestarting, boolean permissionsReviewRequired)
                throws TransactionTooLargeException {
            //...
            realStartServiceLocked(r, app, execInFg);
            //...      
        }
    

    这个方法里调用realStartServiceLocked方法:

    //ActiveServices
    private final void realStartServiceLocked(ServiceRecord r,
                ProcessRecord app, boolean execInFg) throws RemoteException {
            //...
            bumpServiceExecutingLocked(r, execInFg, "create");
            //...
            app.thread.scheduleCreateService(r, r.serviceInfo,
                        mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                        app.repProcState);
            //...
        }
    

    这里先调用了bumpServiceExecutingLocked方法用来设置超时消息:

    //ActiveServices
    private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
                    + why + " of " + r + " in app " + r.app);
            else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
                    + why + " of " + r.shortName);
    
            // For b/34123235: Services within the system server won't start until SystemServer
            // does Looper.loop(), so we shouldn't try to start/bind to them too early in the boot
            // process. However, since there's a little point of showing the ANR dialog in that case,
            // let's suppress the timeout until PHASE_THIRD_PARTY_APPS_CAN_START.
            //
            // (Note there are multiple services start at PHASE_THIRD_PARTY_APPS_CAN_START too,
            // which technically could also trigger this timeout if there's a system server
            // that takes a long time to handle PHASE_THIRD_PARTY_APPS_CAN_START, but that shouldn't
            // happen.)
            boolean timeoutNeeded = true;
            if ((mAm.mBootPhase < SystemService.PHASE_THIRD_PARTY_APPS_CAN_START)
                    && (r.app != null) && (r.app.pid == android.os.Process.myPid())) {
    
                Slog.w(TAG, "Too early to start/bind service in system_server: Phase=" + mAm.mBootPhase
                        + " " + r.getComponentName());
                timeoutNeeded = false;
            }
    
            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 (timeoutNeeded && r.app.executingServices.size() == 1) {
                        scheduleServiceTimeoutLocked(r.app);
                    }
                }
            } else if (r.app != null && fg && !r.app.execServicesFg) {
                r.app.execServicesFg = true;
                if (timeoutNeeded) {
                    scheduleServiceTimeoutLocked(r.app);
                }
            }
            r.executeFg |= fg;
            r.executeNesting++;
            r.executingStart = now;
        }
    

    这个方法中调用scheduleServiceTimeoutLocked方法:

    //ActiveServices
    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;
            mAm.mHandler.sendMessageDelayed(msg,
                    proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
        }
    

    调用ActivityManagerService的MainHandler发送一个SERVICE_TIMEOUT消息,这里具体根据是否为前台消息发送的消息不同。

    前台服务超时时间:20s,后台服务超时时间:200s

     // How long we wait for a service to finish executing.
        static final int SERVICE_TIMEOUT = 20*1000;
    
        // How long we wait for a service to finish executing.
        static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
    

    设置超时消息就先看到这里,接着看启动Service

    调用ProcessRecord中的IApplicationThread类型的thread属性的scheduleCreateService方法,即调用到了ActivityThread的内部类ApplicationThread的scheduleCreateService方法:

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

    这个方法里调用了ActivityThread的sendMessage方法:

    //ActivityThread
    void sendMessage(int what, Object obj) {
            sendMessage(what, obj, 0, 0, false);
        }
    

    最终通过Handler发送了一个消息出去

    //Handler
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
            if (DEBUG_MESSAGES) Slog.v(
                TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
                + ": " + arg1 + " / " + obj);
            Message msg = Message.obtain();
            msg.what = what;
            msg.obj = obj;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            if (async) {
                msg.setAsynchronous(true);
            }
            mH.sendMessage(msg);
        }
    

    在mH的handleMessage中回调处理:

    //ActivityThread$H.handleMessage
    case CREATE_SERVICE:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                        handleCreateService((CreateServiceData)msg.obj);
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
    
    //ActivityThread
     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 = packageInfo.getAppFactory()
                        .instantiateService(cl, data.info.name, data.intent);
            } 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,
                        ActivityManager.getService());
                //完成Service的创建,并回调onCreate方法
                service.onCreate();
                mServices.put(data.token, service);
                try {
                    //通知移除延时消息
                    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);
                }
            }
        }
    

    如果在规定的时间内完成处理,则会调用AMS的serviceDoneExecuting:

    //AMS
    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
            synchronized(this) {
                if (!(token instanceof ServiceRecord)) {
                    Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                    throw new IllegalArgumentException("Invalid service token");
                }
                mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
            }
        }
    

    这个方法调用ActiveServices中的serviceDoneExecutingLocked方法:

    //ActiveServices
    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
            //...
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            //...
        }
    

    这个方法会调用另一个重载方法:

    //ActiveServices
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
                boolean finishing) {
            //...
            mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            //...
        }
    

    在这个方法里移除了前面的延时消息,就不会触发ANR。

    如果没有及时移除这个消息,那么将会在ActivityManagerService的MainHandler中触发:

    MainHandler收到消息的处理:

    //ActivityManagerService$MainHandler.handleMessage
    @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    //...
                    case SERVICE_TIMEOUT_MSG: {
                        mServices.serviceTimeout((ProcessRecord)msg.obj);
                    } break;
                    //...
                }
            }
    

    这个方法调用ActiveServices中的serviceTimeout方法:

    void serviceTimeout(ProcessRecord proc) {
            //...
            if (anrMessage != null) {
                mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
            }
        }
    

    在这里弹出了不响应的界面。

  • 相关阅读:
    drf规范——请求与响应
    序列化器——Serializer
    drf 安装与配置
    CBV源码——View,APIView
    django restful framework —— Drf 规范一
    Vue——五
    Vue——四
    今日复习
    冒泡排序
    考试总结
  • 原文地址:https://www.cnblogs.com/milovetingting/p/12507814.html
Copyright © 2011-2022 走看看