App为什么会crash?一篇文章带你探究根本原因 ,事情没有你想得那么简单!(2),2024年Android工作或更难找

initialized = true;

}

此处会给 Thread 设置一个 KillApplicationHandler 对象,我们可以看到这个 KillApplicationHandler 是实现了 Thread.UncaughtExceptionHandler 这个接口的,所以自然会重写 uncaughtException 方法。

public void uncaughtException(Thread t, Throwable e) {

try {

ensureLogging(t, e);

// Don’t re-enter – avoid infinite loops if crash-reporting crashes.

if (mCrashing) return;

mCrashing = true;

// Try to end profiling. If a profiler is running at this point, and we kill the

// process (below), the in-memory buffer will be lost. So try to stop, which will

// flush the buffer. (This makes method trace profiling useful to debug crashes.)

if (ActivityThread.currentActivityThread() != null) {

ActivityThread.currentActivityThread().stopProfiling();

}

// Bring up crash dialog, wait for it to be dismissed

ActivityManager.getService().handleApplicationCrash(

mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));

} catch (Throwable t2) {

if (t2 instanceof DeadObjectException) {

// System process is dead; ignore

} else {

try {

Clog_e(TAG, “Error reporting crash”, t2);

} catch (Throwable t3) {

// Even Clog_e() fails! Oh well.

}

}

} finally {

// Try everything to make sure this process goes away.

Process.killProcess(Process.myPid());

System.exit(10);

}

}

在代码的最后执行了 System.exit(10) ;这个方法就会直接干掉当前进程,也就是所谓的 App crash 了。

所以我们一旦抛出异常,并且没有捕捉的话,程序就会被强制干掉。

第二个问题:能否让 App 不要 crash

答案自然是肯定的,我们刚才在看代码的时候也看到下面这段代码:

public UncaughtExceptionHandler getUncaughtExceptionHandler() {

//可以看到当uncaughtExceptionHandler没有赋值的时候,会返回ThreadGroup对象

return uncaughtExceptionHandler != null ?

uncaughtExceptionHandler : group;

}

只有在我们没有设置 UncaughtExceptionHandler 的时候,才会调用 defaultUncaughtExceptionHandler 对象,所以自然而然的就想到了实现这个类,然后在这里面做相应的处理。

说干就干试试吧:

我们先试一下主动抛出异常的效果吧,先是在 MainActivity 里面放置一个 Button,让它点击可以主动抛出异常:

package com.netease.demo;

import android.os.Bundle;

import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

public void click(View view) throws Exception{

throw new Exception(“主动抛出异常”);

}

}

来看一下执行效果:

不出意料程序崩溃了。。。

那我们接下来写一个 CrashHandler 的类实现了 Thread.UncaughtExceptionHandler 接口:

package com.netease.demo;

import android.util.Log;

// Created by chendanfeng on 2020-08-19.

public class CrashHandler implements Thread.UncaughtExceptionHandler {

private static final String TAG = “CrashHandler”;

@Override

public void uncaughtException(Thread t, Throwable e) {

Toast.makeText(MyApplication.sApp,"uncaughtException : " + e.getMessage(),Toast.LENGTH_SHORT).show();

}

}

然后在 MyApplication 里面对这个 Handler 进行设置:

package com.netease.demo;

import android.app.Application;

// Created by chendanfeng on 2020-08-19.

public class MyApplication extends Application {

public static MyApplication sApp;

@Override

public void onCreate() {

super.onCreate();

sApp = this;

Thread.currentThread().setUncaughtExceptionHandler(new CrashHandler());

}

}

然后再看下效果:

我们发现确实 App 已经不会 crash 了,但是又出现了另外一个问题,那就是 App 卡死在了这个界面,点击无效了。

那么这到底是怎么一回事呢?其实这也不难理解,我们的页面启动的入口是在 ActivityThread 的 main 方法:

public static void main(String[] args) {

…代码省略…

Looper.prepareMainLooper();

…代码省略…

Looper.loop();

throw new RuntimeException(“Main thread loop unexpectedly exited”);

}

在这里面进行初始化主线程的 Loop ,然后执行 loop 循环,我们知道 Looper 是用来循环遍历消息队列的,一旦消息队列中存在消息,那么就会执行里面的操作。整个 Android 系统就是基于事件驱动的,而事件主要就是基于 Looper 来获取的。所以如果这里一旦出现 crash,那么就直接会跳出整个 main 方法,自然 loop 循环也就跳出了,那么自然而然事件也就接收不到,更没法处理,所以整个 App 就会卡死在这里。

既然如此,那有没有其他办法可以保证 App 在抛出异常不 crash 的情况下,又能保证不会卡死呢?

既然 looper 是查询事件的核心类,那么我们是否可以不让跳出 loop 循环呢,乍一想好像没办法做到,我们没法给 loop 方法 try-catch 。但是我们可以给消息队列发送一个 loop 循环,然后给这个 loop 做一个 try-catch ,一旦外层的 loop 检测到这个事件,就会执行我们自己创建的 loop 循环,这样以后 App 内的所有事件都会在我们自己的 loop 循环中处理。一旦抛出异常,跳出 loop 循环以后,我们也可以在 loop 外层套一层 while 循环,让自己的 loop 再次工作。

还是一句老话**“Talk is cheap,show me the code”**,没有什么比代码验证来的更直接的:

package com.netease.demo;

import android.app.Application;

import android.os.Handler;

import android.os.Looper;

import android.widget.Toast;

// Created by chendanfeng on 2020-08-19.

public class MyApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

Handler handler = new Handler(getMainLooper());

