Created
February 27, 2017 14:25
-
-
Save hayukleung/dbad8abad5579bcb14bf23e972653314 to your computer and use it in GitHub Desktop.
NeverCrash [@see https://github.com/android-notes/Cockroach]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cndatacom.pmg.application; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
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.Date; | |
import java.util.HashMap; | |
import java.util.Locale; | |
import java.util.Map; | |
import com.cndatacom.cdcutils.method.LogMgr; | |
import com.cndatacom.cdcutils.method.SharedPreferencesUtil; | |
import com.cndatacom.pmg.util.DirMgr; | |
import com.cndatacom.pmg.util.MethodUtil; | |
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.Environment; | |
import android.os.Looper; | |
import android.util.Log; | |
/** | |
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告. | |
* | |
* @author user | |
* | |
*/ | |
public class CrashHandler implements UncaughtExceptionHandler { | |
public static final String TAG = "CrashHandler"; | |
// 系统默认的UncaughtException处理类 | |
private Thread.UncaughtExceptionHandler mDefaultHandler; | |
// CrashHandler实例 | |
private static CrashHandler INSTANCE = new CrashHandler(); | |
// 程序的Context对象 | |
private Context mContext; | |
// 用来存储设备信息和异常信息 | |
private Map<String, String> infos = new HashMap<String, String>(); | |
// 用于格式化日期,作为日志文件名的一部分 | |
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()); | |
/** 保证只有一个CrashHandler实例 */ | |
private CrashHandler() { | |
} | |
/** 获取CrashHandler实例 ,单例模式 */ | |
public static CrashHandler getInstance() { | |
return INSTANCE; | |
} | |
/** | |
* 初始化 | |
* | |
* @param context | |
*/ | |
public void init(Context context) { | |
mContext = context; | |
// 获取系统默认的UncaughtException处理器 | |
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); | |
// 设置该CrashHandler为程序的默认处理器 | |
Thread.setDefaultUncaughtExceptionHandler(this); | |
} | |
/** | |
* 当UncaughtException发生时会转入该函数来处理 | |
*/ | |
@Override | |
public void uncaughtException(Thread thread, Throwable ex) { | |
if (!handleException(ex) && mDefaultHandler != null) { | |
// 如果用户没有处理则让系统默认的异常处理器来处理 | |
mDefaultHandler.uncaughtException(thread, ex); | |
} else { | |
try { | |
Thread.sleep(3000); | |
} catch (InterruptedException e) { | |
Log.e(TAG, "error : ", e); | |
} | |
// 退出程序 | |
android.os.Process.killProcess(android.os.Process.myPid()); | |
System.exit(1); | |
} | |
} | |
/** | |
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. | |
* | |
* @param ex | |
* @return true:如果处理了该异常信息;否则返回false. | |
*/ | |
private boolean handleException(Throwable ex) { | |
if (ex == null) { | |
return false; | |
} | |
// 使用Toast来显示异常信息 | |
new Thread() { | |
@Override | |
public void run() { | |
Looper.prepare(); | |
MethodUtil.showToast(mContext, "很抱歉,程序出现异常,即将退出。"); | |
Looper.loop(); | |
} | |
}.start(); | |
// 收集设备参数信息 | |
collectDeviceInfo(mContext); | |
// 保存日志文件 | |
String logFileName = saveCrashInfo2File(ex); | |
LogMgr.showLog(mContext, "logfile --> " + logFileName); | |
new SharedPreferencesUtil(mContext).saveString("logfile", logFileName); | |
return true; | |
} | |
/** | |
* 收集设备参数信息 | |
* | |
* @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 == null ? "null" : 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()); | |
Log.d(TAG, field.getName() + " : " + field.get(null)); | |
} catch (Exception e) { | |
Log.e(TAG, "an error occured when collect crash info", e); | |
} | |
} | |
} | |
/** | |
* 保存错误信息到文件中 | |
* | |
* @param ex | |
* @return 返回文件名称,便于将文件传送到服务器 | |
*/ | |
private String saveCrashInfo2File(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"); | |
} | |
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.close(); | |
String result = writer.toString(); | |
sb.append(result); | |
try { | |
long timestamp = System.currentTimeMillis(); | |
String time = formatter.format(new Date()); | |
String fileName = "crash-" + time + "-" + timestamp + ".log"; | |
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { | |
String path = DirMgr.PATH_LOG; | |
File dir = new File(path); | |
if (!dir.exists()) { | |
dir.mkdirs(); | |
} | |
FileOutputStream fos = new FileOutputStream(path + File.separator +fileName); | |
fos.write(sb.toString().getBytes()); | |
fos.close(); | |
} | |
return fileName; | |
} catch (Exception e) { | |
Log.e(TAG, "an error occured while writing file...", e); | |
} | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cndatacom.pmg.application; | |
import java.lang.reflect.Field; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import android.app.Application; | |
import android.util.DisplayMetrics; | |
import com.baidu.mapapi.SDKInitializer; | |
import com.cndatacom.pmg.album.ImageUtil; | |
import com.cndatacom.pmg.db.DataBase; | |
import com.cndatacom.pmg.file.DownloadTask; | |
import com.cndatacom.pmg.file.DownloadTaskHelper; | |
import com.cndatacom.pmg.model.OperatorTypeForm; | |
import com.cndatacom.pmg.util.Constants; | |
import com.cndatacom.pmg.util.UrlConstants; | |
public class UIApplication extends Application implements DownloadTaskHelper { | |
public static int currentNetwork = Constants.NETWORK_TYPE_MOBILE; | |
private Map<String, Integer> notificationIds = new HashMap<String, Integer>(); // 用于通知栏显示新消息推送 | |
/** 屏幕宽高,标题栏高度 */ | |
private int displayWidth; | |
private int displayHeight; | |
private int statusBarHeight; | |
/** 登陆获取定位信息 */ | |
private String currProvince; | |
private String currCity; | |
public static boolean isLogin; | |
/** 运营商列表 */ | |
private List<OperatorTypeForm> mOperatorList = new ArrayList<OperatorTypeForm> (); | |
/** 当前选择的运营商 */ | |
private OperatorTypeForm mCurrentOperator = new OperatorTypeForm( | |
Constants.OPERATOR_ALL_ID, Constants.OPERATOR_ALL_NAME); | |
/** 上次选择的运营商 */ | |
private OperatorTypeForm mPreviousOperator = new OperatorTypeForm( | |
Constants.OPERATOR_ALL_ID, Constants.OPERATOR_ALL_NAME); | |
/** 微聊下载语音|图片任务集合 */ | |
private Set<String> chatFileList = new HashSet<String>(); // 微聊中,正在下载的语音|图片的路径集合 | |
public Set<String> getChatFileList() { | |
return chatFileList; | |
} | |
public void setChatFileList(Set<String> chatFileList) { | |
this.chatFileList = chatFileList; | |
} | |
@Override | |
public void onCreate() { | |
getParameters(); | |
super.onCreate(); | |
ImageUtil.initImageLoader(this, UrlConstants.SERVERURL); | |
SDKInitializer.initialize(getApplicationContext()); | |
DataBase dBase = new DataBase(getApplicationContext()); | |
dBase.close(); | |
// startService(new Intent(this, NetworkStateMonitorService.class)); // 暂时先不使用 | |
CrashHandler crashHandler = CrashHandler.getInstance(); | |
crashHandler.init(getApplicationContext()); | |
mOperatorList.add(new OperatorTypeForm( | |
Constants.OPERATOR_ALL_ID, Constants.OPERATOR_ALL_NAME)); | |
} | |
/** | |
* 获取屏幕宽高等参数 | |
*/ | |
private void getParameters() { | |
// DisplayMetrics displayMetrics = new DisplayMetrics(); | |
// getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); | |
DisplayMetrics displayMetrics = getApplicationContext().getResources() | |
.getDisplayMetrics(); | |
displayWidth = displayMetrics.widthPixels; | |
displayHeight = displayMetrics.heightPixels; | |
// | |
Class<?> c = null; | |
Object obj = null; | |
Field field = null; | |
int x = 0, sbar = 0; | |
try { | |
c = Class.forName("com.android.internal.R$dimen"); | |
obj = c.newInstance(); | |
field = c.getField("status_bar_height"); | |
x = Integer.parseInt(field.get(obj).toString()); | |
sbar = getResources().getDimensionPixelSize(x); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
statusBarHeight = sbar; | |
} | |
public Map<String, Integer> getNotificationIds() { | |
return notificationIds; | |
} | |
public void setNotificationIds(Map<String, Integer> notificationIds) { | |
this.notificationIds = notificationIds; | |
} | |
public int getDisplayWidth() { | |
return displayWidth; | |
} | |
public int getDisplayHeight() { | |
return displayHeight; | |
} | |
public int getStatusBarHeight() { | |
return statusBarHeight; | |
} | |
public String getCurrProvince() { | |
return currProvince; | |
} | |
public void setCurrProvince(String currProvince) { | |
this.currProvince = currProvince; | |
} | |
public String getCurrCity() { | |
return currCity; | |
} | |
public void setCurrCity(String currCity) { | |
this.currCity = currCity; | |
} | |
private Map<String, DownloadTask> mDownloadMap = new HashMap<String, DownloadTask>(); | |
@Override | |
public Map<String, DownloadTask> getDownloadMap() { | |
return this.mDownloadMap; | |
} | |
@Override | |
public void setDownloadMap(Map<String, DownloadTask> downloadMap) { | |
this.mDownloadMap = downloadMap; | |
} | |
public List<OperatorTypeForm> getmOperatorList() { | |
return mOperatorList; | |
} | |
public void addmOperatorList(List<OperatorTypeForm> mOperatorList) { | |
this.mOperatorList.addAll(mOperatorList); | |
} | |
public OperatorTypeForm getmCurrentOperator() { | |
return mCurrentOperator; | |
} | |
public void setmCurrentOperator(OperatorTypeForm mCurrentOperator) { | |
this.mCurrentOperator = mCurrentOperator; | |
} | |
public OperatorTypeForm getmPreviousOperator() { | |
return mPreviousOperator; | |
} | |
public void setmPreviousOperator(OperatorTypeForm mPreviousOperator) { | |
this.mPreviousOperator = mPreviousOperator; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment