android 線程那點事

在操做系統中,線程是操做系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制的產生,而且線程的建立和銷燬都會有相應的開銷,當系統中存在大量的線程時,系統會經過時間片輪轉的方式調度每一個線程,在這麼多線程中有一個被稱爲主線程,主線程是指進程所擁有的線程,在JAVA中默認狀況下一個進程只有一個線程,這個線程就是主線程。主線程主要處理界面交互相關的邏輯,由於用戶隨時會和界面發生交互,所以主線程在任什麼時候候都必須有比較高的響應速度,不然就會產生一種界面卡頓的感受。爲了保持較高的響應速度,這就要求主線程中不能執行耗時的任務,這個時候子線程就派上用場了。子線程也叫工做線程,除了主線程之外的線程都是子線程。html

Android中的線程java

Android沿用了JAVA的線程模型,其中的線程也分爲主線程和子線程,其中主線程又叫UI線程。在Android系統中,在默認狀況下,一個應用程序內的各個組件(如Activity、BroadcastReceiver、Service)都會在同一個進程(Process)裏執行,且由此進程的主線程負責執行。若是有特別指定(經過android:process),也可讓特定組件在不一樣的進程中運行。不管組件在哪個進程中運行,默認狀況下,他們都由此進程的主線程負責執行。主線程既要處理Activity組件的UI事件,又要處理Service後臺服務工做,一般會忙不過來。爲了解決此問題,主線程能夠建立多個子線程來處理後臺服務工做,而自己專心處理UI畫面的事件。子線程的任務則是執行耗時任務,好比網絡請求,I/O操做等。從Android4.0開始系統要求網絡訪問必須在子線程中進行,不然網絡訪問將會失敗並拋出NetWorkOnMainThreadException這個異常,這樣作是爲了不主線程因爲被耗時操做阻塞從而出現ANR現象。android

爲何會出現ANR程序員

Android但願UI線程能根據用戶的要求作出快速響應,若是UI線程花太多時間處理後臺的工做,當UI事件發生時,讓用戶等待時間超過5秒而未處理,Android系統就會給用戶顯示ANR提示信息。主線程除了處理UI事件以外,還要處理Broadcast消息。因此在BroadcastReceiver的onReceive()函數中,不宜佔用太長的時間,不然致使主線程沒法處理其它的Broadcast消息或UI事件。若是佔用時間超過10秒,Android系統就會給用戶顯示ANR提示信息。解決辦法天然仍是解放UI主線程,將耗時操做交給子線程,避免阻塞。安全

Android中也有main()方法網絡

剛接觸Android的開發者可能會由於找不到Java程序的執行入口main()方法而以爲疑惑,其實Android中固然是也有main()方法的(以下),它被包裝在源碼中的ActivityThread類裏。ActivityThread爲應用程序的主線程類,全部的Apk程序都有且僅有一個ActivityThread類,程序的入口爲該類中的static main()方法,ActivityThread所在的線程即爲UI線程或主線程。Activity從main()方法開始執行,調用prepareMain()爲UI線程建立一個消息隊列(MessageQueue)。而後建立一個ActivityThread對象,在ActivityThread的初始化代碼中會建立一個H(Handler)對象和一個ApplicationThread(Binder)對象。其中Binder負責接收遠程AmS的IPC調用,接收到調用後,則經過Hander把消息發送到消息隊列,UI主線程會異步地從消息隊列中取出消息並執行相應操做,好比start,pause,stop等。接着UI主線程調用Looper.loop()方法進入消息循環體,進入後就會不斷地從消息隊列中讀取並處理消息。多線程



public static final void main(String[] args) { SamplingProfilerIntegration.start(); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); if (Process.supportsProcesses()) { throw new RuntimeException("Main thread loop unexpectedly exited"); } thread.detach(); String name = (thread.mInitialApplication != null) ? thread.mInitialApplication.getPackageName() : "<unknown>"; Slog.i(TAG, "Main thread of " + name + " is now exiting"); } }

public static final void main(String[] args) {   
     SamplingProfilerIntegration.start();        
    Process.setArgV0("<pre-initialized>");     
    Looper.prepareMainLooper();       
    if (sMainThreadHandler == null) {           
    sMainThreadHandler = new Handler();    
    }       
   ActivityThread thread = new ActivityThread();       
   thread.attach(false);       
    if (false) {            
    Looper.myLooper().setMessageLogging(new                
    LogPrinter(Log.DEBUG, "ActivityThread"));        }      
     Looper.loop();        
     if (Process.supportsProcesses()) {       
     throw new RuntimeException("Main thread loop unexpectedly exited");        }        
    thread.detach();        String name = (thread.mInitialApplication != null)        
      ? thread.mInitialApplication.getPackageName();
        Slog.i(TAG, "Main thread of " + name + " is now exiting");    }}<strong>
</strong>


Android中的子線程異步

Android中開啓一個子線程無非仍是這兩種方法ide

1:繼承Thread類函數



public class MyThread extends Thread { public void run(){ } } public class MyThread extends Thread { public void run(){ } } newMyThread().start();

2:實現Runnable接口



