快速锁定 APP 崩溃问题~

作者:Android帅次
转载地址:https://juejin.cn/post/7018377536799244295

前言

从刚开始接触Android开发,第一次发版,遇到程序崩溃,那就一个慌张。好几年过去了,现在的听到程序崩溃?嗯,稍等我看看什么问题,然后该锁定该锁定该解决解决。

发版前减少bug、崩溃等,发版后遇到bug、崩溃也不要慌张,毕竟 bug不 会因为你的慌张而自动修复对吧?要以最快的速度解决(解决问题同样是能力的体现),并说明问题轻重,看看是直接发版还是坐等下次。同时,吸取教训避免同样问题发生。

今天咱们就聊聊Android程序闪退。一个应用的崩溃率高低,决定了这个应用的质量。

为了解决崩溃问题,Android 系统会输出各种相应的 log 日志,当然还各式各样的三方库,大程度上降低了工程师锁定崩溃问题的难度。

如果要给 crash 日志进行分类,可以分成 2 大类

  • JVM 异常(Exception)堆栈信息,如下:

  • native 代码崩溃日志,如下:

JVM 异常堆栈信息

Java 中异常(Exception)分两种:

  • 检查异常 checked Exception
  • 非检查异常 unchecked Exception

检查异常:就是在代码编译时期,Android Studio 就会提示代码有错误,无法通过编译,比如 InterruptedException。如果我们没有在代码中将这些异常 catch,而是直接抛出,最终也有可能导致程序崩溃。

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

非检查异常:包括 error 和运行时异常(RuntimeException),Android Studio 并不会在编译时期提示这些异常信息,而是在程序运行时期因为代码错误而直接导致程序崩溃,比如 OOM 或者空指针异常(NullPointerException)。

2021-09-13 11:50:27.327 19984-19984/com.scc.demo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.scc.demo,PID: 19984
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.scc.demo/com.scc.demo.actvitiy.HandlerActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        ...
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        at com.scc.demo.actvitiy.HandlerActivity.onCreate(HandlerActivity.java:41)
        at android.app.Activity.performCreate(Activity.java:8000)
        ...

Java 异常

对于上述两种异常我们都可以使用 UncaughtExceptionHandler 来进行捕获操作,它是 Thread 的一个内部接口,定义如下:

    public interface UncaughtExceptionHandler {
        /**
         * 当给定Thread由于给定的Throwable而终止时调用的方法。
         * 此方法抛出的任何异常都将被 Java 虚拟机忽略。
         * @param t Thread
         * @param e Throwable
         */
        void uncaughtException(Thread t,Throwable e);
    }

从官方对其介绍能够看出,对于传入的 Thread,如果因为"未捕获"异常而导致被终止,uncaughtException 则会被调用。我们可以借助它来间接捕获程序异常,并进行异常信息的记录工作,或者给出更友好的异常提示信息。

自定义异常处理类

  • 1.收集 crash 现场的相关信息,如当前 App 的版本信息,设备的相关信息以及异常信息。

  • 2.日志的记录工作(如保存在本地),等开发人员排查问题或等下次启动APP上传至服务器。

自定义异常处理类

自定义类实现 UncaughtExceptionHandler 接口,并实现 uncaughtException 方法:

public class SccExceptionHandler implements Thread.UncaughtExceptionHandler {
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    private static SccExceptionHandler sccExceptionHandler;
    private Context mContext;

    public static SccExceptionHandler getInstence() {
        if (sccExceptionHandler == null) {
            synchronized (SccExceptionHandler.class) {
                sccExceptionHandler = new SccExceptionHandler();
            }
        }
        return sccExceptionHandler;
    }

    public void init(Context context) {
        mContext = context;
        //系统默认未捕获异常handler
        //the default uncaught exception handler for all threads
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //将当前Handler设为系统默认
        Thread.setDefaultUncaughtExceptionHandler(this);

    }

    @Override
    public void uncaughtException(@NonNull @NotNull Thread t,@NonNull @NotNull Throwable e) {
        if (!handlerUncaughtException(e) && mDefaultHandler != null) {
            //注释1:系统处理
            mDefaultHandler.uncaughtException(t,e);
        } else {
            //注释2:自己处理
            Intent intent = new Intent(mContext,ImageViewActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mContext.startActivity(intent);
            //终止进程
            android.os.Process.killProcess(android.os.Process.myPid());
            //终止当前运行的 Java 虚拟机。
            //参数用作状态代码; 按照惯例,非零状态代码表示异常终止。
            System.exit(0);
        }
    }

    //处理程序未捕获异常
    private boolean handlerUncaughtException(Throwable e) {
        //1.收集 crash 现场的相关信息,如当前 App 的版本信息,设备的相关信息以及异常信息。
        //2.日志的记录工作(如保存在本地),等开发人员排查问题或等下次启动APP上传至服务器。
        return true;
        //不想处理 return false;
    }
}

注释1:在自定义异常处理类中需要持有线程默认异常处理类。这样做的目的是在自定义异常处理类无法处理或者处理异常失败时,还可以将异常交给系统做默认处理。

注释2:如果自定义异常处理类成功处理异常,需要进行页面跳转,或者将程序进程"杀死"。否则程序会一直卡死在崩溃界面,并弹出无响应对话框。

android.os.Process.myPid():返回此进程的标识符,可与 killProcess 和 sendSignal 一起使用。

android.os.Process.killProcess(android.os.Process.myPid()):使用给定的 PID 终止进程。 请注意,尽管此 API 允许我们根据其 PID 请求终止任何进程,但内核仍会对您实际能够终止的 PID 施加标准限制。 通常这意味着只有运行调用者的包/应用程序的进程和由该应用程序创建的任何其他进程; 共享一个通用 UID 的包也将能够杀死彼此的进程。

使用自定义异常处理类

SccExceptionHandler 定义好之后,就可以将其初始化,并将主线程注册到 SccExceptionHandler 中。如下:

public class SccApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        SccExceptionHandler.getInstence().init(this);
    }
}

native 异常

当程序中的 native 代码发生崩溃时,系统会在 /data/tombstones/ 目录下保存一份详细的崩溃日志信息。由于对 native 还不是很熟悉就不误导大家,感兴趣的自己玩玩。

对于程序崩溃信号机制的介绍,可以参考腾讯的这篇文章:Android 平台 Native 代码的崩溃捕获机制及实现

线上崩溃日志获取

上面介绍的 Java 和 Native 崩溃的捕获都是基于自己能够复现 bug 的前提下。但是对于线上的用户,这种操作方式是不太现实的。

对于大多数公司来说,针对线上版本,没有必要自己实现一个抓取 log 的平台系统。最快速的实现方式就是集成第三方 SDK。目前比较成熟,采用也比较多的就是腾讯的 Bugly。

Bugly

Bugly 基本能够满足线上版本捕获 crash 的所有需求,包括 Java 层和 Native 层的 crash 都可以获取相应的日志。并且每天 Bugly 都会邮件通知上一天的崩溃日志,方便测试和开发统计 bug 的分布以及崩溃率。

接入文档

异常概括

崩溃分析

程序崩溃分析这块我没做调整,这个是bugly自动抓取的。

错误分析

具体内容

这里我用来存放去服务端请求接口时的参数和返回的数据。,下面看看具体效果。

使用起来相当方便,而且错误还提供解决方案,美滋滋。

xCrash

xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。

xCrash 能在 app 进程崩溃或 ANR 时,在你指定的目录中生成一个 tombstone 文件(格式与安卓系统的 tombstone 文件类似)。

xCrash 已经在 爱奇艺 的不同平台(手机,平板,电视)的很多安卓 app(包括爱奇艺视频)中被使用了很多年。

xCrash传送门

Sentry

Sentry 是一项可帮助您实时监控和修复崩溃的服务。 服务器使用 Python,但它包含一个完整的 API,用于在任何应用程序中从任何语言发送事件。

Sentry传送门

XCrash 和 Sentry,这两者比 Bugly 好的地方就是除了自动拦截界面崩溃事件,还可以主动上报错误信息。

可以看出 XCrash 的使用更加灵活,工程师的掌控性更高。可以通过设置不同的过滤方式,针对性地上报相应的 crash 日志。并且在捕获到 crash 之后,可以加入自定义的操作,比如本地保存日志或者直接进行网络上传等。

另外:Sentry 还有一个好处就是可以通过设置过滤,来判断是否上报 crash 日志。这对于 SDK 的开发人员是很有用的。比如一些 SDK 的开发商只是想收集自身 SDK 引入的 crash,对于用户的其他操作导致的 crash 进行过滤,这种情况就可以考虑集成 Sentry。

Bugly 简单使用

感觉教程乱的可以自己去上文找Buyle文档自己集成,很简单的。

库文件导入

自动集成(推荐)

plugins {
    id 'com.android.application'
}
android {
    compileSdkVersion 30//项目的编译版本
    defaultConfig {
        applicationId "com.scc.demo"//包名
        minSdkVersion 23//最低的兼容的Android系统版本
        targetSdkVersion 30//目标版本,表示你在该Android系统版本已经做过充分的测试
        versionCode 1//版本号
        versionName "1.0.0"//版本名称
        ndk {
            abiFilters 'armeabi-v7a','arm64-v8a','x86'
            //运行环境,要上传Google Play必须兼容64位,这里仅兼容ARM架构
            //对于ARM架构,32 位库位于armeabi-v7a 中。64 位等效项是arm64-v8a。
            //对于x86体系结构,查找x86(用于 32 位)和 x86_64(用于 64 位)。
        }
    }
}

dependencies {
    implementation 'com.tencent.bugly:crashreport:3.4.4'
    //集成Bugly NDK时,需要同时集成Bugly SDK。
    implementation 'com.tencent.bugly:nativecrashreport:3.9.2'

}

注意:自动集成时会自动包含Bugly SO库,建议在Module的build.gradle文件中使用NDK的"abiFilter"配置,设置支持的SO库架构。

如果在添加"abiFilter"之后Android Studio出现以下提示:

NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.

