Asynchronous Android读书笔记二Staying Responsive with AsyncTask

来源:互联网 发布:阿里云服务器学生9块 编辑:程序博客网 时间:2024/05/12 19:16
  • Introducing AsyncTask



    If we invoke AsyncTask's cancel method before doInBackground completes,onPostExecute will not be called. Instead, the alternative onCancelled callback method is invoked so that we can implement different behavior for a successful versus cancelled completion. 


1、 AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。

2、线程池中的工作线程执行doInBackground(mParams)方法执行异步任务

3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数


  • Declaring AsyncTasktypes

     public class PrimesTask   extends AsyncTask<Integer, Integer, BigInteger> {
         private TextView resultView;
         public PrimesTask(TextView resultView) {       this.resultView = resultView;

    }

         @Override     protected BigInteger doInBackground(Integer... params) {
           int n = params[0];       BigInteger prime = new BigInteger("2");       for (int i=0; i<n; i++) {
             prime = prime.nextProbablePrime();       }
           return prime;     }
         @Override     protected void onPostExecute(BigInteger result) {
             resultView.setText(result.toString());     }
    page31image13576


  • Executing AsyncTasks

    new PrimesTask(resultView).execute(500);
    params for: doInBackground(Integer... params)

  • Providing feedback to the user

    handle with:
    onPreExecute() 
    onPostExecute(BigInteger result)

  • Providing progress updates

    doInBackground(Integer... params) { 

    publishProgress(percent);

       }
     onProgressUpdate(Integer... values) {
         progress.setProgress(values[0]);   }

  • Canceling AsyncTasks

    public final boolean cancel(boolean mayInterruptIfRunning)

    Simply invoking cancel is not sufficient to cause our task to finish early. We need to actively support cancellation by periodically checking the value returned from isCancelled and reacting appropriately in doInBackground

     protected BigInteger doInBackground(Integer... params) {     int primeToFind = params[0];     BigInteger prime = new BigInteger("2");     for (int i=0; i<primeToFind; i++) {

    prime = prime.nextProbablePrime();
    int percentComplete = (int)((i * 100f)/primeToFind);publishProgress(percentComplete);
    if (isCancelled())

    break;

    }

         return prime;   }
    instead of onPostExecute

    we have the opportunity to implement different behavior for a cancelled executionby implementingonCancelled. There are two variants of this callback method:

    //If we are performing an incremental computation in our AsyncTask 
    protected void onCancelled(Result result);
    //If AsyncTask can provide either a complete result (such as a fully downloadedimage) or nothing, then we will probably want to override the zero argu///mentonCancelled() method. 
    protected void onCancelled();


  • Handling exceptions

    For the callback methods that run on the main thread—onPreExecute,onProgressUpdate,onPostExecute, and onCancelled—we can catch exceptionsin the method and directly update the user interface to alert the user.

    Of course, exceptions are likely to arise in our doInBackground method too, as this is where the bulk of the work of AsyncTask is done, but unfortunately, we can't update the user interface from doInBackground. A simple solution is to have doInBackground return an object that may contain either the result or an exception, as follows: 

     @Override   protected final Result<T> doInBackground(Void... params) {
         Result<T> result = new Result<T>();     try {
           result.actual = calculateResult();     } catch (Exception exc) {
           result.exc = exc;     }
           return result;   }
       protected abstract T calculateResult() throws Exception;

    Now we can check inonPostExecute for the presence of an Exception in the Result object. If there is one, we can deal with it, perhaps by alerting the user;otherwise, we just use the actual result as normal.

       @Override   protected final void onPostExecute(Result<R> result) {
           if (result.exc != null) {           // ... alert the user ...
           } else {           // ... success, continue as normal ...

    }} 


  • Controlling the level of concurrency

    1.The original goal of AsyncTask was to help developers avoid blocking the main thread. In its initial form at API level 3, AsyncTasks were queued and executed serially (that is, one after the other) on a single background thread, guaranteeing that they would complete in the order they were started. 

    2.This changed in API level 4 to use a pool of up to 128 threads to execute multiple AsyncTasks concurrently with each other—a level of concurrency of up to 128. So there are no guarantees that AsyncTasks will complete in the same order they were started 

    3.As a result, a further change was made at API level 11, switching back to serial execution by default, and introducing a new method that gives concurrency control back to the app developer:

       public final AsyncTask<Params, Progress, Result>   executeOnExecutor(Executor exec, Params... params)

    Implementations of Executor may run tasks sequentially using a single thread(SERIAL_EXECUTOR)

    ,use a limited pool of threads to control the level of concurrency, or even directly create a new thread for each task. (THREAD_POOL_EXECUTOR: This Executor runs tasks using a pool of threads for efficiency (starting a new thread comes with some overhead cost that can be avoided through pooling and reuse).THREAD_POOL_EXECUTOR is an instance of the JDK class ThreadPoolExecutor, which uses a pool of threads that grows and shrinks with demand. In the case of AsyncTask, the pool is configured to maintain at least five threads, and expands up to 128 threads. )

    As the default behavior of execute since API level 11 is to run AsyncTasks serially on a single background thread, the following two statements are equivalent:

       task.execute(params);   task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);

    Besides the default executors provided by AsyncTask, we can choose to create our own. For example, we might want to allow some concurrency by operating off a small pool of threads, and allow many tasks to be queued if all threads are currently busy.

    This is easily achieved by configuring our own instance ofThreadPoolExecutoras a static member of one of our own classes—for example, our Activity class.Here's how we might configure an executor with a pool of four to eight threads and an effectively infinite queue:

       private static final Queue<Runnable> QUEUE =     new LinkedBlockingQueue<Runnable>();
       public static final Executor MY_EXECUTOR =     new ThreadPoolExecutor(4, 8, 1, TimeUnit.MINUTES, QUEUE);

    The parameters to the constructor indicate the core pool size (4), the maximum poolsize (8), the time for which idle additional threads may live in the pool before being removed (1), the unit of time (minutes), and the queue to use when the pool threads are busy.

    Using our own Executor is then as simple as invoking our AsyncTaskas follows:task.executeOnExecutor(MY_EXECUTOR, params); 


  • Common AsyncTask issues

    Fragmentation issues 

    1.The most obvious approach is to deliberately target devices running at least Honeycomb, by setting a minSdkVersion of 11 in the Android Manifestfile. 

    2.A second option is to design our code carefully and test exhaustively on a range

    of devices 

    3.A third solution that has been suggested by the Android development community isto reimplementAsyncTaskin a package within your own project, then extend yourownAsyncTaskclass instead of the SDK version.  

    Activity lifecycle issues 

    1.If we continue processing a background task after the Activity has finished, we areprobably doing unnecessary work, and therefore wasting CPU and other resources(including battery life), which could be put to better use. 

    2.Also, any object references held by theAsyncTaskwill not be eligible for garbagecollection until the task explicitly nulls those references or completes and is itselfeligible forGC(garbage collection). Since ourAsyncTaskprobably references theActivity or parts of theViewhierarchy, we can easily leak a significant amount ofmemory in this way. 

    Handling lifecycle issues with early cancellation 

    The most appropriate Activitylifecycle method for this isonPause, which is guaranteed to be called before theActivity finishes.

       protected void onPause() {     super.onPause();     if ((task != null) && (isFinishing()))
           task.cancel(false);   }

    Handling lifecycle issues with retained headlessfragments 

    A Fragmentthat does not manage a view of its own is known as a headlessFragment

     public class PrimesFragment extends Fragment {     private AsyncListener<Integer,BigInteger> listener;     private PrimesTask task;     public void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);       setRetainInstance(true);       task = new PrimesTask();       task.execute(2000);

    }

         public void onAttach(Activity activity) {       super.onAttach(activity);       listener = (AsyncListener<Integer,BigInteger>)activity;
    page44image14064

    }

    public void onDetach() {  super.onDetach();  listener = null;
    }
         class PrimesTask

         extends AsyncTask<Integer, Integer, BigInteger>{
           protected void onPreExecute() {         if (listener != null) listener.onPreExecute();         }
           protected void onProgressUpdate(Integer... values) {         if (listener != null)
               listener.onProgressUpdate(values);       }
           protected void onPostExecute(BigInteger result) {         if (listener != null)
               listener.onPostExecute(result);       }
           protected void onCancelled(BigInteger result) {         if (listener != null)
               listener.onCancelled(result);       }
           // ... doInBackground elided for brevity ...     }
    }
    }

    We're using the Fragmentlifecycle methods (onAttachandonDetach) to add or remove the currentActivityas a listener, andPrimesTaskdelegates directly to it from all of its main-thread callbacks. 

  • Applications of AsyncTask 

    Good candidate applications forAsyncTasktend to be relatively short-livedoperations (at most, for a second or two), which pertain directly to a specificFragmentorActivityand need to update its user interface. 

    AsyncTask is ideal for running short, CPU-intensive tasks, such as numbercrunching or searching for words in large text strings, moving them off the mainthread so that it can remain responsive to input and maintain high frame rates.

    Blocking I/O operations such as reading and writing text files, or loading imagesfrom local files withBitmapFactory, are also good use cases forAsyncTask

    Taking these things into account, and the rate at which complexity increases as wetry to deal with them (for example, retained headless fragments!),AsyncTaskstartsto lose its shine for longer operations. 





0 0
原创粉丝点击