Nothing seek,Nothing find

美圖欣賞

picture-01.jpg
picture-02.jpg
picture-03.jpg
picture-04.jpg
picture-05.jpg
picture-06.jpg
picture-07.jpg

Java、Android知識點(diǎn)匯集

Java集合類

** Java集合相關(guān)的博客**


Java線程相關(guān)

1.線程

synchronized的作用域:
synchronized的用法有兩種,同步方法和同步代碼塊。

一、同步方法。

  • 1、非靜態(tài)同步方法的鎖是當(dāng)前類對象this,即非靜態(tài)的synchronized方法的作用域?yàn)閷ο蟊旧?。例如,一個對象有多個同步方法,只要有一個線程訪問了其中的一個synchronized方法,其他線程就不能同時訪問這個對象中的任何一個synchronized方法。但是不同對象實(shí)例之間的synchronized方法是互不干擾的。

  • 2、靜態(tài)同步方法的鎖是當(dāng)前類的字節(jié)碼文件,作用域?yàn)楫?dāng)前類,即作用于該類的所有對象實(shí)例。

二、同步代碼塊。對該代碼塊的資源實(shí)行互斥訪問。

注:
0、同步方法和同步代碼塊的區(qū)別,同步代碼塊不會鎖住整個對象(當(dāng)然你也可以讓它鎖住整個對象)。同步方法會鎖住整個對象,哪怕這個類中有多個不相關(guān)聯(lián)的同步塊,這通常會導(dǎo)致他們停止執(zhí)行并需要等待獲得這個對象上的鎖。Java的synchronized的同步代碼塊和同步方法的區(qū)別
1、wait(),notify(),notifyAll()方法只能在synchronized中調(diào)用。
2、synchronized中調(diào)用wait()方法時立即阻塞讓出CPU執(zhí)行權(quán),代碼塊中剩下的代碼不會繼續(xù)執(zhí)行,只能等待喚醒繼續(xù)執(zhí)行。
3、synchronized中調(diào)用notify()或者notifyAll()后,synchronized中的剩余代碼會繼續(xù)執(zhí)行。并且只有等待該同步方法或者同步代碼塊中剩余代碼執(zhí)行完成后,其他正在等待獲取該對象鎖的同步方法或者同步代碼塊才會繼續(xù)執(zhí)行。
4、死鎖

public class Test {
 public static Object object = new Object();
 /**
  * @param args
  * @throws ClassNotFoundException 
  */
 public static void main(String[] args) throws ClassNotFoundException {
        
  Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
         
        thread1.start();
         
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         
        thread2.start();
 }

  static class Thread1 extends Thread{
         @Override
         public void run() {
             synchronized (object) {
                 try {
                  System.out.println("開始Thread1");
                  Thread.sleep(2000);
                     object.wait();
                 } catch (InterruptedException e) {
                 }
                 System.out.println("線程"+Thread.currentThread().getName()+"獲取到了鎖");
             }
         }
     }
      
     static class Thread2 extends Thread{
         @Override
         public void run() {
             synchronized (object) {
                 object.notifyAll();
                 System.out.println("線程"+Thread.currentThread().getName()+"調(diào)用了object.notify()");
                 try {
      sleep(10000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
                 
                 System.out.println("sleep end");
             }
             System.out.println("線程"+Thread.currentThread().getName()+"釋放了鎖");
         }
     }
}

控制臺輸出結(jié)果如下:

1 開始Thread1
2 線程Thread-1調(diào)用了object.notify()
3 sleep end
4 線程Thread-1釋放了鎖
5 線程Thread-0獲取到了鎖

需要注意的是1,2,3是先執(zhí)行的而且是順序執(zhí)行。4,5執(zhí)行順序不確定。

2.ThreadLocal(線程本地存儲)
3.線程池

對比new Thread()和線程池的優(yōu)劣
new Thread()弊端:
1、new Thread()每次創(chuàng)建新的對象,性能差;
2、缺乏統(tǒng)一管理,資源開銷大。無限制的創(chuàng)建可能會因?yàn)橘Y源占用過高導(dǎo)致OOM或者死機(jī);
3、功能單一,沒有辦法定時,定期執(zhí)行任務(wù)等;
相比于new Thread()
1、重用線程,減少對象的創(chuàng)建,回收空閑的線程,提升性能;
2、可以有效控制最大并發(fā)數(shù),提高系統(tǒng)資源利用率,同時避免過多的資源競爭,避免堵塞;
3、提供可定時,定期,單線程,并發(fā)控制等功能

4.volatile關(guān)鍵字

** volatile特性**

當(dāng)一個共享變量被volatile修飾的時候,它能保證修改的值能夠立即被更新到主存。
內(nèi)存可見性:線程A對一個volatile修飾的共享變量的修改,對于其他線程來說是可見的,即線程每次獲取volatile變量的值都是最新的。

volatile的使用條件

volatile相當(dāng)于一個輕量級的synchronize。但是volatile必須滿足兩個條件:
1、對變量的寫操作不能依賴于本身(即不能依賴于當(dāng)前值)。如多線程下執(zhí)行a++,這樣是無法通過volatile保證結(jié)果的準(zhǔn)確性的。
2、該變量沒有包含在具有其他變量的不變式中

public class NumberRange {
    private volatile int lower = 0;
     private volatile int upper = 10;

    public int getLower() { return lower; }
    public int getUpper() { return upper; }

    public void setLower(int value) { 
        if (value > upper) 
            throw new IllegalArgumentException(...);
        lower = value;
    }

    public void setUpper(int value) { 
        if (value < lower) 
            throw new IllegalArgumentException(...);
        upper = value;
    }
}

上述代碼中,上下界初始化分別為0和10,假設(shè)線程A和B在某一時刻同時執(zhí)行了setLower(8)和setUpper(5),且都通過了不變式的檢查,設(shè)置了一個無效范圍(8, 5),所以在這種場景下,需要通過sychronize保證方法setLower和setUpper在每一時刻只有一個線程能夠執(zhí)行。

volatile的使用場景

1、狀態(tài)標(biāo)記
2、double check(雙重檢查)

    private volatile static Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    } 
}

volatile關(guān)鍵字

Java代理模式

組合與繼承

-深入理解Java中的組合和繼承

泛型

Java反射

Java內(nèi)存相關(guān)

注意點(diǎn)


java異常

java異??蚣軋D.jpg

所有的異常都繼承自一個共同的父類Throwable,而Throwable有兩個重要的子類:Exception(異常)和Error(錯誤)

  • ** Error**(錯誤)
    是程序無法處理的錯誤,表示運(yùn)行應(yīng)用程序中較嚴(yán)重問題。大多數(shù)錯誤與代碼編寫者執(zhí)行的操作無關(guān),而表示代碼運(yùn)行時 JVM(Java 虛擬機(jī))出現(xiàn)的問題。例如,Java虛擬機(jī)運(yùn)行錯誤(Virtual MachineError),當(dāng) JVM 不再有繼續(xù)執(zhí)行操作所需的內(nèi)存資源時,將出現(xiàn) OutOfMemoryError。這些異常發(fā)生時,Java虛擬機(jī)(JVM)一般會選擇線程終止。
    這些錯誤表示故障發(fā)生于虛擬機(jī)自身、或者發(fā)生在虛擬機(jī)試圖執(zhí)行應(yīng)用時,如Java虛擬機(jī)運(yùn)行錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因?yàn)樗鼈冊趹?yīng)用程序的控制和處理能力之 外,而且絕大多數(shù)是程序運(yùn)行時不允許出現(xiàn)的狀況。對于設(shè)計(jì)合理的應(yīng)用程序來說,即使確實(shí)發(fā)生了錯誤,本質(zhì)上也不應(yīng)該試圖去處理它所引起的異常狀況。在 Java中,錯誤通過Error的子類描述。

  • ** Exception**(異常)
    是程序本身可以處理的異常。主要包含RuntimeException等運(yùn)行時異常和IOException,SQLException等非運(yùn)行時異常。

** 運(yùn)行時異常包括:**都是RuntimeException類及其子類異常,如NullPointerException(空指針異常)、IndexOutOfBoundsException(下標(biāo)越界異常)等,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應(yīng)該從邏輯角度盡可能避免這類異常的發(fā)生。
運(yùn)行時異常的特點(diǎn)是Java編譯器不會檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句聲明拋出它,也會編譯通過。

** 非運(yùn)行時異常**(編譯異常)包括:RuntimeException以外的異常,類型上都屬于Exception類及其子類。從程序語法角度講是必須進(jìn)行處理的異常,如果不處理,程序就不能編譯通過。如IOException、SQLException等以及用戶自定義的Exception異常,一般情況下不自定義檢查異常。

從** 編譯器是否要求強(qiáng)制處理**的角度分類,異常類別又可分為:

** 可查異常**
正確的程序在運(yùn)行中,很容易出現(xiàn)的、情理可容的異常狀況??刹楫惓km然是異常狀況,但在一定程度上它的發(fā)生是可以預(yù)計(jì)的,而且一旦發(fā)生這種異常狀況,就必須采取某種方式進(jìn)行處理。
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于可查異常。這種異常的特點(diǎn)是Java編譯器會檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常,要么用try-catch語句捕獲它,要么用throws子句聲明拋出它,否則編譯不會通過。

** 不可查異常**
包括運(yùn)行時異常(RuntimeException與其子類)和錯誤(Error)。

面試常見問題

1.描述Java 7 ARM(Automatic Resource Management,自動資源管理)特征和多個catch塊的使用如果一個try塊中有多個異常要被捕獲,catch塊中的代碼會變丑陋的同時還要用多余的代碼來記錄異常。有鑒于此,Java 7的一個新特征是:一個catch子句中可以捕獲多個異常。示例代碼如下:

