A. 搭配handlerthread可以在handler
每個android應用程序都運行在一個dalvik虛擬機進程中,進程開始的時候會啟動一個主線(MainThread),主線程負責處理和ui相關的事件,因此主線程通常又叫UI線程。
而由於Android採用UI單線程模型,所以只能在主線程中對UI元素進行操作。如果在非UI線程直接對UI進行了操作,則會報錯: :only the original thread that created aviewhierarchy can touch its views Android為我們提供了消息循環的機制,我們可以利用這個機制來實現線程間的通信。那麼,我們就可以在非UI線程發送消息到UI線程,最終讓Ui線程來進行ui的操作。
B. android 怎麼處理線程安全
UI線程及Android的單線程模型原則當應用啟動,系統會創建一個主線程(main thread)。這個主線程負責向UI組件分發事件(包括繪制事件),也是在這個主線程里,應用和Android的UI組件(components from the Android UI toolkit (components from the android.widget and android.view packages))發生交互。 當App做一些比較重(intensive)的工作的時候,除非合理地實現,否則單線程模型的performance會很poor。特別的是,如果所有的工作都在UI線程,做一些比較耗時的工作比如訪問網路或者資料庫查詢,都會阻塞UI線程,導致事件停止分發(包括繪制事件)。對於用戶來說,應用看起來像是卡住了,更壞的情況是,如果UI線程blocked的時間太長(大約超過5秒),用戶就會看到ANR(application not responding)的對話框。 另外,Andoid UI toolkit並不是線程安全的,所以不能從非UI線程來操縱UI組件。必須把所有的UI操作放在UI線程里,所以Android的單線程模型有兩條原則: 1.不要阻塞UI線程。 2.不要在UI線程之外訪問Android UI toolkit(主要是這兩個包中的組件:android.widget and android.view)。
C. Android進程和線程的區別
Android進程和線程的區別
下面我先介紹下Android進程和線程各是什麼,然後再一一比較區別下
Android進程基本知識:
當一個程序第一次啟動的時候,Android會啟動一個LINUX進程和一個主線程。默認的情況下,所有該程序的組件都將在該進程和線程中運行。 同時,Android會為每個應用程序分配一個單獨的LINUX用戶。Android會盡量保留一個正在運行進程,只在內存資源出現不足時,Android會嘗試停止一些進程從而釋放足夠的資源給其他新的進程使用, 也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。
我們可以將一些組件運行在其他進程中,並且可以為任意的進程添加線程。組件運行在哪個進程中是在manifest文件里設置的,其中<Activity>,<Service>,<receiver>和<provider>都有一個process屬性來指定該組件運行在哪個進程之中。我們可以設置這個屬性,使得每個組件運行在它們自己的進程中,或是幾個組件共同享用一個進程,或是不共同享用。<application>元素也有一個process屬性,用來指定所有的組件的默認屬性。
Android中的所有組件都在指定的進程中的主線程中實例化的,對組件的系統調用也是由主線程發出的。每個實例不會建立新的線程。對系統調用進行響應的方法——例如負責執行用戶動作的View.onKeyDown()和組件的生命周期函數——都是運行在這個主線程中的。這意味著當系統調用這個組件時,這個組件不能長時間的阻塞主線程。例如進行網路操作時或是更新UI時,如果運行時間較長,就不能直接在主線程中運行,因為這樣會阻塞這個進程中其他的組件,我們可以將這樣的組件分配到新建的線程中或是其他的線程中運行。
Android會根據進程中運行的組件類別以及組件的狀態來判斷該進程的重要性,Android會首先停止那些不重要的進程。按照重要性從高到低一共有五個級別:
1.1前台進程
前台進程是用戶當前正在使用的進程。只有一些前台進程可以在任何時候都存在。他們是最後一個被結束的,當內存低到根本連他們都不能運行的時候。一般來說, 在這種情況下,設備會進行內存調度,中止一些前台進程來保持對用戶交互的響應。
1.2可見進程
可見進程不包含前台的組件但是會在屏幕上顯示一個可見的進程是的重要程度很高,除非前台進程需要獲取它的資源,不然不會被中止。
1.3服務進程
運行著一個通過startService() 方法啟動的service,這個service不屬於上面提到的2種更高重要性的。service所在的進程雖然對用戶不是直接可見的,但是他們執行了用戶非常關注的任務(比如播放mp3,從網路下載數據)。只要前台進程和可見進程有足夠的內存,系統不會回收他們。
1.4後台進程
運行著一個對用戶不可見的activity(調用過 onStop() 方法).這些進程對用戶體驗沒有直接的影響,可以在服務進程、可見進程、前台進 程需要內存的時候回收。通常,系統中會有很多不可見進程在運行,他們被保存在LRU (least recently used) 列表中,以便內存不足的時候被第一時間回收。如果一個activity正 確的執行了它的生命周期,關閉這個進程對於用戶體驗沒有太大的影響。
1.5空進程
未運行任何程序組件。運行這些進程的唯一原因是作為一個緩存,縮短下次程序需要重新使用的啟動時間。系統經常中止這些進程,這樣可以調節程序緩存和系統緩存的平衡。
單線程模型
線程在代碼是使用標準的java Thread對象來建立,那麼在Android系統中提供了一系列方便的類來管理線程——Looper用來在一個線程中執行消息循環,Handler用來處理消息,HandlerThread創建帶有消息循環的線程。具體可以看下面的詳細介紹。
當一個程序第一次啟動時,Android會同時啟動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。
在開發Android應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
2.1 子線程更新UI Android的UI是單線程(Single-threaded)的。
為了避免拖住GUI,一些較費時的對象應該交給獨立的線程去執行。如果幕後的線程來執行UI對象,Android就會發出錯誤訊息 。以後遇到這樣的異常拋出時就要知道怎麼回事了!
2.2 Message Queue
在單線程模型下,為了解決類似的問題,Android設計了一個Message Queue(消息隊列), 線程間可以通過該Message Queue並結合Handler和Looper組件進行信息交換。下面將對它們進行分別介紹:
2..3 Message 消息
理解為線程間交流的信息,處理數據後台線程需要更新UI,則發送Message內含一些數據給UI線程。
2.4. Handler 處理者
是Message的主要處理者,負責Message的發送,Message內容的執行處理。後台線程就是通過傳進來的Handler對象引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message) 方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。
2.5. Message Queue 消息隊列
用來存放通過Handler發布的消息,按照先進先出執行。 每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。
2.6 Looper Looper是每條線程里的Message Queue的管家。
Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)建立Message Queue,但在子線程里並沒有建立Message Queue。所以調用Looper.getMainLooper()得到的主線程的Looper不為NULL,但調用Looper.myLooper()得到當前線程的Looper就有可能為NULL。
從以上幾點,不難看出Android進程和線程的二者的區別所在。
D. 為什麼 Android 的 UI 框架使用單線程模型,比多線程模型有什麼優點
·如果有大量的線程,會影響性能,因為操作系統需要在它們之間切換。·的線程需要的內存空間。·線程可能會給程序帶來「bug」,因此要小心使用。·線程的中止需要考慮其對程序運行的影響。·通常塊模型數據是在多個線程間共享的,需要防止線程死鎖情況的發生。一些線程模型的背景可以重點討論一下在Win32環境中常用的一些模型。·單線程模型在這種線程模型中,一個進程中只能有一個線程,剩下的進程必須等待當前的線程執行完。這種模型的缺點在於系統完成一個很小的任務都必須佔用很長的時間。·塊線程模型(單線程多塊模型STA)這種模型里,一個程序里可能會包含多個執行的線程。在這里,每個線程被分為進程里一個單獨的塊。每個進程可以含有多個塊,可以共享多個塊中的數據。程序規定了每個塊中線程的執行時間。所有的請求通過Windows消息隊列進行串列化,這樣保證了每個時刻只能訪問一個塊,因而只有一個單獨的進程可以在某一個時刻得到執行。這種模型比單線程模型的好處在於,可以響應同一時刻的多個用戶請求的任務而不只是單個用戶請求。但它的性能還不是很好,因為它使用了串列化的線程模型,任務是一個接一個得到執行的。·多線程塊模型(自由線程塊模型)多線程塊模型(MTA)在每個進程里只有一個塊而不是多個塊。這單個塊控制著多個線程而不是單個線程。這里不需要消息隊列,因為所有的線程都是相同的塊的一個部分,並且可以共享。這樣的程序比單線程模型和STA的執行速度都要快,因為降低了系統的負載,因而可以優化來減少系統idle的時間。這些應用程序一般比較復雜,因為程序員必須提供線程同步以保證線程不會並發的請求相同的資源,因而導致競爭情況的發生。這里有必要提供一個鎖機制。但是這樣也許會導致系統死鎖的發生。進程和線程都是操作系統的概念。進程是應用程序的執行實例,每個進程是由私有的虛擬地址空間、代碼、數據和其它各種系統資源組成,進程在運行過程中創建的資源隨著進程的終止而被銷毀,所使用的系統資源在進程終止時被釋放或關閉。線程是進程內部的一個執行單元。系統創建好進程後,實際上就啟動執行了該進程的主執行線程,主執行線程以函數地址形式,比如說main或WinMain函數,將程序的啟動點提供給Windows系統。主執行線程終止了,進程也就隨之終止。每一個進程至少有一個主執行線程,它無需由用戶去主動創建,是由系統自動創建的。用戶根據需要在應用程序中創建其它線程,多個線程並發地運行於同一個進程中。一個進程中的所有線程都在該進程的虛擬地址空間中,共同使用這些虛擬地址空間、全局變數和系統資源,所以線程間的通訊非常方便,多線程技術的應用也較為廣泛。多線程可以實現並行處理,避免了某項任務長時間佔用CPU時間。要說明的一點是,到2015年為止,大多數的計算機都是單處理器(CPU)的,為了運行所有這些線程,操作系統為每個獨立線程安排一些CPU時間,操作系統以輪換方式向線程提供時間片,這就給人一種假象,好象這些線程都在同時運行。由此可見,如果兩個非常活躍的線程為了搶奪對CPU的控制權,在線程切換時會消耗很多的CPU資源,反而會降低系統的性能。這一點在多線程編程時應該注意。C++11標准中,STL類庫也實現了多線程的類std::thread,使得多線程編程更加方便。
E. android 非同步方法和子線程方法有什麼區別
子線程沒有控制並發數量,當並發過多的時候非同步方法的作用就體現出來了。
非同步是相對於同步而言的,顧名思義,同步就是各個通訊節點之間有統一的時鍾,按照相同的時鍾工作,非同步相反,各節點之間沒有統一的時鍾,每個節點按照自己內部的時鍾工作。
android在所有Thread當中,有一個Thread,我們稱之為UI Thread。UI
Thread在Android程序運行的時候就被創建,是一個Process當中的主線程Main
Thread,主要是負責控制UI界面的顯示、更新和控制項交互。在Android程序創建之初,一個Process呈現的是單線程模型,所有的任務都在一個線程中運行。因此,我們認為,UI
Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載數據,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程。
F. android 主線程和子線程有什麼區別
本文較為深入的分析了android中UI主線程與子線程。分享給大家供大家參考。具體如下:
在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認的情況下,所有這個程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的兩種,除此之外還有Content Provider和Broadcast Receiver)都會跑在這個Process。
一個Android 程序默認情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程序運行的時候就被創建,是一個Process當中的主線程Main Thread,主要是負責控制UI界面的顯示、更新和控制項交互。在Android程序創建之初,一個Process呈現的是單線程模型,所有的任務都在一個線程中運行。因此,我們認為,UI Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載數據,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程。
那麼,UI Thread如何和其他Thread一起工作呢?常用方法是:誕生一個主線程的Handler物件,當做Listener去讓子線程能將訊息Push到主線程的Message Quene里,以便觸發主線程的handlerMessage()函數,讓主線程知道子線程的狀態,並在主線程更新UI。
例如,在子線程的狀態發生變化時,我們需要更新UI。如果在子線程中直接更新UI,通常會拋出下面的異常:
11-07 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$:Only the original thread that created a view hierarchy can touch its views.
意思是,無法在子線程中更新UI。為此,我們需要通過Handler物件,通知主線程Ui Thread來更新界面。
如下,首先創建一個Handler,來監聽Message的事件:
private final int UPDATE_UI = 1;
private Handler mHandler = new MainHandler();
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
或者:
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
當子線程的狀態發生變化,則在子線程中發出Message,通知更新UI。
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);
在我們的程序中,很多Callback方法有時候並不是運行在主線程當中的,所以如果在Callback方法中更新UI失敗,也可以採用上面的方法。
G. android 中Bn 和Bp的區別
Bn意味著Binder Native 端
Bp是Binder Proxy端,
這兩端會實現相同的介面,但Proxy端只是通過binder ipc發送一個binder transaction,
native端是真正做事情,再將結果返回。
Android用此機制實現高效的遠程調用。
H. 為什麼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日
去首頁
看看更多熱門內容
I. android啟動後怎麼查看其裡面的進程和線程
1)一個 Android 程序開始運行時,會單獨啟動一個Process。
默認情況下,所有這個程序中的Activity或者Service都會跑在這個Process。
默認情況下,一個Android程序也只有一個Process,但一個Process下卻可以有許多個Thread。
2)一個 Android 程序開始運行時,就有一個主線程Main Thread被創建。該線程主要負責UI界面的顯示、更新和控制項交互,所以又叫UI Thread。
3)一個Android程序創建之初,一個Process呈現的是單線程模型--即MainThread,所有的任務都在一個線程中運行,所以,MainThread所調用的每一個函數,其耗時應該越短越好,而對於比較耗時的工作,應該交給子線程去做,以避免主線程(UI線程)被阻塞,導致程序出現ANR(Application not response)
一個Activity就運行在一個線程中嗎?或者編碼時,如果不是明確安排在不同線程中的兩個Activity,其就都是在同一個線程中?那從一個Activity跳轉到另一個Activity時,是不是跳出的那個Activity就處在睡眠狀態了?
【答】 每個Activity都有一個Process屬性,可以指定該Activity是屬於哪個進程的。當然如果不明確指明,應該就是從屬於默認進程(Application指定的,如其未指定,應該就是默認主進程)。
Android中有Task的概念,而同一個Task的各個Activity會形成一個棧,只有站定的Activity才有機會與用戶交互。
原文地址:Android中的進程與線程 原文作者:江鵬
當應用程序的組件第一次運行時,Android將啟動一個只有一個執行線程的Linux進程。默認,應用程序所有的組件運行在這個進程和線程中。然而,你可以安排組件運行在其他進程中,且你可以為進程衍生出其它線程。本文從下面幾點來介紹Android的進程與線程:
1、進程
組件運行於哪個進程中由清單文件控制。組件元素——<activity>、<service>、<receiver>、<provider>,都有一個process屬性可以指定組件運行在哪個進程中。這個屬性可以設置為每個組件運行在自己的進程中,或者某些組件共享一個進程而其他的不共享。他們還可以設置為不同應用程序的組件運行在同一個進程中——假設這些應用程序共享同一個Linux用戶ID且被分配了同樣的許可權。<application>元素也有process屬性,為所有的組件設置一個默認值。
所有的組件都在特定進程的主線程中實例化,且系統調用組件是由主線程派遣。不會為每個實例創建單獨的線程,因此,對應這些調用的方法——諸如View.onKeyDown()報告用用戶的行為和生命周期通知,總是運行在進程的主線程中。這意味著,沒有組件當被系統調用時應該執行很長時間或阻塞操作(如網路操作或循環計算),因為這將阻塞進程中的其它組件。你可以為長操作衍生獨立的線程。
public boolean onKeyDown(int keyCode,KeyEvent event):默認實現KeyEvent.Callback.onKeyMultiple(),當按下視圖的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然後釋放時執行,如果視圖可用且可點擊。
參數
keyCode-表示按鈕被按下的鍵碼,來自KeyEvent
event-定義了按鈕動作的KeyEvent對象
返回值
如果你處理事件,返回true;如果你想下一個接收者處理事件,返回false。
當內存剩餘較小且其它進程請求較大內存並需要立即分配,Android要回收某些進程,進程中的應用程序組件會被銷毀。當他們再次運行時,會重新開始一個進程。
當決定終結哪個進程時,Android會權衡他們對用戶重要性的相對權值。例如,與運行在屏幕可見的活動進程相比(前台進程),它更容易關閉一個進程,它的活動在屏幕是不可見(後台進程)。決定是否終結進程,取決於運行在進程中的組件狀態。關於組件的狀態,將在後面一篇——組件生命周期中介紹。
2、線程
雖然你可能會將你的應用程序限制在一個進程中,但有時候你會需要衍生一個線程做一些後台工作。因為用戶界面必須很快地響應用戶的操作,所以活動寄宿的線程不應該做一些耗時的操作如網路下載。任何不可能在短時間完成的操作應該分配到別的線程。
線程在代碼中是用標準的Java線程對象創建的,Android提供了一些方便的類來管理線程——Looper用於在線程中運行消息循環、Handler用戶處理消息、HandlerThread用戶設置一個消息循環的線程。
Looper類
該類用戶在線程中運行消息循環。線程默認沒有消息循環,可以在線程中調用prepare()創建一個運行循環;然後調用loop()處理消息直到循環結束。大部分消息循環交互是通過Handler類。下面是一個典型的執行一個Looper線程的例子,分別使用prepare()和loop()創建一個初始的Handler與Looper交互:
1. Android中進程與進程、線程與線程之間如何通信?
1)一個 Android 程序開始運行時,會單獨啟動一個Process。
默認情況下,所有這個程序中的Activity或者Service都會跑在這個Process。
默認情況下,一個Android程序也只有一個Process,但一個Process下卻可以有許多個Thread。
2)一個 Android 程序開始運行時,就有一個主線程Main Thread被創建。該線程主要負責UI界面的顯示、更新和控制項交互,所以又叫UI Thread。
3)一個Android程序創建之初,一個Process呈現的是單線程模型--即MainThread,所有的任務都在一個線程中運行,所以,MainThread所調用的每一個函數,其耗時應該越短越好,而對於比較耗時的工作,應該交給子線程去做,以避免主線程(UI線程)被阻塞,導致程序出現ANR(Application not response)
一個Activity就運行在一個線程中嗎?或者編碼時,如果不是明確安排在不同線程中的兩個Activity,其就都是在同一個線程中?那從一個Activity跳轉到另一個Activity時,是不是跳出的那個Activity就處在睡眠狀態了?
【答】 每個Activity都有一個Process屬性,可以指定該Activity是屬於哪個進程的。當然如果不明確指明,應該就是從屬於默認進程(Application指定的,如其未指定,應該就是默認主進程)。
Android中有Task的概念,而同一個Task的各個Activity會形成一個棧,只有站定的Activity才有機會與用戶交互。
原文地址:Android中的進程與線程 原文作者:江鵬
當應用程序的組件第一次運行時,Android將啟動一個只有一個執行線程的Linux進程。默認,應用程序所有的組件運行在這個進程和線程中。然而,你可以安排組件運行在其他進程中,且你可以為進程衍生出其它線程。本文從下面幾點來介紹Android的進程與線程:
1、進程
組件運行於哪個進程中由清單文件控制。組件元素——<activity>、<service>、<receiver>、<provider>,都有一個process屬性可以指定組件運行在哪個進程中。這個屬性可以設置為每個組件運行在自己的進程中,或者某些組件共享一個進程而其他的不共享。他們還可以設置為不同應用程序的組件運行在同一個進程中——假設這些應用程序共享同一個Linux用戶ID且被分配了同樣的許可權。<application>元素也有process屬性,為所有的組件設置一個默認值。
所有的組件都在特定進程的主線程中實例化,且系統調用組件是由主線程派遣。不會為每個實例創建單獨的線程,因此,對應這些調用的方法——諸如View.onKeyDown()報告用用戶的行為和生命周期通知,總是運行在進程的主線程中。這意味著,沒有組件當被系統調用時應該執行很長時間或阻塞操作(如網路操作或循環計算),因為這將阻塞進程中的其它組件。你可以為長操作衍生獨立的線程。
public boolean onKeyDown(int keyCode,KeyEvent event):默認實現KeyEvent.Callback.onKeyMultiple(),當按下視圖的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然後釋放時執行,如果視圖可用且可點擊。
參數
keyCode-表示按鈕被按下的鍵碼,來自KeyEvent
event-定義了按鈕動作的KeyEvent對象
返回值
如果你處理事件,返回true;如果你想下一個接收者處理事件,返回false。
當內存剩餘較小且其它進程請求較大內存並需要立即分配,Android要回收某些進程,進程中的應用程序組件會被銷毀。當他們再次運行時,會重新開始一個進程。
當決定終結哪個進程時,Android會權衡他們對用戶重要性的相對權值。例如,與運行在屏幕可見的活動進程相比(前台進程),它更容易關閉一個進程,它的活動在屏幕是不可見(後台進程)。決定是否終結進程,取決於運行在進程中的組件狀態。關於組件的狀態,將在後面一篇——組件生命周期中介紹。
2、線程
雖然你可能會將你的應用程序限制在一個進程中,但有時候你會需要衍生一個線程做一些後台工作。因為用戶界面必須很快地響應用戶的操作,所以活動寄宿的線程不應該做一些耗時的操作如網路下載。任何不可能在短時間完成的操作應該分配到別的線程。
線程在代碼中是用標準的Java線程對象創建的,Android提供了一些方便的類來管理線程——Looper用於在線程中運行消息循環、Handler用戶處理消息、HandlerThread用戶設置一個消息循環的線程。
Looper類
該類用戶在線程中運行消息循環。線程默認沒有消息循環,可以在線程中調用prepare()創建一個運行循環;然後調用loop()處理消息直到循環結束。大部分消息循環交互是通過Handler類。下面是一個典型的執行一個Looper線程的例子,分別使用prepare()和loop()創建一個初始的Handler與Looper交互:
2.1、遠程過程調用(Remote procere calls,RPCs)
Android有一個輕量級的遠程過程調用機制——方法在本地調用卻在遠程(另外一個進程中)執行,結果返回給調用者。這需要將方法調用和它伴隨的數據分解為操作系統能夠理解的層次,從本地進程和地址空間傳輸到遠程進程和地址空間,並重新組裝調用。返回值以相反方向傳輸。Android提供了做這些工作的所有代碼,這樣我們可以專注於定義和執行RPC介面本身。
一個RPC介面僅包含方法。所有的方法同步地執行(本地方法阻塞直到遠程方法執行完成),即使是沒有返回值。簡言之,該機制工作原理如下:首先,你用簡單的IDL(interface definition language,介面定義語言)聲明一個你想實現的RPC介面。從這個聲明中,aidl工具生成一個Java介面定義,提供給本地和遠程進程。它包含兩個內部類,如下圖所示:
內部類有管理你用IDL定義的介面的遠程過程調用所需要的所有代碼。這兩個內部類都實現了IBinder介面。其中之一就是在本地由系統內部使用,你寫代碼可以忽略它。另外一個是Stub,擴展自Binder類。除了用於有效地IPC(interprocess communication)調用的內部代碼,內部類在RPC介面聲明中還包含方法聲明。你可以定義Stub的子類實現這些方法,如圖中所示。
通常情況下,遠程過程有一個服務管理(因為服務能通知系統關於進程和它連接的其它進程的信息)。它有由aidl工具生成的介面文件和Stub子類實現的RPC方法。服務的客戶端僅有由aidl工具生成的介面文件。
下面介紹服務如何與它的客戶端建立連接:
· 服務的客戶端(在本地端的)應該實現onServiceConnected() 和onServiceDisconnected() 方法,因此當與遠程服務建立連接成功和斷開連接是會通知它。然後調用bindService() 建立連接。
· 服務的onBind()方法將實現為接受或拒絕連接,者取決於它接受到的意圖(該意圖傳送到binServive())。如果連接被接受,它返回一個Stub子類的實例。
· 如果服務接受連接,Android調用客戶端的onServiceConnected()方法且傳遞給它一個IBinder對象,返回由服務管理的Stub子類的一個代理。通過代理,客戶端可以調用遠程服務。
這里只是簡單地描述,省略了一些RPC機制的細節。你可以查閱相關資料或繼續關注Android開發之旅,後面將為你奉上。
2.2、線程安全方法
在一些情況下,你實現的方法可能會被不止一個線程調用,因此必須寫成線程安全的。這對遠程調用方法是正確的——如上一節討論的RPC機制。當從IBinder進程中調用一個IBinder對象中實現的一個方法,這個方法在調用者的線程中執行。然而,當從別的進程中調用,方法將在Android維護的IBinder進程中的線程池中選擇一個執行,它不在進程的主線程中執行。例如,一個服務的onBind()方法在服務進程的主線程中被調用,在onBind()返回的對象中執行的方法(例如,實現RPC方法的Stub子類)將在線程池中被調用。由於服務可以有一個以上的客戶端,所以同時可以有一個以上的線程在執行同一個IBinder方法。因此,IBinder的方法必須是線程安全的。
同樣,一個內容提供者可以接受其它進程產生的數據請求。雖然ContentResolver 和 ContentProvider 類隱藏進程通信如何管理的,對應哪些請求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在內容提供者的進程的線程池中被調用,而不是在這一進程的主線程中。因為這些方法可以同時從任意數量的線程中調用,他們也必須實現為線程安全的。