崩溃日志保存本地log,服务器上传

废话不多说,源码献上!

package com.anrongbc.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.anrongbc.login.LoginActivity;

import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局捕获异常
 * 当程序发生Uncaught异常的时候,有该类来接管程序,并记录错误日志
 * 文件地址:内置sd卡路径下“一键核Error" + 日期时间 + ".log”
 */
@SuppressLint("SimpleDateFormat")
public class CrashHandler_Ma {
    public static final String DEFAULT_FORMAT_DATE = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    //分隔符.
    public final static String FILE_EXTENSION_SEPARATOR = ".";
    public static String TAG = "MyCrash";
    // 系统默认的UncaughtException处理类
    private UncaughtExceptionHandler mDefaultHandler;

    private static CrashHandler_Ma instance = new CrashHandler_Ma();
    private Context mContext;

    // 用来存储设备信息和异常信息
    private Map infos = new HashMap();

    // 用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 保证只有一个CrashHandler实例
     */
    private CrashHandler_Ma() {
    }

    /**
     * 获取CrashHandler实例 ,单例模式
     */
    public static CrashHandler_Ma getInstance() {
        return instance;
    }

    public void init(Context context) {
        mContext = context;
        //文件保存天数
        autoClear(10);
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {
                Log.e(TAG, "uncaughtException: ", ex);
                try {
                    // 使用Toast来显示异常信息
                    new Thread() {
                        @Override
                        public void run() {
                            Looper.prepare();
                            Toast.makeText(mContext, "很抱歉,程序出现异常,即将重启.", Toast.LENGTH_LONG).show();
                            Looper.loop();
                        }
                    }.start();
                    // 收集设备参数信息
                    collectDeviceInfo(mContext);
                    // 保存日志文件
                    saveCrashInfoFile(ex);
                    // 重启应用(按需要添加是否重启应用)
                    SystemClock.sleep(1000);
                    restartApp();
                } catch (Exception e) {
                    SystemClock.sleep(1000);
                    // 退出程序
                    android.os.Process.killProcess(android.os.Process.myPid());
                    System.exit(1);
                }
            }
        });
    }

    /**
     * 重启AppMyActivityManager
     */
    public void restartApp() {
        Intent intent = new Intent(mContext, LoginActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
        MyActivityManager.finishAllActivity();
        //结束进程之前可以把你程序的注销或者退出代码放在这段代码之前
        android.os.Process.killProcess(android.os.Process.myPid());
    }

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    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 + "";
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    private String saveCrashInfoFile(Throwable ex) {
        StringBuffer sb = new StringBuffer();
        try {
            SimpleDateFormat sDateFormat = new SimpleDateFormat(
                    "yyyy-MM-dd HH:mm:ss");
            String date = sDateFormat.format(new Date());
            sb.append("\r\n" + date + "\n");
            for (Map.Entry entry : infos.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key + "=" + value + "\n");
            }
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            ex.printStackTrace(printWriter);
            Throwable cause = ex.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.flush();
            printWriter.close();
            String result = writer.toString();
            sb.append(result);

            String fileName = writeFile(sb.toString());
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
            sb.append("an error occured while writing file...\r\n");
            writeFile(sb.toString());
        }
        return null;
    }

    private String writeFile(String sb) {
        String time = formatter.format(new Date());
        String fileName = "卡扣核查Error" + time + ".log";
        FileUtils.saveFile(sb, fileName, true);
        return fileName;
    }

    public static String getGlobalpath() {
        return Environment.getExternalStorageDirectory().getAbsolutePath()
                + File.separator + "crash" + File.separator;
    }

    public static void setTag(String tag) {
        TAG = tag;
    }

    /**
     * 文件删除
     *
     * @param autoClearDay 文件保存天数
     */
    public void autoClear(final int autoClearDay) {
        delete(getGlobalpath(), new FilenameFilter() {

            @Override
            public boolean accept(File file, String filename) {
                String s = getFileNameWithoutExtension(filename);
                int day = autoClearDay < 0 ? autoClearDay : -1 * autoClearDay;
                String date = "crash-" + getOtherDay(day);
                return date.compareTo(s) >= 0;
            }
        });

    }

    /**
     * 获得不带扩展名的文件名称
     *
     * @param filePath 文件路径
     * @return
     */
    public String getFileNameWithoutExtension(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }
        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
        int filePosi = filePath.lastIndexOf(File.separator);
        if (filePosi == -1) {
            return (extenPosi == -1 ? filePath : filePath.substring(0,
                    extenPosi));
        }
        if (extenPosi == -1) {
            return filePath.substring(filePosi + 1);
        }
        return (filePosi < extenPosi ? filePath.substring(filePosi + 1,
                extenPosi) : filePath.substring(filePosi + 1));
    }

    /**
     * 判断SD卡是否可用
     *
     * @return SD卡可用返回true
     */
    public boolean hasSdcard() {
        String status = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(status);
    }

    /**
     * 删除指定目录中特定的文件
     *
     * @param dir
     * @param filter
     */
    public static void delete(String dir, FilenameFilter filter) {
        if (TextUtils.isEmpty(dir)) {
            return;
        }
        File file = new File(dir);
        if (!file.exists()) {
            return;
        }
        if (file.isFile()) {
            file.delete();
        }
        if (!file.isDirectory()) {
            return;
        }

        File[] lists = null;
        if (filter != null) {
            lists = file.listFiles(filter);
        } else {
            lists = file.listFiles();
        }

        if (lists == null) {
            return;
        }
        for (File f : lists) {
            if (f.isFile()) {
                f.delete();
            }
        }
    }

    /**
     * 获得几天之前或者几天之后的日期
     *
     * @param diff 差值:正的往后推,负的往前推
     * @return
     */
    public String getOtherDay(int diff) {
        Calendar mCalendar = Calendar.getInstance();
        mCalendar.add(Calendar.DATE, diff);
        return getDateFormat(mCalendar.getTime());
    }

    /**
     * 将date转成yyyy-MM-dd字符串
* * @param date Date对象 * @return yyyy-MM-dd */ public String getDateFormat(Date date) { return dateSimpleFormat(date, defaultDateFormat.get()); } /** * 将date转成字符串 * * @param date Date * @param format SimpleDateFormat *
* 注: SimpleDateFormat为空时,采用默认的yyyy-MM-dd HH:mm:ss格式 * @return yyyy-MM-dd HH:mm:ss */ public static String dateSimpleFormat(Date date, SimpleDateFormat format) { if (format == null) { format = defaultDateTimeFormat.get(); } return (date == null ? "" : format.format(date)); } /** * yyyy-MM-dd HH:mm:ss格式 */ public static final ThreadLocal defaultDateTimeFormat = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT); } }; /** * yyyy-MM-dd格式 */ public static final ThreadLocal defaultDateFormat = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat(DEFAULT_FORMAT_DATE); } }; }

