1.1 What are the usage scenarios and usages of HandlerThread?
HandlerThread is essentially a handler in the child thread (HandlerThread=Handler+Thread);
Its use:
Step 1: Create HandlerThread instance object
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
Step 2: Start the thread
();
Step 3: Create a worker thread Handler & rewrite handleMessage()
Handler workHandler = new Handler( () ) { @OverRide public boolean handleMessage(Message msg) { ...//Message processing return true; } });
Step 4: Use the worker thread Handler to send a message to the worker's message queue
Message msg = (); = 2; //Message identification = "B"; // Message storage// b. Send messages through Handler to its bound message queue(msg);
Step 5: End the thread, that is, stop the thread's message loop
();
Advantages:
- Running loops in child threads to process, reducing the pressure on the main thread and making the main thread smoother
- Serial execution, opening a thread plays the role of multiple threads
- Have your own message queue, which will not interfere with UI threads
Disadvantages:
- Since each task queue is gradually executed, once the queue takes too long, the message delays
- For IO and other operations, threads are waiting and cannot be concurrent
1.2 What are the application scenarios and usage postures of IntentService?
IntentService is a subclass of Service. By default, we have enabled a worker thread. This worker thread uses it to process all startup requests one by one. The service will be automatically stopped after the task is executed. It is simple to use. Just implement a method onHandleIntent, which will receive the Intent for each startup request, which can perform background work and time-consuming operations. IntentService can be started multiple times, and each time-consuming operation will be executed in the onHandlerIntent callback method of the IntentService in a queue, and only one worker thread will be executed at a time, and the first one will be executed and the second one will be executed after the execution. And wait until all messages are executed before the service is terminated.
IntentService is suitable for APPs. Do some operations silently in the background without affecting the current user's operations.
IntentService source code:
- Open a thread called IntentService separately through HandlerThread
- Create an internal Handler called ServiceHandler
- Bind the child thread corresponding to the internal Handler and HandlerThread
- Pass to the service intent through onStartCommand(), insert it into the work queue in turn, and send it to onHandleIntent() one by one
- Use onHandleIntent() to process the tasks corresponding to all Intent request objects in turn
Example of usage:
public class MyIntentService extends IntentService { public static final String TAG = "MyIntentService"; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { boolean isMainThread = () == ().getThread(); (TAG, "is main thread:" + isMainThread); // False will be printed here, which means it is not the main thread // Simulation time-consuming operation download(); } /** * Simulation execution download */ private void download() { try { (5000); (TAG, "Download completed..."); } catch (Exception e) { (); } } }
1.3 What are the advantages and disadvantages of AsyncTask?
The implementation principle of AsyncTask:
- AsyncTask is an abstract class, mainly composed of Handler+2 thread pools. SERIAL_EXECUTOR is a task queue thread pool, used to schedule tasks and execute in order. THREAD_POOL_EXECUTOR is an execution thread pool, which truly executes specific thread tasks. Handler is used for asynchronous communication between worker threads and main threads.
- AsyncTask<Params, Progress, Result>, where Params is the parameter type of the doInBackground() method, Result is the return value type of the doInBackground() method, and Progress is the parameter type of the onProgressUpdate() method.
- When executing the execute() method, it is actually called the execute() method of SERIAL_EXECUTOR, which is to add the task to the end of the queue, then take out the tasks in the queue from the beginning, and call the execute() method of THREAD_POOL_EXECUTOR to execute it in turn, and stop when there are no tasks in the queue.
- AsyncTask can only execute the execute(params) method once, otherwise an error will be reported. But SERIAL_EXECUTOR and
The THREAD_POOL_EXECUTOR thread pools are all static, so they can form queues.
Q: AsyncTask can only execute execute() method once, so why use thread pool queue management?
Because the SERIAL_EXECUTOR and THREAD_POOL_EXECUTOR thread pools are both static, all AsyncTask instances share these 2 thread pools, thus forming a queue.
Q: What is the calling process of AsyncTask's onPreExecute(), doInBackground(), and onPostExecute() methods?
When AsyncTask creates an object, it will create an mWorker(workerRunnable) mFuture(FutureTask) object in the constructor.
mWorker implements the call() method of the Callable interface. In the call() method, the doInBackground() method is called, and the postResult() method is called at the end, that is, sending a message to the main thread through the Handler, and calling the AsyncTask finish() method in the main thread, deciding whether to call onCancelled() or onPostExecute().
mFuture implements the Runnable and Future interfaces. When creating an object, the member variable mWorker is initialized, and in the run() method, the call() method of mWorker is called.
When asyncTask executes the execute() method, it will first call the onPreExecute() method, and then call the execution(mFuture) of SERIAL_EXECUTOR to add the task to the tail of the queue and wait for execution. Call THREAD_POOL_EXECUTOR's execution(mFuture) during execution.
1.4 Tell me about your understanding of ?
It is generally used to bind a Runnable to the main thread. In the runOnUiThread source code, it will determine whether the current Runnable is the main thread. If it is run directly, if it is not, the Runnable post to the looper through a default empty constructor Handler, create the constructor Handler, and the looper object of the main thread will be bound by default.
1.5 Can child threads update the UI? Why?
Note that child threads cannot update the UI directly, this sentence cannot be updated directly, not that they cannot be updated (extremely).
Updated below)
The drawing process must be kept synchronized (otherwise the page will not be smooth), and our main thread is responsible for drawing the UI. The extreme case is that in the life cycle before the Activity's onResume (inclusive), child threads can update the UI, that is, onCreate, onStart and onResume. At this time, the drawing of the main thread has not started yet.
1.6 Talking about the Handler mechanism and principles?
First, we create a Handler instance object in the UI thread. Whether it is an anonymous internal class or a Handler instance object generated by a custom class, we need to rewrite the handleMessage method. In the handleMessage method, we can use the parameter msg to write the logical processing of the UI thread after receiving the message. Then we create a child thread. When the UI needs to be updated in the child thread, we create a new Message object and record the message data inside the message object Message, such as arg1, arg2, obj, etc., and then pass the previous Handl The er instance object calls the sendMessage method to send this Message instance object, and then the message will be stored in the MessageQueue waiting for processing. At this time, the MessageQueue manager Looper is constantly taking out the message existing in the MessageQueue, and passing the message to the Handler's handleMessage method through the callback dispatchMessage method. Finally, the aforementioned message will be retrieved from the MessageQueue by Looper and passed to the handleMessage method.
1.7 Why does creating a Handler in a child thread throw an exception?
You cannot create a Handler in a thread that has not yet called the () method.
Because the exception is thrown, an exception will be thrown when the mLooper object is null. The return value of (); here is null. Only when the() method is called will a Looper object be constructed and the Looper object of the current thread is stored in ThreadLocal.
In this way, when calling () the result will not be null.
1.8 Try to analyze the differences and application scenarios of Handler's post and sendMessage methods from the source code perspective?
and the method will finally call the sendMessageAtTime method to send messages, but in the post method, the message is obtained through the getPostMessage(Runnable r). In this method, there is a code = r, which assigns the callback of the message as a runnable object. When distributing the message in the dispatchMessage method, the != null is first judged. If it is not null, the message is processed through the handleCallback(msg); method. In this method (); the call is the run method in the runnable passed by the post method. If it is empty, then the callback judgment inside the handler is performed mCallback != null. If the callback in the handler is not empty, execute (msg) this processing message and determine whether it is true. If true is returned, the message processing ends. If false is returned, the message is handed over to the handleMessage(msg) of the handler.
So the difference is that the message calling the post method is processed in the run method of the Runnable object passed by the post, while calling the sendMessage method requires rewriting the handleMessage method or setting a callback for the handler, processing it in the handleMessage of the callback and returning true.
1.9 There is a Loop dead loop in Handler, why is the main thread not blocking? What is the principle?
Main thread hangs
Looper is a dead loop, constantly reading messages in MessageQueue. The loop method will call the next method of MessageQueue to get new messages. The next operation is a blocking operation. When there is no message, the next method will keep blocking, which will cause the loop to keep blocking. In theory, it will cause the thread to hang-block-block. But why, when sending a delay 10s message, assuming that there is only this message in the message queue at present;
So why is the UI operable, or the list page is swipable, or the animation is executable?
Let’s not talk about how nativePollOnce is implemented to block, we also know that another nativeWake implements thread wake-up;
So when will this method be called triggered? When a new message is added, the message is not added manually?
display Refresh the screen every 16 milliseconds;
SurfaceFlingerVsyncChoreographer sends a vSync signal every 16 milliseconds;
After FrameDisplayEventReceiver receives the signal, it calls the onVsync method and sends it to the main thread for processing through the handler message, so a message will be added and the UI thread will be awakened;
In fact, in the Android system, there is not only a screen refresh signal, but also other mechanisms, such as input method and system broadcasting, which will also add messages to the main thread's MessageQueue;
Therefore, it can be understood that the main thread is also suspended at any time and blocked at any time;
How to implement blocking and wake-up of the system
This mechanism is implemented through the pipe (pipe) mechanism;
Simply put, a pipeline is a file at both ends of the pipeline, which are two open files, file descriptors. These two open file descriptors correspond to the same file, one of which is used for reading, and the other is used for writing;
The general way to use is that a thread reads the content of the pipeline by reading the file descriptor. When the pipeline has no content, the thread will enter a waiting state.
Another thread writes content into the pipeline by writing file descriptors. When writing content, if there is a thread on the other end waiting for content in the pipeline, then the thread will be awakened;
How does this waiting and wake up operation take place? This requires the use of the epoll mechanism in the Linux system. The epoll mechanism in the Linux system has been improved to handle large batches of handles. It is an enhanced version of the multiplexed IO interface select/poll under Linux. It can significantly reduce the system CPU utilization in a large number of concurrent connections;
That is, when there is content in the pipeline that can be read, the thread that is currently waiting for content in the pipeline is awakened;
How to prove that the thread is suspended
@Override public void onCreateData(@Nullable Bundle bundle) { new Thread() { @SuppressLint("HandlerLeak") @Override public void run() { (); (" = " + ().getId()); (); Handler handler = new Handler(()) { @Override public void handleMessage(Message msg) { (msg); (" = " + ().getId() + ", what = " + ); } }; ("loop."); // Execute (); // Execute ("loop."); // Cannot execute } }.start(); }
The above is the detailed analysis of the interview questions for Android asynchronous task and message mechanism. For more information about Android asynchronous task message mechanism, please pay attention to my other related articles!