Android UncaughtExceptionHandlerによるappCrash処理

11880 ワード

このような問題を解決するためには、異常をタイムリーにキャプチャする必要がありますが、キャプチャする場所が多すぎます.そのため、グローバルな異常キャプチャが必要です.では、グローバル異常をキャプチャするにはどうすればいいのでしょうか.
答えは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