"* * * * * * * * * * *" in the process of Android application development, we need to always pay attention to ensure the stability of the application and UI operation response timely, because the unstable or slow response to the application will bring bad impression on the app, serious users uninstall your app, so your efforts will not reflect the value of. This paper tries to explain the internal realization mechanism from the role of Asnyctask. If you have some experience in the development of people, after reading should be used in the process of asnyctask some of the problems to be enlightened, the development experience is not rich can also find the use of attention points in the process.
Why introduce Asnynctask?
When the Android program starts running, a single process is started, and by default all of the program actions are performed in this process. An Android program has only one process by default, but a process can have threads.
In these threads, there is a thread called the UI thread, also called the main thread, which can be called worker thread except for threads other than main thread. Main thread is responsible for controlling the UI page display, update, interaction and so on. Therefore, the shorter the action requirement in the UI thread, the better, the more smooth the user will feel. A good practice is to encapsulate some of the more time-consuming operations, such as network requests, database operations, and complex computations, into separate threads so that the main thread can be avoided. For this reason, someone has written the following code:
Private TextView TextView; public void OnCreate (bundle bundle) {super.oncreate (bundle); Setcontentview (R.LAYOUT.THREAD_ON_UI); TextView = (TextView) Findviewbyid (r.id.tvtest); New Thread (New Runnable () {@Override public void run () {try {Ht Tpget httpget = new HttpGet ("http://www.baidu.com"); HttpClient HttpClient = new Defaulthttpclient (); HttpResponse Httpresp = Httpclient.execute (HttpGet); if (Httpresp.getstatusline (). Getstatuscode () = = () {String result = entityutils.tostring (httpres P.getentity (), "UTF-8"); Textview.settext ("The request returns to normal, the result is:" + result); } else {Textview.settext ("request returns an exception! "); }}catch (IOException e) {e.printstacktrace (); }}). Start (); }
Run, as expected, the exception information is as follows:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How did it break? The handler object can be created on the main thread, replacing the Textview.settext place with handler to send the return value back to the thread where the handler is located, that is, the main process. This approach is slightly more complicated, and Android gives us a lightweight asynchronous class that can directly inherit Asynctask, implement asynchronous operations in the class, and provide interface feedback on the results of the current asynchronous execution and the progress of the execution, which are directly running in the main thread. , such as the Onpostexecute,onpreexecute method.
In other words, the Android program is multithreaded, in order to more easily handle the interaction of child threads and UI threads, Asynctask is introduced.
Asnynctask internal mechanism
Asynctask internal logic consists of two parts:
1, * * Interaction with the main line * *, it internally instantiates a static custom class Internalhandler, this class is inherited from handler, in this custom class is bound to a object called Asynctaskresult, each child thread needs to notify the main thread, Sends a message to handler on call Sendtotarget. Then in handler's Handlemessage asynctaskresult depending on the type of message (for example message_post_progress updates the progress bar, Message_post_ Cancel task) and do different things, it is worth mentioning that these actions are done on the UI thread, meaning that the handler object is automatically called from the child thread once it is required to interact with the UI thread, and the message is placed on the main thread. Source Address
Mfuture = new Futuretask<result> (mworker) {@Override protected void more ... done () { Message message; result = NULL; try {result = get (); } catch (Interruptedexception e) {ANDROID.UTIL.LOG.W (Log_tag, E); } catch (Executionexception e) {throw new RuntimeException ("An error occured while executing doinbackg Round () ", E.getcause ()); } catch (Cancellationexception e) {message = Shandler.obtainmessage (Message_post_cancel, New Asynctaskresult<result> (Asynctask.this, (result[]) null); Message.sendtotarget (); Return } catch (Throwable t) {throw new RuntimeException ("An error occured while executing" + "Doinbackground ()", t); } message = Shandler.obtainmessage (Message_post_result, New asynctaskresult< Result> (asynctask.this, result)); Message.sendtotarget (); } };
private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void More ...handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }
2, **asynctask Internal Dispatch * *, although you can create a new instance of multiple Asynctask subclasses, but Asynctask's internal handler and threadpoolexecutor are static, so defined variables belong to the class, is process-wide, so asynctask controls all subclass instances in the process scope, and all instances of the class share a thread pool and handler. The code is as follows:
Public abstract class Asynctask<params, Progress, result> {private static final String Log_tag = "Asynctask"; private static final int core_pool_size = 5; private static final int maximum_pool_size = 128; private static final int keep_alive = 1; private static final blockingqueue<runnable> Sworkqueue = new linkedblockingqueue<runnable> (10); private static final Threadfactory sthreadfactory = new Threadfactory () {private final atomicinteger MCount = NE W Atomicinteger (1); Public Thread More ... newthread (Runnable r) {return new Thread (R, "Asynctask #" + mcount.getandincrement ()); } }; private static final Threadpoolexecutor Sexecutor = new Threadpoolexecutor (core_pool_size, Maximum_pool_size, K Eep_alive, Timeunit.seconds, Sworkqueue, sthreadfactory); private static final int message_post_result = 0x1; private static final int message_post_progress = 0x2; private static final int MESSage_post_cancel = 0x3;
As you can see from the code, the size of the default core thread pool is 5, and the cache task queue is 10. This means that if the thread pool has fewer than 5 threads, a new asynchronous task will create a new thread at this time, and if the number of thread pools is greater than or equal to 5, a new asynchronous task will be placed in the cache queue for execution. Restricting the number of threads in an app's asynctask may seem necessary, but it also poses a problem if someone needs to run 10 instead of 5, or limit the number of threads, such as the loading of n multiple images in some app's waterfall stream pages.
On the other hand, at the same time running many tasks, threads are more, if these tasks are to access the network, will lead to a short period of time the phone that poor bandwidth is accounted for, so the overall performance of who are hard to quickly load completely, because they are competitive relationship. So, give the choice to the developer.
In fact, probably starting with Android from 3.0, each time you create a new asynchronous task, the Asnyctask internal default rule is to run only one asynchronous task at a time in the order in which it was committed. Of course, you can also specify your own thread pool yourself.
It can be seen that there are a lot of places to pay attention to during the Asynctask
- Because handler needs to interact with the main thread, and handler is built into Asnyctask, Asynctask must be created in the main thread.
- The Asynctaskresult Doinbackground (Mparams) method executes an asynchronous task that runs in a child thread, and other methods run in the main thread to manipulate the UI component.
- Do not manually call Asynctask's OnPreExecute, Doinbackground, publishprogress, Onprogressupdate, OnPostExecute method, These are automatically called by the Android system.
- A task Asynctask task can only be executed once.
- You can cancel a task at any time by calling the Cancel (Boolean) method in the run, if the successful call to IsCancelled () returns True, and the OnPostExecute () method is not executed, instead of calling the Oncancelled () method. And from the source, if this task has been executed at this time call cancel will not really end the task, but continue to execute, but change is the execution of the callback method is OnPostExecute or oncancelled.
Asnynctask and Activity Onconfiguration
There are so many points of attention mentioned above, do you have anything else to pay attention to? Of course! When we use Asynctask to request network data in the process of developing the app, it is customary to show the progress bar in OnPreExecute, OnPostExecute close the progress bar after the data request is completed. This may seem perfect, but if your app does not explicitly specify screen orientation and configchanges, the activity restarts when the user rotates the screen, and this time your asynchronous load data thread might be requesting the network. When a new activity is re-created, a new task may be restarted to request the network, so that a previous asynchronous task inadvertently leaked, assuming you still write some other logic in OnPostExecute, this time there will be unexpected exceptions.
Generally simple data types, dealing with configchanges we are good at handling, we can directly through Onsaveinstancestate () and Onrestoreinstancestate () to save and restore. Android will call the Onsaveinstancestate () method before destroying your activity, so you can store data about the state of the app in this method. You can then recover from the OnCreate () or Onrestoreinstancestate () method.
But what about the asynctask? The problem stems from the fact that the activity was destroyed during the re-creation process and the Asynctask and previous activity was lost, resulting in some problems. Then the idea of solving the problem can also be developed in this direction. The official Android documentation also has some clues to solve the problem.
Here is another solution that uses event bus, written by an android from abroad. In the middle of the square open source Eventbus class library http://square.github.io/otto/. First, customize a subclass of Asynctask, and in the OnPostExecute method, throw the return result to the event bus, the code is as follows:
@Override protected String doInBackground(Void... params) { Random random = new Random(); final long sleep = random.nextInt(10); try { Thread.sleep(10 * 6000); } catch (InterruptedException e) { e.printStackTrace(); } return "Slept for " + sleep + " seconds"; } @Override protected void onPostExecute(String result) { MyBus.getInstance().post(new AsyncTaskResultEvent(result)); }
The event bus is registered in the activity's oncreate so that the message of the asynchronous thread is Otta distributed to the currently registered activity, and this time the return result is in the current activity's Onasynctaskresult, the code is as follows:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.otto_layout); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(); } }); MyBus.getInstance().register(this); } @Override protected void onDestroy() { MyBus.getInstance().unregister(this); super.onDestroy(); } @Subscribe public void onAsyncTaskResult(AsyncTaskResultEvent event) { Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show(); }
The personal feeling of this method is quite good, of course, you can not otta this library, their own individual use of the interface callback method can also be estimated to achieve, we can try.
How to monitor Asynctask?
However, although the asynctask is very useful, but there are many problems, there are many places to pay attention to. What if there's a problem? Customer feedback A problem, the first reaction of the developer is: "This is not possible ah, I test no problem here!" "However, the boss lets you solve the problem, the customer's environment can not be reproduced, the operation steps can not be repeated, this time the development is puzzled ... In short, it is very important to monitor this matter.
ONEAPM, an Application performance management (APM) leader, provides a new generation of application performance management software and services that can be easily implemented by enterprise users and developers, with slow program code and real-time crawling of SQL statements. ONEAPM launched the mobile Insight for the application performance monitoring product of the moving end, the user can visit the ONEAPM official website, download the mobile Monitoring SDK, test under Asynctask.
——
The author of this article is compiled and collated by ONEAPM engineers. ONEAPM is the emerging leader in the field of basic software in China. Focus on delivering next-generation application performance management software and services to help enterprise users and developers easily implement: Slow program code and real-time crawling of SQL statements. To read more technical articles, please visit the ONEAPM Official technology blog.
Android Developer: Do you really want to use Asynctask?