ページ

2013/05/19

[Android]独自クラッシュレポート送信機能を実装する レポート作成編

throw Life - Androidアプリのバグ報告システムを考えるとか
コジオニルク - Android - 独自のバグレポート機能とかを参考に
独自クラッシュレポート送信機能を実装してみた。

[Android]Logクラスをもっと使いやすく!クラス名、メソッド名、行数を表示するLogUtilクラス | DevAchieveのクラスにスタックトレースからクラス名、メソッド名、行数を取得する処理を依存しています。
/**
 * キャッチされなかったExceptionが発生した場合にログを記録する
 * 
 * @author wada
 * 
 */
public final class CrashExceptionHandler implements UncaughtExceptionHandler {

    public static final String             FILE_NAME = "report.txt";
    public static final String             MAILTO    = "my.mail@address.com";

    private final Context                  mContext;
    private final PackageInfo              mPackageInfo;
    private final UncaughtExceptionHandler mHandler;

    public CrashExceptionHandler(Context context) {
        mContext = context;
        try{
            mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
        }catch(NameNotFoundException e){
            throw new RuntimeException(e);
        }
        mHandler = Thread.getDefaultUncaughtExceptionHandler();
    }

    /**
     * キャッチされなかった例外発生時に各種情報をJSONでテキストファイルに書き出す
     */
    @Override
    public void uncaughtException(Thread thread, Throwable throwable){
        PrintWriter writer = null;
        try{
            FileOutputStream outputStream = mContext.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
            writer = new PrintWriter(outputStream);
            JSONObject json = new JSONObject();
            json.put("Build", getBuildInfo());
            json.put("PackageInfo", getPackageInfo());
            json.put("Exception", getExceptionInfo(throwable));
            json.put("SharedPreferences", getPreferencesInfo());
            writer.print(json.toString());
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(JSONException e){
            e.printStackTrace();
        }finally{
            if(writer != null){
                writer.close();
            }
        }
        mHandler.uncaughtException(thread, throwable);
    }

    /**
     * ビルド情報をJSONで返す
     * 
     * @return
     * @throws JSONException
     */
    private JSONObject getBuildInfo() throws JSONException{
        JSONObject buildJson = new JSONObject();
        buildJson.put("BRAND", Build.BRAND); // キャリア、メーカー名など(docomo)
        buildJson.put("MODEL", Build.MODEL); // ユーザーに表示するモデル名(SO-01C)
        buildJson.put("DEVICE", Build.DEVICE); // デバイス名(SO-01C)
        buildJson.put("MANUFACTURER", Build.MANUFACTURER); // 製造者名(Sony Ericsson)
        buildJson.put("VERSION.SDK_INT", Build.VERSION.SDK_INT); // フレームワークのバージョン情報(10)
        buildJson.put("VERSION.RELEASE", Build.VERSION.RELEASE); // ユーザーに表示するバージョン番号(2.3.4)
        return buildJson;
    }

    /**
     * パッケージ情報を返す
     * 
     * @return
     * @throws JSONException
     */
    private JSONObject getPackageInfo() throws JSONException{
        JSONObject packageInfoJson = new JSONObject();
        packageInfoJson.put("packageName", mPackageInfo.packageName);
        packageInfoJson.put("versionCode", mPackageInfo.versionCode);
        packageInfoJson.put("versionName", mPackageInfo.versionName);
        return packageInfoJson;
    }

    /**
     * 例外情報を返す
     * 
     * @param throwable
     * @return
     * @throws JSONException
     */
    private JSONObject getExceptionInfo(Throwable throwable) throws JSONException{
        JSONObject exceptionJson = new JSONObject();
        exceptionJson.put("name", throwable.getClass().getName());
        exceptionJson.put("cause", throwable.getCause());
        exceptionJson.put("message", throwable.getMessage());
        // StackTrace
        JSONArray stackTrace = new JSONArray();
        for(StackTraceElement element : throwable.getStackTrace()){
            stackTrace.put("at " + LogUtil.getMetaInfo(element));
        }
        exceptionJson.put("stacktrace", stackTrace);
        return exceptionJson;
    }

    /**
     * Preferencesを返す
     * 
     * @return
     * @throws JSONException
     */
    private JSONObject getPreferencesInfo() throws JSONException{
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        JSONObject preferencesJson = new JSONObject();
        Map<String, ?> map = preferences.getAll();
        for(Entry<String, ?> entry : map.entrySet()){
            preferencesJson.put(entry.getKey(), entry.getValue());
        }
        return preferencesJson;
    }
}
呼び出しは1アプリにつき1回で良いらしいです。
Thread.setDefaultUncaughtExceptionHandler(new CrashExceptionHandler(getApplicationContext()));

[Android]独自クラッシュレポート送信機能を実装する レポート送信編 | DevAchieveに続く。