Code is a technology platform that focuses on big front-end areas
Author: Geekholt links: https://www.jianshu.com/p/83b96506a449 disclaimer: this article has been published, it has been approved Geekholt forwarding etc. Please contact the author
directory
preface
Every Android developer must have encountered all kinds of Crash problems in the process of project development, and we really don’t want the program to Crash. So the question is, do you really know Crash?
Why does Android Crash
Recently I was thinking about a problem, why the Android application will crash and the process will end when the null pointer and other exceptions occur. In Java Web applications, these exceptions do not shut down the virtual machine or terminate the process as long as other threads are running
I simulated an array out-of-bounds exception in the App, and the Android system will print the exception log for us
Main thread exception
Child thread exception
Whenever an exception occurs, we tend to resolve it by looking at the log. So can we look at the code that prints the exception log to find out how the Android system throws these uncaught exceptions and why the Crash occurs when the uncaught exception occurs
. We found the com. Android. Internal OS. RuntimeInit class, here we only post the code we need
public class RuntimeInit { final static String TAG = "AndroidRuntime"; . private static class LoggingHandler implements Thread.UncaughtExceptionHandler { public volatile boolean mTriggered = false; @Override public void uncaughtException(Thread t, Throwable e) { mTriggered = true; if (mCrashing) return; If (mApplicationObject == null && (process.system_uid == process.myuid ())) {Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n"); final String processName = ActivityThread.currentProcessName(); if (processName ! = null) { message.append("Process: ").append(processName).append(", "); } message.append("PID: ").append(Process.myPid()); Clog_e(TAG, message.toString(), e); } } } private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler { private final LoggingHandler mLoggingHandler; public KillApplicationHandler(LoggingHandler loggingHandler) { this.mLoggingHandler = Objects.requireNonNull(loggingHandler); } @Override public void uncaughtException(Thread t, Throwable e) { try { ensureLogging(t, e); if (mCrashing) return; mCrashing = true; if (ActivityThread.currentActivityThread() ! = null) { ActivityThread.currentActivityThread().stopProfiling(); } ActivityManager.getService().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e)); } catch (Throwable t2) { if (t2 instanceof DeadObjectException) { } else { try { Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) {}}} finally {// killProcess process.killprocess (process.mypid ()); System.exit(10); } } private void ensureLogging(Thread t, Throwable e) { if (! mLoggingHandler.mTriggered) { try { mLoggingHandler.uncaughtException(t, e); } catch (Throwable loggingThrowable) { } } } .... } protected static final void commonInit() {LoggingHandler LoggingHandler = new LoggingHandler(); Thread.setUncaughtExceptionPreHandler(loggingHandler); Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler)); . }
Copy the code
RuntimeInit has two inner classes, LoggingHandler and KillApplicationHandler. LoggingHandler is used to print exception logs, and the KillApplicationHandler is the actual cause of the Crash. Process.killprocess (process.mypid ()) is called internally to kill the Process that Uncaught
We also found that the two inner classes have realized the Thread UncaughtExceptionHandler interface. Respectively through Thread. SetUncaughtExceptionPreHandler and Thread. SetDefaultUncaughtExceptionHandler method to register
- Thread. SetUncaughtExceptionPreHandler, covering all of the threads, before the callback DefaultUncaughtExceptionHandler calls, call this method only within the Android Framework
- Thread setDefaultUncaughtExceptionHandler, if in any Thread calls can cover all of the threads is unusual, can be invoked in the application layer, each call to the incoming Thread. UncaughtExceptionHandler will overwrite the previous, That is, we can manually override the KillApplicationHandler implemented by the system
- New Thread (). SetUncaughtExceptionHandler (), can only cover the current Thread is unusual, if a Thread is defined UncaughtExceptionHandler, Global DefaultUncaughtExceptionHandler is ignored
When Uncaught occurs, the thread is terminated. UncaughtExceptionHandler is notified of the terminated thread and the corresponding exception. UncaughtException is then called. If the handler is not explicitly set, the default handler for the corresponding thread group is called. If we want to catch this exception, we must implement our own handler
Can we make it Crash free
Said to the above we can in the application layer. The Thread setDefaultUncaughtExceptionHandler to implement all threads Uncaught exception monitoring, and will cover the system default implementation KillApplicationHandler, This way we can make it possible to kill only the current thread when an Uncaught occurs, rather than the entire process. This applies if our child thread gets Uncaught. What if our main thread gets Uncaught? The main thread has been destroyed. Is there a way to make the main thread Uncaught without crashing?
There is, but before we talk about how to do that let’s talk about a few things, okay
We know that a Java program starts with a Main function, and if you do a finite number of tasks sequentially pretty soon the thread that Main is on ends. How do you keep Main alive and constantly handling known or unknown tasks?
- Use an infinite loop. But what task does a loop of an infinite loop need to handle? What if the task is temporarily unavailable and the application is kept in an active wait state?
- How can two or more threads collaborate to complete a mini-system task?
If you’re familiar with the Android Handler mechanism, you know that the entire Android system is actually message-driven. Inside Looper is an endless loop, constantly pulling messages from inside MessageQueue, which tells you what task to do
Such as received MSG = H.L AUNCH_ACTIVITY, call the ActivityThread. HandleLaunchActivity () method, which will eventually through reflection mechanism, create the Activity instance, and then execute the Activity. The onCreate () method
Such as MSG = have received H.P AUSE_ACTIVITY, call the ActivityThread. HandlePauseActivity () method, which will eventually perform the Activity. The onPause () method
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; . for (;;) MSG = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } / / send message sent to the corresponding Handler, the target is the Handler instance MSG. Target. DispatchMessage (MSG); . MSG. RecycleUnchecked (); }}
Copy the code
Looper. Loop is called from ActiivtyThread (the main thread), so why doesn’t loop death cause the application to freeze?
If the main thread MessageQueue has no message, the nativePollOnce() method in looper.loop () is blocked in queue.next(). The main thread then releases CPU resources and goes to sleep until the next message arrives or a transaction occurs, waking up the main thread by writing data to the pipe end. The epoll mechanism adopted here is an IO multiplexing mechanism, which can monitor multiple descriptors at the same time. When a descriptor is ready (read or write ready), it immediately notifies the corresponding program to carry out read or write operations, which is essentially synchronous I/O, that is, read and write is blocked. Therefore, the main thread is dormant most of the time and does not consume a lot of CPU resources. Take action when different messages are received: Once you exit the Message loop, your program can exit, too. Fetching a message from a message queue may block and will be processed accordingly. If a message processing time is too long, it may affect the refresh rate of the UI thread, the caton phenomenon in the child thread, if manual to create the stars, then after everything done should call the quit () method to terminate the message loop, or the child thread will have been waiting for (block), If Looper exits, the thread will terminate immediately, so it is recommended to terminate Looper when it is not needed
To summarize, the native layer method blocks when there is no message, so the looper.loop () loop does not jam the application
Looper.loop() = looper.loop (); looper.loop () = looper.loop ()
The code is shown below
public class CrashCatch { private CrashHandler mCrashHandler; private static CrashCatch mInstance; private CrashCatch(){ } private static CrashCatch getInstance(){ if(mInstance == null){ synchronized (CrashCatch.class){ if(mInstance == null){ mInstance = new CrashCatch(); } } } return mInstance; } public static void init(CrashHandler crashHandler){ getInstance().setCrashHandler(crashHandler); } private void setCrashHandler(CrashHandler crashHandler){ mCrashHandler = crashHandler; New Handler(looper.getMainLooper ()).post(new Runnable() {@override public void run() {for (;)) { try { Looper.loop(); } catch (Throwable e) { if (mCrashHandler ! . = null) {/ / handle exceptions mCrashHandler handlerException (stars. GetMainLooper () getThread (), e); }}}}}); // All thread exceptions are blocked. So the following code are intercepted the child Thread abnormal Thread. SetDefaultUncaughtExceptionHandler (new Thread. UncaughtExceptionHandler () {@ Override public void uncaughtException(Thread t, Throwable e) { if(mCrashHandler! Exception handling = null) {/ / mCrashHandler. HandlerException (t, e); }}}); } public interface CrashHandler{ void handlerException(Thread t,Throwable e); }}
Copy the code
The Handler adds a Runnable to the main thread’s MessageQueue. When the main thread runs to the Runnable, it enters our while loop. If the inside of the while is empty, the code will get stuck there, resulting in ANR. However, we call looper.loop () in the while loop, which causes the main thread to start reading and executing messages from the queue again. This ensures that all exceptions from the main thread will be thrown from the looper.loop () we call manually. Once thrown, it will be caught by a try{}catch, and then the main thread won’t crash. If we don’t have the while, then the main thread won’t catch the next time it throws an exception, and then the App will crash again. So we’re going to use the while to get the message loop again after every crash, The use of the while is limited to forcing the main thread into the message loop again each time it throws an exception
While (true) {try {Looper. Loop (); while (true) {try {Looper. } catch (Throwable e) {}}. This is because this method is an infinite loop. If it is executed in the main thread, such as in the Activity’s onCreate, the code after the while will not be executed completely, and the Activity life cycle will not be completed. Handler.post ensures that the logic behind the message will not be affected
It’s also very simple to use
CrashCatch.getInstance().setCrashHandler(new CrashHandler(){ @Override void handlerException(Thread t,Throwable e){ //TODO implements its own exception handling logic}catch(Exeception e){}}})
Copy the code
conclusion
Most of the time, apps crash due to trivial bugs. Unfortunately, android’s default process killing mechanism is simple and brutal, but most of the time, letting apps crash is not a particularly good option. Some bugs may be system bugs, for which we can not bypass the unpredictable system bugs, and some bugs are caused by our own coding. For some bugs, if we ignore them directly, some unimportant functions may not be used, or they have no impact on users at all. It’s better than crashing every time. We can also make our own logical decisions after catching exceptions.
This article mainly talks about the principle, how to use how to choose, or depending on the actual situation of their own projects
reading
3 Handle Loading, retry, and empty data gracefully 4 Android trifles 5 Android SDK development and use
If you want to share your article with everyone
Welcome to contribute