クラッシュ・ログの保存と再起動を実現するための例外プロセッサUncaughtExceptionHandler
前言
プログラムを作成するとき、異常な方法が投げ出されると、try...catchの方法を採用します.
しかし、もし私たちが捕まえられなかったら、プログラムはFCして、あるアプリケーションが停止したダイアログボックスをポップアップします.そしてユーザーがショックを受けた後にアプリケーションを再開するのは2つの点から言えばよくありません.これは私たちが今回実現する機能です. APPがユーザーが使用している場合、私たちは得られた異常 を処理できません.アプリケーションは、クラッシュ後に応答しない処理 を適用する.
問題外の小技を話す
ログを手動で取得する方法(ROOT権限が必要)/data/system/dropboxには、すべてのアプリケーションの例外ログファイルが保存されており、手動で抽出できる場合があります
生成されたログファイル:
クラッシュ後のヒント:
再起動すると、前回の例外ログが表示されます.
例外プロセッサの作成を開始
使用するパラメータ
1.Thread.UncaughtExceptionHandlerインタフェースはApplicationクラスを継承
(Applicationの継承はs e t e D e f a u l t U n c aughtExceptionHandlerを設定し、ApplicationContextを使用するためだけであり、他の実装方法があれば継承しなくてもよい)
2.プログラムがクラッシュした後、対応するActivityを起動する
3.プログラムがクラッシュしたときにログファイルを記録する
4.作業を終了し、作成した方法を使用します.
メソッドpublic void uncaughtException(Thread thread,Throwable ex)でメソッドを呼び出す
プロファイルでこのクラスを使用します(独自のアプリケーションクラスを使用する場合は必要ありません):
に注意
プログラムがクラッシュしたときにToastやダイアログボックスを表示しようとする人もいるかもしれませんが、直接実行してもHandlerを使ってもだめです.Stack OverflowのQAを見て、個人的な観点があり、私が最も認めている観点でもあります.
アプリケーションがクラッシュすると、もう使えるアプリケーションContextがないので、使っても反応しません.
でももしあなたがActivityを使っていたらContextは目的を達成することができるが、Activityが回収できないリスクがあるため、非常に推奨しない.なにしろ一言表示のためにメモリが漏れてしまったので、ゴマを拾ってスイカをなくした.
欠点を補う
私たちはUncaughtExceptionHandlerでヒントを与えるのは不便ですが、起動したActivityにIntentを伝える以上、直接それを使えばいいです.
起動するActivityで使用します.
みんな見終わったら何か言いたいことがあるんだよ~~~
頂点をクリックしてQQQ
プログラムを作成するとき、異常な方法が投げ出されると、try...catchの方法を採用します.
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
しかし、もし私たちが捕まえられなかったら、プログラムはFCして、あるアプリケーションが停止したダイアログボックスをポップアップします.そしてユーザーがショックを受けた後にアプリケーションを再開するのは2つの点から言えばよくありません.これは私たちが今回実現する機能です.
問題外の小技を話す
ログを手動で取得する方法(ROOT権限が必要)/data/system/dropboxには、すべてのアプリケーションの例外ログファイルが保存されており、手動で抽出できる場合があります
生成されたログファイル:
クラッシュ後のヒント:
再起動すると、前回の例外ログが表示されます.
例外プロセッサの作成を開始
使用するパラメータ
/** * Created by OCWVAR * Package: com.ocwvar.surfacetest.ExceptionHandler * Date: 2016/5/25 9:20 * Project: SurfaceTest * * * Activity Bundle * OCExceptionHandler.handleIncomingBundle() * * : * IsRecover . . true * hasLogs . . * Throwable .Serializable . * * * : * SLEEPTIME_RESTART_ACTIVITY Activity . . 1000ms = 1s * RESTART_ACTIVITY Activity * LOG_NAME_HEAD * LOG_SAVE_FOLDER * SAVE_LOGS */
public class OCExceptionHandler{
private final static long SLEEPTIME_RESTART_ACTIVITY = 2000;
private final static Class RESTART_ACTIVITY = MainActivity.class;
private final static String LOG_NAME_HEAD = "OCLog";
private final static String LOG_SAVE_FOLDER = "/log/";
private final static boolean SAVE_LOGS = true;
public final static String THROWABLE_OBJECT = "Throwable";
public final static String IS_RECOVERY = "IsRecover";
public final static String HAS_LOGS = "hasLogs";
private boolean logsCreated = false;
...
}
1.Thread.UncaughtExceptionHandlerインタフェースはApplicationクラスを継承
(Applicationの継承はs e t e D e f a u l t U n c aughtExceptionHandlerを設定し、ApplicationContextを使用するためだけであり、他の実装方法があれば継承しなくてもよい)
public class OCExceptionHandler extends Application implements Thread.UncaughtExceptionHandler {
@Override
public void onCreate() {
super.onCreate();
//
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable ex){
...
}
2.プログラムがクラッシュした後、対応するActivityを起動する
/** * * @param activityClass Activity */
private void restartActivity(Class activityClass , Throwable throwable){
// Intent ,
Intent intent = new Intent(getApplicationContext(),activityClass);
intent.putExtra("IsRecover",true);
intent.putExtra("hasLogs",logsCreated);
intent.putExtra("Throwable",throwable);
PendingIntent pendingIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
intent,
PendingIntent.FLAG_ONE_SHOT
);
// ,
AlarmManager mgr = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
// PendingIntent +SLEEPTIME_RESTART_ACTIVITY
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + SLEEPTIME_RESTART_ACTIVITY , pendingIntent);
}
3.プログラムがクラッシュしたときにログファイルを記録する
/** * * @param throwable * @return */
private boolean createLogs(Throwable throwable){
if (throwable == null || !SAVE_LOGS){
return false;
}
if (Build.VERSION.SDK_INT >= 23 && !checkPermission()){
Log.e(" -- ", " , Android 6.0+ . " );
return false;
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//
//
String savePath = Environment.getExternalStorageDirectory().getPath()+LOG_SAVE_FOLDER;
File file = new File(savePath);
file.mkdirs();
if (file.canWrite()){
//
FileWriter fileWriter;
PrintWriter printWriter;
// ,
String exceptionTime = DateFormat.format("yyyy-MM-dd hh:mm:ss", new Date()).toString();
//
file = new File(savePath + LOG_NAME_HEAD + " " + exceptionTime + " .log");
try {
if (file.createNewFile()){
// ,
fileWriter = new FileWriter(file,true);
printWriter = new PrintWriter(fileWriter);
printWriter.println("Date:"+exceptionTime+"
");
printWriter.println("Exception Class Name: ");
printWriter.println(throwable.getStackTrace()[0].getClassName());
printWriter.println("");
printWriter.println("Exception Class Position: ");
printWriter.println("Line number: "+throwable.getStackTrace()[0].getLineNumber());
printWriter.println("");
printWriter.println("Exception Cause: ");
printWriter.println(throwable.getMessage());
printWriter.println("");
printWriter.println("-----------------------------------
Exception Message:
");
for (int i = 0; i < throwable.getStackTrace().length; i++) {
printWriter.println(throwable.getStackTrace()[i]);
}
//
printWriter.flush();
fileWriter.flush();
printWriter.close();
fileWriter.close();
Log.w(" -- ", " " );
return true;
}else {
Log.e(" -- ", " , " );
return false;
}
} catch (IOException e) {
Log.e(" -- ", " , " );
return false;
}
}else {
// ,
Log.e(" -- ", " , " );
file = null;
return false;
}
}else {
Log.e(" -- ", " , " );
return false;
}
}
/** * Android 6.0+ * @return */
@TargetApi(23)
private boolean checkPermission(){
return checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
4.作業を終了し、作成した方法を使用します.
メソッドpublic void uncaughtException(Thread thread,Throwable ex)でメソッドを呼び出す
@Override
public void uncaughtException(Thread thread, Throwable ex) {
// , Intent
logsCreated = createLogs(ex);
restartActivity(RESTART_ACTIVITY , ex);
// , . ANR
System.exit(2);
}
プロファイルでこのクラスを使用します(独自のアプリケーションクラスを使用する場合は必要ありません):
<application
android:icon="@mipmap/ic_launcher"
android:label="TEST TEST TEST"
android:theme="@style/AppTheme"
// Application
android:name=".ExceptionHandler.OCExceptionHandler">
...
</application>
に注意
プログラムがクラッシュしたときにToastやダイアログボックスを表示しようとする人もいるかもしれませんが、直接実行してもHandlerを使ってもだめです.Stack OverflowのQAを見て、個人的な観点があり、私が最も認めている観点でもあります.
アプリケーションがクラッシュすると、もう使えるアプリケーションContextがないので、使っても反応しません.
でももしあなたがActivityを使っていたらContextは目的を達成することができるが、Activityが回収できないリスクがあるため、非常に推奨しない.なにしろ一言表示のためにメモリが漏れてしまったので、ゴマを拾ってスイカをなくした.
欠点を補う
私たちはUncaughtExceptionHandlerでヒントを与えるのは不便ですが、起動したActivityにIntentを伝える以上、直接それを使えばいいです.
/** * Activity Bundle * @param bundle Bundle * @return True: False: */
public static boolean handleIncomingBundle(@NonNull Bundle bundle , Context context){
// Bundle
if (bundle.get(IS_RECOVERY) != null){
if (bundle.getBoolean(HAS_LOGS)){
Toast.makeText(context, " , ", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, " ", Toast.LENGTH_SHORT).show();
}
Throwable throwable = (Throwable)bundle.getSerializable(THROWABLE_OBJECT);
if (throwable != null){
Log.e(" ", "---------------------------------------------");
for (int i = 0; i < throwable.getStackTrace().length; i++) {
System.out.println(throwable.getStackTrace()[i]);
}
Log.e(" ", "---------------------------------------------");
}else {
Log.e(" ", " !");
}
return true;
}else {
return false;
}
}
起動するActivityで使用します.
if (getIntent().getExtras() != null){
OCExceptionHandler.handleIncomingBundle(getIntent().getExtras(),getApplicationContext());
}
みんな見終わったら何か言いたいことがあるんだよ~~~
頂点をクリックしてQQQ