catch(IOException | SQLException | Exception ex){
     logger.error(ex);
     throw new MyException(ex.getMessage());
}

大多數(shù)情況下,當(dāng)忘記關(guān)閉資源或因資源耗盡出現(xiàn)運(yùn)行時異常時,我們只是用finally子句來關(guān)閉資源。這些異常很難調(diào)試,我們需要深入到資源使用的每一步來確定是否已關(guān)閉。因此,Java 7用try-with-resources進(jìn)行了改進(jìn):在try子句中能創(chuàng)建一個資源對象,當(dāng)程序的執(zhí)行完try-catch之后,運(yùn)行環(huán)境自動關(guān)閉資源。下面是這方面改進(jìn)的示例代碼:

try (MyResource mr = new MyResource()) {
     System.out.println("MyResource created in try-with-resources");
} catch (Exception e) {
     e.printStackTrace();
}

2.在Java中throw與throws關(guān)鍵字之間的區(qū)別?
throws用于在方法簽名中聲明此方法可能拋出的異常,而throw關(guān)鍵字則是中斷程序的執(zhí)行并移交異常對象到運(yùn)行時進(jìn)行處理。

3.** 被檢查的異常 不受檢查的異常**有什么區(qū)別?

  • 被檢查的異常應(yīng)該用try-catch塊代碼處理,或者在main方法中用throws關(guān)鍵字讓JRE了解程序可能拋出哪些異常。不受檢查的異常在程序中不要求被處理或用throws語句告知。

  • Exception是所有被檢查異常的基類,然而,RuntimeException是所有不受檢查異常的基類。

  • 被檢查的異常適用于那些不是因程序引起的錯誤情況,比如:讀取文件時文件不存在引發(fā)的FileNotFoundException。然而,不被檢查的異常通常都是由于糟糕的編程引起的,比如:在對象引用時沒有確保對象非空而引起的NullPointerException。

4.Java中** final, finally, finalize**的區(qū)別?

** final finally在Java中是關(guān)鍵字,而 finalize則是一個方法。
** final
關(guān)鍵字使得類變量不可變,避免類被其它類繼承或方法被重寫。
** finally try-catch塊一起使用,即使是出現(xiàn)了異常,其子句總會被執(zhí)行,通常, finally子句用來關(guān)閉相關(guān)資源。
** finalize
方法中的對象被銷毀之前會被垃圾回收。

異常相關(guān)的博客

JVM模型


Java常用算法

Android基本架構(gòu)

Android基本架構(gòu)圖.gif

android基本架構(gòu)
Android基本框架結(jié)構(gòu)

Mac OS 下Android環(huán)境搭建

Android環(huán)境搭建

四大組件之 Activity

1. Activity生命周期
20160229214006125.png

正常情況下Activity會經(jīng)歷如下生命周期:

  1. onCreate(),表示Activity的創(chuàng)建。在這個方法中我們可以做一些初始化的工作。比如調(diào)用setContentView去加載layout布局資源,初始化Activity所需的數(shù)據(jù)等。

  2. onRestart(),重新啟動Activity。一般情況下,當(dāng)當(dāng)前的Activity從不可見變?yōu)榭梢姷臅r候,該方法會被調(diào)用。這中情況一般是用戶按Home健回到桌面或者啟動了一個新的Activity,這個時候onPause和onStop方法就會被調(diào)用。上面的操作完成后用戶如果點(diǎn)擊桌面應(yīng)用的圖標(biāo)或者按back鍵回到之前的Activity,這時候onRestart就會被調(diào)用。

  3. onStart(),表示開始啟動Activity。這個時候Activity就變?yōu)榭梢娏?,但是前臺還是看不到的,還沒有辦法和用戶進(jìn)行交互。只有當(dāng)onResume方法被調(diào)用的時候,才會真正的出現(xiàn)在前臺。

  4. onResume(),表示Activity已經(jīng)變得可見,可以和用戶進(jìn)行交互了。需要注意的是,雖然onStart和onResume雖然都表示Activity變得可見。但是onStart的時候,Activity還在后臺,onResume的時候才真正的出現(xiàn)在前臺。

  5. onPause(),此時的Activity正準(zhǔn)備停止,正常情況下,接著就會調(diào)用onStop方法。特殊情況下,如果這個時候用戶又回到這個Activity,那么onResume就會被調(diào)用。這是一種非常極端的情況,很難重現(xiàn)。onPause中不要做太多耗時的操作,因?yàn)闀绊懶碌腁ctivity的展現(xiàn),onPause必須先執(zhí)行完成,新的Activity的onResume方法才會被執(zhí)行。

  6. onStop(),表示Activity即將停止。可以做一些稍微重量級的回收工作。同樣不能做太多耗時的操作。

  7. onDestroy(),這是Activity生命周期中最后一個回調(diào),表示Activity即將被銷毀??梢宰鲆恍┗厥展ぷ鳎尫刨Y源等等。

Activity啟動模式

