一、實現Thread.UncaughtExceptionHandler
UnChecked異常發生時,由於沒有相應的try…catch處理該異常對象,所以Java運行環境將會終止,程序將退出,也就是我們所說的Crash。Java API提供了一個全局異常捕獲處理器,Android應用在Java層捕獲Crash依賴的就是Thread.UncaughtExceptionHandler處理器接口,通常我們只需實現這個接口,並重寫其中的uncaughtException方法,在該方法中可以讀取Crash的堆棧信息
public class CrashManager implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler mDefaultHandler;
private Map<String, String> infos;
private MyApplication application;
private static SimpleDateFormat logfile = new SimpleDateFormat("yyyy-MM-dd");// 日志文件格式
public CrashManager(MyApplication application){
//獲取系統默認的UncaughtExceptionHandler
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
this.application = application;
}
private boolean handleException(final Throwable exc){
if (exc == null) {
return false;
}
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//准備
Log.i("Urmytch","崩潰正在寫入日志");
flushBufferedUrlsAndReturn();
//處理崩潰
collectDeviceAndUserInfo(application);
writeCrash(exc);
Looper.loop();
}
}).start();
return true;
}
/**
* 把未存盤的url和返回數據寫入日志文件
*/
private void flushBufferedUrlsAndReturn(){
//TODO 可以在請求網絡時把url和返回xml或json數據緩存在隊列中,崩潰時先寫入以便查明原因
}
/**
* 采集設備和用戶信息
* @param context 上下文
*/
private void collectDeviceAndUserInfo(Context context){
PackageManager pm = context.getPackageManager();
infos = new HashMap<String, String>();
try {
PackageInfo pi = pm.getPackageInfo(context.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);
infos.put("crashTime",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
} catch (PackageManager.NameNotFoundException e) {
Log.e("Urmytch",e.getMessage());
}
Field[] fields = Build.class.getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
}
} catch (IllegalAccessException e) {
Log.e("Urmytch",e.getMessage());
}
}
/**
* 采集崩潰原因
* @param exc 異常
*/
private void writeCrash(Throwable exc){
StringBuffer sb = new StringBuffer();
sb.append("------------------crash----------------------");
sb.append("\r\n");
for (Map.Entry<String,String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key+"="+value+"\r\n");
}
Writer writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
exc.printStackTrace(pw);
Throwable excCause = exc.getCause();
while (excCause != null) {
excCause.printStackTrace(pw);
excCause = excCause.getCause();
}
pw.close();
String result = writer.toString();
sb.append(result);
sb.append("\r\n");
sb.append("-------------------end-----------------------");
sb.append("\r\n");
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
String sdcardPath = Environment.getExternalStorageDirectory().getPath();
Log.i("路徑:",""+Environment.getExternalStorageDirectory().getPath());
//String filePath = sdcardPath + "//Urmytch/crash/";
String filePath = sdcardPath + "//kantu/crash/";
writeLog(sb.toString(), filePath);
}
}
/**
*
* @param log 文件內容
* @param name 文件路徑
* @return 返回寫入的文件路徑
* 寫入Log信息的方法,寫入到SD卡里面
*/
private String writeLog(String log, String name)
{
Date nowtime = new Date();
String needWriteFiel = logfile.format(nowtime);
//String filename = name + "mycrash"+ ".log";
String filename = name + "mycrash"+ needWriteFiel+".txt";
File file =new File(filename);
if(!file.getParentFile().exists()){
Log.i("Urmytch","新建文件");
file.getParentFile().mkdirs();
}
if (file != null && file.exists() && file.length() + log.length() >= 64 * 1024) {
//控制日志文件大小
file.delete();
}
try
{
file.createNewFile();
FileWriter fw=new FileWriter(file,true);
BufferedWriter bw = new BufferedWriter(fw);
//寫入相關Log到文件
bw.write(log);
bw.newLine();
bw.close();
fw.close();
//發送郵件
SendMailUtil.send(file,"changyiqiang123@126.com");
return filename;
}
catch(IOException e)
{
Log.w("Urmytch",e.getMessage());
return null;
}
}
@Override
public void uncaughtException(Thread thread, Throwable exc) {
if(!handleException(exc) && mDefaultHandler != null){
//如果用戶沒有處理則讓系統默認的異常處理器來處理
mDefaultHandler.uncaughtException(thread, exc);
Log.i("打印:","1111");
}else{
Log.i("打印:","222");
try{
Thread.sleep(2000);
}catch (InterruptedException e){
Log.w("Urmytch",e.getMessage());
}
Intent intent = new Intent(application.getApplicationContext(), LoginActivity.class);
PendingIntent restartIntent = PendingIntent.getActivity(application.getApplicationContext(), 0, intent, 0);
//退出程序
AlarmManager mgr = (AlarmManager)application.getSystemService(Context.ALARM_SERVICE);
//1秒后重啟應用
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent);
android.os.Process.killProcess(android.os.Process.myPid());
}
////這里可以上傳異常信息到服務器,便於開發人員分析日志從而解決Bug
// uploadExceptionToServer();
}
/**
* 將錯誤信息上傳至服務器
*/
private void uploadExceptionToServer() {
File file = new File(Environment.getExternalStorageDirectory()+File.separator+"test.txt");
OutputStream os = null;
try {
os = new FileOutputStream(file);
String str = "hello world";
byte[] data = str.getBytes();
os.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if (os != null)os.close();
} catch (IOException e) {
}
}
SendMailUtil.send(file,"changyiqiang123@126.com");
}
}
二、在Application中注冊
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashManager crashHandler = new CrashManager(this);
Thread.setDefaultUncaughtExceptionHandler(crashHandler);
}
}
最后權限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
完成
參考於:https://blog.csdn.net/urmytch/article/details/53642945
