日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 打造异常崩溃捕获工具

發布時間:2024/9/30 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 打造异常崩溃捕获工具 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/112476811
本文出自【趙彥軍的博客】

文章目錄

    • 前言
    • Thread.dumpStack()
    • 如何把線程堆棧日志保存到文件
    • 如何捕捉Crash
    • 異常傳遞
    • 驚喜
    • 擴展設備信息

前言

因為疫情原因,今年的年會取消了,對于這個年會期待已久,心里還是有點失落。疫情當前,祝福所有人平安。

本文所有代碼示例都上傳至:https://github.com/zyj1609wz/AndroidCrash

Thread.dumpStack()

打印當前線程調用堆棧, 這個在調試時特別好用,舉例如下:

Util.java

public class Util {public static void print(){Thread.dumpStack();} }

MainActivity.java

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Util.print()} }

效果如下:

看到這個日志,你想到什么? 肯定是想到崩潰日志,是吧?

線程堆棧日志清晰的標明了當前發生的類及其行號,更重要的是顯示了方法調用路徑。這個很重要,對于排查問題,調試項目提供了很好的幫助。

下次遇到問題需要調試時,可以試試這個方法,很有用?

如何把線程堆棧日志保存到文件

我們先看看 Thread.dumpStack() 源碼


很簡單,其實就是調用了 Throwable 的 printStackTrace 方法。

除此之外, Throwable 還有一個方法,允許外部傳入一個 PrintWriter


完整的代碼如下:

package com.cootek.remoteapp;import android.content.Context; import android.os.Environment;import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.Format; import java.text.SimpleDateFormat; import java.util.Locale;/*** @author yanjun.zhao* @time 2021/1/11 4:44 PM* @desc*/ public class Util {private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());/*** 打印當前線程堆棧,保存到本地文件** @param con*/public static void print(Context con) {Context context = con.getApplicationContext();String fileName = getFileDir(context) + FORMAT.format(System.currentTimeMillis()) + ".txt";if (createOrExistsFile(fileName)) {PrintWriter pw = null;try {pw = new PrintWriter(new FileWriter(fileName, false));new Exception("Stack trace").printStackTrace(pw);} catch (IOException e) {e.printStackTrace();} finally {if (pw != null) {pw.close();}}}}/*** 創建文件* @param filePath* @return*/private static boolean createOrExistsFile(String filePath) {File file = new File(filePath);if (file.exists()) {return file.isFile();}if (!createOrExistsDir(file.getParentFile())) {return false;}try {return file.createNewFile();} catch (IOException e) {e.printStackTrace();return false;}}private static boolean createOrExistsDir(File file) {return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());}/*** 獲取堆棧日志存儲目錄** @param context* @return*/private static String getFileDir(Context context) {if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())&& context.getExternalCacheDir() != null) {return context.getExternalCacheDir() + File.separator + "crash" + File.separator;} else {return context.getCacheDir() + File.separator + "crash" + File.separator;}} }

MainActivity 如下:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Util.print(this)} }

允許起來,看一下效果:


可以看到文件已經寫入了,打開看看 :

很完美啊 !!!

如何捕捉Crash

沒有 try…catch 住的異常,即 Uncaught 異常,都會導致應用程序崩潰。那么面對崩潰,我們是否可以做些什么呢?比如程序退出前,彈出個性化對話框,而不是默認的強制關閉對話框,或者彈出一個提示框安慰一下用戶,甚至重啟應用程序等。

其實Java提供了一個接口給我們,可以完成這些,這就是 UncaughtExceptionHandler,該接口含有一個純虛函數:public abstract void uncaughtException (Thread thread, Throwableex)。

Uncaught 異常發生時會終止線程,此時,系統便會通知 UncaughtExceptionHandler ,告訴它被終止的線程以及對應的異常,然后便會調用 uncaughtException 函數。如果該 handler 沒有被顯式設置,則會調用對應線程組的默認 handler 。如果我們要捕獲該異常,必須實現我們自己的handler,并通過以下函數進行設置:

public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)

實現自定義的 handler,只需要繼承UncaughtExceptionHandler該接口,并實現uncaughtException方法即可。

