Ensuring the success rate of network requests (inter-thread communication) through HandlerThread

Encountered a bug, because the server in China, foreign users may use because of network reasons even if the request was sent, but the server still did not receive the uploaded data, need to ensure that the client successfully sent the request.
So my idea is to collect failed requests and add them to the request failure queue. After a period of cyclical time, I make a new request for all failed requests in the queue. If successful deletion from the queue, failure will wait for the next request.

I started thinking about Timer, but found that there was a bug in timer.

If TimerTask throws an unchecked exception, Timer will produce unpredictable behavior. The Timer thread does not catch exceptions, so an unchecked exception thrown by TimerTask terminates the timer thread. In this case, Timer will no longer restore thread execution; it mistakenly assumes that the entire Timer has been cancelled. At this point, TimerTask, which has been scheduled but has not yet been executed, will never be executed, nor will new tasks be scheduled.

Then think of the new Scheduled ThreadPool in the four thread pools provided by Java

New CachedThreadPool creates a cacheable thread pool, which can flexibly reclaim idle threads if the length of the thread pool exceeds the processing requirement, and new threads if it is not.
New Fixed ThreadPool creates a fixed-length thread pool that controls the maximum number of concurrent threads, and the threads that exceed it will wait in the queue.
New Scheduled ThreadPool creates a fixed-length thread pool that supports both timing and periodic task execution.
New Single ThreadExecutor creates a single threaded thread pool, which only uses a single worker thread to perform tasks, ensuring that all tasks are executed in a specified order (FIFO, LIFO, priority).

newScheduledThreadPool

Create a fixed-length thread pool to support timed and periodic task execution. The sample code for delayed execution is as follows:

//Thread pool can execute tasks according to time schedule, allowing users to set the scheduled time to execute tasks. The parameter of int type is set.  
//The minimum number of threads in the thread pool. When there are more tasks, the thread pool may automatically create more worker threads to perform tasks  
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
scheduledThreadPool.schedule(new Runnable() {  

    @Override  
    public void run() {  
        System.out.println("delay 3 seconds");  
    }  
}, 3, TimeUnit.SECONDS);  
//Represents a delay of 3 seconds

The sample code is executed periodically as follows:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  

    @Override  
    public void run() {  
        System.out.println("delay 1 seconds, and excute every 3 seconds");  
    }  
}, 1, 3, TimeUnit.SECONDS);  
//Represents execution every 3 seconds after a delay of 1 second.

Scheduled Executor Service is safer and more powerful than Timer. After testing it for half a day under the happy Java project, I think it can be used, but when it comes to Android project, it's a problem.
When a periodic task is executed in a failed callback of a request, task fails and can't create handler in thread that has not been called Looper. prepare () exception is caught.

@Override
public void fail(Object result) {
    executorService.scheduleAtFixedRate(new Runnable() {
       @Override
       public void run() {
            UpdateBusStatusTime updateBusStatusTime = new UpdateBusStatusTime();
            //Network Request
             GetHttpResultTool tool = new GetHttpResultTool(updateBusStatusTime);
             tool.setCallback(new GetHttpResultTool.CallBack() {
                 @Override
                  public void success(Object result) {
                       throw new NullPointerException();
                   }

                  @Override
                  public void fail(Object result) {}
              });
              tool.setPost(true);
              tool.execute();
         }
   },5,5,TimeUnit.SECONDS);
}

Removing the okhttp access request is all right, should be okhttp pot, can not be replaced, and finally found Handler Thread.

HandlerThread

Handler Thread inherits from Thread, so it is essentially a Thread. The difference between Thread and normal Thread is that it not only establishes a thread, but also a message queue. It has its own looper, which allows us to distribute and process messages in our own thread, and provides the get method of our Looper object.
Handler Thread comes with Looper so that it can reuse the current thread through message queues. When a time-consuming task is put into the loop thread, the thread executes the time-consuming task. After execution, the loop thread is in a waiting state until the next time-consuming task is put in. This avoids performance problems caused by creating Thread threads many times.
This is its advantage and disadvantage. Every task will be executed in a queue one by one. Once a task in the queue is executed for too long, it will cause subsequent tasks to be delayed.

Common usage of Handler Thread
  1. Initialization in the onCreate method
    protected void onCreate(Bundle savedInstanceState) {
    //Create an instance object of HandlerThread
    mCheckMsgThread= new HandlerThread("handler_thread");
    //Start a Handler Thread thread
    mCheckMsgThread.start();
    //Building Handler in Subthreads
    mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper(), mSubCallback);
    //Delay 1 minute for loop detection if there is a failed request
    mCheckMsgHandler.sendEmptyMessageDelayed(1, 60 * 1000);
    }
  2. Constructing a Loop Message Processing Mechanism
    private Handler.Callback mSubCallback = new Handler.Callback() {
         //The implementation of this interface deals with asynchronous time-consuming tasks, so the method is executed in sub-threads.
         @Override
         public boolean handleMessage(Message msg) {
             checkForUpdate();
             //Retry every five minutes
             mCheckMsgHandler.sendEmptyMessageDelayed(1, 5 * 60 * 1000);
             return false;
         }
     };
    /**
     * Repeated execution of requests
     */
     private void checkForUpdate() {
         if (errorQueueMap != null && errorQueueMap.size() > 0) {
             for (final Map.Entry<UpdateBusStatusTime, Boolean> entry : errorQueueMap.entrySet()) {
                 if (!entry.getValue()) {
                     //Re-execute network requests after failure
                     final GetHttpResultTool tool = new GetHttpResultTool(entry.getKey());
                     tool.setCallback(new GetHttpResultTool.CallBack() {
                         @Override
                         public void success(final Object result) {
                             Log.i("", "Network Success, Cycle End " + result);
                             entry.setValue(true);
                             //Now that I'm done, I'll notify the main thread of the message.
                             mUIHandler .sendEmptyMessage(2) ;
                         }
                         @Override
                         public void fail(Object result) {
                             Log.i("", "The network request failed and the loop continued " + result);
                         }
                     });
                 }
             }
         }
     }
  3. Build UI thread Handler to process messages

     private Handler mUIHandler = new Handler(){
         @Override
         public void handleMessage(Message msg) {
             //When the handler of the sub-thread processes the event, it notifies the user what to do in the UI thread, such as playing a toast to remind the user.
         }
     };
  4. Exit the cycle
    Looper drives the message loop by calling the loop method: a message is blocked from the MessageQueue and then handled by the Handler, which is a dead-loop method. So on Destroy, you need to exit to release resources.

    mCheckMsgThread.quit() ;
Native inter-thread communication

To mention briefly, there are ways to communicate between threads in native Java without Handler. Only the method is not elegant enough, or it will cause the cpu computing load to be too high or deadlock. And often because the technology is not in place, leading to overturning. So the Hanlder communication mechanism in android is very clever to avoid such problems and provide some reference learning for native methods.

  • Shared memory variables use synchronous locks (deadlock-prone)
  • Shared Memory Variables Judge Variable Status (Extremely Memory-consuming)
  • pipeline communication
summary
  1. HandelrThread is a thread with Looper, so it can only be used as a sub-thread, so it also implements the ability to perform network request operations in a circular way.
  2. Handler Thread processing tasks are executed serially and processed in the order in which messages are sent. There is no conflict between asynchronous network requests.
  3. With mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper(), mSubCallback), the mCheckMsgHandler obtains the Loper of the Handler Thread thread, which connects them together. The reason why this code is executed after mCheckMsgThread.start() is that the run() method can run after start, the Looper can be created, and the mCheckMsgThread.getLooper() can be correctly obtained.
  4. The communication between threads is realized by using Handler Thread to connect the handler of sub-threads and the handler of UI threads.

Reference resources
http://www.cnblogs.com/wufeng0927/p/5374191.html
http://www.jianshu.com/p/69c826c8a87d
http://blog.csdn.net/m0_37837382/article/details/70143224

Keywords: network Java Android OkHttp

Added by sageman on Mon, 10 Jun 2019 02:02:52 +0300