Android捕获全局异常代码实现

  做Android开发应用上线的时候,需要测试客户端稳定性,所以我们需要提前发现问题,收集更多异常信息,当Android客户端发生异常的时候,如何捕获异常信息呢,这里我们通过重写一个类,实现Thread.UncaughtExceptionHandler接口,重写uncaughtException方法,在uncaughtException方法中,可以把异常存到本地或上传服务端。

由于异常大多发生在在多线程环境中,子线程抛出的异常是不能用主线程中try….catch捕获。可以给线程设置UncaughtExceptionHandler,当出现异常时会回调UncaughtExceptionHandler中的uncaughtException(Thread t, Throwable e) 方法。设置可以为某个线程单独

thread.setUncaughtExceptionHandler(new DefalutUncaughtExceptionHandler());

当想给所有线程设置时使用:

thread.setDefaultUncaughtExceptionHandler(new DefalutUncaughtExceptionHandler());

t实现  UncaughtExceptionHandler中的 uncaughtException方法aughtEx

ceptionHandler(new DefalutUncaughtExceptionHandler())

@Override
public void uncaughtException(final Thread t, final Throwable e) {
	重写此方法做相关操作
}

自己实现一个异常检查的类,根据抛出的异常,将异常信息保存在sd卡中,并且提供了文件头部信息,显示应该版本号,机型,系统版本等相关属性:

package org.ss.ssdemo.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName CrashTool
 * @Description 异常工具类
 */

public class CrashTool {

    private static final String FILE_SEP = System.getProperty("file.separator");

    //----------------------------------------------------------------------------------------------
    private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());
    private static final String CRASH_HEAD;
    private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER;
    private static final UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER;
    private static Context mContext;
    private static String mCrashDirPath;
    private static String dir;
    private static String versionName;
    private static int versionCode;
    private static ExecutorService sExecutor;


    //显示异常日志的头部信息,比如设备名称,设备型号,系统版本,SDK版本
    static {
        CRASH_HEAD = "\n************* Crash Log Head ****************" +
                "\nDevice Manufacturer: " + Build.MANUFACTURER +// 设备厂商
                "\nDevice Model       : " + Build.MODEL +// 设备型号
                "\nAndroid Version    : " + Build.VERSION.RELEASE +// 系统版本
                "\nAndroid SDK        : " + Build.VERSION.SDK_INT +// SDK版本
                "\nApp VersionName    : " + versionName +
                "\nApp VersionCode    : " + versionCode +
                "\n************* Crash Log Head ****************\n\n";

        DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();

        UNCAUGHT_EXCEPTION_HANDLER = new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread t, final Throwable e) {
                //从操作系统中结束掉当前程序的进程
                if (e == null) {
                    android.os.Process.killProcess(android.os.Process.myPid());
                    System.exit(0);
                    return;
                }
                //根据时间生成一个文本,进行异常日志写入
                Date now = new Date(System.currentTimeMillis());
                String fileName = FORMAT.format(now) + ".txt";
                final String fullPath = (dir == null ? mCrashDirPath : dir) + fileName;
                if (!createOrExistsFile(fullPath)) {
                    return;
                }
                if (sExecutor == null) {
                    sExecutor = Executors.newSingleThreadExecutor();
                }
                sExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        PrintWriter pw = null;
                        try {
                            pw = new PrintWriter(new FileWriter(fullPath, false));
                            pw.write(CRASH_HEAD);
                            e.printStackTrace(pw);
                            Throwable cause = e.getCause();
                            while (cause != null) {
                                cause.printStackTrace(pw);
                                cause = cause.getCause();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            if (pw != null) {
                                pw.close();
                            }
                        }
                    }
                });
                if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
                    DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);
                }
            }
        };
    }

    /**
     * 初始化
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
     */
    public static void init(Context context) {
        mContext = context;
        try {
            PackageInfo pi = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
            if (pi != null) {
                versionName = pi.versionName;
                versionCode = pi.versionCode;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        init("");
    }

    /**
     * 初始化
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
     *
     * @param crashDir 崩溃文件存储目录
     */
    public static void init(@NonNull final File crashDir) {
        init(crashDir.getAbsolutePath());
    }

    /**
     * 初始化
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
     *
     * @param crashDir 崩溃文件存储目录
     */
    public static void init(final String crashDir) {
        versionName = getAppVersionName(mContext);
        versionCode = getAppVersionNo(mContext);
        if (isSpace(crashDir)) {
            dir = null;
        } else {
            dir = crashDir.endsWith(FILE_SEP) ? crashDir : crashDir + FILE_SEP;
        }

        try {
            mCrashDirPath = getRootPath() + File.separator + "ssdemo" + File.separator + "crash" + File.separator;
        } catch (Exception e) {
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                mCrashDirPath = mContext.getExternalCacheDir().getPath() + File.separator + "crash" + File.separator;
            } else {
                mCrashDirPath = mContext.getCacheDir().getPath() + File.separator + "crash" + File.separator;
            }
        }


        Thread.setDefaultUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
    }

    private static boolean createOrExistsFile(final 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(final File file) {
        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
    }

    private static boolean isSpace(final String s) {
        if (s == null) {
            return true;
        }
        for (int i = 0, len = s.length(); i < len; ++i) {
            if (!Character.isWhitespace(s.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static File getRootPath() {
        File path = null;
        if (sdCardIsAvailable()) {
            path = Environment.getExternalStorageDirectory();
        } else {
            path = Environment.getDataDirectory();
        }

        return path;
    }


    public static boolean sdCardIsAvailable() {
        if (Environment.getExternalStorageState().equals("mounted")) {
            File sd = new File(Environment.getExternalStorageDirectory().getPath());
            return sd.canWrite();
        } else {
            return false;
        }
    }

    /****
     * @Method getAppVersionName
     * @Description 获取当前应用版本名称
     * ***/
    public static String getAppVersionName(Context context) {
        PackageManager packageManager = context.getPackageManager();
        PackageInfo packInfo = null;

        try {
            packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException var4) {
            var4.printStackTrace();
        }

        String version = packInfo.versionName;
        return version;
    }


    /****
     * @Method getAppVersionNo
     * @Description 获取当前应用版本号
     * ***/
    public static int getAppVersionNo(Context context) {
        PackageManager packageManager = context.getPackageManager();
        PackageInfo packInfo = null;
        try {
            packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException var4) {
            var4.printStackTrace();
        }
        int version = packInfo.versionCode;
        return version;
    }
}

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值