public class MyRunnable implements Runnable{ @Override public void run(){ //TODOAuto-generatedmethodstub } } public class MyRunnable implements Runnable{ @Override public void run(){ //TODOAuto-generatedmethodstub } } new MyThread().start();

Android APK程序中都有哪些線程?
經過debug,咱們能夠捕獲當前應用程序中的線程(以下圖),其中藍色選中部分即爲當前應用程序的主線程,當前程序中還運行了三個Binder,每一個Binder對象都對應一個線程,這些Binder線程主要負責接收Linux Binder驅動發送的IPC調用。除此之外還有Java中的守護線程和垃圾回收線程堆裁剪守護進程等在運行。


Paste_Image.png

程序中自定義Thread和UI線程的區別是什麼?

自定義Thread和UI線程的區別在於,UI線程是從ActivityThread運行的,在該類中的main()方法中,已經使用Looper.prepareMainLooper()爲該線程添加了Looper對象,即已經爲該線程建立了消息隊列(MessageQueue),所以,程序員才能夠在Activity中定義Hander對象(由於聲明Hander對象時,所在的線程必須已經建立了MessageQueue)。而普通的自定義Thread是一個裸線程,所以,不能直接在Thread中定義Hander對象,從使用場景的角度講,即不能直接給Thread對象發消息,但卻能夠給UI線程發消息。

子線程爲何不能更新UI

由於UI訪問是沒有加鎖的,在多個線程中訪問UI是不安全的,若是有多個子線程都去更新UI,會致使界面不斷改變而混亂不堪。因此最好的解決辦法就是隻有一個線程有更新UI的權限,因此這個時候就只能有一個線程振臂高呼:放開那女孩,讓我來!那麼最合適的人選只能是主線程。

子線程也能夠更新UI

SurfaceView是 android 裏惟一一個能夠在子線程更新的控件。SurfaceView能夠在主線程以外的線程中向屏幕繪圖。這樣能夠避免畫圖任務繁重的時候形成主線程阻塞,從而提升了程序的反應速度。當須要快速,主動地更新View的UI,或者當前渲染代碼阻塞GUI線程的時間過長的時候,SurfaceView就是解決上述問題的最佳選擇。

子線程能夠更新除SurfaceView之外的UI

子線程更新UI?沒錯,不信下面的代碼跑一遍試試,並不會報錯,並且正確顯示。



public class MainActivity extends AppCompatActivity { private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView=(TextView)findViewById(R.id.textView); new Thread(new Runnable() { @Override public void run() { mTextView.setText("Child Thread"); } }).start(); } }

這是爲何呢?一個應用程序中有一個主線程和若干個子線程,而線程的檢查工做是由ViewRoot完成的。ViewRoot是什麼呢?能夠簡單的理解爲Window和View以前的橋樑或者紐帶。而ViewRoot的建立是在onResume()以後才完成的,也就是說在onResume()以前,系統自己是沒法區分當前線程究竟是主線程仍是子線程,而上面的代碼中UI的更新操做在onCreate()中完成,先於onResume(),因此上述的子線程纔有機會越俎代庖。

子線程如何與主線程通訊

一、Activity.runOnUiThread(Runnable)



mHandle.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { // 耗時操做 loadNetWork(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mTextView.setText(來自網絡的文字); } }); } }); } });

二、 View.post(Runnable)

mHandle.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                              // 耗時操做           
                              loadNetWork();      
                        mTextView.post(new Runnable() {

                            @Override
                            public void run() {
                                mTextView.setText(來自網絡的文字);    
                            }
                        });

                    }
                });



            }
        })<span style="font-family: Arial, Helvetica, sans-serif;">;</span>

三、View.postDelayed(Runnable,long)



mHandle.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { // 耗時操做 loadNetWork(); mTextView.postDelayed(new Runnable() { @Override public void run() { mTextView.setText(來自網絡的文字); } }, 10); } }); } });

四、Handler(子線程調用Handler的
handle.sendMessage(msg);



Handler handle = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); mTextView.setText(來自網絡的文字); } }; class MyThread extends Thread { @Override public void run() { // 耗時操做 loadNetWork(); Message msg = new Message(); handle.sendMessage(msg); super.run(); } }

五、AsyncTask

主線程調用:



aTask ak = new aTask(); ak.execute(); AsyncTask private class aTask extends AsyncTask { //後臺線程執行時 @Override protected Object doInBackground(Object... params) { // 耗時操做 return loadNetWork(); } //後臺線程執行結束後的操做,其中參數result爲doInBackground返回的結果 @Override protected void onPostExecute(Object result) { super.onPostExecute(result); mTextView.setText(result); } }

總結

最後來個總結,Android中的線程延續了JAVA的設計模型,默認一個應用程序只有一個主線程,主線程的開啓是在Activity的main()方法。主線程其實是一個死循環,不斷的循環處理系統以及其餘子線程發來的消息。主線程的綁定是在DecorView初始化的時候,也就是生命週期的onResume()以後。主線程主要處理UI操做,和Broadcast相關消息,主線程若是長時間沒法響應,將出現ANR,爲了不ANR,耗時操做通常都開啓子線程處理。子線程處理完再發消息通知主線程來改變UI。



本文同步分享在 博客「xiangzhihong8」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索