到这里就完事了,别忘了在“Application”里面onCreate进行注册;

注册代码:CrashHandler_Ma.getInstance().init(this);

不过有些人反馈说找不到FileUtils工具类,那个只是本地写入文件的一个工具类,自己封装的,那我贴一下好了:

package com.anrongtec.zcpt.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 文件工具类
 *
 * @author mazhanzhu 2018年8月19日
 */
public class FileUtils {
    /**
     * 分隔符.
     */
    public final static String FILE_EXTENSION_SEPARATOR = ".";

    /**
     * 判断手机是否安装某个应用
     *
     * @param context
     * @param appPackageName 应用包名
     * @return true:安装,false:未安装
     */
    public static boolean isApplicationAvilible(Context context, String appPackageName) {
        try {
            // 获取packagemanager
            PackageManager packageManager = context.getPackageManager();
            // 获取所有已安装程序的包信息
            List pinfo = packageManager.getInstalledPackages(0);
            if (pinfo != null) {
                for (int i = 0; i < pinfo.size(); i++) {
                    String pn = pinfo.get(i).packageName;
                    if (appPackageName.equals(pn)) {
                        return true;
                    }
                }
            }
            return false;
        } catch (Exception e) {
            //出现异常,直接打开应用,避免停留在这个页面不动
            return true;
        }
    }

