線程按功能分的話,可以分為兩個,一個是主線程(UI線程),其他的都是子線程主線程不能執行那些耗時過長的代碼或任務(執行耗時過長的代碼會出現應用未響應的提示),是以都是使用子線程來執行耗時過長的代碼,比如說下載下傳檔案等任務。
一般情況,子線程中執行過長的代碼,都需要進行更新UI操作。
但是Android中,為了防止安全,是不允許在子線程更新UI的,但是我們可以使用到Android官方給予的API來實作子線程更新UI的操作(本質上,這些API也是切換回了主線程來進行更新UI)
例子:點選一個按鈕,過了1s後完成了下載下傳任務,傳回了資料,此資料會顯示在界面上

具體解釋:
點選按鈕,之後開啟一個子線程來模拟下載下傳過程(線程休眠1s),之後任務執行完畢會傳回資料(一個String),使用傳回的資料更新UI
建立一個方法,用來模拟下載下傳任務
/** * 模拟下載下傳 */fun download(): String { Thread.sleep(1000) return "this is data"}
下面的使用6種方式和上面的模拟下載下傳任務的方法,來實作上面例子的效果
1.Activity.runOnUiThread()
runOnUiThread是Activity中的方法,隻有目前對象是Activity,就可以直接使用,如果目前的對象不是Activity,需要找到Activity對象,才能執行此方法
runOnUiThread方法的參數為一個Runnable接口,我使用的kotlin,是以有很多東西都是省略了
設定按鈕的點選事件,點選按鈕開啟一個線程
btn_start.setOnClickListener { thread { val data = download() runOnUiThread({ //這裡進行更新UI操作 tv_show.text = data }) }}
Java版
btn_start.setOnClickListener(new OnClickListener(){ new Thread(new Runnable(){ String data = download(); runOnUiThread(new Runnable(){ @Override public void run() { tv_show.setText(data); } }) }).start();});
2.View.post()
post方法是View對象的方法,參數也是接收一個runnable接口
這裡我選用的view對象是需要進行更新textview的本身,當然也可以選用其他的View對象,隻要是在目前Activity的對象都可以
btn_start.setOnClickListener { thread { val data = download() //選擇目前Activity的View對象都可以 tv_show.post { tv_show.text = data } }}
3.View.PostDelayed()
此方法和上面的post方法類似,隻是多一個參數,用來實作延遲更新UI的效果
btn_start.setOnClickListener { thread { val data = download() tv_show.postDelayed({ tv_show.text = data },2000) }}
上面的代碼實作的效果是點選按鈕之後,過了3s後才會看到界面發生改變
4.Handler.post()
new一個Handler對象(全局變量)
private val handler = Handler()
使用post方法更新UI,此post方法和之前的post方法一樣,參數都是為Runnable接口
btn_start.setOnClickListener { thread { val data = download() handler.post { tv_show.text = data } }}
5.AsyncTask(推薦)
說明
AsyncTask是一個抽象類,必須建立一個子類類繼承它
這裡介紹一下關于AsyncTask的三個泛型參數和幾個方法
泛型參數可以為任意類型,為空的話使用Void
參數 說明 params 參數泛型,doInBackground方法的參數 progress 進度泛型,onProgressUpdate方法的參數 result 結果泛型,onPostExecute方法的參數 抽象方法說明:
方法名 說明 onPreExectute() 此方法中,常常進行初始化操作,如進度條顯示 doInBackground(Params...) 此方法必須實作, onProgressUpdate(Progress...) 進行更新UI的操作 publishProgress(Progress...) 在doInBackground方法中調用,調用此方法後會回調執行onProgressUpdate方法進行更新UI onPostExcute(Result) 任務結束之後進行更新UI 簡單來說,如果子類繼承了AsyncTask,它的抽象方法的參數都會變成泛型對應的類型
例子
下面的代碼是取自我的APP,簡單地說明一下AsyncTask
我傳入的是3個泛型參數分别為String,DownloadingItem,DownloadedItem,分别對應的params,progress和result泛型
這裡我是根據自己的需要而兩個類DownloadingItem和DownloadedItem,從下面的代碼可以看到,抽象方法的參數變為了我們的泛型的類型
internal inner class DownloadingTask : AsyncTask() { override fun onPreExecute() { //一些初始化操作 } override fun doInBackground(vararg params: String?): DownloadedItem { //params是一個參數數組,如果建立DownloadingTask對象隻傳入了一個參數,直接取下标為0的那個即可(需要轉型) //耗時操作(如下載下傳操作),獲得進度資料 //将新的進度資料傳遞到onProgressUpdate方法,更新UI publishProgress(messageItem) //任務執行完畢,傳回結果(回調onPostExecute方法) } override fun onProgressUpdate(vararg values: DownloadingItem?) { //這裡使用最新的進度來進行相關UI的更新 //values是一個DownloadingItem數組,取末尾那個即為最新的進度資料 } override fun onPostExecute(result: DownloadedItem?) { //下載下傳成功提示或者是其他更新UI的操作 }}
執行:
執行Task的時候需要在主線程(UI線程調用)
DownloadingTask().execute("參數")
批量下載下傳:
//允許在同一時刻有5個任務正在執行,并且最多能夠存儲50個任務private val exec = ThreadPoolExecutor(5, 50, 10, TimeUnit.SECONDS, LinkedBlockingQueue())DownloadingTask().executeOnExecutor(exec, url)
6.Handler機制實作(核心)
其實,Handler機制是子程序更新UI的核心
我們上面的五種實作子程序更新UI的方式,都是基于Handler機制實作的
具體機制本文就不多說了,網上有許多的機制說明,這裡就隻講一下實作的步驟
Message中有幾個屬性,what,arg1,arg2,這三個都是接收一個Int,是以,傳遞資料不是很友好,這裡就不準備實作之前的例子效果了
what表示來源,arg1和arg2用來傳遞Int資料
1.重寫Handler類中的handleMessage方法
一般都是規定好一個Int的常量,來表示what
private val handler =object : Handler(){ override fun handleMessage(msg: Message?) { if (msg.what == 1) { //來源為1,則 } }}
2.發送Message
val msg = handler.obtainMessage()//一般都是規定好一個Int的常量,來表示whatmsg.what = 1//傳遞Int資料msg.arg1 = 20handler.sendMessage(msg)