Android開發——實現子線程更新UI

Android中線程按功能分的話,能夠分爲兩個,一個是主線程(UI線程),其餘的都是子線程數組

主線程不能執行那些耗時過長的代碼或任務(執行耗時過長的代碼會出現應用未響應的提示),因此都是使用子線程來執行耗時過長的代碼,好比說下載文件等任務安全

通常狀況,子線程中執行過長的代碼,都是須要進行更新UI操做。async

可是Android中,爲了防止安全,是不容許在子線程更新UI的,可是咱們可使用到Android官方給予的API來實現子線程更新UI的操做(本質上,這些API也是切換回了主線程來進行更新UI)ide

**例子:**點擊一個按鈕,過了1s後完成了下載任務,返回了數據,此數據會顯示在界面上post

具體解釋:ui

點擊按鈕,以後開啓一個子線程來模擬下載過程(線程休眠1s),以後任務執行完畢會返回數據(一個String),使用返回的數據更新UIthis

新建一個方法,用來模擬下載任務url

/**
 * 模擬下載
 */
fun download(): String {
	Thread.sleep(1000)
	return "this is data"
}

下面的使用6種方式和上面的模擬下載任務的方法,來實現上面例子的效果線程

1.Activity.runOnUiThread()

runOnUiThread是Activity中的方法,只有當前對象是Activity,就能夠直接使用,若是當前的對象不是Activity,須要找到Activity對象,才能執行此方法code

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<String, DownloadingItem, DownloadedItem>

我傳入的是3個泛型參數分別爲StringDownloadingItemDownloadedItem,分別對應的paramsprogressresult泛型

這裏我是根據本身的須要而兩個類DownloadingItemDownloadedItem,從下面的代碼能夠看到,抽象方法的參數變爲了咱們的泛型的類型

internal inner class DownloadingTask : AsyncTask<String, DownloadingItem, DownloadedItem>() {

	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<Runnable>())
DownloadingTask().executeOnExecutor(exec, url)

6.Handler機制實現(核心)

其實,Handler機制是子進程更新UI的核心

咱們上面的五種實現子進程更新UI的方式,都是基於Handler機制實現的

具體機制本文就很少說了,網上有許多的機制說明,這裏就只講一下實現的步驟

Message中有幾個屬性,whatarg1arg2,這三個都是接收一個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的常量,來表示what
msg.what = 1
//傳遞Int數據
msg.arg1 = 20
handler.sendMessage(msg)
相關文章
相關標籤/搜索