handler.post(new Runnable() {

@Override

public void run() {

while(true){

try {

Looper.loop();

} catch (Exception e) {

Toast.makeText(MyApplication.this,“抛出了异常”,Toast.LENGTH_SHORT).show();

}

}

}

});

}

}

执行以下效果:

这样就解决了抛出异常导致 App crash 的问题了~

不过事情当然没有那么快就结束,这里给主线程的Looper 发送 loop 循环都是主线程操作的,那么子线程如果抛出异常怎么办呢,这么处理应该也是会 crash 吧,那就再做个实验吧:

package com.netease.demo;

import android.os.Bundle;

import android.view.View;

import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

public void click(View view) throws Exception{

new Thread(new Runnable() {

@Override

public void run() {

TextView tv = null;

tv.setText(“hello,word”);

}

}).start();

}

}

这段代码TextView没有初始化,然后看下效果:

没错,确实是直接crash的,那这个时候该怎么办呢?刚才说的**Thread.currentThread().setUncaughtExceptionHandler(new CrashHandler());**似乎也不行,这是设置当前 Thread 的方法,总不能给每个 Thread 都设置一个吧,这肯定不可取。不过 Thead 里面貌似还有个全局静态的 UncaughtExceptionHandler 对象被遗忘了

// null unless explicitly set

private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

ThreadGroup 里面最终会调用到他的方法,一开始在 RunTimeInit 里面初始化的。既然这样,那我们直接覆盖这个对象应该就可以了吧?那就试试吧:

package com.netease.demo;

import android.app.Application;

import android.os.Handler;

import android.os.Looper;

import android.widget.Toast;

// Created by chendanfeng on 2020-08-19.

public class MyApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

Handler handler = new Handler(getMainLooper());

handler.post(new Runnable() {

@Override

public void run() {

while(true){

try {

Looper.loop();

} catch (Exception e) {

Toast.makeText(MyApplication.this,“抛出了异常”,Toast.LENGTH_SHORT).show();

}

}

}

});

//代码其实也很简单,只需要这里加上这么一句话

Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());

}

}

另外,这里还稍微改造了一下 CrashHandler :
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

[外链图片转存中…(img-WzkG1Xck-1712451377917)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-5hTxkQlH-1712451377917)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果在测试过程中遇到app出现crash或者ANR,我这样处理: 1. 首先,我收集尽可能多的信息来描述crash或ANR发生的情况。这可能包括app的版本号、设备的型号、操作系统版本、运行的进程、以及在发生crash或ANR时用户正在做什么。 2. 然后,我检查crash日志或ANR报告,以了解crash或ANR是由哪个线程引起的,以及它们发生时的上下文。这些信息可以帮助我定位问题的根本原因。 3. 接下来,我尝试重现这个问题,以便于进一步分析。 4. 最后,我尝试解决这个问题。这可能包括修改代码来避免crash或ANR,或者调整app的配置以改善性能。 总之,在处理app出现crash或ANR的情况时,我尽可能收集信息、分析问题的原因,并到有效的解决方案。 ### 回答2: 在测试过程中,若遇到应用程序出现crash(崩溃)或者ANR(应用无响应),我采取以下步骤进行处理。 首先,我记录下相关信息,包括出现crash或ANR的具体操作步骤、系统版本、设备型号等,这些信息有助于后续分析和解决问题。 其次,我尝试重现问题,通过重新执行导致crash或ANR的步骤,验证问题是否可复现。如果可以复现,我进一步分析出错的原因。 对于crash,我查看应用崩溃时的日志信息,包括堆栈追踪,以确定哪一部分代码出现异常。可能的原因包括内存泄漏、空指针引用、并发冲突等。我使用调试工具或日志分析工具来帮助定位并解决问题。 对于ANR,我检查是否存在耗时操作阻塞主线程的情况,例如网络请求、磁盘访问等。如果存在这样的耗时操作,我尝试优化或重构代码,将其移至后台线程执行,以避免主线程阻塞导致ANR。 另外,我检查相关资源的释放情况,例如数据库连接、文件句柄等,确保资源在使用完毕后正确释放,避免资源泄漏。 最后,在定位到问题的原因后,我与开发人员和其他测试人员进行沟通,确保问题得到彻底解决,并进行相关的测试用例修复或添加,以防止类似问题再次出现。 总之,当遇到应用程序崩溃或无响应时,我积极记录信息、重现问题、分析原因、解决问题,并与相关人员合作确保问题修复。 ### 回答3: 在测试过程中遇到app出现crash或者ANR(Application Not Responding),我采取以下步骤进行处理。 首先,我记录下出现crash或者ANR的具体情况,包括时间、设备型号、操作步骤等信息,以便后续的分析和复现。 然后,我尝试复现这个问题,通过重复操作或者使用不同的设备进行测试,以确定问题的复现性和范围。如果能成功复现问题,就能好地到问题的根源。 接下来,我检查app的日志文件,查任何与crash或者ANR相关的错误信息或异常堆栈。这些信息常常提供有关问题的线索,指引我进一步定位问题的来源。 如果问题比较复杂或到明显的原因,我使用一些辅助工具来帮助我分析。比如使用Android Studio的调试功能,可以设置断点,逐步调试代码,以出问题发生的具体位置。 一旦到问题的根源,我联系开发团队,向他们提供详细的问题报告和复现步骤。同时,我与他们沟通,讨论是否需要提供多信息或者进行深入的调查。 最后,在修复问题后,我重新测试app,验证修复的有效性,并确保其他功能或模块没有受到负面影响。 总结而言,对于app出现crash或者ANR,我记录、复现、分析和修复问题。通过这些步骤,我能尽可能准确地到问题的根源,并与开发团队合作解决问题,以提升app的质量和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值