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

来源:https://www.cnblogs.com/kexing/archive/2019/10/13/11666803.html
-Advertisement-
Play Games

Android中線程按功能分的話,可以分為兩個,一個是主線程(UI線程),其他的都是子線程 主線程不能執行那些耗時過長的代碼或任務(執行耗時過長的代碼會出現應用未響應的提示),所以都是使用子線程來執行耗時過長的代碼,比如說下載文件等任務 一般情況,子線程中執行過長的代碼,都是需要進行更新UI操作。 ...


Android中線程按功能分的話,可以分為兩個,一個是主線程(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<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)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 優秀文章 https://blog.csdn.net/qq_38351824/article/details/82619734 一、STM32通用定時器(TIM2、TIM3、TIM4和TIM5共四個通用定時器,它們之間完全獨立,不共用任何資源) STM32F1系列的通用定時器是一個通過可編程預分頻器 ...
  • 1.本地Navicat for MySQL無法連接至伺服器(Centos 7 x86_64 bbr) 2.部署到伺服器上後(Centos 7 x86_64 bbr)表單提交亂碼問題 ...
  • 環境準備: master:192.168.0.106:3306slave:192.168.0.105:3306 主和從都必須配置有唯一的ID(server_id:建議ip最後一組+MySQL埠號,例如:1063306,1053306) 採用gtid模式複製,需要配置mysqld開啟以下兩個參數 操 ...
  • 此題有兩個解法: 我初步嘗試用以下 解決問題(要刪除的記錄 肯定大於相同內容的 ): 但是無法通過,究其原因是在 語句中, 與`DELETE`操作不能同時存在. 答案一 因此嘗試新的解法,直接使用刪除語句,結果如下所示: 此答案通過測試,但是效率較低. 答案二 後續思考中發現,可以使用臨時表解決 與 ...
  • 前言 Redis 沒有直接使用 C 語言傳統的字元串表示(以空字元結尾的字元數組,以下簡稱 C 字元串), 而是自己構建了一種名為簡單動態字元串(simple dynamic string,SDS)的抽象類型, 並將 SDS 用作 Redis 的預設字元串表示。 個人感覺SDS類似於Java的Arr ...
  • mysql 的邏輯架構分為三層: 最上層的服務大多數基於網路的客戶端、伺服器的工具或者服務都有類似的架構,比如連接處理,授權認證、安全等 第二層架構:mysql的核心服務功能都在這一層,包括查詢解析,分析,優化,緩存以及所有的內置函數,所有跨存儲引擎的功能都在這一層實現:存儲過程,觸發器、視圖 第三 ...
  • 因為公司基本都是用存儲過程所以本來寫的乾貨基本都是存儲過程的。 用以上語句來說一下例子: 查詢 一定要指定欄位就算你要查全部欄位也不要用*號來代替 ,以及 能用TOP儘量TOP 避免沒必要的鎖 必須加 WITH(NOLOCK) 避免產生沒有必要的鎖出來。 因為欄位多,數據多一個索引沒有走。 加了欄位 ...
  • 本文基於 Android 9.0 , 代碼倉庫地址 : "android_9.0.0_r45" 文中源碼鏈接: "SystemServer.java" "SystemServiceManager.java" "SystemService.java" 首先來回顧一下上篇文章 "Java 世界的盤古和女 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...