    /**
     * 格式转换文件大小
     *
     * @param context
     * @param fileSize
     * @return
     */
    @SuppressLint("NewApi")
    public static String formatFileSize(Context context, long fileSize) {
        return Formatter.formatFileSize(context, fileSize);
    }

    /**
     * 判断该文件是否存在
     *
     * @return
     */
    public static boolean isExist(String path) {
        try {
            File f = new File(path);
            if (f.exists()) {
                return true;
            }
        } catch (Exception e) {
            return false;
        }
        return false;

    }

    /**
     * 保存异常信息到文件
     *
     * @param data
     * @param file_name
     * @param isAppend  是否是追加, true为追加。  false为覆盖
     */
    public static void saveFile(String data, String file_name, boolean isAppend) {
        //内置sd卡路径
        File sdPath = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
        if (!sdPath.exists()) {
            sdPath.mkdirs();
        }
        Log.e("fd", "saveFile: " + sdPath.toString());
        File file = new File(sdPath, file_name);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file, isAppend);
            fos.write(data.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void saveFile(String data, String file_name) {
        saveFile(data, file_name, false);
    }

    public static void saveLog(String data, String file_name) {
        String time = new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date());
        saveFile("\n" + time + "\n" + data, file_name, true);
    }

    /**
     * 根据路径删除文件
     */
    public static boolean deleteFile(File file) {
        if (file == null) {
            throw new NullPointerException("file is null");
        }
        if (!file.exists()) {
            return true;
        }
        if (file.isFile()) {
            return file.delete();
        }
        if (!file.isDirectory()) {
            return false;
        }

        File[] files = file.listFiles();
        if (files == null) {
            return true;
        }
        for (File f : files) {
            if (f.isFile()) {
                f.delete();
            } else if (f.isDirectory()) {
                deleteFile(f.getAbsolutePath());
            }
        }
        return file.delete();
    }

    /**
     * 判断SD卡是否可用
     *
     * @return SD卡可用返回true
     */
    public static boolean hasSdcard() {
        String status = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(status);
    }

    /**
     * 删除指定文件或指定目录内的所有文件
     *
     * @param path 文件或目录的绝对路径
     * @return 路径为空或空白字符串,返回true;文件不存在,返回true;文件删除返回true;
     * 文件删除异常返回false
     */
    public static boolean deleteFile(String path) {
        if (TextUtils.isEmpty(path)) {
            return true;
        }
        return deleteFile(new File(path));
    }

    /**
     * 删除指定目录中特定的文件
     *
     * @param dir
     * @param filter
     */
    public static void delete(String dir, FilenameFilter filter) {
        if (TextUtils.isEmpty(dir)) {
            return;
        }
        File file = new File(dir);
        if (!file.exists()) {
            return;
        }
        if (file.isFile()) {
            file.delete();
        }
        if (!file.isDirectory()) {
            return;
        }

        File[] lists = null;
        if (filter != null) {
            lists = file.listFiles(filter);
        } else {
            lists = file.listFiles();
        }
        if (lists == null) {
            return;
        }
        for (File f : lists) {
            if (f.isFile()) {
                f.delete();
            }
        }
    }

    /**
     * 获得不带扩展名的文件名称
     *
     * @param filePath 文件路径
     * @return
     */
    public static String getFileNameWithoutExtension(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }
        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
        int filePosi = filePath.lastIndexOf(File.separator);
        if (filePosi == -1) {
            return (extenPosi == -1 ? filePath : filePath.substring(0,
                    extenPosi));
        }
        if (extenPosi == -1) {
            return filePath.substring(filePosi + 1);
        }
        return (filePosi < extenPosi ? filePath.substring(filePosi + 1,
                extenPosi) : filePath.substring(filePosi + 1));
    }
}
当程序发生Uncaught异常的时候,记录错误日志;文件地址:内置sd卡路径下“crash-" + 日期时间 + ".log”

你可能感兴趣的:(使用技巧,内存管理,优化等,技术总结,bug)