Android 捕获全局异常CrashHandler,防止异常闪退,记录异常日志

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 捕获全局异常CrashHandler,防止异常闪退,记录异常日志相关的知识,希望对你有一定的参考价值。

参考技术A import android.content.Context;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import android.content.pm.PackageManager.NameNotFoundException;

import android.os.Build;

import android.os.Looper;

import android.widget.Toast;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.lang.Thread.UncaughtExceptionHandler;

import java.lang.reflect.Field;

import java.util.HashMap;

import java.util.Map;

/**

* UncaughtException handler class

*

*/

public class CrashHandler implements UncaughtExceptionHandler

public static final String TAG = "CrashHandler";

public static final String PROGRAM_BROKEN_ACTION = "com.teligen.wccp.PROGRAM_BROKEN";

private UncaughtExceptionHandler mDefaultHandler;

private static CrashHandler instance = new CrashHandler();

private Context mContext;

private Class<?> mainActivityClass;

private Map<String, String> infos = new HashMap<String, String>();

private CrashHandler()



public static CrashHandler getInstance()

return instance;



public void init(Context context, Class<?> activityClass)

mContext = context;

this.setMainActivityClass(activityClass);

mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

Thread.setDefaultUncaughtExceptionHandler(this);



@Override

public void uncaughtException(Thread thread, Throwable ex)

if (!handleException(ex) && mDefaultHandler != null)

mDefaultHandler.uncaughtException(thread, ex);

else

System.out.println("uncaughtException--->" + ex.getMessage());

// Log.e(TAG, ex.getMessage());

logError(ex);

try

Thread.sleep(3000);

catch (InterruptedException e)

// Log.e("debug", "error:", e);



exitApp();





private boolean handleException(Throwable ex)

if (ex == null)

return false;



new Thread(new Runnable()

@Override

public void run()

Looper.prepare();

Toast.makeText(mContext.getApplicationContext(),

"unknown exception and exiting...Please checking logs in sd card!", Toast.LENGTH_LONG).show();

Looper.loop();



).start();

collectDeviceInfo(mContext.getApplicationContext());

logError(ex);

return true;



private void exitApp()

android.os.Process.killProcess(android.os.Process.myPid());

System.exit(0);



public void collectDeviceInfo(Context ctx)

try

PackageManager pm = ctx.getPackageManager();

PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),

PackageManager.GET_ACTIVITIES);

if (pi != null)

String versionName = pi.versionName == null ? "null"

: pi.versionName;

String versionCode = pi.versionCode + "";

infos.put("versionName", versionName);

infos.put("versionCode", versionCode);



catch (NameNotFoundException e)

e.printStackTrace();



Field[] fields = Build.class.getDeclaredFields();

for (Field field : fields)

try

field.setAccessible(true);

infos.put(field.getName(), field.get(null).toString());

catch (Exception e)







private void logError(Throwable ex)

StringBuffer sb = new StringBuffer();

for (Map.Entry<String, String> entry : infos.entrySet())

String key = entry.getKey();

String value = entry.getValue();

sb.append(key + "=" + value + "\n");



int num = ex.getStackTrace().length;

for (int i=0;i<num;i++)

sb.append(ex.getStackTrace()[i].toString());

sb.append("\n");



File file = new File(filePath+"/log.txt");

FileOutputStream fos = null;

try

fos = new FileOutputStream(file);

fos.write((sb.toString()+"exception:"+ex.getLocalizedMessage()).getBytes());

catch (FileNotFoundException e)

e.printStackTrace();

catch (IOException e)

e.printStackTrace();

finally

try

fos.close();

catch (IOException e)

e.printStackTrace();







public Class<?> getMainActivityClass()

return mainActivityClass;



public void setMainActivityClass(Class<?> mainActivityClass)

this.mainActivityClass = mainActivityClass;





filePath是记录日志的路径

在Applicaton中初始化

@Override

public void onCreate()

    super.onCreate();

    CrashHandler mCrashHandler = CrashHandler.getInstance();

    mCrashHandler.init(getApplicationContext(), getClass());

    initFile();

android 全局异常捕获,防止崩溃发生

主类

/**
 * 用途:全局异常拦截
 * <p>
 * 作者:mjSoftKing
 */