Android中Activity的啟動模式有四種,分別是standard、singleTop、singleTask和singleInstance。
1、standard:標(biāo)準(zhǔn)模式。這也是Activity默認(rèn)的啟動模式。每次啟動一個Activity都會重新創(chuàng)建新的Activity實(shí)例。不管這個Activity實(shí)例在任務(wù)棧中是否已經(jīng)存在。被創(chuàng)建的Activity實(shí)例的生命周期符合典型情況下的Activity生命周期。
事例說明:此時有兩個Activity,SecondActivity和ThirdActivity。當(dāng)我們當(dāng)我們從SecondActivity跳轉(zhuǎn)到ThirdActivity再從ThirdActivity跳轉(zhuǎn)到SecondActivity。我們可以發(fā)現(xiàn)SecondActivity被創(chuàng)建了兩次。這就說明標(biāo)準(zhǔn)模式下的Activity不會復(fù)用,每次啟動都會創(chuàng)建新的Activity實(shí)例。

2、singleTop:棧頂復(fù)用模式。這種模式下,如果要被啟動的Activity實(shí)例已經(jīng)位于任務(wù)棧的棧頂,則不再創(chuàng)建新的Activity實(shí)例。同時它的onNewIntent方法會被調(diào)用。通過這個方法我們可以取出當(dāng)前請求的信息。需要注意的是,這個Activity的onCreate。onStart不會被調(diào)用。因?yàn)樗]有發(fā)生改變。如果新的Activity不是位于棧頂,那么新的Activity任然會被創(chuàng)建。例如現(xiàn)在任務(wù)棧中有ABCD,ABCD啟動模式均為singleTop模式,A位于棧底,D位于棧頂,如果此時要啟動C,那么任務(wù)棧的情況就會變成ABCDC。

3、singleTask:這是站內(nèi)復(fù)用模式。這種模式下,只要Activity實(shí)例在任務(wù)棧中存在,啟動的時候就不會去再次創(chuàng)建新的Activity實(shí)例,而是沿用已經(jīng)存在的Activity實(shí)例。不論啟動多少次,任務(wù)棧都只會存在一個Activity實(shí)例。這就是站內(nèi)復(fù)用模式。和singleTop一樣,系統(tǒng)啟動Activity的時候,如果任務(wù)棧中存在該Activity實(shí)例,就不會調(diào)用onCreate和onStart方法,而是會調(diào)用onNewIntent方法。
singleTask站內(nèi)復(fù)用模式的幾種情況:
1)如果目前任務(wù)棧T1中存在ABC三個activity實(shí)例,這個時候Activity D以singleTask模式請求啟動,其所需要的任務(wù)棧未T2.由于T2和D的實(shí)例都不存在,所以系統(tǒng)會首先創(chuàng)建T2,然后將D壓入到T2中。
2)第二種情況,如果目前任務(wù)棧T1中存在ABCD三個activity實(shí)例,這個時候Activity D以singleTask模式請求啟動,由于D已經(jīng)存在T1的任務(wù)棧,所以D就不會被再次創(chuàng)建,而是直接沿用已經(jīng)存在的D的實(shí)例。
3)第三種情況,如果目前任務(wù)棧T1中存在ADBC三個activity實(shí)例,這個時候Activity D以singleTask模式請求啟動,由于D已經(jīng)在T1中已經(jīng)存在,同樣不會再次創(chuàng)建,也是沿用已經(jīng)存在的D的實(shí)例。同時會將位于D上面的其他Activity實(shí)例移出棧,使自己位于棧頂位置。

4、singleInstance:單實(shí)例模式。這種模式可以看成是singleTask的加強(qiáng)模式,它除了具有singleTask的模式所具有的一些列特性之外,還增加了一點(diǎn)特殊的特性。具有這種模式的activity只能單獨(dú)的運(yùn)行在獨(dú)立的任務(wù)棧中。什么叫運(yùn)行在獨(dú)立的任務(wù)棧,其實(shí)就是如果Activity A是singleInstance模式,當(dāng)A啟動后,系統(tǒng)會為A創(chuàng)建一個新的任務(wù)棧使其獨(dú)立運(yùn)行在其中,這個棧中只有A自己不存在其他Activity實(shí)例。由于它也具有singleTask的復(fù)用的特性,在后續(xù)啟動的時候,也會復(fù)用這個Activity。

Fragment

四大組件之 Service

  1. Service詳解
  2. Service學(xué)習(xí)筆記
  3. IntentService
  4. Android 5.0之后隱式聲明Intent 啟動Service引發(fā)的問題

四大組件之 Broadcast Receiver


四大組件之 ContentProvider


Notification


SharedPreferences

SharedPreferences類是一個接口類,真正的實(shí)現(xiàn)類是SharedPreferencesImpl。修改SharedPreferences需要獲取它的Editor,在對Editor進(jìn)行put操作后,最后通過commit或者apply提交修改到內(nèi)存和文件。當(dāng)然有了兩種都可以提交的方法,肯定要區(qū)別一下的。從實(shí)現(xiàn)類SharedPreferencesImpl的源碼上看也很容易看出兩者的區(qū)別:

