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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/35ac600f0c0713dbad0b28faf31dd0c6.jpeg)
学习分享
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
[外链图片转存中…(img-WzkG1Xck-1712451377917)]
七大模块学习资料:如NDK模块开发、Android框架体系架构…
[外链图片转存中…(img-5hTxkQlH-1712451377917)]
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!