㈠ 為什麼loop之後就可以子線程更新ui
我們常常聽到這么一句話:更新UI要在UI線程(或者說主線程)中去更新,不要在子線程中更新UI,而android官方也建議我們不要在非UI線程直接更新UI。
事實是不是如此呢,做一個實驗:
更新之前:
代碼:
package com.bourne.android_common.ServiceDemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.bourne.android_common.R;
public class ThreadActivity extends AppCompatActivity {
private Thread thread;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});
thread.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
登錄後復制
這里在Activity裡面新建了一個子線程去更新UI,按理說會報錯啊,可是執行結果是並沒有報錯,如圖所示:
接下來讓線程休眠一下:
package com.bourne.android_common.ServiceDemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.bourne.android_common.R;
public class ThreadActivity extends AppCompatActivity {
private Thread thread;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);
thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
textView.setText("text text text");
}
});
thread.start();
}
}
登錄後復制
應用報錯,拋出異常:
android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views
只有創建View層次結構的線程才能修改View,我們在非UI主線程裡面更新了View,所以會報錯
因為在OnCreate裡面睡眠了一下才報錯,這是為什麼呢?
Android通過檢查我們當前的線程是否為UI線程從而拋出一個自定義的AndroidRuntimeException來提醒我們「Only the original thread that created a view hierarchy can touch its views」並強制終止程序運行,具體的實現在ViewRootImpl類的checkThread方法中:
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
// 省去海量代碼…………………………
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new (
"Only the original thread that created a view hierarchy can touch its views.");
}
}
// 省去巨量代碼……………………
}
登錄後復制
這就是Android在4.0後對我們做出的一個限制。
其實造成這個現象的根本原因是:
還沒有到執行checkThread方法去檢查我們的當前線程那一步。」Android對UI事件的處理需要依賴於Message Queue,當一個Msg被壓入MQ到處理這個過程並非立即的,它需要一段事件,我們在線程中通過Thread.sleep(200)在等,在等什麼呢?在等ViewRootImpl的實例對象被創建。」
ViewRootImpl的實例對象是在OnResume中創建的啊!
看onResume方法的調度,其在ActivityThread中通過handleResumeActivity調度:
public final class ActivityThread {
// 省去海量代碼…………………………
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
unscheleGcIdler();
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
// 省去無關代碼…………
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
} else if (!willBeVisible) {
// 省去無關代碼…………
r.hideForNow = true;
}
cleanUpPendingRemoveWindows(r);
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
// 省去無關代碼…………
performConfigurationChanged(r.activity, r.newConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));
r.newConfig = null;
}
// 省去無關代碼…………
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
// 省去無關代碼…………
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// 省去與ActivityManager的通信處理
} else {
// 省略異常發生時對Activity的處理邏輯
}
}
// 省去巨量代碼……………………
}
登錄後復制
handleResumeActivity方法邏輯相對要復雜一些,除了對當前顯示Window的邏輯判斷以及沒創建的初始化等等工作外其在最終會調用Activity的makeVisible方法
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2 {
// 省去海量代碼…………………………
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
// 省去巨量代碼……………………
}
登錄後復制
在makeVisible方法中邏輯相當簡單,獲取一個窗口管理器對象並將根視圖DecorView添加到其中,addView的具體實現在WindowManagerGlobal中:
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 省去很多代碼
ViewRootImpl root;
// 省去一行代碼
synchronized (mLock) {
// 省去無關代碼
root = new ViewRootImpl(view.getContext(), display);
// 省去一行代碼
// 省去一行代碼
mRoots.add(root);
// 省去一行代碼
}
// 省去部分代碼
}
}
登錄後復制
在addView生成了一個ViewRootImpl對象並將其保存在了mRoots數組中,每當我們addView一次,就會生成一個ViewRootImpl對象,其實看到這里我們還可以擴展一下問題一個APP是否可以擁有多個根視圖呢?答案是肯定的,因為只要我調用了addView方法,我們傳入的View參數就可以被認為是一個根視圖,但是!在framework的默認實現中有且僅有一個根視圖,那就是我們上面makeVisible方法中addView進去的DecorView,所以為什麼我們可以說一個APP雖然可以有多個Activity,但是每個Activity只會有一個Window一個DecorView一個ViewRootImpl,看到這里很多童鞋依然會問,也就是說在onResume方法被執行後我們的ViewRootImpl才會被生成對吧,但是為什麼下面的代碼依然可以正確運行呢:
package com.bourne.android_common.ServiceDemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.bourne.android_common.R;
public class ThreadActivity extends AppCompatActivity {
private Thread thread;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});
thread.start();
}
@Override
protected void onResume() {
super.onResume();
}
}
登錄後復制
Activity.onResume前,ViewRootImpl實例沒有建立,所以沒有checkThread檢查。但是使用了Thread.sleep(200)的時候,ViewRootImpl已經被創建完畢了,自然checkThread就起作用了,拋出異常順理成章。
第一種做法中,雖然是在子線程中setText,但是這時候View還沒畫出來呢,所以並不會調用之後的invalidate,而相當於只是設置TextView的一個屬性,不會invalidate,就沒有後面的那些方法調用了,歸根結底,就不會調用ViewRootImpl的checkThread,也就不會報錯。而第二種方法,調用setText之後,就會引發後面的一系列的方法調用,VIew要刷新界面,ViewGroup要更新布局,計運算元View的大小位置,到最後,ViewRootImpl就會checkThread,就崩了。
所以,嚴格上來說,第一種方法雖然在子線程了設置View屬性,但是不能夠歸結到」更新View」的范疇,因為還沒畫出來呢,就沒有所謂的更新。
當我們執行Thread.sleep時候,這時候onStart、onResume都執行了,子線程再調用setText的時候,就會崩潰。
那麼說,在onStart()或者onResume()裡面執行線程操作UI也是可以的:
@Override
protected void onStart() {
super.onStart();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});
thread.start();
}
登錄後復制
@Override
protected void onResume() {
super.onResume();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});
thread.start();
}
登錄後復制
注意的是:當你在來回切換界面的時候,onStart()和onResume()是會再執行一遍的,這時候程序就崩潰了!
1、能不能在非UI線程中更新UI呢?
答案:能、當然可以
2、View的運行和Activity的生命周期有什麼必然聯系嗎?
答案:沒有、或者隱晦地說沒有必然聯系
3、除了Handler外是否還有更簡便的方式在非UI線程更新UI呢?
答案:有、而且還不少,Activity.runOnUiThread(Runnable)、View.Post(Runnable)、View.PostDelayed(Runnable,long)、AsyncTask、其內部實現原理都是向此View的線程的內部消息隊列發送一個Message消息,並傳送數據和處理方式,省去了自己再寫一個專門的Handler去處理。
4、在子線程裡面用Toast也會報錯,加上Looper.prepare和Looper.loop就可以了,這里可以這樣做嗎?
答案當然是不可以。Toast和View本質上是不一樣的,Toast在子線程報錯,是因為Toast的顯示需要添加到一個MessageQueue中,然後Looper取出來,發給Handler調用顯示,子線程因為沒有Looper,所以需要加上Looper.prepare和Looper.loop創建一個Looper,但是實質上,這還是在子線程調用,所以還是會報錯的!
5、為什麼Android要求只能在UI主線程中更改View呢
這就要說到Android的單線程模型了,因為如果支持多線程修改View的話,由此產生的線程同步和線程安全問題將是非常繁瑣的,所以Android直接就定死了,View的操作必須在UI線程,從而簡化了系統設計。
參考文章
為什麼我們可以在非UI線程中更新UI
【Android開發經驗】來來來,同學,咱們討論一下「只能在UI主線程更新View」這件小事
線程
android
ui
女式涼鞋,時尚,優雅,透氣,貨到付款!
精選推薦
廣告
可能是全網最簡單透徹的安卓子線程更新 UI 解析
71閱讀·0評論·0點贊
2019年4月24日
android 不能在子線程中更新ui的討論和分析
1.5W閱讀·7評論·14點贊
2016年1月26日
android.view.ViewRootImpl$: Only the original thread that created
140閱讀·0評論·0點贊
2022年9月23日
Looper.prepare()和Looper.loop(),在子線程中更新UI
2520閱讀·0評論·0點贊
2016年5月2日
Android為什麼能在子線程中更新UI
305閱讀·0評論·1點贊
2020年4月9日
android多線程中更新ui,Android 在子線程中更新UI
146閱讀·0評論·0點贊
2021年6月3日
Android子線程更新UI就會Crash么
1781閱讀·0評論·4點贊
2017年4月1日
為什麼只能在主線程中操作UI?為什麼子線程中setText不報錯?
3970閱讀·1評論·5點贊
2017年6月27日
Android 子線程更新TextView的text 不拋出異常原因 分析總結
1211閱讀·0評論·3點贊
2019年7月10日
非主線程更新UI
210閱讀·0評論·0點贊
2018年4月12日
非 UI 線程中更新 UI
215閱讀·0評論·0點贊
2021年4月29日
【Android】 Handler——子線程更新UI
722閱讀·1評論·4點贊
2019年12月29日
android-如何在子線程中更新ui
4019閱讀·4評論·2點贊
2016年8月23日
SurfaceView
251閱讀·0評論·0點贊
2019年3月8日
非UI線程中更新UI
373閱讀·0評論·0點贊
2018年7月10日
QT非UI線程更新UI(跨線程更新UI)
275閱讀·0評論·0點贊
2022年9月21日
Android開發之UI線程和非UI線程
1619閱讀·0評論·1點贊
2020年4月5日
非UI線程可不可以更新UI(一)
1265閱讀·0評論·2點贊
2016年2月29日
為什麼我們可以在非UI線程中更新UI
2.8W閱讀·56評論·35點贊
2015年2月3日
去首頁
看看更多熱門內容
㈡ Android UI線程
思考:
先必須了解下面2個問題
1.顧名思義 UI線程 就是刷新UI 所在線程
2.UI是單線程刷新
1.對Activity 來說 UI線程就是其主線程
2.對View來說 UI線程就是創建ViewRootImpl所在的線程
可以通過 WindowManager 內部會創建ViewRootImpl對象
好了,進入主題。我們來慢慢揭開面紗。
我們可以分別從幾個方面切入
我們可能都有使用過 runOnUiThread 現在來看看的源碼實現。
可以從上面的源碼 看到
不是UI線程 就用Handler切到Handler所在的線程中,如果是UI線程直接就調用run方法。
Activity的創建:
1.Activity創建:mInstrumentation.newActivity
2.創建Context :ContextImpl (r)
我們經常用這個方法乾的事情就是,要麼在onCreate中獲取View寬高的值。要麼就是在子線程中做一些耗時操作 ,然後post切到對應View所在的線程 來繪制UI操作。那麼這個對應的線程就是UI線程了。
那麼這個UI線程就一定是主線程嗎?
接來繼續來看。它的源碼View:post
mAttachInfo 在dispatchAttachedToWindow 中被賦值 ,也就是在ViewRootImpl創建的時候,所以是創建ViewRootImpl所在的線程。
attachInfo 上面時候為null 呢?在ViewRootImpl 還沒來得及創建的時候,ViewRootImpl 創建是在 「onResume" 之後。所以在 Activity 的 onCreate 去View.post 那麼AttachInfo 是為null 。
當 AttachInfo == null 那麼會調用 getRunQueue().post(action) 。
最終這個Runnable 被 緩存到 HandlerActionQueue 中。
直到ViewRootImpl 的 performTraversals 中 調用dispatchAttachedToWindow(mAttachInfo, 0);, 那麼才會去處理 RunQueue() 中的Runnable。
來張圖 便於理解這個流程
我們有時候去子線程操作UI的時候(如:requestLayout),會很經常見到下面的 報錯日誌:
Only the original thread that created a view hierarchy can touch its views
為什麼會報這個錯誤呢?
翻譯一下:只有創建視圖層次結構的原始線程才能接觸到它的視圖。
也就是操作UI的線程要和ViewRootImpl創建的線程是同一個線程才行,並不是只有主線程才能更新UI啊。
ViewRootImpl創建的線程?那麼 ViewRootImpl 在哪裡被創建的呢?
從上圖可以看到ViewRootImpl創建最開始是從 ActivityThread 的HandleResumeActivity中開始 一直 ViewRootImpl 創建,也就是說ViewRootImpl 對應的UI線程和 ActivityThread 在同一個線程 也就是主線程。
好了 通過上面的講解,上面的問題相信你可以自己回答啦~
㈢ Android子線程真的不能更新UI么
可以的
如果在onCreate中通過子線程直接更新UI,並不會拋異常
㈣ 每個Android 都應必須了解的多線程知識點~
進程是系統調度和資源分配的一個獨立單位。
在Android中,一個應用程序就是一個獨立的集成,應用運行在一個獨立的環境中,可以避免其他應用程序/進程的干擾。當我們啟動一個應用程序時,系統就會創建一個進程(該進程是從Zygote中fork出來的,有獨立的ID),接著為這個進程創建一個主線程,然後就可以運行MainActivity了,應用程序的組件默認都是運行在其進程中。開發者可以通過設置應用的組件的運行進程,在清單文件中給組件設置:android:process = "進程名";可以達到讓組件運行在不同進程中的目的。讓組件運行在不同的進程中,既有好處,也有壞處。我們依次的說明下。
好處:每一個應用程序(也就是每一個進程)都會有一個內存預算,所有運行在這個進程中的程序使用的總內存不能超過這個值,讓組件運行不同的進程中,可以讓主進程可以擁有更多的空間資源。當我們的應用程序比較大,需要的內存資源比較多時(也就是用戶會抱怨應用經常出現OutOfMemory時),可以考慮使用多進程。
壞處:每個進程都會有自己的虛擬機實例,因此讓在進程間共享一些數據變得相對困難,需要採用進程間的通信來實現數據的共享。
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
在Android中,線程會有那麼幾種狀態:創建、就緒、運行、阻塞、結束。當應用程序有組件在運行時,UI線程是處於運行狀態的。默認情況下,應用的所有組件的操作都是在UI線程里完成的,包括響應用戶的操作(觸摸,點擊等),組件生命周期方法的調用,UI的更新等。因此如果UI線程處理阻塞狀態時(在線程里做一些耗時的操作,如網路連接等),就會不能響應各種操作,如果阻塞時間達到5秒,就會讓程序處於ANR(application not response)狀態。
1.線程作用
減少程序在並發執行時所付出的時空開銷,提高操作系統的並發性能。
2.線程分類
守護線程、非守護線程(用戶線程)
2.1 守護線程
定義:守護用戶線程的線程,即在程序運行時為其他線程提供一種通用服務
常見:如垃圾回收線程
設置方式:thread.setDaemon(true);//設置該線程為守護線程
2.2 非守護線程(用戶線程)
主線程 & 子線程。
2.2.1 主線程(UI線程)
定義:Android系統在程序啟動時會自動啟動一條主線程
作用:處理四大組件與用戶進行交互的事情(如UI、界面交互相關)
因為用戶隨時會與界面發生交互,因此主線程任何時候都必須保持很高的響應速度,所以主線程不允許進行耗時操作,否則會出現ANR。
2.2.2 子線程(工作線程)
定義:手動創建的線程
作用:耗時的操作(網路請求、I/O操作等)
2.3 守護線程與非守護線程的區別和聯系
區別:虛擬機是否已退出,即
a. 當所有用戶線程結束時,因為沒有守護的必要,所以守護線程也會終止,虛擬機也同樣退出
b. 反過來,只要任何用戶線程還在運行,守護線程就不會終止,虛擬機就不會退出
3.線程優先順序
3.1 表示
線程優先順序分為10個級別,分別用Thread類常量表示。
3.2 設置
通過方法setPriority(int grade)進行優先順序設置,默認線程優先順序是5,即 Thread.NORM_PRIORITY。
4.線程狀態
創建狀態:當用 new 操作符創建一個線程的時候
就緒狀態:調用 start 方法,處於就緒狀態的線程並不一定馬上就會執行 run 方法,還需要等待CPU的調度
運行狀態:CPU 開始調度線程,並開始執行 run 方法
阻塞(掛起)狀態:線程的執行過程中由於一些原因進入阻塞狀態,比如:調用 sleep/wait 方法、嘗試去得到一個鎖等
結束(消亡)狀態:run 方法執行完 或者 執行過程中遇到了一個異常
(1)start()和run()的區別
通過調用Thread類的start()方法來啟動一個線程,這時此線程是處於就緒狀態,並沒有運行。調用Thread類調用run()方法來完成其運行操作的,方法run()稱為線程體,它包含了要執行的這個線程的內容,run()運行結束,此線程終止,然後CPU再調度其它線程。
(2)sleep()、wait()、yield()的區別
sleep()方法屬於Thread類,wait()方法屬於Object類。
調用sleep()方法,線程不會釋放對象鎖,只是暫停執行指定的時間,會自動恢復運行狀態;調用wait()方法,線程會放棄對象鎖,進入等待此對象的等待鎖定池,不調用notify()方法,線程永遠處於就緒(掛起)狀態。
yield()直接由運行狀態跳回就緒狀態,表示退讓線程,讓出CPU,讓CPU調度器重新調度。禮讓可能成功,也可能不成功,也就是說,回到調度器和其他線程進行公平競爭。
1.Android線程的原則
(1)為什麼不能再主線程中做耗時操作
防止ANR, 不能在UI主線程中做耗時的操作,因此我們可以把耗時的操作放在另一個工作線程中去做。操作完成後,再通知UI主線程做出相應的響應。這就需要掌握線程間通信的方式了。 在Android中提供了兩種線程間的通信方式:一種是AsyncTask機制,另一種是Handler機制。
(2)為什麼不能在非UI線程中更新UI 因為Android的UI線程是非線程安全的,應用更新UI,是調用invalidate()方法來實現界面的重繪,而invalidate()方法是非線程安全的,也就是說當我們在非UI線程來更新UI時,可能會有其他的線程或UI線程也在更新UI,這就會導致界面更新的不同步。因此我們不能在非UI主線程中做更新UI的操作。
2.Android實現多線程的幾種方式
3.為何需要多線程
多線程的本質就是非同步處理,直觀一點說就是不要讓用戶感覺到「很卡」。
4.多線程機制的核心是啥
多線程核心機制是Handler
推薦Handler講解視頻: 面試總被問到Handler?帶你從源碼的角度解讀Handler核心機制
根據上方提到的 多進程、多線程、Handler 問題,我整理了一套 Binder與Handler 機制解析的學習文檔,提供給大家進行學習參考,有需要的可以 點擊這里直接獲取!!! 裡面記錄許多Android 相關學習知識點。
㈤ 如何在android一條單獨線程,更新ui
方法有兩種:
通過繼承Thread類,重寫Run方法來實現
通過繼承介面Runnable實現多線程
主要接受子線程發送的數據, 並用此數據配合主線程更新UI.
Handler的主要作用:主要用於非同步消息的處理
Handler的運行過程:
當(子線程)發出一個消息之後,首先進入一個(主線程的)消息隊列,發送消息的函數即刻返回,而在主線程中的Handler逐個的在消息隊列中將消息取出,然後對消息進行處理。這樣就實現了跨線程的UI更新(實際上還是在主線程中完成的)。
這種機制通常用來處理相對耗時比較長的操作,如訪問網路比較耗時的操作,讀取文大文件,比較耗時的操作處理等。
在大白話一點的介紹它的運行過程:
啟動應用時Android開啟一個主線程
(也就是UI線程) , 如果此時需要一個耗時的操作,例如:
聯網讀取數據,或者讀取本地較大的一個文件的時候,你不能把這些操作放在主線程中,如果你放在主線程中的話,界面會出現假死現象(這也就是你在主線程中直接訪問網路時會提示你異常的原因,如我們上篇文章所述Android主線程不能訪問網路異常解決辦法)。
㈥ android 線程中怎麼更新ui進度條
如果你用的是標準的ProgressBar類,那麼調用setProgress(int progress)方法進行更新即可。
不過如果載入處理不是在UI線程,那就需要用Handler去更新了
舉例如下:
int progress = calculateProgress();
ProgressBar progressBar = mActivity.getProgressBar();
// 獲取主線程Handler。需要編寫getHandler()方法
Handler handler = mActivity.getHandler();
handler.post(new Runnable() {
void run() {
progressBar.setProgress(progress);
}
})
㈦ android怎麼更新UI
首先,android的UI刷新是在主線程(UI線程)中完成的。四大組件中,activity和service運行在主線程中。現在總結自己在項目中常用到的UI刷新方式。
第一,利用子線程發消息刷新UI。
子線程負責處理UI需要的數據,然後發消息到主線程來刷新UI。代碼結構如下:
new Thread(new Runnable() {
@Override
public void run() {
Person person=new Person();
person.setName(mName.getText().toString().trim());
person.setPhone(mPhone.getText().toString().trim());
Log.i("person",person.toString());
DatabaseInfoFactory.getPersonDao(mContext).addPerson(person);
Looper.prepare();
Message msg=Message.obtain();
msg.what=0x123456;
handler.sendMessage(msg);
Looper.loop();
}
}).start();
主線程中:
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what==0x123456||msg.what==0x123){
fillData();
setListener();
}
}
};
第二,利用非同步任務更新UI。代碼結構如下:
new AsyncTask<void,void,void>() {
@Override
protected void onPostExecute(Void result) {
if(mAdapter==null){
mAdapter=new LeaveInfoAdapter();
//設置數據適配器
mLVleaveInfos.setAdapter(mAdapter);
Log.i("測試", "非同步任務顯示後台獲得資料庫數據");
}
else {
mAdapter.notifyDataSetChanged();
}
super.onPostExecute(result);
}
@Override
protected Void doInBackground(Void... params) {
//獲得要顯示的數據
mleaveInfos=mLeaveInfosDao.findAll();
if (mleaveInfos==null) {
Toast.makeText(HomeActivity.this,"請假數據不存在或是已經清除!", 500).show();
}
Log.i("測試", "非同步任務後台獲得資料庫數據"+mleaveInfos.size());
return null;
}
}.execute();</void,void,void>
第三,利用配置文件+activity的生命周期方法刷新UI。
㈧ Android 在子線程中更新UI的幾種方法示例
請您慢慢看:
直接在UI線程中開啟子線程來更新TextView顯示的內容,運行程序我們會發現,如下錯誤:android.view.ViewRoot$: Only the original thread that created a view hierarchy can touch its views.翻譯過來就是:只有創建這個控制項的線程才能去更新該控制項的內容。
所有的UI線程要去負責View的創建並且維護它,例如更新冒個TextView的顯示,都必須在主線程中去做,我們不能直接在UI線程中去創建子線程,要利用消息機制:handler,如下就是handler的簡單工作原理圖:
既然android給我們提供了Handler機制來解決這樣的問題,請看如下代碼:
public class HandlerTestActivity extends Activity { private TextView tv; private static final int UPDATE = 0; private Handler handler = new Handler() { @Overridepublic void handleMessage(Message msg) { // TODO 接收消息並且去更新UI線程上的控制項內容if (msg.what == UPDATE) { // Bundle b = msg.getData();// tv.setText(b.getString("num")); tv.setText(String.valueOf(msg.obj)); } super.handleMessage(msg); } }; /** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) findViewById(R.id.tv); new Thread() { @Overridepublic void run() { // TODO 子線程中通過handler發送消息給handler接收,由handler去更新TextView的值try { for (int i = 0; i < 100; i++) { Thread.sleep(500); Message msg = new Message(); msg.what = UPDATE; // Bundle b = new Bundle();// b.putString("num", "更新後的值:" + i);// msg.setData(b); msg.obj = "更新後的值:" + i; handler.sendMessage(msg); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
我們就通過Handler機制來處理了子線程去更新UI線程式控制制項問題,Andrid開發中要經常用到這種機制。
㈨ 為什麼我們可以在非UI線程中更新UI
先看setProgress(int progress)方法的實現
如果有描述不清楚的話,大家可以看看ProgressBar.java源碼的實現。
㈩ android編程為什麼不可以在子線程更新ui
沒有所謂好不好,在其他線程更新UI最終還是轉變為在UI線程里更新,因為UI線程是主線程,其他線程想直接操作UI是不行的,可以藉助Handler and message機制。如果你的功能邏輯復雜度較高,就是說運行時間較長,那麼最好是另開一個線程做邏輯處理,...