commit這種方式很常用,在比較早的SDK版本中就有了,這種提交修改的方式是同步的,會阻塞調(diào)用它的線程,并且這個方法會返回boolean值告知保存是否成功(如果不成功,可以做一些補(bǔ)救措施)。
而apply是異步的提交方式,目前Android Studio也會提示大家使用這種方式。

還有一點(diǎn)用得比較少的,就是SharedPreferences還提供一個監(jiān)聽接口可以監(jiān)聽SharedPreferences的鍵值變化,需要監(jiān)控鍵值變化的可以用registerOnSharedPreferenceChangeListener添加監(jiān)聽器。

public interface SharedPreferences { 
    /** 
    * Interface definition for a callback to be invoked when a shared 
    * preference is changed. 
    */ 
    public interface OnSharedPreferenceChangeListener { 
       void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key); 
    }
}

多進(jìn)程操作和讀取SharedPreferences的問題
在SDK 3.0及以上版本,可以通過Context.MODE_MULTI_PROCESS屬性來實(shí)現(xiàn)SharedPreferences多進(jìn)程共享。如下設(shè)置:

public static SharedPreferences getSharedPreferences(String name) { 
     if (null != context) {
          if (Build.VERSION.SDK_INT >= 11) { 
             return context.getSharedPreferences(name, Context.MODE_MULTI_PROCESS); 
          } else { 
             return context.getSharedPreferences(name, Context.MODE_PRIVATE); } 
          } 

      return null; 
 }

MODE_MULTI_PROCESS屬性使用SharedPreferences也不能完全保證進(jìn)程間的共享數(shù)據(jù)不會出問題,真正使用中發(fā)現(xiàn)有會有一定概率出現(xiàn)這個取值出錯(變?yōu)槌跏贾担﹩栴}。
Google也在SDK 6.0的版本將這個MODE_MULTI_PROCESS標(biāo)識為deprecated(不贊成使用)。目前來說,越來越多的項(xiàng)目在不斷的膨脹,為了降低單個進(jìn)程的內(nèi)存占用率,使用"android:process"配置一些組件在單獨(dú)的進(jìn)程中運(yùn)行已經(jīng)是司空見慣了,所以大家在遇到自己的項(xiàng)目有多進(jìn)程時,要注意一下SharedPreferences的問題。

注意: 在一個進(jìn)程中,SharedPreference往往建單個實(shí)例就可以了,一般不會出現(xiàn)并發(fā)沖突,如果對提交的結(jié)果不關(guān)心的話,建議使用apply,當(dāng)然需要確保提交成功且有后續(xù)操作的話,還是需要用commit的。關(guān)于SharedPreferences多進(jìn)程數(shù)據(jù)共享問題,可以借鑒開源?替代方案如Github上的tray。

Android消息處理機(jī)制(Looper、Handler、MessageQueue、Message)

** 概述**:Android應(yīng)用程序是通過消息來驅(qū)動的,Android某種意義上也可以說成是一個以消息驅(qū)動的系統(tǒng),UI、事件、生命周期都和消息處理機(jī)制息息相關(guān),并且消息處理機(jī)制在整個Android知識體系中也是尤其重要。

