android asynchronous task design elaboration of AsyncTask

  • 2020-05-26 10:05:25
  • OfStack

This is based on the logic I extracted from looking at the Android source code, so it won't be 100% (or maybe 0%) true to the original design of the Google engineers, but this article will certainly help you understand AsyncTask, and there may be something you haven't found before.

As you all know, the main thread of Android (also called UI threads, thread ID to 1) 1 some limitation strategy, make the main thread can't do some things, such as access to the network is not allowed, otherwise it is, but after 2.3 version, you can change its limit strategy by adding the following code, to force the main thread can access the network:


if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

But StrictMode is a development tool is mainly used to detect the main thread of disk and network access, and not let you do this "bad", actually Android this restriction is beneficial, forcing developers to attach importance to the user experience, a negative example is Windows, what can be done in the main thread, 1 some lazy developers put all the tasks on the main thread, thread often good makes CARDS, such as editor UE or Notepad + + opened on a network, such as samba server files, if the network is down suddenly, your whole editor is stuck, It takes a long time to get a response, but I'm not sure if that's because the network is being accessed in the main thread, but Windows often gets stuck with that. Another positive example is iOS, which is extremely responsive, so when a user enters an event, its kernel has a corresponding schedule to respond to user actions first.

Or get back to business, is for the thread of the these restrictions forcing developers to write multiple threads, of course, you can also need not AsyncTask, but you don't have to also avoid the multithreading, if you don't have to, is likely to use Handler and Thread, I think many beginners, it is so dry, including me, because at that time is likely to have not yet found the class, so he often write Handler and Thread, 1 was found something I write this code is the same, you write Handler and Thread anonymous classes must be duplicated code, as follows:


[final Handler handler = new Handler() {
    public void handleMessage(Message msg) {
     System.out.println("The hard have done!");
     // ... front end code
    }
};
new Thread() {
    public void run() {
        doHardWork();
        handler.sendEmptyMessage(0);
    }
    private void doHardWork() {
        // ... back end code
    }
}.start();

You might want to reuse this code, of course, you can reuse this code by Copy, just write your code in the ellipsis, but a better way to reuse it is to encapsulate it in a class, ok, so let's just encapsulate it 1, so it looks like this:


public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            //...
        }
    };

    public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

            private void doHardWork() {
                // ...
            }
        };
    }       
}

We can add two methods, one in the foreground and one in the background. As long as we define a new class, we can reuse the Copy code. So the code looks like this:


public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            runInFrontend();    // added
        }
    };

    public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }
            private void doHardWork() {
                runInBackend();    //added
            }
        };
    }

    //added
    protected void runInBackend() {
    }

    //added
    protected void runInFrontend() {
    }
}

A reusable class is out, so let's write a subclass and call 1 with an Activity:


public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new SubHandlerAndThread().doInBackground();
    }

    class SubHandlerAndThread extends HandlerAndThread {
        protected void runInBackend() {
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        protected void runInFrontend() {
            System.out.println("Task has been done");
        }
    }
}

That isn't directly write Thread and Handler concise than the many, I am here to analog with sleep affairs for a long time, if in a real environment, we may be to download, if is to download, we may want to download address parameter to a background thread, to make him according to our need to download, we gave doInBackground method with a parameter, then HandlerAndThread class code becomes like this:


public class HandlerAndThread {
    ...

    public void doInBackground(final String url) { // added url
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }
            private void doHardWork() {
                runInBackend(url);  // added url
            }
        };
    }

    protected void runInBackend(String url) { // added url
    }

    ...
}

The code for the calling class looks like this:


public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);   //added url
    }

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {    // added url
            System.out.println("Start download from url:" + url);
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        protected void runInFrontend() {
            System.out.println("finish download");
        }
    }
}

If it is the next file, shall we add a progress update method? Then it will look like this again:


public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {        // added
            case 0:
                runInFrontend();
                break;
            case 1:
                runInFrontendProgress(msg.arg1);
                break;
            }
        }
    };
    ...
    final protected void publishProgress(int progress) {    // added
        handler.obtainMessage(1, progress, 0);
    }

    protected void runInFrontendProgress(int progress) {    // added
    }
}
public class MainActivity extends Activity {
    ...

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrack();
                }
                publishProgress(i*10);    // added
            }
        }

        ...

        @Override
        protected void runInFrontendProgress(int progress) { // added
            System.out.println("Progress: " + progress);
        }
    }
}

You may have run out of patience with the evolution of version 1 and version 1, then I will jump 1, 1 time to add a few more need: 1. We have downloaded the file may want to get the path, so we add an input parameter to the runInFrontend method filePath to represent the path; 2. 2. Change the method that the subclass must implement into an abstract method, and the class into an abstract method; I changed the names of some of the methods in the code by 1 to make it easier to understand. I changed doInBackground to execute, runInFrontend to onPostExecute, and runInFrontendProgress to onProgressUpdate. The final version is as follows:


public abstract class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 0:
                onPostExecute((String) msg.obj);
                break;

            case 1:
                onProgressUpdate(msg.arg1);
                break;
            }
        }
    };

    public void doInBackground(final String url) {
        new Thread() {
            public void run() {
                String result = runInBackend(url);
                handler.obtainMessage(0, result);
            }

        };
    }

    final protected void publishProgress(int progress) {
        handler.obtainMessage(1, progress, 0);
    }

    abstract protected String runInBackend(String url);
    protected void onPostExecute(String filePath) { }
    protected void onProgressUpdate(int progress) {    }
}
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);
    }

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected String runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i*10);
            }

            return "/path/to/file";
        }

        @Override
        protected void onPostExecute(String filePath) {
            System.out.println("Download finished");
        }

        @Override
        protected void onProgressUpdate(int progress) {
            System.out.println("Progress: " + progress);
        }
    }
}

Is this similar to Android's AsyncTask? I think Google made this class because of this requirement. The Android website describes AsyncTask like this:

This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

This class makes it possible to perform operations in the background and then publish the results to the UI thread without using Thread or Handler. In fact, its internal implementation is to encapsulate Thread and Handler, so you don't have to use these two low-level classes directly, but its purpose is also code reuse, and its implementation is similar to what we wrote above. The main differences are as follows: 1. AsyncTask USES the thread pool rather than a single thread to perform background tasks, the thread pool is Shared by the entire process, because of his thread pool object is a static member variables, this 1 points a lot of people make a mistake, think AsyncTask threads to create more more, this is not absolutely correct, because the thread pool depending on the load dynamic adjustment, and has a maximum value and the idle timeout, AsyncTask configuration is least 5, 128, idle timeout 1 second, of course you also can be configured to the number of threads according to the increasing number of task thread, about the thread pool, can be the reference here, I'll blog about the Java thread pool later; 2. The input and output parameters of AsyncTask use generics; 3. AsyncTask supports interrupting the current task.

Now know the design idea of AsyncTask, is not very simple, so I suggest children to look at 1 it source code, anyway I write code when the habit of looking at the source code, because I will be curious about how it is implemented, look at the source code has a lot of benefits, such as can learn good API design idea, software architecture.


Related articles: