Template method mode and practice: application crash handling
This paper mainly introduces the template method pattern, and uses the template method pattern to develop an extensible Android application crash handling tool class.
Principle and Application
- AsyncTask
- Drawing process of View
Wait..
Template method mode focuses on
-Encapsulation of immutable part
-Extract common part code for easy maintenance
For example, the drawing process of View:
- public final void measure(..) –> prptected void onMeasure(..)
- public final void layout(..) –> prptected void onLayout(..)
- public final void draw(..) –> prptected void onDraw(..)
xxx method is defined as final, which means that the order of core logic cannot be modified; onMeasure can be overridden, which means that this part can be customized (Extended).
Determine the process and abstract the specific implementation details.
Actual combat: application crash handling
Use the template method pattern to implement an application crash handling tool class.
Two processes of crash handling:
-Collect crash data
-Save crash data
We abstract these two specific details and let the subclass implement them:
public abstract class CrashHandler<T> {
abstract T collectData(Context context, Thread t, Throwable e);
abstract void saveData(Context context, T data);
}
Then write the logic of crash handling:
public void install(Context context) {
mContext = context;
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
new Thread() {
@Override
public void run() {
//Concrete logic of implementing subclass
saveData(mContext, collectData(mContext, t, e));
//Exit procedure
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}.start();
}
});
}
The whole tool code is as follows:
Abstract class CrashHandler:
package com.newsapp.tool;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
/**
* @author mingC
* @date 2018/8/5
*/
public abstract class CrashHandler<T> {
private Context mContext;
/**
* Crash data collection
* @param t Crash thread
* @param e Cause of collapse
* @return Crash data
*/
abstract T collectData(Context context, Thread t, Throwable e);
/**
* To save the crash data, you can save it to a file or upload it to the server here
* @param data
*/
abstract void saveData(Context context, T data);
private void showTipToast() {
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "I'm sorry,Program exception is about to exit.", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
}
public void install(Context context) {
mContext = context;
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
e.printStackTrace();
showTipToast();
Log.e("CrashHandler", "thread" + t.getName() + "An uncaught exception occurred, crash processing in progress...");
final long beginTime = System.currentTimeMillis();
new Thread() {
@Override
public void run() {
long transcatTime = System.currentTimeMillis() - beginTime;
//Concrete logic of implementing subclass
saveData(mContext, collectData(mContext, t, e));
Log.e("CrashHandler", "Crash processing completed at(ms): " + transcatTime);
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// Exit program, some models will fail to exit until ANR
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}.start();
}
});
}
}
Subclass RealCrashHandler:
package com.newsapp.tool;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author mingC
* @date 2018/8/5
*/
public class RealCrashHandler extends CrashHandler<RealCrashHandler.CrashData>{
@Override
CrashData collectData(Context context, Thread t, Throwable e) {
CrashData data = new CrashData();
//Save exception information
int s = 0;
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
data.map.put("err-stack" + (s++), stackTraceElement.toString());
}
//Save application information
PackageManager pm = context.getPackageManager();
PackageInfo pi = null;
try {
pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e1) {
e1.printStackTrace();
}
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
data.map.put("versionName", versionName);
data.map.put("versionCode", versionCode);
}
//Save device information
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
data.map.put(field.getName(), field.get(null).toString());
Log.d("CrashHandler", field.getName() + " : " + field.get(null));
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
public void readCrashInfo(Context context) {
Log.d("RealCrashHandler", "Reading crash data");
try {
File file = new File(context.getFilesDir(), "crash-info");
if (! file.exists()) {
Log.d("RealCrashHandler", "No crash data");
return;
}
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
Log.d("RealCrashHandler", line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
void saveData(Context context, RealCrashHandler.CrashData data) {
try {
FileOutputStream out = new FileOutputStream(new File(context.getFilesDir(), "crash-info"));
for (Map.Entry<String, String> entry : data.map.entrySet()) {
String row = entry.getKey() + "-" + entry.getValue() + "\n";
out.write(row.getBytes());
}
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
static class CrashData {
Map<String, String> map = new HashMap<>();
}
}
Use to register in Application:
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
RealCrashHandler crashHandler = new RealCrashHandler();
crashHandler.install(this);
//To read the last crash data, you can upload the data at the time of crash. If the upload fails, you can also upload the data at the time of opening the next application
crashHandler.readCrashInfo(this);
}
}