package com.cootek.remoteapp;import android.content.Context; import android.os.Environment;import androidx.annotation.NonNull;import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.Format; import java.text.SimpleDateFormat; import java.util.Locale;/*** @author yanjun.zhao* @time 2021/1/11 4:44 PM* @desc*/ public class CrashUtil implements Thread.UncaughtExceptionHandler {private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());private Context mContext;private static CrashUtil INSTANCE = new CrashUtil();/*** 保證只有一個CrashHandler實例*/private CrashUtil() {}/*** 獲取CrashHandler實例 ,單例模式*/public static CrashUtil getInstance() {return INSTANCE;}public void init(Context context) {this.mContext = context.getApplicationContext();//這一句,至關重要,一定要設置 Thread.setDefaultUncaughtExceptionHandler(this);}/*** 打印當前線程堆棧,保存到本地文件*/public void print(Throwable throwable) {String fileName = getFileDir(mContext) + FORMAT.format(System.currentTimeMillis()) + ".txt";if (createOrExistsFile(fileName)) {PrintWriter pw = null;try {pw = new PrintWriter(new FileWriter(fileName, false));throwable.printStackTrace(pw);} catch (IOException ioException) {} finally {if (pw != null) {pw.close();}}}}/*** 創建文件** @param filePath* @return*/private static boolean createOrExistsFile(String filePath) {File file = new File(filePath);if (file.exists()) {return file.isFile();}if (!createOrExistsDir(file.getParentFile())) {return false;}try {return file.createNewFile();} catch (IOException e) {e.printStackTrace();return false;}}private static boolean createOrExistsDir(File file) {return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());}/*** 獲取堆棧日志存儲目錄** @param context* @return*/private static String getFileDir(Context context) {if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())&& context.getExternalCacheDir() != null) {return context.getExternalCacheDir() + File.separator + "crash" + File.separator;} else {return context.getCacheDir() + File.separator + "crash" + File.separator;}}@Overridepublic void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {print(e);} }

MyApp 代碼:

/*** @author yanjun.zhao* @time 2021/1/11 7:52 PM* @desc*/ public class MyApp : Application() {override fun onCreate() {super.onCreate()CrashUtil.getInstance().init(this)} }

主要代碼,我們就寫完了,下面我們來測試一下:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewById<TextView>(R.id.bt).setOnClickListener {//制造一個crash5 / 0}} }

允許起來,點擊 button , 人為制造一個 crash ,發現 Android 應用程序沒有崩潰,再點開crash 日志目錄,發現已經生成了日志,打開看看:

異常傳遞

在上面的例子中,我們人為的制造了一個 crash , 并且成功的捕捉了,把 crash 日志寫入本地文件。

一個直觀的感覺是:app 不會崩潰了。

但是也有一個問題,其他 crash 捕捉器就捕捉不到了,比如 :bugly 。如何才能解決這個問題。

第一步,在 Thread.setDefaultUncaughtExceptionHandler 之前,先獲取 defaultUncaughtExceptionHandler

public void init(Context context) {this.mContext = context.getApplicationContext();defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this); }

在處理異常的地方,先處理自己的邏輯,然后把異常向后傳遞

@Override public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {//優先處理自己的邏輯,把crash日志存起來print(e);if (defaultUncaughtExceptionHandler != null) {//如果原來的 Thread 有自己的 handler , 就把 crash 傳遞下去,//比如:如果集成了bugly , 那就傳給bugly 處理defaultUncaughtExceptionHandler.uncaughtException(t, e);} else {} }

完整的 CrashUtil 類如下:

package com.cootek.remoteapp;import android.content.Context; import android.os.Environment;import androidx.annotation.NonNull;import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.Format; import java.text.SimpleDateFormat; import java.util.Locale;/*** @author yanjun.zhao* @time 2021/1/11 4:44 PM* @desc*/ public class CrashUtil implements Thread.UncaughtExceptionHandler {private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());private Context mContext;private static CrashUtil INSTANCE = new CrashUtil();private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;/*** 保證只有一個CrashHandler實例*/private CrashUtil() {}/*** 獲取CrashHandler實例 ,單例模式*/public static CrashUtil getInstance() {return INSTANCE;}public void init(Context context) {this.mContext = context.getApplicationContext();defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this);}/*** 打印當前線程堆棧,保存到本地文件*/public void print(Throwable throwable) {String fileName = getFileDir(mContext) + FORMAT.format(System.currentTimeMillis()) + ".txt";if (createOrExistsFile(fileName)) {PrintWriter pw = null;try {pw = new PrintWriter(new FileWriter(fileName, false));throwable.printStackTrace(pw);} catch (IOException ioException) {} finally {if (pw != null) {pw.close();}}}}/*** 創建文件** @param filePath* @return*/private static boolean createOrExistsFile(String filePath) {File file = new File(filePath);if (file.exists()) {return file.isFile();}if (!createOrExistsDir(file.getParentFile())) {return false;}try {return file.createNewFile();} catch (IOException e) {e.printStackTrace();return false;}}private static boolean createOrExistsDir(File file) {return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());}/*** 獲取堆棧日志存儲目錄** @param context* @return*/private static String getFileDir(Context context) {if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())&& context.getExternalCacheDir() != null) {return context.getExternalCacheDir() + File.separator + "crash" + File.separator;} else {return context.getCacheDir() + File.separator + "crash" + File.separator;}}@Overridepublic void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {//優先處理自己的邏輯,把crash日志存起來print(e);if (defaultUncaughtExceptionHandler != null) {//如果原來的 Thread 有自己的 handler , 就把 crash 傳遞下去,//比如:如果集成了bugly , 那就傳給bugly 處理defaultUncaughtExceptionHandler.uncaughtException(t, e);} else {}} }

這樣就同時兼容了 bugly 等 crash捕捉工具。

驚喜

最近看到一個很出名的異常捕獲工具,翻了它的源碼,發現和我的做法一致,因此可以證明,我的做法沒有問題。下面截一個圖給大家看看,這個工具是怎么處理的?

擴展設備信息

像 bugly 一樣,每一個 crash日志都會包含設備信息,app 版本號等。其實這個也很簡單,把設備信息寫入文件就行了,我在 github 上已經完善了,這里就不展示了。

這里展示一個完整的 crash日志:

總結

以上是生活随笔為你收集整理的Android 打造异常崩溃捕获工具的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久精品2| 免费的av网址 | 精品国产精品 | 日韩激情视频网站 | 日韩五码在线 | 天堂色av | 想要视频在线 | 国产亚洲美女精品久久久2020 | 久久sp| 国产乡下妇女三片 | 日本黄a三级三级三级 | 老司机在线观看视频 | 岳睡了我中文字幕日本 | 91免费观看视频在线 | 国产精品suv一区二区69 | 91插插插影库永久免费 | 亚洲性视频在线 | 国产日韩欧美在线 | 天天爽天天做 | 免费成年人视频在线观看 | 欧美超碰在线观看 | 日本裸体视频 | 亚洲精品视频免费看 | 91免费看 | 精人妻无码一区二区三区 | 中文字幕一区二区三区免费视频 | 亚洲第一二区 | 日日天天| 色综合久久久久久久 | 亚洲黄色精品 | 在线视频免费观看 | 涩涩视频免费看 | 国产一区久久久 | 亚洲熟妇av日韩熟妇在线 | 色图综合网 | 日韩图片区 | 日韩视频在线观看 | 潘金莲一级淫片免费放动漫 | 亚洲国产视频网站 | 久久在线电影 | 日韩免费福利视频 | 国产男女猛烈无遮挡a片漫画 | 中文字字幕一区二区三区四区五区 | 精品国产av 无码一区二区三区 | 天堂av2021| 名人明星三级videos | 国产激情视频在线播放 | 欧美少妇诱惑 | 亚洲国产日韩在线一区 | 久久成人毛片 | www.香蕉视频| 国产自产自拍 | 欧美一区二区三区久久 | 男女视频在线免费观看 | 动漫美女露胸网站 | 一眉道姑 | 麻豆网站在线 | 欧美日韩黄色一级片 | bt男人天堂 | 天天射寡妇射 | 亚洲日本激情 | 国产sm调教一区二区 | 婷婷综合激情网 | 一级看片免费视频 | 久久综合免费 | 色xxxxxx| 里番精品3d一二三区 | 国产精品一区二区免费视频 | 国产农村熟妇videos | 爽爽窝窝午夜精品一区二区 | 亚洲美女自拍偷拍 | 国产美女极度色诱视频www | 久久久久亚洲色欲AV无码网站 | 九九热视频在线免费观看 | 日本狠狠爱 | 日韩欧美高清一区 | 欧美日韩一二区 | 国产福利在线导航 | 美国一级黄色大片 | 久久久精品免费看 | hs网站在线观看 | 精品人妻一区二区三区四区不卡 | 久久高潮视频 | 丰满人妻一区二区三区免费视频棣 | 青青青青在线 | 日本精品一区二区三区视频 | 久久久久国产精品一区 | 中文字幕免费播放 | 国产91亚洲精品 | 成人四色 | 人人插插 | 国产不卡在线播放 | 国产在线成人 | 午夜性生活片 | 久久久综合视频 | 最新黄色av | 狂野欧美性猛交xxⅹ李丽珍 | 久草av在线播放 | 长篇h版少妇沉沦交换 |