Handler的運(yùn)行機(jī)制描述:談到Handler的運(yùn)行機(jī)制一般情況下,都會涉及到幾個比較重要的類,Looper,MessageQueue,Message等等。Handler的實(shí)例必須在Looper線程中創(chuàng)建,否則就會拋出RuntimeException異常(Can't create handler inside thread that has not called Looper.prepare())提示handler實(shí)例的創(chuàng)建必須在looper線程中進(jìn)行。handler實(shí)例的創(chuàng)建一般都是在UI線程,因?yàn)?,一般情況下我們使用handler的目的是為了執(zhí)行完后臺任務(wù)后,和UI線程進(jìn)行交互。由于UI線程在創(chuàng)建之初,就被設(shè)置成了Looper線程(這個可以在ActivityThread源碼中看到,里面有一個main方法,在實(shí)例化ActivityThread的之前,調(diào)用了 Looper.prepareMainLooper()),所以我們在實(shí)例化Handler的時候不需要手動再次調(diào)用Looper.prepare()方法。Looper線程中會維護(hù)一個MessageQueue消息隊(duì)列。handler通過sendMessage()方法向Looper中的消息隊(duì)列插入一條Message消息。MessageQueue通過enqueueMessage方法將handler發(fā)送來的message消息放到消息隊(duì)列中。由于Looper線程是一個循環(huán)進(jìn)程,里面有一個阻塞方法loop()。在該方法中l(wèi)ooper會調(diào)用MessageQueue的next()不停的循環(huán)遍歷MessageQueue中的消息(可以在Looper源碼的loop()方法中看到)。當(dāng)Looper發(fā)現(xiàn)有新的message來的時候,就會回調(diào)給Handler中handMessage方法進(jìn)行處理。

備注:handler在主線程中實(shí)例化后就會拿到主線程的MessageQueue的引用(Looper中維護(hù)的MessageQueue其實(shí)就是主線程中MessageQueue)。handler在sendMessage的時候,發(fā)送的Message里面持有Handler的引用。這樣在整個消息流程中就就把Looper,MessageQueue,Message,Handler串聯(lián)連起來了,不會出現(xiàn)錯亂。

消息處理機(jī)制的本質(zhì):** 一個線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理各個線程發(fā)來的消息**

簡單的說:一個線程開啟一個無限循環(huán)模式,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理,如果列表沒消息,自己就堵塞(相當(dāng)于wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什么事,就往該線程的消息隊(duì)列插入消息,該線程會不斷從隊(duì)列里拿出消息做處理。

** 消息處理機(jī)制的描述:**
線程其實(shí)就是一段可執(zhí)行代碼,當(dāng)這段代碼執(zhí)行完成后,該線程的生命周期就結(jié)束了,線程就會退出。既然如此,UI線程在執(zhí)行完成后為什么沒有退出呢?因?yàn)閁I線程在創(chuàng)建的時候就被變成了Looper線程(這個可以在ActivityThread的main方法中看到,在ActivityThread創(chuàng)建之前調(diào)用了Looper.prepareMainLooper();ActivityThread創(chuàng)建完成后接著就調(diào)用了Looper.loop();),Looper里面有一個loop()方法,這個方法里面有一段死循環(huán)的代碼。

主線程會一直處于這個死循環(huán)中,由于Looper里面維護(hù)一個MessageQueue消息隊(duì)列,這個消息隊(duì)列就用來維護(hù)其他線程發(fā)送來的Message消息(例如,Activity的啟動,生命周期,UI的更新,控件的事件等等),UI線程會根據(jù)消息隊(duì)列中的消息(例如,Activity的啟動,生命周期,UI的更新,控件的事件等等),依次做出處理。

那么其他線程是如何發(fā)送Message到UI線程的MessageQueue消息隊(duì)列中的?首先肯定是要拿到MessageQueue的實(shí)例,Google為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類,那么這個Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.getQueue();),Handler 在sendMessage的時候就通過這個引用往消息隊(duì)列里插入新消息。Handler 的另外一個作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做

** Looper、Handler、MessageQueue、Message作用和存在的意義?**

  • ** Looper** 循環(huán)線程,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會變當(dāng)前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程)

  • ** Handler**簡單說Handler用于同一個進(jìn)程的線程間通信。Google 為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類,那么這個Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時候就通過這個引用往消息隊(duì)列里插入新消息。Handler 的另外一個作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設(shè)計(jì)非常的贊。具體做法就是在隊(duì)列里面的Message持有Handler的引用(哪個handler 把它放到隊(duì)列里,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候,就來回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法。

  • ** MessageQueue**MessageQueue 存在的原因很簡單,就是同一線程在同一時間只能處理一個消息,同一線程代碼執(zhí)行是不具有并發(fā)性,所以需要隊(duì)列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那么多任務(wù)),然后挨個拿出來處理,這種設(shè)計(jì)很簡單,我們平時寫代碼其實(shí)也經(jīng)常這么做。每一個Looper線程都會維護(hù)這樣一個隊(duì)列,而且僅此一個,這個隊(duì)列的消息只能由該線程處理。
  • ** Message** UI線程和子線程通信的載體,主要存儲和傳遞一些子線程想讓UI線程處理的內(nèi)容

** 關(guān)于Looper中loop()方法的疑問**
1.UI線程一直在這個循環(huán)里跳不出來,主線程不會因?yàn)長ooper.loop()里的死循環(huán)卡死嗎,那還怎么執(zhí)行其他的操作呢?

在looper啟動后,主線程上執(zhí)行的任何代碼都是被looper從消息隊(duì)列里取出來執(zhí)行的。也就是說主線程之后都是通過其他線程給它發(fā)消息來實(shí)現(xiàn)執(zhí)行其他操作的。生命周期的回調(diào)也是如此的,系統(tǒng)服務(wù)ActivityManagerService通過Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后,通過App進(jìn)程的Binder線程給主線程的消息隊(duì)列插入一條消息來實(shí)現(xiàn)的。

2.主線程是UI線程和用戶交互的線程,優(yōu)先級應(yīng)該很高,主線程的死循環(huán)一直運(yùn)行是不是會特別消耗CPU資源嗎?App進(jìn)程的其他線程怎么辦?

這基本是一個類似生產(chǎn)者消費(fèi)者的模型,簡單說如果在主線程的MessageQueue沒有消息時,就會阻塞在loop的queue.next()方法里,這時候主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個消息進(jìn)來時候就會喚醒主線程,在2.2 版本以前,這套機(jī)制是用我們熟悉的線程的wait和notify 來實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。