则在项目根目录的gradle.properties文件中添加:

android.useDeprecatedNdk=true

参数配置

  • 在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />

注意:如果您的App需要上传到Google Play Store,您需要将READ_PHONE_STATE权限屏蔽掉或者移除,否则可能会被下架。

  • 请避免混淆Bugly,在Proguard混淆文件中增加以下配置:
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

初始化

public class SccApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //70594a1ff8 Bugly新建产品的 App ID
        CrashReport.initCrashReport(getApplicationContext(),"70594a1ff8",false);
    }
}

错误分析

设置

    private void setCrashReport(String url,String name,Map<String,String> params,String message) {
        try {
            if (params != null && !MStringUtils.isNullOrEmpty(url) && !MStringUtils.isNullOrEmpty(name) && !MStringUtils.isNullOrEmpty(params.toString()) && !MStringUtils.isNullOrEmpty(message)) {
                CrashReport.putUserData(AppGlobalUtils.getApplication(),"SccParams",params.toString());
                CrashReport.putUserData(AppGlobalUtils.getApplication(),"Data","LoginName-Scc001:" + message);
                CrashReport.postCatchedException(new RuntimeException(name + ":" + url + ":" + message));
            }
        } catch (Exception e) {
        }
    }

调用

        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("name","scc001");
        hashMap.put("pass","111111");
        String returnData = "哈哈哈哈哈";
        setCrashReport("loin/register","Main",hashMap,returnData);

效果

错误列表

错误详情

出错堆栈

跟踪数据

崩溃分析

这个不用咱自己设置,Bugly自动抓取,下面提供跟错误分析类似功能这里就不多描述了。

本文内容到这里就算结束了。希望能帮你快速锁定 bug 并解决,让应用更完美,让你的老板更放心,票票来的更多一些。


为了帮助到大家更好的掌握 Framework 底层与性能优化相关知识点,这准备了 Android11.0最新Framework解析 与 性能优化知识点汇总和Android 性能监控框架 的学习文档,中间记录了 AMS、PMS、WMS、Hander、Binder、启动优化、内存优化、UI优化……等知识点,可谓是很全面了,↓↓↓

有需要的可以复制下方链接,传送直达!!!
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE

内功心法不是一天两天就可以修炼出来的,而是需要每天的坚持,技术提升也是如此。所以最好的速成修炼方法就是每天学习一点,日积月累后就会发现自己进步的效果。

原文地址:https://blog.csdn.net/weixin_61845324

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


更新Android SDK到3.0版本时,遇到Failed to rename directory E:\android\tools to E:\android\temp\ToolPackage.old01问题,导致无法更新,出现该问题的原因是由于3.0版本与较早的sdk版本之间文件结构有冲突,解决
Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方
Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法。一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小。在Button的属性中有一个是drawable
Android中的&quot;Unable to start activity ComponentInfo&quot;的错误 最近在做一款音乐播放器的时候,然后在调试的过程中发现一直报这个错误&quot;Unable to start activity ComponentInfo&quot;,从字面
Android 关于长按back键退出应用程序的实现最近在做一个Android上的应用,碰到一个问题就是如何实现长按back键退出应用程序。在网上查找了很多资料,发现几乎没有这样的实现,大部分在处理时是双击back键来退出应用程序。参考了一下双击back键退出应用程序的代码,网上主流的一种方法是下面
android自带的时间选择器只能精确到分,但是对于某些应用要求选择的时间精确到秒级,此时只有自定义去实现这样的时间选择器了。下面介绍一个可以精确到秒级的时间选择器。 先上效果图: 下面是工程目录: 这个控件我也是用的别人的,好像是一个老外写的,com.wheel中的WheelView是滑动控件的主
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing
Android ListView的item背景色设置以及item点击无响应等相关问题 在Android开发中,listview控件是非常常用的控件,在大多数情况下,大家都会改掉listview的item默认的外观,下面讲解以下在使用listview时最常见的几个问题。1.如何改变item的背景色和按
如何向Android模拟器中导入含有中文名称的文件在进行Android开发的时候,如果需要向Android模拟器中导入文件进行测试,通过DDMS下手动导入或者在命令行下通过adb push命令是无法导入含有中文文件名的文件的。后来发现借用其他工具可以向模拟器中导入中文名称的文件,这个工具就是Ultr
Windows 下搭建Android开发环境一.下载并安装JDK版本要求JDK1.6+,下载JDK成功后进行安装,安装好后进行环境变量的配置【我的电脑】-——&gt;【属性】——&gt;【高级】 ——&gt;【环境变量】——&gt;【系统变量】中点击【新建】:变量名:CLASSPATH变量值:……
如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹出菜单,此时可以考虑用PopupWindow来实现。下面就来介绍一下如何使用PopupWindow实现一个弹出窗。 主Activity代码:public void onCreat
解决Android中的ERROR: the user data image is used by another emulator. aborting的方法 今天调试代码的时候,突然出现这个错误,折腾了很久没有解决。最后在google上找到了大家给出的两种解决方案,下面给出这两种方法的链接博客:ht
AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具