二十四、App崩溃那些事

概述

App的崩溃率 是衡量一个app好坏的通用标准,崩溃率低不一定是好app,但是崩溃率高,我卸载它的几率也很高。

安卓系统会自发输出App的崩溃日志,这些日志大概可以分为两类:

  • JVM异常堆栈信息
  • native代码崩溃日志

JVM异常堆栈信息

也就是指的由于java代码的问题导致的崩溃。

Java中的异常分为两类,检查时异常非检查时异常

检查时异常

androidStudio等编译器能够主动提示你,这段代码中抛出了一个异常,如果你不去捕获并处理的话,在运行时可能会引起崩溃。 比如我们用到了 IO流,却没有捕获 IOException,那么就可能在运行时引起崩溃,但是,你不去捕获,也能正常编译安装。

非检查时异常

包括 error运行时异常, 这是两个不同的概念。 error指的是 JVM内部发生的错误,这些错误可能是内存问题引起的(比如 OOM内存溢出)。运行时异常,则是 程序运行时期,由于 JAVA代码的错误,在运行app时发生的异常(比如 空指针异常)。

我们主要能够处理的就是这种运行时异常,由于它是在JVM中运行时出现的,所以有一个通用的处理方式,这也是 JVM允许的异常捕获机制。

UncaughtExceptionHandler,我们可以通过它记录异常堆栈信息,并且 给出更友好的错误提示。

下面是一个简单案例,使用toast新页面 两种方式改善崩溃的错误提示

举个例子

定义核心异常处理类

java 复制代码
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import android.os.Process;
import android.widget.Toast;

import androidx.annotation.NonNull;

public class MyCrashHandler implements Thread.UncaughtExceptionHandler {

    private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;

    private Context mContext;

    private static volatile MyCrashHandler instance;

    private int tipType = 0; // 1 toast方式提示,2 弹出新页面的方式提示

    private MyCrashHandler() {
        // 私有构造函数,防止外部实例化
    }

    public static MyCrashHandler getInstance() {
        if (instance == null) {
            synchronized (MyCrashHandler.class) {
                if (instance == null) {
                    instance = new MyCrashHandler();
                }
            }
        }
        return instance;
    }

    public void init(Context context) {
        this.mContext = context;
        mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();

        Thread.setDefaultUncaughtExceptionHandler(this);
    }


    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {

        if (tipType == 0) {
            startCrashActivity(t, e);
        } else {
            handleUncaughtException(e);
        }
    }


    private void handleUncaughtException(Throwable t) {
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "应用发生了异常" + t.toString(), Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();

        // 延时一段时间后,退出应用
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 退出应用
        System.exit(1);
    }

    private void startCrashActivity(@NonNull Thread t, @NonNull Throwable e) {
        Intent intent = new Intent(mContext, ExceptionActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);

        if (mDefaultExceptionHandler != null) {
            mDefaultExceptionHandler.uncaughtException(t, e);
        } else {
            Process.killProcess(Process.myPid());
            System.exit(1);
        }
    }

}

注册到 application中

java 复制代码
public class MyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        MyCrashHandler.getInstance().init(this);
    }

}

主页面

java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_fake_crash).setOnClickListener(v -> {
            throw new NullPointerException(" 这里有个空指针异常!!!");
        });
    }
}
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">


    <Button
        android:id="@+id/btn_fake_crash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_blue_light"
        android:padding="10dp"
        android:text="触发空指针崩溃" />
</LinearLayout>

专用异常页面

java 复制代码
public class ExceptionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exception);
    }
}
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">


    <Button
        android:id="@+id/btn_fake_crash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_blue_light"
        android:padding="10dp"
        android:text="触发空指针崩溃" />
</LinearLayout>

native崩溃

线上崩溃的处理

未完待续。

相关推荐
QQ1__81151751523 分钟前
Spring boot名城小区物业管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
钛态24 分钟前
前端微前端架构:大项目的救命稻草还是自找麻烦?
前端·vue·react·web
一粒黑子26 分钟前
【实战解析】阿里开源 PageAgent:纯前端 GUI Agent,一行JS让网页支持自然语言操控
前端·javascript·开源
独角鲸网络安全实验室27 分钟前
2026微信小程序抓包全解析:从实操落地到合规风控,解锁前端调试新范式
前端·微信小程序·小程序·抓包·系统代理绕过·https证书严格校验·进程隔离
紫微AI28 分钟前
前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了
前端·人工智能·typescript
GISer_Jing28 分钟前
AI前端(From豆包)
前端·aigc·ai编程
IT枫斗者28 分钟前
前端部署后如何判断“页面是不是最新”?一套可落地的版本检测方案(适配 Vite/Vue/React/任意 SPA)
前端·javascript·vue.js·react.js·架构·bug
测试修炼手册28 分钟前
[测试技术] 深入理解 JSON Web Token (JWT)
前端·json
AI老李30 分钟前
2026 年 Web 前端开发的 8 个趋势!
前端
里欧跑得慢32 分钟前
15. Web可访问性最佳实践:让每个用户都能平等访问
前端·css·flutter·web