編輯:Android編程入門
AsyncTask是Android給開發者提供的一個簡單輕量級的多線程類,通過它我們可以很容易新建一個線程讓在後台做一些耗時的操作(如IO操作、網絡訪問等),並在這個過程中更新UI。之所以說它輕量級,是因為不需要直接使用Handler、Thread等知識,使用起來比較簡單,但也失去了一些靈活性,對於一些復雜的場景處理起來不方便。
如果一個APP進程中同時只創建和運行一個AsyncTask實例,則不會有任何問題。但如果在一個進程中如果有多個AsyncTask任務同時在執行,問題就比較復雜了。下面我們通過例子來看(我們例子是在Android 4中運行的)。
一、測試1(默認多個Task是串行執行的)
1、創建一個默認的app工程
2、創建一個類繼承AsyncTask,代碼如下
package com.example.asynctaskdemo; import android.os.AsyncTask; public class MyAsyncTask extends AsyncTask<String, Void, String>{ private String name; public MyAsyncTask(String name){ this.name = name; } @Override protected String doInBackground(String... params) { System.out.println(name+" is run "+System.currentTimeMillis()+" thread id "+Thread.currentThread().getId()); try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); } return null; } }
3、在Activity的onCreate方法中使用該Task,代碼如下
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for(int i=1;i<5;i++){ MyAsyncTask task = new MyAsyncTask("task"+i); task.execute(new String[0]); } }
我們在調試窗口,觀察MyAsyncTask打印信息的間隔和順序。發現這創建的4個任務是串行執行的,並不是並發的。
研究了AsyncTask的實現細節,在創建一個AsyncTask並通過其execute方法啟動執行時,AsyncTask並不是創建一個獨立的線程去執行。AsyncTask是通過線程池來管理和調度進程中的所有Task的。
在 Android2.3以前的版本(SDK/API 大於等於10的版本)
多個AsyncTask任務是並發執行的,也就是說如果啟動多個task,則會並發執行。但並發執行的數量取決於AsyncTask內部的線程池限制數量。如果超過了這個限額,新的任務只能等待。
在Android 3.0及以後版本(SDK/API 大於等於11的版本)
Google從Android 3.0開始對AsyncTask的調度執行做出了一些變化,對於execute提交的任務,按先後順序每次只運行一個。也就是說它是按提交的次序,每次只啟動一個線程執行一個任務,完成之後再執行第二個任務,也就是相當於只有一個後台線程在執行所提交的任務。上面的例子就驗證了這一點。
二、讓AsyncTask並發執行
因為默認情況下多個task是串行的,那怎麼樣讓並發執行呢?AsyncTask增加了一個新的接口executeOnExecutor,這個接口允許開發者提供自定義的線程池來運行和調度Thread。我們把上面代碼改下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for(int i=1;i<11;i++){ MyAsyncTask task = new MyAsyncTask("task"+i); //task.execute(new String[0]); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[0]); } }
這裡我們使用了executeOnExecutor方法代替了execute方法。並且executeOnExecutor方法的第一個參數是一個預定義的線程池。這時這幾個task就可以並發執行了。這時我們觀察打印的結果,發現有5個任務並發執行,可以看出有5個不同的線程號,查看AsyncTask的源碼,發現並發線程數跟設備的cpu數量是有關的,因此不同的設備上可能看到的結果不完全一致,這點需要注意。只有前面的5個任務執行完後,才會執行後面的,並且通過打印的線程號可以看出,後面執行的任務是重用原來的線程,並沒有創建新的線程,這就是線程池的作用。
我們再來改下代碼,使用Android提供的另外一個預定義的線程池。代碼如下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for(int i=1;i<11;i++){ MyAsyncTask task = new MyAsyncTask("task"+i); //task.execute(new String[0]); task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, new String[0]); } }
觀察打印信息我們可以發現,這和調用execute方法一樣,每個任務都是串行執行的。並且這個過程中最多創建了5個新的線程。
三、自定義線程池
我們可以利用java.util.concurrent.Executors中的各種靜態方法創建供AsyncTask執行的線程池 ,可以指定線程的數量和調度的方式。其方法很多,我們這裡介紹其中兩種較為常用的。
1、讓每個AsyncTask任務都單獨起一個線程執行,也就是說所有的都是並發的。代碼如:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); for(int i=1;i<11;i++){ MyAsyncTask task = new MyAsyncTask("task"+i); task.executeOnExecutor(newCachedThreadPool, new String[0]); } }
通過觀察打印可以看出,這多個任務都是並發執行的。
2、創建指定線程數量的線程池,並發數上限就是指定的線程數。但新任務產生,沒有空閒的線程,就只能等待。代碼如:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3); for(int i=1;i<11;i++){ MyAsyncTask task = new MyAsyncTask("task"+i); task.executeOnExecutor(newFixedThreadPool, new String[0]); } }
通過觀察可以看出,有3個線程在並發執行。
總結下,從上面的例子中可以看出,如果一個進程中存在多個TASK需要並發執行的情況,那就需要用到AsyncTask一些更深的知識,需要考慮的問題更多。
Activity的完整生命周期如下圖:Activity的加載模式有四種:standard: 標准模式,默認的加載模式,每次通過這種模式啟動目標Acitivity,都創建一
這幾天在回顧Android的基礎知識,就把一些常見的知識點整理一下,以後忘了也可以翻出來看一看。簡單介紹一下Activity的生命周期在API文檔中對生命周期回調的函數描
通常情況下,Android實現自定義控件無非三種方式。 Ⅰ、繼承現有控件,對其控件的功能進行拓展。 Ⅱ、將現有控件進行組合,實現功能更加強大控件。 Ⅲ、重寫View
activity_ui6.xml<?xml version=1.0 encoding=utf-8?><GridView xmlns:android=ht