** 關(guān)于Handler實(shí)例的創(chuàng)建**

  • Handler實(shí)例的創(chuàng)建,是需要在創(chuàng)建之前調(diào)用Looper.prepare()將當(dāng)前線程變成Looper線程的,同時調(diào)用Looper.loop()。那為什么我們平時在UI線程中創(chuàng)建Handler實(shí)例的時候?yàn)槭裁礇]有調(diào)用Looper.prepare()和Looper.loop(),那時因?yàn)閁I線程在創(chuàng)建的時候就變成了Looper線程,所以不再需要調(diào)用Looper.prepare()和Looper.loop()。如果再調(diào)用這兩個方法的話就會拋出Only one Looper may be created per thread異常。就是告訴你Looper實(shí)例已經(jīng)存在了,并且只能有一個。

我們在Activity中創(chuàng)建的Handler實(shí)例IDE常常提示出一些警告,這些警告一半都是提示這種定義方式可能出現(xiàn)內(nèi)存泄露。那么如何正確創(chuàng)建Handler實(shí)例??可以采用static方式,定義一個內(nèi)部類繼承Handler,利用軟引用WeakReference,接收外部類的引用。具體可以參照下面這兩篇文章

** Android消息處理相關(guān)機(jī)制相關(guān)的博文**


AsyncTask

1.AsyncTask機(jī)制

AnsycTask執(zhí)行任務(wù)時,內(nèi)部會創(chuàng)建一個進(jìn)程作用域的線程池來管理要運(yùn)行的任務(wù),也就就是說當(dāng)你調(diào)用了AsyncTask.execute()后,AsyncTask會把任務(wù)交給線程池,由線程池來管理創(chuàng)建Thread和運(yùn)行Therad。最后和UI打交道就交給Handler去處理了。

這是從API 23的AsyncTask源碼中截取的一部分源碼:

Paste_Image.png

從上面的代碼中我們可以看出,AsyncTask中的線程池,核心線程數(shù)(CORE_POOL_SIZE)由可獲得的CPU內(nèi)核數(shù)決定,最大線程數(shù)量(MAXIMUM_POOL_SIZE)是可獲得CPU內(nèi)核數(shù)的兩倍加1(CPU_COUNT*2+1)。keepAliveTime是30秒,工作隊(duì)列是128。

  • corePoolSize: 核心線程數(shù)目,即使線程池沒有任務(wù),核心線程也不會終止(除非設(shè)置了allowCoreThreadTimeOut參數(shù))可以理解為“常駐線程”
  • maximumPoolSize: 線程池中允許的最大線程數(shù)目;一般來說,線程越多,線程調(diào)度開銷越大;因此一般都有這個限制。
  • keepAliveTime: 當(dāng)線程池中的線程數(shù)目比核心線程多的時候,如果超過這個keepAliveTime的時間,多余的線程會被回收;這些與核心線程相對的線程通常被稱為緩存線程
  • unit: keepAliveTime的時間單位
  • workQueue: 任務(wù)執(zhí)行前保存任務(wù)的隊(duì)列;這個隊(duì)列僅保存由execute提交的Runnable任務(wù)
  • threadFactory: 用來構(gòu)造線程池的工廠;一般都是使用默認(rèn)的;
  • handler: 當(dāng)線程池由于線程數(shù)目和隊(duì)列限制而導(dǎo)致后續(xù)任務(wù)阻塞的時候,線程池的處理方式。

2.多個AsyncTask任務(wù)是串行還是并行?

從Android 1.6到2.3(Gingerbread) AsyncTask是并行的,即上面我們提到的有5個核心線程的線程池(ThreadPoolExecutor)負(fù)責(zé)調(diào)度任務(wù)。從Android 3.0開始,Android團(tuán)隊(duì)又把AsyncTask改成了串行,默認(rèn)的Executor被指定為SERIAL_EXECUTOR。

3.AsyncTask容易引發(fā)的Activity內(nèi)存泄露

如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會保留一個對創(chuàng)建了AsyncTask的Activity的引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺線程還在執(zhí)行,它將繼續(xù)在內(nèi)存里保留這個引用,導(dǎo)致Activity無法被回收,引起內(nèi)存泄露。

4.博文推薦

你真的了解AsyncTask?

總結(jié)

AsyncTask的運(yùn)行機(jī)制
AsyncTask分為兩個部分,一部分和主線程交互,一部分負(fù)責(zé)線程調(diào)度。雖然可能會存在多個AsyncTask的子類實(shí)例,但是其內(nèi)部的Handler和ThreadPoolExecutor都是靜態(tài)的,是進(jìn)程范圍內(nèi)共享的。所以AsyncTask控制著進(jìn)程范圍內(nèi)所有其子實(shí)例。

與主線程交互 AsyncTask和主線程交互是通過Handler來完成的;

