Android-implementation (episode) of switching from sub-thread to main thread at any time, android thread
Reprinted please indicate the source: http://blog.csdn.net/l1028386804/article/details/45951149
I. Introduction
Android development often encountersNetwork request,Database Data PreparationAnd so onTime consumedAnd these operations areNot allowed in main thread. This will block the main thread and cause the program to be unresponsive.
Therefore, only one sub-thread can be used to perform these time-consuming operations and then display them on the interface. As we all know, interface and other control operations can only be completed in the main thread; therefore, it is inevitableSwitch from sub-thread to main thread.
Ii. Method
In this case, it is common for Android to useAsynTaskClass or Handler.AsynTaskIt is an officially encapsulated class, which is relatively simple and efficient, but not suitable for all situations. at least once or twice, I will never use it again. Using Handler is the most omnipotent method. Its principle is message loop. When the Handler variable is created in the main thread, the Handler message loop is started to process tasks in the message queue one by one. But it also has a tough time; the tricky thing is the trouble.
Each time, you need to create a Handler class, and then use the voidhandleMessage (Messagemsg) method to retrieve the message for interface operations. In addition, you need to encounter parameters transmission and other problems, it is really troublesome.
Iii. Ideas
Since there are so many problems but they have their advantages, why don't we encapsulate them once?
Here I will sort out the ideas:
Or use Handler to switch the thread. In the subthread, you can switch to the main thread through simple calls. When the subthread switches to the main thread, the sub-thread is blocked until the execution of the main thread is completed (Do you know why this is required ?) Make sure that the efficiency of the main thread has a time limit for execution, and cannot be executed for too long, leading to the main thread Blocking
What can I think? Can I have other requirements?
Just do what you want, and sort out the implementation methods.
Implemented Using Handler, in this case, the main method is to use the inherited Handler to implement it. If it is simple and the method can be accessed at any time, it is a good choice to adopt the static method externally and ensure efficiency, therefore, Handler's Message Queue cannot be too many, but it must be able to be called at any time. The use of external Queue is more likely to be blocked or not blocked by sub-threads, we should use two Queue to separate them. It is better to ensure that it cannot be executed in the main thread for a long time. Therefore, we must add a time variable for Queue execution. Of course, we finally considered it, since it is easy to use, it is nice to use Runnable as the input parameter. 4. Code
/** * @author liuyazhuang * */public class ToolKit { /** * Asynchronously * * @param runnable Runnable Interface */ public static void runOnMainThreadAsync(Runnable runnable) { } /** * Synchronously * * @param runnable Runnable Interface */ public static void runOnMainThreadSync(Runnable runnable) { }}
This is simply the case for the two external methods. However, to implement the functions, you must use the inherited Handler.
The HandlerPoster class is inherited from Handler:
/** * @author liuyazhuang * */final class HandlerPoster extends Handler { private final int ASYNC = 0x1; private final int SYNC = 0x2; private final Queue asyncPool; private final Queue syncPool; private final int maxMillisInsideHandleMessage; private boolean asyncActive; private boolean syncActive; HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; asyncPool = new LinkedList<>(); syncPool = new LinkedList<>(); } void dispose() { this.removeCallbacksAndMessages(null); this.asyncPool.clear(); this.syncPool.clear(); } void async(Runnable runnable) { synchronized (asyncPool) { asyncPool.offer(runnable); if (!asyncActive) { asyncActive = true; if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException(Could not send handler message); } } } } void sync(SyncPost post) { synchronized (syncPool) { syncPool.offer(post); if (!syncActive) { syncActive = true; if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException(Could not send handler message); } } } } @Override public void handleMessage(Message msg) { if (msg.what == ASYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { Runnable runnable = asyncPool.poll(); if (runnable == null) { synchronized (asyncPool) { // Check again, this time in synchronized runnable = asyncPool.poll(); if (runnable == null) { asyncActive = false; return; } } } runnable.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException(Could not send handler message); } rescheduled = true; return; } } } finally { asyncActive = rescheduled; } } else if (msg.what == SYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { SyncPost post = syncPool.poll(); if (post == null) { synchronized (syncPool) { // Check again, this time in synchronized post = syncPool.poll(); if (post == null) { syncActive = false; return; } } } post.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException(Could not send handler message); } rescheduled = true; return; } } } finally { syncActive = rescheduled; } } else super.handleMessage(msg); }}
Next I will talk about the class that I spent a lot of time getting out.
Class variables:
Two identifiers, two queues, two execution statuses, and one time limit. Is it easy to understand? The identifiers are used to process the queue respectively. The queue contains tasks. The execution status is to avoid too many message queues due to repeated message sending. It is better to understand the time limit.
The method section is as follows:
Constructor HandlerPoster (looper_loster, int_maxMillisInsideHandleMessage ):
Two parameters are passed in: logoff, which is used to initialize to the main thread, followed by a time limit, and then two queues are initialized.
Destroy function void_dispose ():First remove unprocessed messages and then clear the queue.
Add the asynchronous execution method void_async (Runnable_runnable ):
void async(Runnable runnable) { synchronized (asyncPool) { asyncPool.offer(runnable); if (!asyncActive) { asyncActive = true; if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException(Could not send handler message); } } } }
You can see that the first thing after entering the method is to enter the synchronization status, and then call asyncPool. offer (runnable) to write the task to the queue. Then determine whether the current asynchronous task is in progress. If it is not: change the status immediately, and then send a message to the current Handler. Of course, do not forget the input identity. The obtainMessage (ASYNC) method is also used to construct the Message to Improve the efficiency. This is to create more new messages and try to use idle messages in the current queue.
Add the synchronous execution method void_sync (SyncPost_post ):
void sync(SyncPost post) { synchronized (syncPool) { syncPool.offer(post); if (!syncActive) { syncActive = true; if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException(Could not send handler message); } } } }
We can see that here we passed in not Runnable, but SyncPost. This is an encapsulated class for Runnable for synchronization. We will introduce it later. In the same way, you can access synchronization, add, judge, and send messages.
Task performer @ Override_void_handleMessage (Message_msg ):
Here is the message processing method of the Handler that is rewritten. When there are messages in the current Handler message queue, this method will be called one by one in sequence.
Segmentation:
if (msg.what == ASYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { Runnable runnable = asyncPool.poll(); if (runnable == null) { synchronized (asyncPool) { // Check again, this time in synchronized runnable = asyncPool.poll(); if (runnable == null) { asyncActive = false; return; } } } runnable.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException(Could not send handler message); } rescheduled = true; return; } } } finally { asyncActive = rescheduled; } }
After entering, first determine whether the message is asynchronously processed. If yes, enter this location. After entering, we performed try_finally and there was a variable long_started used to identify the start time. After a task is executed, the system determines whether or not the tasks in the queue are executed and exited, and initiates a new message to the Handler loop queue. In the while part, we take a task from the queue and use the Poll method to determine whether it is null. If it is null, it enters the queue synchronization block. Then we take the while part and judge again. If a new task comes before it enters the synchronization queue, the second task is not NULL and will continue to be executed. Otherwise, if it is null, the current queue status is reset to false and the loop jumps out.
The second part is shown below:
else if (msg.what == SYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { SyncPost post = syncPool.poll(); if (post == null) { synchronized (syncPool) { // Check again, this time in synchronized post = syncPool.poll(); if (post == null) { syncActive = false; return; } } } post.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException(Could not send handler message); } rescheduled = true; return; } } } finally { syncActive = rescheduled; } } else super.handleMessage(msg);
First, we should judge that if the message of the synchronization task is entered, and if it is not, only super. handleMessage (msg); is called. From the above processing, we can see that the processing process is exactly the same as that in the first part. It is just to retrieve different classes of SyncPost from different queues, then judge the execution, and send messages with different identifiers. It can be said that if you understand the first part, this part is not nutritious.
There is a problem here. Since the method operation process is the same, where is the distinction between synchronization and Asynchronization?
Here we will look at SyncPost:
/** * @author liuyazhuang * */final class SyncPost { boolean end = false; Runnable runnable; SyncPost(Runnable runnable) { this.runnable = runnable; } public void run() { synchronized (this) { runnable.run(); end = true; try { this.notifyAll(); } catch (Exception e) { e.printStackTrace(); } } } public void waitRun() { if (!end) { synchronized (this) { if (!end) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }}
First, let's look at the constructor of SyncPost:
Does it pass in a Runnable interface? Therefore, it is a simple encapsulation of Runnable.
You can see its public_void_run () method:
In this method, we enter the synchronization block and then call the run method of the Runnable interface. At the same time, after the execution is complete, change one of the state variables boolean_end = true; then call this. notifyAll (); indicates that the waiting part can continue. Of course, this is the case. If the subthread has not yet executed this when it enters the synchronization block. what about wait? So we have prepared end and try for this.
Then let's take a look at the public_void_waitRun () method:
In this case, we first determine the status. If the status has changed, it indicates that the main thread and void_run () are executed when the sub-thread is executed here (). So you don't have to enter the synchronization block to wait. Otherwise, it won't be too dead? Otherwise, wait until the main thread calls this. policyall ();
5. Passion
Back to ToolKit
/** * @author liuyazhuang * */public class ToolKit { private static HandlerPoster mainPoster = null; private static HandlerPoster getMainPoster() { if (mainPoster == null) { synchronized (ToolKit.class) { if (mainPoster == null) { mainPoster = new HandlerPoster(Looper.getMainLooper(), 20); } } } return mainPoster; } /** * Asynchronously * The child thread asynchronous run relative to the main thread, * not blocking the child thread * * @param runnable Runnable Interface */ public static void runOnMainThreadAsync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } getMainPoster().async(runnable); } /** * Synchronously * The child thread relative thread synchronization operation, * blocking the child thread, * thread for the main thread to complete * * @param runnable Runnable Interface */ public static void runOnMainThreadSync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } SyncPost poster = new SyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(); } public static void dispose() { if (mainPoster != null) { mainPoster.dispose(); mainPoster = null; } }}
One static variable is HandlerPoster.
Then an initialization part of HandlerPoster_getMainPoster () is initialized in synchronous mode to adapt to simultaneous calls of multiple threads. Of course, we passed in
MainPoster = newHandlerPoster (lorule. getmainloiter (), 20); this determines the HandlerPoster executed in the main thread, and specifies that the single running time of the main thread is 20 milliseconds.
In method void_runOnMainThreadAsync (Runnable_runnable:
First, determine whether the method is called by the main thread. If so, what else can I do in the queue? Run the command directly. If it is a child thread, call getMainPoster (). async (runnable) and append it to the queue for execution.
In method void_runOnMainThreadSync (Runnable_runnable:
public static void runOnMainThreadSync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } SyncPost poster = new SyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(); }
It is also a thread judgment, encapsulation, and then thrown into the queue for execution, and the poster is called in this method. waitRun (); wait until the main thread executes the run method of the SyncPost class. At last, of course, there was a destroy method. Mom said she had to learn to clean up without leaving garbage: void_dispose ()
OK.
// The Runnable class implements the run () method // run () runs in the main thread, where Interface operations can be performed // synchronized to the main thread, wait until the main thread finishes processing and continue to execute the sub-thread ToolKit. runOnMainThreadSync (Runnable runnable); // enters the main thread asynchronously without waiting for ToolKit. runOnMainThreadAsync (Runnable runnable );
The two methods are simple and convenient;
Code class:
- ToolKit. java
- HandlerPoster. java
- SyncPost. java
Open-source project: Genius-Android