2024年安卓最全ANR 之 原理分析,2024年最新面试被面试官怼哭

总结

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

跨平台开发:Flutter.png

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

发生 ANR 调用 onANRLocked() 的过程会将 doNotifyANRLockedInterruptible 加入 mCommandQueue。在下一轮 InputDispatcher.dispatchOnce 的过程中会先执行 runCommandsLockedInterruptible() 方法,取出 mCommandQueue 队列的所有命令逐一执行。那么 ANR 所对应的命令 doNotifyANRLockedInterruptible,接下来看该方法。

3.2 doNotifyANRLockedInterruptible


InputDispatcher.cpp


void InputDispatcher::doNotifyANRLockedInterruptible(

        CommandEntry* commandEntry) {

    mLock.unlock();

    

    nsecs_t newTimeout = mPolicy->notifyANR(

        commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,

        commandEntry->reason);



    mLock.lock();

    //newTimeout =5s

    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,

            commandEntry->inputWindowHandle != NULL

            ? commandEntry->inputWindowHandle->getInputChannel() : NULL);

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

mPolicy 是指 NativeInputManager

3.3 NativeInputManager.notifyANR


com_android_server_input_InputManagerService.cpp


nsecs_t NativeInputManager::notifyANR(

    const sp<InputApplicationHandle>& inputApplicationHandle,

    const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {

    ......

    JNIEnv* env = jniEnv();

    ScopedLocalFrame localFrame(env);



    jobject tokenObj = javaObjectForIBinder(env, token);

    jstring reasonObj = env->NewStringUTF(reason.c_str());



    //调用Java方法

    jlong newTimeout = env->CallLongMethod(mServiceObj,

                gServiceClassInfo.notifyANR, tokenObj,

                reasonObj);

    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {

        newTimeout = 0; //抛出异常,则清理并重置timeout

    } else {

        assert(newTimeout >= 0);

    }

    return newTimeout;

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

先看看 register_android_server_InputManager 过程:


int register_android_server_InputManager(JNIEnv* env) {

    int res = jniRegisterNativeMethods(env,

    "com/android/server/input/InputManagerService",

    gInputManagerMethods, NELEM(gInputManagerMethods));



    jclass clazz;

    FIND_CLASS(clazz, "com/android/server/input/InputManagerService");

    ......

    GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,

            "notifyANR",

            "(Landroid/os/IBinder;Ljava/lang/String;)J");

    ......

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

可知 gServiceClassInfo.notifyANR 是指 IMS.notifyANR

3.4 IMS.notifyANR



private long notifyANR(IBinder token, String reason) {

    return mWindowManagerCallbacks.notifyANR(

            token, reason);

}



  • 1

  • 2

  • 3

  • 4

此处 mWindowManagerCallbacks 是指 InputManagerCallback 对象。

3.5 InputManagerCallback.notifyANR


InputManagerCallback.java


public long notifyANR(IBinder token, String reason) {

    AppWindowToken appWindowToken = null;

    WindowState windowState = null;

    boolean aboveSystem = false;

    synchronized (mService.mGlobalLock) {

        if (token != null) {

                windowState = mService.windowForClientLocked(null, token, false);

                if (windowState != null) {

                    appWindowToken = windowState.mAppToken;

                }

        }

        //输出input事件分发超时log

        if (windowState != null) {

                Slog.i(TAG_WM, "Input event dispatching timed out "

                        + "sending to " + windowState.mAttrs.getTitle()

                        + ".  Reason: " + reason);

                // Figure out whether this window is layered above system windows.

                // We need to do this here to help the activity manager know how to

                // layer its ANR dialog.

                int systemAlertLayer = 

                mService.mPolicy.getWindowLayerFromTypeLw(

                TYPE_APPLICATION_OVERLAY,

                windowState.mOwnerCanAddInternalSystemWindow);

                aboveSystem = windowState.mBaseLayer > systemAlertLayer;

            } else if (appWindowToken != null) {

                Slog.i(TAG_WM, "Input event dispatching timed out "

                        + "sending to application " + appWindowToken.stringName

                        + ".  Reason: " + reason);

            } else {

                Slog.i(TAG_WM, "Input event dispatching timed out "

                        + ".  Reason: " + reason);

            }

        mService.saveANRStateLocked(appWindowToken, windowState, reason);

    }



    // All the calls below need to happen without the WM

    // lock held since they call into AM.

    mService.mAtmInternal.saveANRState(reason);

        

    if (appWindowToken != null && appWindowToken.appToken != null) {

        final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,

                (windowState != null) ? windowState.mSession.mPid : -1);

        if (! abort) {

            return appWindowToken.inputDispatchingTimeoutNanos; //5s

        }

    } else if (windowState != null) {

        long timeout = mService.mAmInternal.inputDispatchingTimedOut(

                windowState.mSession.mPid, aboveSystem, reason);

        if (timeout >= 0) {

            return timeout * 1000000L; //5s

        }

    }

    return 0;

}



AppWindowToken.java

boolean keyDispatchingTimedOut(String reason, int windowPid) {

        return mActivityRecord != null &&

        mActivityRecord.keyDispatchingTimedOut(reason, windowPid);

    }



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

发生 input 相关的 ANR 时在 system log 输出 ANR 信息,并且 tag 为 WindowManager。主要有 3 类 log:

  • Input event dispatching timed out sending to [windowState.mAttrs.getTitle()]

  • Input event dispatching timed out sending to application [appWindowToken.stringName)]

  • Input event dispatching timed out sending

3.6 DispatchingTimedOut


3.6.1 ActivityRecord.keyDispatchingTimedOut


