Android UncaughtExceptionHandlerによるappCrash処理
11880 ワード
このような問題を解決するためには、異常をタイムリーにキャプチャする必要がありますが、キャプチャする場所が多すぎます.そのため、グローバルな異常キャプチャが必要です.では、グローバル異常をキャプチャするにはどうすればいいのでしょうか.
答えはUncauthExceptionHandler+Thread.setDefaultUncaughtExceptionHandler
1.UncaughtExceptionHandlerは異常をキャプチャしていない処理インタフェースであり、このクラスが先に異常をキャプチャする
このインタフェースを実装する必要があります
ここにはファイルしか保存されていません.一般的には、appが2回目に起動したときに、このファイルをネットワークにアップロードする必要があります.時間に余裕がありません.ここにアップロードするのはしばらくコードを貼らないでください.時間に余裕があれば、すぐに補充します.注目してください.
2.初期化し、グローバル例外情報をリスニングします.ここでは、Applicationを継承し、システムのデフォルトのApplicationを置き換える必要があります.
インベントリファイルの変更
~~~~~~~~~~~~~~~~~~~~~~~~~2014-12-20日更新~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
テンセントにはcrashの収集と処理を実現するbugly製品があります.もちろん、UncaughtExceptionHandlerも同時に使用できます.テンセントbuglyはUncaughtExceptionHandlerのコールバックも実現していますが、テンセントbuglyは異常をキャプチャすると同時に自分のUncaughtExceptionHandlerを呼び出します.
現在、テンセントのbuglyはコールバックをサポートしていませんが、テンセントの内測版がコールバックをサポートすることを申請しました.
try doing it
答えはUncauthExceptionHandler+Thread.setDefaultUncaughtExceptionHandler
1.UncaughtExceptionHandlerは異常をキャプチャしていない処理インタフェースであり、このクラスが先に異常をキャプチャする
UncaughtExceptionHandler: 。
,
: , 。
このインタフェースを実装する必要があります
public class AppCrashHandler implements UncaughtExceptionHandler {
private Context mContext;
private Thread.UncaughtExceptionHandler mDefaultHandler;
/** lock**/
private Lock lock = null;
/** **/
private final String CRASH_REPORTER_EXTENSION = ".crash";
/** tag**/
private final String STACK_TRACE = "logStackTrance";
/** **/
private final String crash_pref_path ="app_crash_pref.xml";
private static final String OOM = "java.lang.OutOfMemoryError";
private static final String HPROF_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/data_crash.hprof"
private AppCrashHandler()
{
lock = new ReentrantLock(true);
}
/**
*
* @param context
* @return AppCrashHandler
*/
public static AppCrashHandler shareInstance(Context context){
AppCrashHandler crashhandler = AppCrashHandler.InstanceHolder.crashHandler;
crashhandler.initCrashHandler(context);
return crashhandler;
}
/**
* ,
* @param cxt
*/
private void initCrashHandler(Context cxt)
{
if(!hasInitilized()){
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = cxt;
}
}
public interface InstanceHolder
{
public static AppCrashHandler crashHandler = new AppCrashHandler();
}
public static boolean isOOM(Throwable throwable){
Log.d(TAG, "getName:" + throwable.getClass().getName());
if(OOM.equals(throwable.getClass().getName())){
return true;
}else{
Throwable cause = throwable.getCause();
if(cause != null){
return isOOM(cause);
}
return false;
}
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if(isOOM(throwable)){
try {
Debug.dumpHprofData(HPROF_FILE_PATH);
} catch (Exception e) {
Log.e(TAG, "couldn’t dump hprof", e);
}
}
if (!handleExceptionMessage(ex) && mDefaultHandler != null) {
//
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(STACK_TRACE, "Error : ", e);
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}
}
/**
* , .
* @param ex
* @return true: ; false
*/
private boolean handleExceptionMessage(Throwable ex)
{
if (ex == null)
{
return false;
}
// Toast
new Thread() {
@Override
public void run() {
// Toast
Looper.prepare();
Toast.makeText(mContext, " , ", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
String fileName = mContext.getPackageName()+"-"+"appCrash-Exception"+ CRASH_REPORTER_EXTENSION;
String crashFileName = saveExceptionToFile(ex,fileName);
SharedPreferences.Editor editor = mContext.getSharedPreferences(crash_pref_path , Context.MODE_PRIVATE).edit();
editor.putString(STACK_TRACE, crashFileName);
editor.commit();
Log.d(STACK_TRACE, "errorLogPath="+crashFileName);
return true;
}
/**
*
* @return
*/
public boolean hasInitilized()
{
return mContext!=null;
}
/**
*
* @param ex
* @return
* @throws IOException
*/
private String saveExceptionToFile(Throwable ex,String fileName)
{
File saveFile = null;
PrintWriter printWriter = null;
try {
lock.tryLock();
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
File sdCardDir = Environment.getExternalStorageDirectory();// SDCard
saveFile = new File(sdCardDir, fileName);
}else{
saveFile =new File(mContext.getFilesDir(),fileName);
}
if(!saveFile.exists())
{
saveFile.createNewFile();
}
printWriter = new PrintWriter(saveFile);
String result = formatException(ex);
printWriter.write(result);
printWriter.flush();
Log.e("CrashException", result);
}catch(Exception e){
e.printStackTrace();
} finally{
if(printWriter!=null)
{
printWriter.close();
}
lock.unlock();
}
return saveFile!=null?saveFile.getAbsolutePath():null;
}
/**
*
* @param e
* @return
*/
@SuppressLint("SimpleDateFormat")
private String formatException(Throwable e)
{
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = e.getStackTrace();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (stackTrace!=null)
{
String timeStramp = sdf.format(new Date(System.currentTimeMillis()));
String format = String.format("DateTime:%s
ExceptionName:%s
",timeStramp,e.getLocalizedMessage());
sb.append(format);
for (int i = 0; i < stackTrace.length; i++)
{
StackTraceElement traceElement = stackTrace[i];
String fileName = traceElement.getFileName();
int lineNumber = traceElement.getLineNumber();
String methodName = traceElement.getMethodName();
String className = traceElement.getClassName();
sb.append(String.format("%s\t%s[%d].%s
",className,fileName,lineNumber,methodName));
}
sb.append(String.format("
%s",e.getMessage()));
Writer stringWriter = new StringWriter();
PrintWriter pw = new PrintWriter(stringWriter);
e.printStackTrace(pw);
pw.flush();
pw.close();
sb.append("
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
");
sb.append(stringWriter.toString());
}
return sb.toString();
}
}
ここにはファイルしか保存されていません.一般的には、appが2回目に起動したときに、このファイルをネットワークにアップロードする必要があります.時間に余裕がありません.ここにアップロードするのはしばらくコードを貼らないでください.時間に余裕があれば、すぐに補充します.注目してください.
2.初期化し、グローバル例外情報をリスニングします.ここでは、Applicationを継承し、システムのデフォルトのApplicationを置き換える必要があります.
public class BaseApplication extends Application
{
private static BaseApplication instance = null;
private AppCrashHandler appCrashHandler = null;
@Override
public void onCreate() {
synchronized (this)
{
if(instance==null)
{
instance = this;
}
appCrashHandler = AppCrashHandler.shareInstance(instance);
}
super.onCreate();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
インベントリファイルの変更
<application
android:name="com.hali.luya.unitest.BaseApplication "
android:hardwareAccelerated="true"
android:icon="@drawable/app_logo"
android:logo="@drawable/app_logo"
android:label="@string/app_name"
android:configChanges="locale|keyboard|screenSize"
android:theme="@style/Theme.AppBaseTheme" >
<!--- .. .. ---->
</Application>
~~~~~~~~~~~~~~~~~~~~~~~~~2014-12-20日更新~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
テンセントにはcrashの収集と処理を実現するbugly製品があります.もちろん、UncaughtExceptionHandlerも同時に使用できます.テンセントbuglyはUncaughtExceptionHandlerのコールバックも実現していますが、テンセントbuglyは異常をキャプチャすると同時に自分のUncaughtExceptionHandlerを呼び出します.
現在、テンセントのbuglyはコールバックをサポートしていませんが、テンセントの内測版がコールバックをサポートすることを申請しました.
public class BaseApplication extends Application
{
private static Application instance = null;
private AppCrashHandler appCrashHandler = null;
private final String APP_CONTEXT_TAG = "appContext";
@Override
public void onCreate() {
synchronized (this)
{
if(instance==null)
{
instance = this;
}
appCrashHandler = AppCrashHandler.shareInstance(instance);
UserStrategy strategy = new UserStrategy(instance); //App Bean
strategy.setAppChannel(getPackageName()); //
strategy.setAppVersion(getVersion()); //App
strategy.setAppReportDelay(1000); // SDK ,
strategy.setDeviceID(GlobalUtil.getInstance().getDeviceID(instance));
strategy.setCrashHandleCallback(new AppCrashHandleCallback());
CrashReport.initCrashReport(instance, "900001335", true, strategy); // , SDK
CrashReport. setUserId("BBDTEK");
}
//shutDownLog();
super.onCreate();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/**
*
* @return
*/
public String getVersion() {
try {
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
String version = info.versionName;
return this.getString(R.string.app_version) + version;
} catch (Exception e) {
e.printStackTrace();
return this.getString(R.string.app_version);
}
}
private class AppCrashHandleCallback extends CrashHandleCallback //bugly
{
@Override
public synchronized Map<String, String> onCrashHandleStart(int crashType, String errorType, String errorMessage, String errorStack)
{
String crashTypeName = null;
switch (crashType)
{
case CrashHandleCallback.CRASHTYPE_JAVA_CATCH:
crashTypeName = "JAVA_CATCH";
break;
case CrashHandleCallback.CRASHTYPE_JAVA_CRASH:
crashTypeName = "JAVA_CRASH";
break;
case CrashHandleCallback.CRASHTYPE_NATIVE:
crashTypeName = "JAVA_NATIVE";
break;
case CrashHandleCallback.CRASHTYPE_U3D:
crashTypeName = "JAVA_U3D";
break;
default:
{
crashTypeName = "unknown";
}
}
Log.e(APP_CONTEXT_TAG, "Crash Happen Type:" + crashType + " TypeName:" + crashTypeName);
Log.e(APP_CONTEXT_TAG, "errorType:" + errorType);
Log.e(APP_CONTEXT_TAG, "errorMessage:" + errorMessage);
Log.e(APP_CONTEXT_TAG, "errorStack:" + errorStack);
Map<String, String> userDatas = super.onCrashHandleStart(crashType, errorType, errorMessage, errorStack);
if (userDatas == null)
{
userDatas = new HashMap<String, String>();
}
userDatas.put("DEBUG", "TRUE");
return userDatas;
}
}
/**
*
*/
private void shutDownLog()
{
LogUtils.allowE = false;
LogUtils.allowI = false;
LogUtils.allowV = false;
LogUtils.allowW = false;
LogUtils.allowWtf = false;
LogUtils.allowD = false;
}
}
try doing it