?線程調(diào)度 關(guān)于AsyncTask內(nèi)部的線程調(diào)度,其內(nèi)部會創(chuàng)建一個進(jìn)程作用域內(nèi)的線程池;也就是說當(dāng)你調(diào)用了AsyncTask的execute(...)方法后,AsyncTask就會把任務(wù)交給線程池,由線程池來管理創(chuàng)建和運(yùn)行Thread。對于內(nèi)部線程池,不同版本的Android內(nèi)部實(shí)現(xiàn)方式是不一樣的。
Android2.3(API 10)之前,線程池限制數(shù)為5個,因?yàn)锳ndroid2.3之前的AsyncTask是并行的,所以同時只能有5個線程在運(yùn)行,超過的只能等待,等待前面5個完成才能繼續(xù)執(zhí)行。
這種情況在Android3.0(API 11)之后的版本得到了改善,Google工程師把AsyncTask的execute(...)方法由之前的并行方式,改成了串行方式,按先后順序每次只允許一個線程執(zhí)行。除此之外,還添加了一個executeOnExecutor(...)方法,這個方法是并行執(zhí)行方法,同時也允許開發(fā)者提供自定義的線程池來運(yùn)行和調(diào)度Thread,如果你想讓所有的任務(wù)都能并發(fā)同時運(yùn)行,那就創(chuàng)建一個沒有限制的線程池(Executors.newCachedThreadPool()),并提供給AsyncTask。這樣這個AsyncTask實(shí)例就有了自己的線程池而不必使用AsyncTask默認(rèn)的。當(dāng)然AsyncTask中還有兩個預(yù)設(shè)的線程池SERIAL_EXECUTORTHREAD_POOL_EXECUTOR。SERIAL_EXECUTOR表示串行執(zhí)行;THREAD_POOL_EXECUTOR表示并行執(zhí)行。SERIAL_EXECUTOR作用是保證任務(wù)執(zhí)行的順序,也就是它可以保證提交的任務(wù)確實(shí)是按照先后順序執(zhí)行的。它的內(nèi)部有一個隊(duì)列用來保存所提交的任務(wù),保證當(dāng)前只運(yùn)行一個,這樣就可以保證任務(wù)是完全按照順序執(zhí)行的,默認(rèn)的execute()使用的就是這個,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是一樣的。?對于THREAD_POOL_EXECUTOR,可以通過調(diào)用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),這樣起碼不用等到前面的都結(jié)束了再執(zhí)行。

AsyncTask帶來的問題
1.生命周期,AsyncTask不會?隨著Activity的?銷毀而銷毀。正常情況下Activity銷毀了,此時如果有AsyncTask在執(zhí)行,AsyncTask并不會隨著Activity的?銷毀而銷毀。而是會繼續(xù)執(zhí)行,如果我們在onPostExecute()方法中做了更新UI的操作,就有可能出現(xiàn)crash現(xiàn)象。解決方法就是在Activity銷毀的時候,檢測當(dāng)前是否存在正在運(yùn)行的AsyncTask(通過isCancelled()),如果有的話,就調(diào)用AsyncTask的cancel(true)方法,取消任務(wù),注意不要再AsyncTask的doBackground()方法中執(zhí)行不可中斷的操作(如BitmapFactory.decodeStream()),否則無法立即cancel掉。除此,還可以在onPostExecute()方法中,在執(zhí)行前判斷當(dāng)前Activity是否已存活,如果是活著的,就繼續(xù)執(zhí)行,反之則return取消掉。

2.內(nèi)存泄露,如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會保留一個對創(chuàng)建了AsyncTask的Activity的引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺線程還在執(zhí)行,它將繼續(xù)在內(nèi)存里保留這個引用,導(dǎo)致Activity無法被回收,引起內(nèi)存泄露。解決辦法,就是在Activity銷毀的時候,檢查是否有還在運(yùn)行的AsyncTask,如果有就cancel掉。

3.結(jié)果丟失,屏幕旋轉(zhuǎn)或Activity在后臺被系統(tǒng)殺掉等情況會導(dǎo)致Activity的重新創(chuàng)建,之前運(yùn)行的AsyncTask會持有一個之前Activity的引用,這個引用已經(jīng)無效,這時調(diào)用onPostExecute()再去更新界面將不再生效。解決方法和上面一樣,也是在銷毀之前cancel掉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,323評論 25 708
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 21,346評論 13 55
  • 1.要做一個盡可能流暢的ListView,你平時在工作中如何進(jìn)行優(yōu)化的? ①Item布局,層級越少越好,使用hie...
    fozero閱讀 887評論 0 0
  • 我已經(jīng)很久沒有耐心去認(rèn)真的看完一篇文章或者用心的去想要寫一篇文章,我花了大量的時間在手機(jī)和電腦上,做著一些自己也不...
    煢人閱讀 269評論 0 1
  • 在電影《機(jī)器紀(jì)元》里,機(jī)器人學(xué)會了自我復(fù)制和創(chuàng)造能力,代替人類成為新紀(jì)元即“機(jī)器紀(jì)元”的主人。 從常識和邏輯上理解...
    威威專欄閱讀 801評論 0 0

友情鏈接更多精彩內(nèi)容