final class ActivityRecord extends ConfigurationContainer {

    ......



    public boolean keyDispatchingTimedOut(String reason, int windowPid) {

        ActivityRecord anrActivity;

        WindowProcessController anrApp;

        boolean windowFromSameProcessAsActivity;

        synchronized (mAtmService.mGlobalLock) {

            anrActivity = getWaitingHistoryRecordLocked();

            anrApp = app;

            windowFromSameProcessAsActivity =

                    !hasProcess() || app.getPid() == windowPid || windowPid == -1;

        }



        if (windowFromSameProcessAsActivity) {

            return mAtmService.mAmInternal.inputDispatchingTimedOut(

            anrApp.mOwner, anrActivity.shortComponentName,

            anrActivity.appInfo, shortComponentName, app, false, reason);

        } else {

            // In this case another process added windows using

            // this activity token. So, we call the

            // generic service input dispatch timed out

            // method so that the right process is blamed.

            return mAtmService.mAmInternal.inputDispatchingTimedOut(

                    windowPid, false /* aboveSystem */, reason) < 0;

        }

    }

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

### 3.6.2 AMS.keyDispatchingTimedOut


long inputDispatchingTimedOut(int pid, final boolean aboveSystem,

    String reason) {

        if (checkCallingPermission(FILTER_EVENTS) !=

            PackageManager.PERMISSION_GRANTED) {

 throw new SecurityException("Requires permission " + FILTER_EVENTS);

        }

        ProcessRecord proc;

        long timeout;

        synchronized (this) {

            synchronized (mPidsSelfLocked) {

                proc = mPidsSelfLocked.get(pid);//根据pid查看进程record

            }

            //超时为KEY_DISPATCHING_TIMEOUT,即timeout = 5s

            timeout = proc != null ?

            proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;

        }



        if (inputDispatchingTimedOut(proc, null, null, null,

            null, aboveSystem, reason)) {

            return -1;

        }

        return timeout;

}





boolean inputDispatchingTimedOut(ProcessRecord proc,

    String activityShortComponentName, ApplicationInfo aInfo,

    String parentShortComponentName, WindowProcessController parentProcess,

    boolean aboveSystem, String reason) {

        if (checkCallingPermission(FILTER_EVENTS) !=

            PackageManager.PERMISSION_GRANTED) {

            throw new SecurityException("Requires permission " + FILTER_EVENTS);

        }



        final String annotation;

        if (reason == null) {

            annotation = "Input dispatching timed out";

        } else {

            annotation = "Input dispatching timed out (" + reason + ")";

        }



        if (proc != null) {

            synchronized (this) {

                if (proc.isDebugging()) {

                    return false;

                }



                if (proc.getActiveInstrumentation() != null) {

                    Bundle info = new Bundle();

                    info.putString("shortMsg", "keyDispatchingTimedOut");

                    info.putString("longMsg", annotation);

                    finishInstrumentationLocked(

                    proc, Activity.RESULT_CANCELED, info);

                    return true;

                }

            }

            proc.appNotResponding(activityShortComponentName, aInfo,

                    parentShortComponentName, parentProcess,

                    aboveSystem, annotation);

        }

        return true;

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

appNotResponding 会输出现场的重要进程的 trace 等信息。再回到【小节3.2】处理完 ANR 后再调用 resumeAfterTargetsNotReadyTimeoutLocked。

3.7 resumeAfterTargetsNotReadyTimeoutLocked


InputDispatcher.cpp


void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(

    nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {

    if (newTimeout > 0) {

        //超时时间增加5s

        mInputTargetWaitTimeoutTime = now() + newTimeout;

    } else {

        // Give up.

        mInputTargetWaitTimeoutExpired = true;



        // Input state will not be realistic.  Mark it out of sync.

        if (inputChannel.get()) {

            ssize_t connectionIndex =

            getConnectionIndexLocked(inputChannel);

            if (connectionIndex >= 0) {

                sp<Connection> connection =

                mConnectionsByFd.valueAt(connectionIndex);

                sp<IBinder> token = connection->inputChannel->getToken();



                if (token != nullptr) {

                    removeWindowByTokenLocked(token);

                }



                if (connection->status == Connection::STATUS_NORMAL) {

                    CancelationOptions options(

                    CancelationOptions::CANCEL_ALL_EVENTS,

                    "application not responding");

         synthesizeCancelationEventsForConnectionLocked(connection, options);

                }

            }

        }

    }

}



  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

总结

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

nnectionIndex);

            sp<IBinder> token = connection->inputChannel->getToken();



            if (token != nullptr) {

                removeWindowByTokenLocked(token);

            }



            if (connection->status == Connection::STATUS_NORMAL) {

                CancelationOptions options(

                CancelationOptions::CANCEL_ALL_EVENTS,

                "application not responding");

     synthesizeCancelationEventsForConnectionLocked(connection, options);

            }

        }

    }

}

}




*   1

    

*   2

    

*   3

    

*   4

    

*   5

    

*   6

    

*   7

    

*   8

    

*   9

    

*   10

    

*   11

    

*   12

    

*   13

    

*   14

    

*   15

    

*   16

    

*   17

    

*   18

    

*   19

    

*   20

    

*   21

    



# **总结**

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,**从来都是我们去适应环境,而不是环境来适应我们!**

这里附上上述的技术体系图相关的几十套**腾讯、头条、阿里、美团等公司20年的面试题**,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。

**相信它会给大家带来很多收获:**
[外链图片转存中...(img-nKoZA3z1-1714998471321)]

[外链图片转存中...(img-BOvkSFlt-1714998471321)]


> 当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值