public class NeverCrash 

    private final static String TAG = NeverCrash.class.getSimpleName();
    private final static NeverCrash INSTANCE = new NeverCrash();

    private boolean debugMode;
    private MainCrashHandler mainCrashHandler;
    private UncaughtCrashHandler uncaughtCrashHandler;

    private NeverCrash() 
    

    public static NeverCrash getInstance() 
        return INSTANCE;
    

    private synchronized MainCrashHandler getMainCrashHandler() 
        if (null == mainCrashHandler) 
            mainCrashHandler = (t, e) -> 
            ;
        
        return mainCrashHandler;
    

    /**
     * 主线程发生异常时的回调,可用于打印日志文件
     * <p>
     * 注意跨线程操作的可能
     */
    public NeverCrash setMainCrashHandler(MainCrashHandler mainCrashHandler) 
        this.mainCrashHandler = mainCrashHandler;
        return this;
    

    private synchronized UncaughtCrashHandler getUncaughtCrashHandler() 
        if (null == uncaughtCrashHandler) 
            uncaughtCrashHandler = (t, e) -> 
            ;
        
        return uncaughtCrashHandler;
    

    /**
     * 子线程发生异常时的回调,可用于打印日志文件
     * <p>
     * 注意跨线程操作的可能
     */
    public NeverCrash setUncaughtCrashHandler(UncaughtCrashHandler uncaughtCrashHandler) 
        this.uncaughtCrashHandler = uncaughtCrashHandler;
        return this;
    

    private boolean isDebugMode() 
        return debugMode;
    

    /**
     * debug模式,会打印log日志,且toast提醒发生异常,反之则都没有
     */
    public NeverCrash setDebugMode(boolean debugMode) 
        this.debugMode = debugMode;
        return this;
    

    /**
     * 完成监听异常的注册
     * @param application application
     */
    public void register(Application application) 
        //主线程异常拦截
        new Handler(Looper.getMainLooper()).post(() -> 
            while (true) 
                try 
                    Looper.loop();
                 catch (Throwable e) 
                    if (isDebugMode()) 
                        Log.e(TAG, "未捕获的主线程异常行为", e);
                        new Handler(Looper.getMainLooper()).post(() ->
                                Toast.makeText(application, "主线程发生异常,请查看控制台日志!\\n此提醒和控制台打印仅在debug版本下有效!", Toast.LENGTH_LONG).show());
                    
                    getMainCrashHandler().mainException(Looper.getMainLooper().getThread(), e);
                
            
        );

        //子线程异常拦截
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> 
            if (isDebugMode()) 
                Log.e(TAG, "未捕获的子线程异常行为", e);
                new Handler(Looper.getMainLooper()).post(() ->
                        Toast.makeText(application, "子线程发生异常,请查看控制台日志!\\n此提醒和控制台打印仅在debug版本下有效!", Toast.LENGTH_LONG).show());
            
            getUncaughtCrashHandler().uncaughtException(t, e);
        );
    

    public interface MainCrashHandler 
        void mainException(Thread t, Throwable e);
    

    public interface UncaughtCrashHandler 
        void uncaughtException(Thread t, Throwable e);
    

application下使用

/**
 * 用途:
 * <p>
 * 作者:mjSoftKing
 * 时间:2021/04/26
 */
public class App extends Application 

    private final static String TAG = App.class.getSimpleName();

    @Override
    public void onCreate() 
        super.onCreate();
        NeverCrash.getInstance()
                .setDebugMode(BuildConfig.DEBUG)
                .setMainCrashHandler((t, e) -> 
                    //todo 跨线程操作时注意线程调度回主线程操作
                    Log.e(TAG, "主线程异常");//此处log只是展示,当debug为true时,主类内部log会打印异常信息
                    //todo 此处做你的日志记录即可
                )
                .setUncaughtCrashHandler((t, e) -> 
                    //todo 跨线程操作时注意线程调度回主线程操作
                    Log.e(TAG, "子线程异常");//此处log只是展示,当debug为true时,主类内部log会打印异常信息
                    //todo 此处做你的日志记录即可
                )
                .register(this);

    

最后别忘了注册application类

<manifest ...>

    <application
        android:name=".App"
        ...>
        ...
    </application>

</manifest>

 

以上是关于Android 捕获全局异常CrashHandler,防止异常闪退,记录异常日志的主要内容,如果未能解决你的问题,请参考以下文章

Android设置全局异常捕获在baseActivity注册,才能捕获所有的activity的异常

在 Android 中设置全局未捕获异常处理程序的理想方法

android 全局异常捕获,防止崩溃发生

Android应用捕获全局异常自定义处理

全局捕获所有可能的android异常并重新加载应用程序

Kotlin 协程协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )