两个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;
}
}
参考链接 代码链接