Last active
June 5, 2017 06:09
-
-
Save KennethYo/b2cc9ae2b8a6c26fbe249b1fb276fff6 to your computer and use it in GitHub Desktop.
Android Crash Handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.Activity; | |
import android.app.ActivityManager; | |
import android.app.Application; | |
import android.os.Process; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.app.FragmentActivity; | |
import android.support.v4.app.FragmentManager; | |
import android.text.TextUtils; | |
import java.lang.ref.WeakReference; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* crash handler | |
* Created by kenneth on 2016/12/20. | |
*/ | |
public class CrashHandler implements Thread.UncaughtExceptionHandler { | |
private final Application app; | |
private final boolean debug; | |
private final String buildTime; | |
private final Thread.UncaughtExceptionHandler defaultHandler; | |
private final SimpleActivityLifecycleCallbacks lifecycleCallbacks; | |
private OnCrashListener onCrashListener; | |
public static CrashHandler create(Application app, boolean debug) { | |
return new CrashHandler(app, debug, null); | |
} | |
public static CrashHandler create(Application app, boolean debug, String buildTime) { | |
return new CrashHandler(app, debug, buildTime); | |
} | |
private CrashHandler(Application app, boolean debug, String buildTime) { | |
this.app = app; | |
this.debug = debug; | |
this.buildTime = buildTime; | |
defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); | |
Thread.setDefaultUncaughtExceptionHandler(this); | |
lifecycleCallbacks = new SimpleActivityLifecycleCallbacks(); | |
app.registerActivityLifecycleCallbacks(lifecycleCallbacks); | |
} | |
public void setOnCrashListener(OnCrashListener onCrashListener) { | |
this.onCrashListener = onCrashListener; | |
} | |
@Override | |
public void uncaughtException(Thread thread, Throwable throwable) { | |
StringBuilder sb = new StringBuilder(); | |
addAppDebug(sb); | |
addProcessAndThread(sb); | |
addActivitiesAndFragments(sb); | |
addLeakedActivities(sb); | |
addMemoryInfo(sb); | |
Throwable crashThrowable = new Throwable(sb.toString(), throwable); | |
if (onCrashListener != null) onCrashListener.onCrash(thread, crashThrowable); | |
if (defaultHandler != null) defaultHandler.uncaughtException(thread, crashThrowable); | |
} | |
/** | |
* add APP is debug or not | |
*/ | |
private void addAppDebug(StringBuilder sb) { | |
sb.append("\n"); | |
sb.append("debug: ").append(debug); | |
if (!TextUtils.isEmpty(buildTime)) { | |
sb.append(", "); | |
sb.append("build: ").append(buildTime); | |
} | |
} | |
/** | |
* add process name and current thread name | |
*/ | |
private void addProcessAndThread(StringBuilder sb) { | |
sb.append("\n"); | |
int myPid = Process.myPid(); | |
ActivityManager am = (ActivityManager) app.getSystemService(Application.ACTIVITY_SERVICE); | |
for (ActivityManager.RunningAppProcessInfo appProcess : am.getRunningAppProcesses()) { | |
if (appProcess.pid == myPid) { | |
sb.append("process: ").append(appProcess.processName).append(", "); | |
break; | |
} | |
} | |
sb.append("thread: ").append(Thread.currentThread().getName()); | |
} | |
/** | |
* add opened activities | |
*/ | |
private void addActivitiesAndFragments(StringBuilder sb) { | |
sb.append("\n"); | |
List<Activity> activityList = lifecycleCallbacks.activityList; | |
sb.append("activities: "); | |
boolean firstTime = true; | |
for (Activity activity : activityList) { | |
if (firstTime) { | |
firstTime = false; | |
} else { | |
sb.append(","); | |
} | |
sb.append(activity.getClass().getSimpleName()); | |
addFragments(sb, activity); | |
} | |
} | |
private void addFragments(StringBuilder sb, Activity activity) { | |
if (activity instanceof FragmentActivity) { | |
FragmentActivity fragmentActivity = (FragmentActivity) activity; | |
FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager(); | |
List<Fragment> fragments = fragmentManager.getFragments(); | |
if (!Utils.isEmpty(fragments)) { | |
sb.append("(").append(Utils.getNamesString(fragments)).append(")"); | |
} | |
} | |
} | |
/** | |
* add leaked activities | |
*/ | |
private void addLeakedActivities(StringBuilder sb) { | |
sb.append("\n"); | |
List<String> leakedActivityNames = new ArrayList<>(); | |
List<WeakReference<Activity>> referenceActivities = lifecycleCallbacks.referenceActivities; | |
for (WeakReference<Activity> wf : referenceActivities) { | |
Activity activity = wf.get(); | |
if (activity == null) continue; | |
if (!lifecycleCallbacks.activityList.contains(activity)) { | |
leakedActivityNames.add(activity.getClass().getSimpleName()); | |
} | |
} | |
sb.append("leaked activities: ").append(TextUtils.join(",", leakedActivityNames)); | |
} | |
/** | |
* add memory information | |
*/ | |
private void addMemoryInfo(StringBuilder sb) { | |
sb.append("\n"); | |
Runtime runtime = Runtime.getRuntime(); | |
float maxMemory = runtime.maxMemory() / 1024 / 1024f; | |
float totalMemory = runtime.totalMemory() / 1024 / 1024f; | |
float freeMemory = runtime.freeMemory() / 1024 / 1024f; | |
sb.append("maxMemory: ").append(maxMemory).append("MB").append(","); | |
sb.append("totalMemory: ").append(totalMemory).append("MB").append(","); | |
sb.append("freeMemory: ").append(freeMemory).append("MB"); | |
} | |
public interface OnCrashListener { | |
void onCrash(Thread thread, Throwable throwable); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.Activity; | |
import android.app.Application; | |
import android.os.Bundle; | |
import java.lang.ref.WeakReference; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* Created by kenneth on 2016/12/20. | |
*/ | |
class SimpleActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { | |
List<Activity> activityList = new ArrayList<>(); | |
List<WeakReference<Activity>> referenceActivities = new ArrayList<>(); | |
SimpleActivityLifecycleCallbacks() { | |
} | |
@Override | |
public void onActivityCreated(Activity activity, Bundle bundle) { | |
activityList.add(activity); | |
referenceActivities.add(new WeakReference<>(activity)); | |
} | |
@Override | |
public void onActivityStarted(Activity activity) { | |
} | |
@Override | |
public void onActivityResumed(Activity activity) { | |
} | |
@Override | |
public void onActivityPaused(Activity activity) { | |
} | |
@Override | |
public void onActivityStopped(Activity activity) { | |
} | |
@Override | |
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { | |
} | |
@Override | |
public void onActivityDestroyed(Activity activity) { | |
activityList.remove(activity); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.text.TextUtils; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
/** | |
* Created by kenneth on 2016/12/20. | |
*/ | |
public class Utils { | |
static <T> String getNamesString(List<T> list) { | |
if (isEmpty(list)) return null; | |
return TextUtils.join(",", getClassNames(list)); | |
} | |
private static <T> List<String> getClassNames(List<T> list) { | |
if (isEmpty(list)) return null; | |
List<String> names = new ArrayList<>(); | |
for (Object o : list) { | |
names.add(o.getClass().getSimpleName()); | |
} | |
return names; | |
} | |
public static boolean isEmpty(Collection collection) { | |
return collection == null || collection.isEmpty(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment