Android------将系统crash内容存到SD卡中(本地崩溃日志采集)

两个Log管理类:
1、debug的时候日志正常打印,release时不打印
2、奔溃日志采集,在APP崩溃时把日志保存起来
代码:

package com.example.uvccamera.log;
import android.util.Log;
import com.example.uvccamera.AppUtils;

public class LogUtils {
    private static final String TAG = "LogUtils";
    public static void v(String msg){
        if(AppUtils.isDebug()){
            Log.v(TAG,msg);
        }
    }

    public static void v(String tag,String msg){
        if(AppUtils.isDebug()){
            Log.v(tag,msg);
        }
    }

    public static void d(String msg){
        if(AppUtils.isDebug()){
            Log.d(TAG,msg);
        }
    }

    public static void d(String tag,String msg){
        if(AppUtils.isDebug()){
            Log.d(tag,msg);
        }
    }

    public static void i(String msg){
        if(AppUtils.isDebug()){
            Log.i(TAG,msg);
        }
    }

    public static void i(String tag,String msg){
        if(AppUtils.isDebug()){
            Log.i(tag,msg);
        }
    }

    public static void w(String msg){
        if(AppUtils.isDebug()){
            Log.w(TAG,msg);
        }
    }

    public static void w(String tag,String msg){
        if(AppUtils.isDebug()){
            Log.w(tag,msg);
        }
    }

    public static void e(String msg){
        if(AppUtils.isDebug()){
            Log.e(TAG,msg);
        }
    }

    public static void e(String tag,String msg){
        if(AppUtils.isDebug()){
            Log.e(tag,msg);
        }
    }
}

import android.content.Context;
import android.content.pm.ApplicationInfo;

public class AppUtils {
    private static Boolean isDebug = null;
    public static boolean isDebug(){
        return isDebug == null?false:isDebug.booleanValue();
    }

    public static void syncIsDebug(Context context){
        if(isDebug == null){
            isDebug = context.getApplicationInfo() !=null &&
                    (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) !=0;
        }
    }
}
//在自定义的Application中调用
import android.app.Application;

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        AppUtils.syncIsDebug(getApplicationContext());
    }
}
//记得修改AndroidManifest.xml 在application节点下添加
//android:name=".MyApplication"

参考链接:
相关信息连接:Android Debug 版本判断及为什么 BuildConfig.DEBUG 始终为 false

奔溃日志采集类

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.os.SystemClock;
import android.provider.AlarmClock;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.example.uvccamera.MainActivity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class CrashHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";
    private static CrashHandler instance = null;
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    private Context mContext;
    private Map<String,String> infos = new HashMap<>();//用于存储设备信息与异常信息
    private CrashHandler(){ }

    public static CrashHandler getInstance(){
        if(instance == null){
            instance = new CrashHandler();
        }
        return instance;
    }

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

    private boolean handleException(Throwable throwable){
        try{
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();
                    Toast.makeText(mContext,"程序出现异常,即将重启",Toast.LENGTH_SHORT).show();
                    Looper.loop();
                }
            }).start();
            getDeviceInfo(mContext);
            saveCrashInfoToFile(throwable);
            SystemClock.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
        return true;
    }

    private void getDeviceInfo(Context context){
        try{
            PackageManager pm = context.getPackageManager();
            PackageInfo info = pm.getPackageInfo(context.getPackageName(),PackageManager.GET_ACTIVITIES);
            if(info != null){
                infos.put("VersionName",info.versionName);
                infos.put("VersionCode",info.versionCode+"");
            }
        }catch (PackageManager.NameNotFoundException e){
            LogUtils.e(TAG,"an error occured when collect package info");
        }
        //获取系统设备相关信息
        Field[] fields = Build.class.getDeclaredFields();
        for(Field field:fields){
            try{
                field.setAccessible(true);
                infos.put(field.getName(),field.get(null).toString());
            }catch (Exception e){
                LogUtils.e(TAG,"an error occured when collect package info");
            }
        }
    }

    private String saveCrashInfoToFile(Throwable throwable)throws Exception{
        StringBuilder sb = new StringBuilder();
        try{
            sb.append(getCurrentTime()).append("\n");
            for (Map.Entry<String,String> entry:infos.entrySet()){
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key).append("=").append(value).append("\n");
            }
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            throwable.printStackTrace(printWriter);
            printWriter.flush();
            printWriter.close();
            String result = writer.toString();
            sb.append(result);
            return writeFile(sb.toString());
        }catch (Exception e){
            LogUtils.e(TAG,"an error occured while writing file...");
            sb.append("an error occured while writing file...\r\n");
            writeFile(sb.toString());
        }
        return null;
    }

    private String getCurrentTime(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(new Date());
    }

    private String writeFile(String sb) throws Exception{
        String time = getCurrentTime();
        String fileName = "/crash-"+time+".log";
        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            String path = getGlobalPath();
            File dir = new File(path);
            if(!dir.exists()) {
                dir.mkdirs();
            }
        	//path+fileName 实际日志路径 /storage/emulated/0/Crash/crash-当前时间.log
            FileOutputStream fos = new FileOutputStream(dir + fileName,true);
            LogUtils.d(TAG,"PATH = "+path + fileName);
            fos.write(sb.getBytes());
            fos.flush();
            fos.close();
        }
        return fileName;
    }

    private static  String getGlobalPath(){
        return Environment.getExternalStorageDirectory().getAbsolutePath()
                + File.separator + "Crash" +File.separator;
    }

    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
        if(!handleException(e) && mDefaultHandler != null){
            mDefaultHandler.uncaughtException(t,e);
        }else{
        //一秒后重启APP
            AlarmManager mgr =(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(mContext, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra("crash",true);
            PendingIntent restartIntent = PendingIntent.getActivity(mContext,
                    0,intent,PendingIntent.FLAG_ONE_SHOT);
            mgr.set(AlarmManager.RTC,System.currentTimeMillis()+1000,restartIntent);
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(0);
            System.gc();
        }
    }
}

使用方法:

//在MainActivity放置一个按钮,触发内容为除数为0
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private Context context;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        CrashHandler.getInstance().init(this);

    }


    public void onBtnClick(View view) {
        int a = 1/0;
    }
}

参考链接 代码链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值