Android面試七(線程及線程間通信)

目錄

一,線程基本概念

二,異步消息機制Handler

三,面試時會問到的問題

 


一,線程基本概念

1,線程的概念

線程是進程中的多條執行線路.

2.開啓線程的三種方法
第一種:繼承Thread類,覆蓋run方法,使用start方法開啓線程,使用簡單。

第二種:實現Runnable接口,重寫run方法,創建runnable實例作爲target,傳入Thread中,即new Thread(runnable).start();這樣耦合度低,類的擴展性更好。

第三種:匿名內部類new Thread(){...}或者new Thread(new Runnable(){...})方法體內重寫run方法。

run():直接調用該方法還是單線程,是同步執行,由main主線程來調用run方法。

start():調用start會通知線程規劃器,此線程已準備就緒,等待調用線程對象的run方法。系統會安排時間來調用Thread中的run方法。

停止線程的方法:通過設置一個flag標記,run方法返回線程結束;調用interrupt方法。

3,線程的狀態

new:新建狀態,new出來了,但還沒有調用start;

runnable:可運行狀態,調用start進入可運行狀態,可能運行也可能沒有運行,取決於操作系統的調度;

blocked(阻塞狀態):被鎖阻塞,暫時不活動,當線程進入synchronized關鍵字修飾的方法或者代碼塊獲取鎖時的狀態;

waitting(等待狀態):不活動,不運行任何代碼,等待線程調度器調度,當wait或者sleep後進入該狀態;

timed_waitting:超時等待,在指定時間自行返回;

terminated(結束).:包含正常終止和異常終止。

通過getState()方法獲取狀態

4,線程的控制方法(線程調度方法)

線程調度器是一個操作系統服務,負責爲Runnable狀態的線程分配CPU時間。

1,setName(String name):設置線程名稱

2,setPriority(int num):設置優先級,默認5,最大10;優先級高的在運行時會有優先權,但這不會保證高優先級的線程會在低優先級的線程前執行。

3,Thread.sleep():睡眠,睡眠後進入blocked(阻塞)狀態;

4,t1.join():是Thread的方法,合併線程,就是將t1線程合併到調用join方法的線程t中,則t線程執行到join方法時必須等待t1線程執行完成後,才能繼續運行,這樣就同步運行了,而不是異步。

public class ThreadJoinDemo {

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()+"首先執行線程1");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                    Thread.sleep(1000);
                    System.out.println(System.currentTimeMillis()+"然後執行線程2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                    Thread.sleep(1000);
                    System.out.println(System.currentTimeMillis()+"最後執行線程3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println(System.currentTimeMillis()+"雖然開啓了線程3,但需等待線程2執行完畢");
        thread3.start();
        System.out.println(System.currentTimeMillis()+"雖然開啓了線程2,但需等待線程1執行完畢");
        thread2.start();
        System.out.println(System.currentTimeMillis()+"開啓線程1");
        thread1.start();
    }
}

執行結果,無論三個線程的開啓順序咋樣,都是會限制性線程1,然後線程2,最後線程3:

1588304517995雖然開啓了線程3,但需等待線程2執行完畢
1588304517996雖然開啓了線程2,但需等待線程1執行完畢
1588304517996開啓線程1
1588304518020首先執行線程1
1588304519024然後執行線程2
1588304520025最後執行線程3

5,wait():是Object的方法,作用是讓當前線程進入waiting(等待)狀態,同時,wait也會讓當前線程釋放它所持有的鎖。直到其他線程調用此對象的notify()方法或者notifyAll()方法,當前線程就會被喚醒,進入就緒狀態;

6,notify()和notifyAll():Object的方法,喚醒當前對象上的等待的單個線程/所有線程。

7,wait(long timeout):讓當前線程處於blocked(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,或者超過指定的時間量,當前線程被喚醒進入就緒狀態。

(wait方法、notify方法都被定義在Object類中,這樣Java的每一個類都有用於線程間通信的基本方法)

public class Test {
    private static Object myLock1 = new Object();//第一個鎖
    private static Object myLock2 = new Object();//第二個鎖
    private static boolean t1Run = false;//線程1是否運行的標誌
    private static boolean t2Run = false;//線程2是否運行的標誌
    public static void main(String[] args) {
        final Thread thread1 = new Thread(() -> {
            synchronized (myLock1){//獲得鎖1
                System.out.println(System.currentTimeMillis()+"首先執行線程1");
                t1Run = true;
                myLock1.notify();//喚醒在鎖1上等待的線程
            }

        });

        final Thread thread2 = new Thread(() -> {
            synchronized (myLock1){
                try {
                    if(!t1Run) {//如果線程1還沒有執行
                        System.out.println(System.currentTimeMillis() + "線程1還沒有運行,線程2先等待");
                        myLock1.wait();//線程2進入等待狀態,同時釋放所持有的鎖1
                        System.out.println(System.currentTimeMillis() + "線程2等待線程1執行結束了");
                    }
                    synchronized (myLock2){//獲取鎖2,在這步前線程1肯定已經執行結束了
                        Thread.sleep(1000);
                        System.out.println(System.currentTimeMillis()+"然後執行線程2");
                        t2Run = true;
                        myLock2.notify();//喚醒鎖2對象上的線程
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        Thread thread3 = new Thread(() -> {
            synchronized (myLock2){
                try {
                    if(!t2Run){
                        System.out.println(System.currentTimeMillis()+"線程2還沒有運行,線程3先等待");
                        myLock2.wait();
                        System.out.println(System.currentTimeMillis() + "線程3等待線程2執行結束了");
                    }
                    System.out.println(System.currentTimeMillis()+"最後執行線程3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        thread1.start();
        thread3.start();
        thread2.start();
    }
}

執行結果:(打印順序不一定,但一定是線程1,2,3依次執行)

1588347849498線程2還沒有運行,線程3先等待
1588347849498首先執行線程1
1588347850499然後執行線程2
1588347850500線程3等待線程2執行結束了
1588347850500最後執行線程3

5,線程的同步鎖

synchronized:可以同步代碼塊,方法;

最好不要鎖定run方法,因爲只有等當前線程執行完run方法釋放鎖後,其他線程纔會執行run方法。在run方法裏面加鎖也不好,因爲這樣每個線程都會有一個鎖,不會實現同步代碼塊的。

volatile:設置的是變量的可見性,所有線程都會直接讀取該變量並且不會緩存它,保證線程讀取的變量是和內存中。

6.線程池的好處

https://juejin.im/post/5b3cf259e51d45194e0b7204

(1)重用線程池的線程,避免總是創建銷燬線程浪費時間,浪費性能。

(2)控制線程最大併發數,避免大量線程因爲搶佔資源造成堵塞現象。

(3)簡單管理線程,可以定時執行或者間隔時間段執行

來源於Java中的空接口Executor,線程池真正實現的是ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {
         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
}

corePoolSize:核心線程數,默認情況下,及時是閒置狀態,也一直存活。當把ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true時,當閒置的核心線程等待新任務時間超過keepAliveTime時,會被自動回收。

maximunPoolSize:最大線程數,超過這個數量時,新任務會被阻塞。

keepAliveTime:非核心線程數閒置的時間

unit:keepAliveTime的時間單位

workQueue:線程池中的任務隊列

threadFactory:線程工廠,爲線程池提供創建新線程的功能。

種類:

FixedThreadPool(可控最大併發數線程池):線程數量固定,即使閒置狀態也不會被回收,除非線程池關閉,只有核心線程,沒有非核心線程。

newFixedThreadPool(int nThreads, ThreadFactory threadFactory){
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),threadFactory);
}

CachedThreadPool(可回收緩存線程池):線程數量不固定,只有非核心線程,當線程都活躍時,就創建新線程,否則利用空閒線程,空閒線程超過60s時會被回收。適合處理大量且耗時較少的任務。

newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                                       new SynchronousQueue<Runnable>());
}

scheduleThreadPool(支持定時與週期性任務的線程池):核心線程固定,非核心線程不固定,當非核心線程空閒時,立即被回收。適合執行定時任務或有固定週期的任務。

SingleThreadExecutor(單線程化線程池):只有1個核心線程,沒有非核心線程,確保所有任務能夠在同一線程且按順序執行,不用考慮同步問題

    private static void testExecutor(){
        //首先創建線程池,這兒拿單線程化線程池舉例
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Thread thread1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis()+"首先執行線程1");
        });
        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()+"然後執行線程2");
        });
        Thread thread3 = new Thread(() -> {
            System.out.println(System.currentTimeMillis()+"最後執行線程3");
        });
        //提交線程
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.submit(thread3);
        //關閉線程池
        executorService.shutdown();
    }

輸出結果:會按順序依次執行

1588349278957首先執行線程1
1588349279959然後執行線程2
1588349279959最後執行線程3

7,handlerThread的使用和原理

繼承Thread,相當於在Thread中生成了一個loop循環器,可以進行Looper循環,即handlerThread=handler+Thread+looper。

HandlerThread繼承Thread後,重寫了run方法,可以看到,顯示通過Looper.prepare()創建線程,然後採用同步代碼,保證線程安全,一個線程沒有執行完,則looper就是阻塞狀態,執行完後,通過notifyAll()方法通知線程進入就緒狀態:

public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

通知的就是getLooper()方法,如果Looper輪詢器還在工作,則讓線程等待,知道run方法執行notifyall()通知後,,纔會返回Looper。

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}
 

特點:

(1)通過獲取handlerThread的loop對象,傳遞給handler對象,在handlerMessage方法中執行異步任務

(2)不會阻塞,減少對性能消耗。但是不能同時多任務處理,需等待處理。

(3)和線程池注重併發不同,只是串行隊列,內部只有一個線程。

使用:

(1)先寫一個類TestHandler繼承Handler,並重寫handlerMessage方法

(2)初始化HandlerThread,並啓動

        HandlerThread handlerThread = new HandlerThread("test_thread");
        handlerThread.start();//必須得先啓動線程,不然沒有創建handler,也就沒有Looper對象
        TestHandler handler = new TestHandler(handlerThread.getLooper());
        handler.sendEmptyMessage(0);
(3)當不需要的時候記得調用HandlerThread.quit()方法移除可能未完成的操作避免內存泄露等危險養成一種良好的習慣。

8.IntentService和Service有什麼區別

IntentService繼承Service,兩個都是服務。IntentService可以執行耗時操作,Service不能直接執行耗時操作,否則會ANR。

(1)創建一個測試類繼承IntentService並重寫onHandleIntent方法,用來處理相應事務

public class TestIntentService extends IntentService {..重寫onHandleIntent(Intent intent)方法..}

(2)正常啓動service即可
    startService(new Intent().setClass(MainActivity.this, TestIntentService.class).putExtra("content", "傳輸的數據"));

(3)在IntentService的onCreate方法中會開啓一個HandlerThread線程,內部loop會創建一個ServiceHandler,在onstart方法中,會把intent轉到ServiceHandler中,

    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

(4)ServiceHandler繼承Handler,重寫了handleMessage方法,可以看到調用了我們一開始重寫的onHandleIntent方法,執行完畢後stopSelf結束service。

private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

Service不能直接執行耗時操作,否則會ANR,只能新開線程執行耗時操作。

二,異步消息機制Handler

1,異步消息機制

總共由四部分組成:handler,message,MessageQueue,Looper

(1)在主線程中創建 Handler 對象,並重寫 handleMessage() 方法。

(2)子線程進行 UI 操作時,創建 Message 對象,通過第一步創建的Handler 發送消息,handler.sendMessage(message),handler將消息發送到MessageQueue中。

(3)Looper 通過loop()循環從 MessageQueue 中取出待處理消息。

(4)looper將取出的message分發回 Handler 的 handleMessage() 方法中處理。

原理:

https://blog.csdn.net/fnhfire_7030/article/details/79518819

2,什麼是handler

handler通過發送和處理Message和Runnable對象來關聯相對應線程的MessageQueue。

(Message傳遞的是消息,MessageQueue是一個消息隊列,Handler機制裏Lopper輪詢器會不斷的從消息隊列裏獲取消息,交給handler處理消息)

(1),可以讓對應的message和runnable在未來的某個時間點進行相應的處理

(2),讓自己想要處理的耗時操作放在子線程,讓更新UI的操作放在主線程。

3,handler的使用(post(runnable)和 sendMessage(mesage))

(1),post(runnable)

(1.1)首先創建Handler()

Handler handler = new Handler();

(1.2)然後新開線程,創建Runnable對象,通過post方法,將runnable傳到handler當中,handler會在合適的時候讓主線程運行runnable更新UI的代碼。

class DownloadThread extends Thread{
    @Override
    public void run() {
        try{
            System.out.println("開始下載文件");
            Thread.sleep(2000);
            System.out.println("文件下載完成");
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    //更新UI的一些操作
                }
            };
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

(1.3)在主線程中開啓DownloadThread線程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

(2),sendMessage(mesage)

(2.1)創建handler,並複寫handleMessage方法

private Handler handler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
        switch (msg.what){
            case 1:
                //執行UI操作
                break;
        }
    }
};

(2.2)在新開線程中創建message對象,通過what,org1,org2進行賦值,然後handler發送該消息,handler通過其handleMessage的方法進行UI的處理

class DownloadThread extends Thread{
    @Override
    public void run() {
        try{
            System.out.println("開始下載文件");
            Thread.sleep(2000);
            System.out.println("文件下載完成");
            Message msg = new Message();
            msg.what = 1;//自定義的識別碼,handler可以識別不同的額msg
            handler.sendMessage(msg);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

(2.3)在主線程中開啓DownloadThread線程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

4,handler的內部機制原理

Handler:

創建handler對象時,則持有了當前線程的Looper和MessageQueue對象

 public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }

Looper:

每個線程都有的,通過prepare()創建Looper對象,保存在本線程的ThreadLocal中,保證每個線程的Looper的唯一性。

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

通過loop()開啓循環,不斷調用MessageQueue.next()方法獲取Message,進行消息的分發msg.target.dispatchMessage(msg),msg這個屬性target就是Handler,其實就是通過handler的dispatchMessage()方法發送消息給消息隊列,最終調用handleMessage()。
 

5,handler引起的內存泄漏

當Activity要回收時,handler沒有被回收,對Activity的引用也不會釋放。即靜態內部類持有外部類的匿名引用,導致外部Activity無法釋放。

解決方法:handler內部持有外部activity的弱引用,並把handler改爲靜態內部類,在activity的onDestroy方法中調用mHandler.removeCallback()方法。

private Handler mHandler = new WeakHandler(this);

static class WeakHandler extends Handler {
    private WeakReference<Activity> mActivity;
    TestHandler(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Activity activity = mActivity.get();
        if (activity != null) {
            //TODO:
        }
    }
}

6,handlerThread

1,產生背景:

開啓Thread子線程進行耗時操作時,多次創建和銷燬線程很耗系統資源。

2,本質

handler+Thread+Looper

本質上是一個線程類,繼承了Thread;

但他有自己的內部Looper對象,可以進行looper循環;

通過獲取HandlerThread的looper對象傳遞給Handler對象,可以在handleMessage方法中執行異步任務;

優點是不會堵塞,減少對性能的消耗,缺點是不能同時進行多任務處理,處理效率低。

與線程池注重併發不同,HandlerThread是一個串行隊列,背後只有一個線程。

三,面試時會問到的問題

1,簡要說下進程和線程的區別

進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,進程在執行過程中擁有獨立的內存單元;

線程是CPU調度和分派的基本單位,是能獨立運行的基本單位,一個進程至少有一個線程,多個線程共享進程的內存資源,但每個線程都擁有單獨的棧內存用來存儲本地數據。

2,Thread類中的start()方法和run()方法的區別

start方法用來啓動線程,內部調用了run方法,run方法只是直接調用run方法,並沒有開啓新線程。

3,在多線程中,什麼是上下文切換?

上下文切換時存儲和恢復CPU狀態的過程,它使得線程執行能夠從中斷點恢復執行。上下文切換是多任務操作系統和多線程環境的基本特徵。

4,有三個線程,如果保證線程順序執行?

(方法1)在線程3調用t1.join()方法,在線程2調用t1.join()方法。

(方法2)使用synchronized鎖住Object對象,然後使Object的wait方法使當前線程進入等待狀態並釋放鎖(對象監視器),在要執行的線程執行完任務後調用notify方法,等待該線程剩餘代碼執行完畢後釋放鎖(對象監視器),然後喚醒之前進入等待狀態的線程。

(方法3)使用SingleThreadExecutor(單線程化線程池)

5,產生死鎖的條件

死鎖指兩個以上的線程永遠阻塞的狀態。

(1)互斥條件:一個資源每次只能被一個進程使用

(2)請求與保持條件:一個進程因請求資源而阻塞時,對已經獲得的資源保持不放。

(3)循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。

6,什麼是線程局部變量ThreadLocal?

ThreadLocal用於創建線程的本地變量,屬於線程自身所有,這是一種實現線程安全的方式。一個對象的所有線程都會共享它的全局變量,所以這些線程是不安全的,我們可以使用同步技術。但是當我們不想使用同步的時候,可以選擇ThreadLocal變量,因爲ThreadLocal是一種以空間換時間的做法在每個Thread內部維護了一個ThreadLocal,ThreadLocalMap把數據進行隔離,數據不共享,自然就不會有線程安全的問題了。

7,簡要說下線程間通信的原理

通過Handler機制

(1)在Handler構造方法中:Looper通過Looper.myLooper()方法獲取一個Looper對象,通過mLooper.mQueue拿到與這個Looper對應的MessageQueue;

(2)然後開啓死循環,對消息隊列進行不停的獲取,獲取到一個消息後,交給Message.target.dispatchMessage()方法對消息進行處理。

8,post和sendMessage的區別

sendMessage一般和handleMessage配合使用,在sendMessage中發送消息,在handleMessage中接收消息並進行UI的處理;

post方法直接在runnable的run方法中更新UI,但兩者本質上沒有區別,都是發送消息到消息隊列中,只是post的方式更簡單些。

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
//獲得了message實例,將r賦給callback,接下來還是和sendMessage一致的操作,進入sendMessageDelayed
 private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //最終還是執行到sendMessageAtTime這個方法裏面
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//接下來看下sendMessageAtTime方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

接下來看下post和sendMessage是如何進行消息的處理的

public void dispatchMessage(Message msg) {
  //如果是post,callback不爲空,直接進入handleCallback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
   //如果是sendMessage,且創建handler時沒有傳入callback,則callback爲空,直接進入handleMessage,也就是我們自己複寫的處理Message的方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

//直接run並不會啓動新線程,所以這就是post的runnable裏面可以直接更新UI的原因
   private static void handleCallback(Message message) {
    message.callback.run();
}

9,爲什麼主線程一直loop死循環但不會ANR(Application Not Responding)?

首先說下造成ANR的原因:

(1)當前事件沒有機會處理:主線程正在處理前一個事件,沒有及時的完成或者Looper被某種原因阻塞住了。

(2)當前事件正在處理,但沒有及時完成。

所以爲了避免ANR,採用了Handler消息處理機制,耗時操作在子線程運行。

先看下主線程中的Looper.loop()循環,在主線程入口類ActivityThread.java的入口函數中:

//先看下入口類的入口函數main函數
public static final void main(String[] args) {
    ...
    //創建Looper和MessageQueue
    Looper.prepareMainLooper();
    ...
    //輪詢器開始輪詢
    Looper.loop();
    ...
}

//再看下Looper.loop()方法
   while (true) {
       //取出消息隊列的消息,可能會阻塞
       Message msg = queue.next(); // might block
       ...
       //解析消息,分發消息
       msg.target.dispatchMessage(msg);
       ...
    }

可以看到,如果主線程沒有loop循環,那麼主線程執行完畢就會退出,那誰來等待用戶的操作啊,比如觸碰屏幕等!

也就是說,ActivityThread的main方法主要就是做消息循環,我們的代碼就是在這個循環裏面去執行,一旦退出循環,那麼這個程序也就退出了。

也就是說,Android是由事件驅動的,loop()不斷的接收事件、處理事件,這樣每一個點擊觸摸或者activity的生命週期都是運行在Looper.loop()的控制下,所以這兒不能混淆概念,Looper.loop()方法本身不會引起主線程的阻塞,只能說當某一個消息或者對消息的處理阻塞了Looper.loop(),纔會造成ANR。

而且,主線程的Looper從消息隊列讀取消息,當讀取完所有消息時,主線程會處於阻塞狀態,當系統喚醒主線程如點擊事件傳遞給了主線程或者其他線程通過Handler向主線程的消息隊列中存放了一條消息時,主線程會被喚醒。當主線程處理完成後,會再次進入睡眠狀態,所以loop循環不會對CPU性能造成過多的消耗。

看下handleMessage()的部分代碼:

 public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case PAUSE_ACTIVITY_FINISHING:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...........
        }
    }

可以看到,activity的生命週期都是依靠主線程的Looper.loop,當收到不同的Message時採用相應的措施,如果這個消息處理時間過長,比如在onCreate()方法處理耗時操作,那麼下一次的消息比如用戶點擊的消息就不能處理了,這時候整個循環就會產生卡頓,時間一長就ANR了。

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章