美圖欣賞
Java、Android知識(shí)點(diǎn)匯集
Java集合類
** Java集合相關(guān)的博客**
- java面試相關(guān)
- Java 學(xué)習(xí)之集合類(Collections)
- Comparable 和 Comparator 比較
- Java程序員筆試必備--集合類
- Java常用集合類功能缤言、區(qū)別和性能
Java線程相關(guān)
1.線程
- Java并發(fā)編程:線程間協(xié)作的兩種方式:wait缩多、notify流酬、notifyAll和Condition
- Java 多線程 并發(fā)編程
- JAVA多線程和并發(fā)基礎(chǔ)面試問(wèn)答
- Java線程面試題 Top 50
- Java并發(fā)編程:Timer和TimerTask
synchronized的作用域:
synchronized的用法有兩種逸寓,同步方法和同步代碼塊植康。
一、同步方法看幼。
1批旺、非靜態(tài)同步方法的鎖是當(dāng)前類對(duì)象
this
幌陕,即非靜態(tài)的synchronized方法的作用域?yàn)閷?duì)象本身诵姜。例如,一個(gè)對(duì)象有多個(gè)同步方法搏熄,只要有一個(gè)線程訪問(wèn)了其中的一個(gè)synchronized方法棚唆,其他線程就不能同時(shí)訪問(wèn)這個(gè)對(duì)象中的任何一個(gè)synchronized方法。但是不同對(duì)象實(shí)例之間的synchronized方法是互不干擾的心例。2宵凌、靜態(tài)同步方法的鎖是當(dāng)前類的字節(jié)碼文件,作用域?yàn)楫?dāng)前類止后,即作用于該類的所有對(duì)象實(shí)例瞎惫。
二溜腐、同步代碼塊。對(duì)該代碼塊的資源實(shí)行互斥訪問(wèn)瓜喇。
注:
0挺益、同步方法和同步代碼塊的區(qū)別,同步代碼塊不會(huì)鎖住整個(gè)對(duì)象(當(dāng)然你也可以讓它鎖住整個(gè)對(duì)象)乘寒。同步方法會(huì)鎖住整個(gè)對(duì)象望众,哪怕這個(gè)類中有多個(gè)不相關(guān)聯(lián)的同步塊,這通常會(huì)導(dǎo)致他們停止執(zhí)行并需要等待獲得這個(gè)對(duì)象上的鎖伞辛。Java的synchronized的同步代碼塊和同步方法的區(qū)別
1烂翰、wait(),notify(),notifyAll()方法只能在synchronized中調(diào)用蚤氏。
2甘耿、synchronized中調(diào)用wait()方法時(shí)立即阻塞讓出CPU執(zhí)行權(quán),代碼塊中剩下的代碼不會(huì)繼續(xù)執(zhí)行瞧捌,只能等待喚醒繼續(xù)執(zhí)行棵里。
3、synchronized中調(diào)用notify()或者notifyAll()后姐呐,synchronized中的剩余代碼會(huì)繼續(xù)執(zhí)行殿怜。并且只有等待該同步方法或者同步代碼塊中剩余代碼執(zhí)行完成后,其他正在等待獲取該對(duì)象鎖的同步方法或者同步代碼塊才會(huì)繼續(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("開(kāi)始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()+"釋放了鎖");
}
}
}
控制臺(tái)輸出結(jié)果如下:
1 開(kāi)始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(線程本地存儲(chǔ))
3.線程池
對(duì)比new Thread()和線程池的優(yōu)劣
new Thread()弊端:
1际度、new Thread()每次創(chuàng)建新的對(duì)象,性能差涵妥;
2乖菱、缺乏統(tǒng)一管理,資源開(kāi)銷大蓬网。無(wú)限制的創(chuàng)建可能會(huì)因?yàn)橘Y源占用過(guò)高導(dǎo)致OOM或者死機(jī)窒所;
3、功能單一帆锋,沒(méi)有辦法定時(shí)吵取,定期執(zhí)行任務(wù)等;
相比于new Thread()
1锯厢、重用線程皮官,減少對(duì)象的創(chuàng)建脯倒,回收空閑的線程,提升性能捺氢;
2盔憨、可以有效控制最大并發(fā)數(shù),提高系統(tǒng)資源利用率讯沈,同時(shí)避免過(guò)多的資源競(jìng)爭(zhēng)郁岩,避免堵塞;
3缺狠、提供可定時(shí)问慎,定期,單線程挤茄,并發(fā)控制等功能
4.volatile關(guān)鍵字
** volatile特性**
當(dāng)一個(gè)共享變量被volatile修飾的時(shí)候如叼,它能保證修改的值能夠立即被更新到主存。
內(nèi)存可見(jiàn)性:線程A對(duì)一個(gè)volatile修飾的共享變量的修改穷劈,對(duì)于其他線程來(lái)說(shuō)是可見(jiàn)的笼恰,即線程每次獲取volatile變量的值都是最新的。
volatile的使用條件
volatile相當(dāng)于一個(gè)輕量級(jí)的synchronize歇终。但是volatile必須滿足兩個(gè)條件:
1社证、對(duì)變量的寫(xiě)操作不能依賴于本身(即不能依賴于當(dāng)前值)。如多線程下執(zhí)行a++评凝,這樣是無(wú)法通過(guò)volatile保證結(jié)果的準(zhǔn)確性的追葡。
2、該變量沒(méi)有包含在具有其他變量的不變式中
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在某一時(shí)刻同時(shí)執(zhí)行了setLower(8)和setUpper(5),且都通過(guò)了不變式的檢查翎碑,設(shè)置了一個(gè)無(wú)效范圍(8, 5)谬返,所以在這種場(chǎng)景下,需要通過(guò)sychronize保證方法setLower和setUpper在每一時(shí)刻只有一個(gè)線程能夠執(zhí)行日杈。
volatile的使用場(chǎng)景
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;
}
}
Java代理模式
組合與繼承
泛型
Java反射
Java內(nèi)存相關(guān)
注意點(diǎn)
java異常
所有的異常都繼承自一個(gè)共同的父類Throwable翰蠢,而Throwable有兩個(gè)重要的子類:Exception(異常)和Error(錯(cuò)誤)
** Error**(錯(cuò)誤)
是程序無(wú)法處理的錯(cuò)誤项乒,表示運(yùn)行應(yīng)用程序中較嚴(yán)重問(wèn)題啰劲。大多數(shù)錯(cuò)誤與代碼編寫(xiě)者執(zhí)行的操作無(wú)關(guān),而表示代碼運(yùn)行時(shí) JVM(Java 虛擬機(jī))出現(xiàn)的問(wèn)題檀何。例如蝇裤,Java虛擬機(jī)運(yùn)行錯(cuò)誤(Virtual MachineError)廷支,當(dāng) JVM 不再有繼續(xù)執(zhí)行操作所需的內(nèi)存資源時(shí),將出現(xiàn) OutOfMemoryError栓辜。這些異常發(fā)生時(shí)恋拍,Java虛擬機(jī)(JVM)一般會(huì)選擇線程終止。
這些錯(cuò)誤表示故障發(fā)生于虛擬機(jī)自身藕甩、或者發(fā)生在虛擬機(jī)試圖執(zhí)行應(yīng)用時(shí)施敢,如Java虛擬機(jī)運(yùn)行錯(cuò)誤(Virtual MachineError)、類定義錯(cuò)誤(NoClassDefFoundError)等狭莱。這些錯(cuò)誤是不可查的僵娃,因?yàn)樗鼈冊(cè)趹?yīng)用程序的控制和處理能力之 外,而且絕大多數(shù)是程序運(yùn)行時(shí)不允許出現(xiàn)的狀況腋妙。對(duì)于設(shè)計(jì)合理的應(yīng)用程序來(lái)說(shuō)默怨,即使確實(shí)發(fā)生了錯(cuò)誤,本質(zhì)上也不應(yīng)該試圖去處理它所引起的異常狀況骤素。在 Java中匙睹,錯(cuò)誤通過(guò)Error的子類描述。** Exception**(異常)
是程序本身可以處理的異常济竹。主要包含RuntimeException等運(yùn)行時(shí)異常和IOException痕檬,SQLException等非運(yùn)行時(shí)異常。
** 運(yùn)行時(shí)異常包括:**都是RuntimeException類及其子類異常送浊,如NullPointerException(空指針異常)谆棺、IndexOutOfBoundsException(下標(biāo)越界異常)等,這些異常是不檢查異常罕袋,程序中可以選擇捕獲處理改淑,也可以不處理。這些異常一般是由程序邏輯錯(cuò)誤引起的浴讯,程序應(yīng)該從邏輯角度盡可能避免這類異常的發(fā)生朵夏。
運(yùn)行時(shí)異常的特點(diǎn)是Java編譯器不會(huì)檢查它,也就是說(shuō)榆纽,當(dāng)程序中可能出現(xiàn)這類異常仰猖,即使沒(méi)有用try-catch語(yǔ)句捕獲它,也沒(méi)有用throws子句聲明拋出它奈籽,也會(huì)編譯通過(guò)饥侵。
** 非運(yùn)行時(shí)異常**(編譯異常)包括:RuntimeException以外的異常,類型上都屬于Exception類及其子類衣屏。從程序語(yǔ)法角度講是必須進(jìn)行處理的異常躏升,如果不處理,程序就不能編譯通過(guò)狼忱。如IOException膨疏、SQLException等以及用戶自定義的Exception異常一睁,一般情況下不自定義檢查異常。
從** 編譯器是否要求強(qiáng)制處理**的角度分類佃却,異常類別又可分為:
** 可查異常**
正確的程序在運(yùn)行中者吁,很容易出現(xiàn)的、情理可容的異常狀況饲帅「吹剩可查異常雖然是異常狀況,但在一定程度上它的發(fā)生是可以預(yù)計(jì)的灶泵,而且一旦發(fā)生這種異常狀況染坯,就必須采取某種方式進(jìn)行處理。
除了RuntimeException及其子類以外丘逸,其他的Exception類及其子類都屬于可查異常单鹿。這種異常的特點(diǎn)是Java編譯器會(huì)檢查它,也就是說(shuō)深纲,當(dāng)程序中可能出現(xiàn)這類異常仲锄,要么用try-catch語(yǔ)句捕獲它,要么用throws子句聲明拋出它湃鹊,否則編譯不會(huì)通過(guò)儒喊。
** 不可查異常**
包括運(yùn)行時(shí)異常(RuntimeException與其子類)和錯(cuò)誤(Error)。
面試常見(jiàn)問(wèn)題
1.描述Java 7 ARM(Automatic Resource Management币呵,自動(dòng)資源管理)特征和多個(gè)catch塊的使用如果一個(gè)try塊中有多個(gè)異常要被捕獲怀愧,catch塊中的代碼會(huì)變丑陋的同時(shí)還要用多余的代碼來(lái)記錄異常。有鑒于此余赢,Java 7的一個(gè)新特征是:一個(gè)catch子句中可以捕獲多個(gè)異常芯义。示例代碼如下:
catch(IOException | SQLException | Exception ex){
logger.error(ex);
throw new MyException(ex.getMessage());
}
大多數(shù)情況下,當(dāng)忘記關(guān)閉資源或因資源耗盡出現(xiàn)運(yùn)行時(shí)異常時(shí)妻柒,我們只是用finally子句來(lái)關(guān)閉資源扛拨。這些異常很難調(diào)試,我們需要深入到資源使用的每一步來(lái)確定是否已關(guān)閉举塔。因此绑警,Java 7用try-with-resources進(jìn)行了改進(jìn):在try子句中能創(chuàng)建一個(gè)資源對(duì)象,當(dāng)程序的執(zhí)行完try-catch之后央渣,運(yùn)行環(huán)境自動(dòng)關(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í)行并移交異常對(duì)象到運(yùn)行時(shí)進(jìn)行處理北启。
3.** 被檢查的異常和 不受檢查的異常**有什么區(qū)別?
被檢查的異常應(yīng)該用try-catch塊代碼處理,或者在main方法中用throws關(guān)鍵字讓JRE了解程序可能拋出哪些異常暖庄。不受檢查的異常在程序中不要求被處理或用throws語(yǔ)句告知。
Exception是所有被檢查異常的基類楼肪,然而培廓,RuntimeException是所有不受檢查異常的基類。
被檢查的異常適用于那些不是因程序引起的錯(cuò)誤情況春叫,比如:讀取文件時(shí)文件不存在引發(fā)的FileNotFoundException肩钠。然而,不被檢查的異常通常都是由于糟糕的編程引起的暂殖,比如:在對(duì)象引用時(shí)沒(méi)有確保對(duì)象非空而引起的NullPointerException价匠。
4.Java中** final, finally, finalize**的區(qū)別?
** final和 finally在Java中是關(guān)鍵字呛每,而 finalize則是一個(gè)方法踩窖。
** final關(guān)鍵字使得類變量不可變,避免類被其它類繼承或方法被重寫(xiě)晨横。
** finally跟 try-catch塊一起使用洋腮,即使是出現(xiàn)了異常笼痹,其子句總會(huì)被執(zhí)行兆衅,通常耻姥, finally子句用來(lái)關(guān)閉相關(guān)資源肴沫。
** finalize方法中的對(duì)象被銷毀之前會(huì)被垃圾回收泌绣。
異常相關(guān)的博客
JVM模型
Java常用算法
Android基本架構(gòu)
android基本架構(gòu)
Android基本框架結(jié)構(gòu)
Mac OS 下Android環(huán)境搭建
四大組件之 Activity
1. Activity生命周期
正常情況下Activity會(huì)經(jīng)歷如下生命周期:
onCreate()司忱,表示Activity的創(chuàng)建氓奈。在這個(gè)方法中我們可以做一些初始化的工作痹籍。比如調(diào)用setContentView去加載layout布局資源瞬欧,初始化Activity所需的數(shù)據(jù)等贷屎。
onRestart(),重新啟動(dòng)Activity艘虎。一般情況下豫尽,當(dāng)當(dāng)前的Activity從不可見(jiàn)變?yōu)榭梢?jiàn)的時(shí)候,該方法會(huì)被調(diào)用顷帖。這中情況一般是用戶按Home健回到桌面或者啟動(dòng)了一個(gè)新的Activity美旧,這個(gè)時(shí)候onPause和onStop方法就會(huì)被調(diào)用。上面的操作完成后用戶如果點(diǎn)擊桌面應(yīng)用的圖標(biāo)或者按back鍵回到之前的Activity贬墩,這時(shí)候onRestart就會(huì)被調(diào)用榴嗅。
onStart(),表示開(kāi)始啟動(dòng)Activity。這個(gè)時(shí)候Activity就變?yōu)榭梢?jiàn)了陶舞,但是前臺(tái)還是看不到的嗽测,還沒(méi)有辦法和用戶進(jìn)行交互。只有當(dāng)onResume方法被調(diào)用的時(shí)候,才會(huì)真正的出現(xiàn)在前臺(tái)唠粥。
onResume(),表示Activity已經(jīng)變得可見(jiàn)疏魏,可以和用戶進(jìn)行交互了。需要注意的是晤愧,雖然onStart和onResume雖然都表示Activity變得可見(jiàn)大莫。但是onStart的時(shí)候,Activity還在后臺(tái)官份,onResume的時(shí)候才真正的出現(xiàn)在前臺(tái)只厘。
onPause(),此時(shí)的Activity正準(zhǔn)備停止,正常情況下舅巷,接著就會(huì)調(diào)用onStop方法羔味。特殊情況下,如果這個(gè)時(shí)候用戶又回到這個(gè)Activity钠右,那么onResume就會(huì)被調(diào)用赋元。這是一種非常極端的情況,很難重現(xiàn)飒房。onPause中不要做太多耗時(shí)的操作们陆,因?yàn)闀?huì)影響新的Activity的展現(xiàn),onPause必須先執(zhí)行完成情屹,新的Activity的onResume方法才會(huì)被執(zhí)行坪仇。
onStop(),表示Activity即將停止±悖可以做一些稍微重量級(jí)的回收工作椅文。同樣不能做太多耗時(shí)的操作。
onDestroy(),這是Activity生命周期中最后一個(gè)回調(diào)惜颇,表示Activity即將被銷毀皆刺。可以做一些回收工作凌摄,釋放資源等等羡蛾。
Activity啟動(dòng)模式
Android中Activity的啟動(dòng)模式有四種,分別是standard锨亏、singleTop痴怨、singleTask和singleInstance。
1器予、standard:標(biāo)準(zhǔn)模式浪藻。這也是Activity默認(rèn)的啟動(dòng)模式。每次啟動(dòng)一個(gè)Activity都會(huì)重新創(chuàng)建新的Activity實(shí)例乾翔。不管這個(gè)Activity實(shí)例在任務(wù)棧中是否已經(jīng)存在爱葵。被創(chuàng)建的Activity實(shí)例的生命周期符合典型情況下的Activity生命周期。
事例說(shuō)明:此時(shí)有兩個(gè)Activity,SecondActivity和ThirdActivity萌丈。當(dāng)我們當(dāng)我們從SecondActivity跳轉(zhuǎn)到ThirdActivity再?gòu)腡hirdActivity跳轉(zhuǎn)到SecondActivity赞哗。我們可以發(fā)現(xiàn)SecondActivity被創(chuàng)建了兩次。這就說(shuō)明標(biāo)準(zhǔn)模式下的Activity不會(huì)復(fù)用辆雾,每次啟動(dòng)都會(huì)創(chuàng)建新的Activity實(shí)例肪笋。
2、singleTop:棧頂復(fù)用模式乾颁。這種模式下涂乌,如果要被啟動(dòng)的Activity實(shí)例已經(jīng)位于任務(wù)棧的棧頂艺栈,則不再創(chuàng)建新的Activity實(shí)例英岭。同時(shí)它的onNewIntent方法會(huì)被調(diào)用。通過(guò)這個(gè)方法我們可以取出當(dāng)前請(qǐng)求的信息湿右。需要注意的是诅妹,這個(gè)Activity的onCreate。onStart不會(huì)被調(diào)用毅人。因?yàn)樗](méi)有發(fā)生改變吭狡。如果新的Activity不是位于棧頂,那么新的Activity任然會(huì)被創(chuàng)建丈莺。例如現(xiàn)在任務(wù)棧中有ABCD划煮,ABCD啟動(dòng)模式均為singleTop模式,A位于棧底缔俄,D位于棧頂弛秋,如果此時(shí)要啟動(dòng)C,那么任務(wù)棧的情況就會(huì)變成ABCDC俐载。
3蟹略、singleTask:這是站內(nèi)復(fù)用模式。這種模式下遏佣,只要Activity實(shí)例在任務(wù)棧中存在挖炬,啟動(dòng)的時(shí)候就不會(huì)去再次創(chuàng)建新的Activity實(shí)例,而是沿用已經(jīng)存在的Activity實(shí)例状婶。不論啟動(dòng)多少次意敛,任務(wù)棧都只會(huì)存在一個(gè)Activity實(shí)例。這就是站內(nèi)復(fù)用模式膛虫。和singleTop一樣空闲,系統(tǒng)啟動(dòng)Activity的時(shí)候,如果任務(wù)棧中存在該Activity實(shí)例走敌,就不會(huì)調(diào)用onCreate和onStart方法碴倾,而是會(huì)調(diào)用onNewIntent方法。
singleTask站內(nèi)復(fù)用模式的幾種情況:
1)如果目前任務(wù)棧T1中存在ABC三個(gè)activity實(shí)例,這個(gè)時(shí)候Activity D以singleTask模式請(qǐng)求啟動(dòng)跌榔,其所需要的任務(wù)棧未T2.由于T2和D的實(shí)例都不存在异雁,所以系統(tǒng)會(huì)首先創(chuàng)建T2,然后將D壓入到T2中僧须。
2)第二種情況纲刀,如果目前任務(wù)棧T1中存在ABCD三個(gè)activity實(shí)例,這個(gè)時(shí)候Activity D以singleTask模式請(qǐng)求啟動(dòng)担平,由于D已經(jīng)存在T1的任務(wù)棧示绊,所以D就不會(huì)被再次創(chuàng)建,而是直接沿用已經(jīng)存在的D的實(shí)例暂论。
3)第三種情況面褐,如果目前任務(wù)棧T1中存在ADBC三個(gè)activity實(shí)例,這個(gè)時(shí)候Activity D以singleTask模式請(qǐng)求啟動(dòng)取胎,由于D已經(jīng)在T1中已經(jīng)存在展哭,同樣不會(huì)再次創(chuàng)建,也是沿用已經(jīng)存在的D的實(shí)例闻蛀。同時(shí)會(huì)將位于D上面的其他Activity實(shí)例移出棧匪傍,使自己位于棧頂位置。
4觉痛、singleInstance:?jiǎn)螌?shí)例模式役衡。這種模式可以看成是singleTask的加強(qiáng)模式,它除了具有singleTask的模式所具有的一些列特性之外薪棒,還增加了一點(diǎn)特殊的特性手蝎。具有這種模式的activity只能單獨(dú)的運(yùn)行在獨(dú)立的任務(wù)棧中。什么叫運(yùn)行在獨(dú)立的任務(wù)棧盗尸,其實(shí)就是如果Activity A是singleInstance模式柑船,當(dāng)A啟動(dòng)后,系統(tǒng)會(huì)為A創(chuàng)建一個(gè)新的任務(wù)棧使其獨(dú)立運(yùn)行在其中泼各,這個(gè)棧中只有A自己不存在其他Activity實(shí)例鞍时。由于它也具有singleTask的復(fù)用的特性,在后續(xù)啟動(dòng)的時(shí)候扣蜻,也會(huì)復(fù)用這個(gè)Activity逆巍。
Fragment
四大組件之 Service
四大組件之 Broadcast Receiver
- BroadcastReceiver(含安全性問(wèn)題)
- Android開(kāi)發(fā)之Intent和BroadcastReceiver
- BroadcastReceiver應(yīng)用詳解
- Android應(yīng)用界面開(kāi)發(fā)——BroadcastReceiver(實(shí)現(xiàn)基于Service的音樂(lè)播放器)
四大組件之 ContentProvider
Notification
SharedPreferences
SharedPreferences類是一個(gè)接口類,真正的實(shí)現(xiàn)類是SharedPreferencesImpl莽使。修改SharedPreferences需要獲取它的Editor锐极,在對(duì)Editor進(jìn)行put操作后,最后通過(guò)commit或者apply提交修改到內(nèi)存和文件芳肌。當(dāng)然有了兩種都可以提交的方法灵再,肯定要區(qū)別一下的肋层。從實(shí)現(xiàn)類SharedPreferencesImpl的源碼上看也很容易看出兩者的區(qū)別:
commit這種方式很常用,在比較早的SDK版本中就有了翎迁,這種提交修改的方式是同步的栋猖,會(huì)阻塞調(diào)用它的線程,并且這個(gè)方法會(huì)返回boolean值告知保存是否成功(如果不成功汪榔,可以做一些補(bǔ)救措施)蒲拉。
而apply是異步的提交方式,目前Android Studio也會(huì)提示大家使用這種方式痴腌。
還有一點(diǎn)用得比較少的雌团,就是SharedPreferences還提供一個(gè)監(jiān)聽(tīng)接口可以監(jiān)聽(tīng)SharedPreferences的鍵值變化,需要監(jiān)控鍵值變化的可以用registerOnSharedPreferenceChangeListener添加監(jiān)聽(tīng)器士聪。
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的問(wèn)題
在SDK 3.0及以上版本锦援,可以通過(guò)Context.MODE_MULTI_PROCESS屬性來(lái)實(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ù)不會(huì)出問(wèn)題戚嗅,真正使用中發(fā)現(xiàn)有會(huì)有一定概率出現(xiàn)這個(gè)取值出錯(cuò)(變?yōu)槌跏贾担﹩?wèn)題雨涛。
Google也在SDK 6.0的版本將這個(gè)MODE_MULTI_PROCESS標(biāo)識(shí)為deprecated(不贊成使用)枢舶。目前來(lái)說(shuō)懦胞,越來(lái)越多的項(xiàng)目在不斷的膨脹,為了降低單個(gè)進(jìn)程的內(nèi)存占用率凉泄,使用"android:process"配置一些組件在單獨(dú)的進(jìn)程中運(yùn)行已經(jīng)是司空見(jiàn)慣了躏尉,所以大家在遇到自己的項(xiàng)目有多進(jìn)程時(shí),要注意一下SharedPreferences的問(wèn)題后众。
注意: 在一個(gè)進(jìn)程中胀糜,SharedPreference往往建單個(gè)實(shí)例就可以了,一般不會(huì)出現(xiàn)并發(fā)沖突蒂誉,如果對(duì)提交的結(jié)果不關(guān)心的話教藻,建議使用apply,當(dāng)然需要確保提交成功且有后續(xù)操作的話右锨,還是需要用commit的括堤。關(guān)于SharedPreferences多進(jìn)程數(shù)據(jù)共享問(wèn)題,可以借鑒開(kāi)源?替代方案如Github上的tray绍移。
Android消息處理機(jī)制(Looper悄窃、Handler、MessageQueue蹂窖、Message)
** 概述**:Android應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的轧抗,Android某種意義上也可以說(shuō)成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng),UI瞬测、事件横媚、生命周期都和消息處理機(jī)制息息相關(guān)纠炮,并且消息處理機(jī)制在整個(gè)Android知識(shí)體系中也是尤其重要。
Handler的運(yùn)行機(jī)制描述:談到Handler的運(yùn)行機(jī)制一般情況下灯蝴,都會(huì)涉及到幾個(gè)比較重要的類抗碰,Looper,MessageQueue绽乔,Message等等弧蝇。Handler的實(shí)例必須在Looper線程中創(chuàng)建,否則就會(huì)拋出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)椋话闱闆r下我們使用handler的目的是為了執(zhí)行完后臺(tái)任務(wù)后睦授,和UI線程進(jìn)行交互两芳。由于UI線程在創(chuàng)建之初,就被設(shè)置成了Looper線程(這個(gè)可以在ActivityThread源碼中看到去枷,里面有一個(gè)main方法怖辆,在實(shí)例化ActivityThread的之前,調(diào)用了Looper.prepareMainLooper()
),所以我們?cè)趯?shí)例化Handler的時(shí)候不需要手動(dòng)再次調(diào)用Looper.prepare()
方法删顶。Looper線程中會(huì)維護(hù)一個(gè)MessageQueue消息隊(duì)列竖螃。handler通過(guò)sendMessage()方法向Looper中的消息隊(duì)列插入一條Message消息。MessageQueue通過(guò)enqueueMessage方法將handler發(fā)送來(lái)的message消息放到消息隊(duì)列中逗余。由于Looper線程是一個(gè)循環(huán)進(jìn)程特咆,里面有一個(gè)阻塞方法loop()
。在該方法中l(wèi)ooper會(huì)調(diào)用MessageQueue的next()
不停的循環(huán)遍歷MessageQueue中的消息(可以在Looper源碼的loop()方法中看到)录粱。當(dāng)Looper發(fā)現(xiàn)有新的message來(lái)的時(shí)候腻格,就會(huì)回調(diào)給Handler中handMessage方法進(jìn)行處理。
備注:handler在主線程中實(shí)例化后就會(huì)拿到主線程的MessageQueue的引用(Looper中維護(hù)的MessageQueue其實(shí)就是主線程中MessageQueue)啥繁。handler在sendMessage的時(shí)候菜职,發(fā)送的Message里面持有Handler的引用。這樣在整個(gè)消息流程中就就把Looper旗闽,MessageQueue酬核,Message,Handler串聯(lián)連起來(lái)了宪睹,不會(huì)出現(xiàn)錯(cuò)亂愁茁。
消息處理機(jī)制的本質(zhì):** 一個(gè)線程開(kāi)啟循環(huán)模式持續(xù)監(jiān)聽(tīng)并依次處理各個(gè)線程發(fā)來(lái)的消息**
簡(jiǎn)單的說(shuō):一個(gè)線程開(kāi)啟一個(gè)無(wú)限循環(huán)模式,不斷遍歷自己的消息列表亭病,如果有消息就挨個(gè)拿出來(lái)做處理鹅很,如果列表沒(méi)消息,自己就堵塞(相當(dāng)于wait罪帖,讓出cpu資源給其他線程)促煮,其他線程如果想讓該線程做什么事邮屁,就往該線程的消息隊(duì)列插入消息,該線程會(huì)不斷從隊(duì)列里拿出消息做處理菠齿。
** 消息處理機(jī)制的描述:**
線程其實(shí)就是一段可執(zhí)行代碼佑吝,當(dāng)這段代碼執(zhí)行完成后,該線程的生命周期就結(jié)束了绳匀,線程就會(huì)退出芋忿。既然如此,UI線程在執(zhí)行完成后為什么沒(méi)有退出呢疾棵?因?yàn)閁I線程在創(chuàng)建的時(shí)候就被變成了Looper線程(這個(gè)可以在ActivityThread的main方法中看到戈钢,在ActivityThread創(chuàng)建之前調(diào)用了Looper.prepareMainLooper();
ActivityThread創(chuàng)建完成后接著就調(diào)用了Looper.loop();
),Looper里面有一個(gè)loop()
方法是尔,這個(gè)方法里面有一段死循環(huán)的代碼殉了。
主線程會(huì)一直處于這個(gè)死循環(huán)中,由于Looper里面維護(hù)一個(gè)MessageQueue消息隊(duì)列拟枚,這個(gè)消息隊(duì)列就用來(lái)維護(hù)其他線程發(fā)送來(lái)的Message消息(例如薪铜,Activity的啟動(dòng),生命周期恩溅,UI的更新隔箍,控件的事件等等),UI線程會(huì)根據(jù)消息隊(duì)列中的消息(例如暴匠,Activity的啟動(dòng)鞍恢,生命周期傻粘,UI的更新每窖,控件的事件等等),依次做出處理弦悉。
那么其他線程是如何發(fā)送Message到UI線程的MessageQueue消息隊(duì)列中的窒典?首先肯定是要拿到MessageQueue的實(shí)例,Google為了統(tǒng)一添加消息和消息的回調(diào)處理稽莉,又專門(mén)構(gòu)建了Handler類瀑志,你只要在主線程構(gòu)建Handler類,那么這個(gè)Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.getQueue();)
污秆,Handler 在sendMessage的時(shí)候就通過(guò)這個(gè)引用往消息隊(duì)列里插入新消息劈猪。Handler 的另外一個(gè)作用,就是能統(tǒng)一處理消息的回調(diào)良拼。這樣一個(gè)Handler發(fā)出消息又確保消息處理也是自己來(lái)做
** Looper战得、Handler、MessageQueue庸推、Message作用和存在的意義常侦?**
** Looper** 循環(huán)線程浇冰,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會(huì)變當(dāng)前線程變成Looper線程(可以先簡(jiǎn)單理解:無(wú)限循環(huán)不退出的線程)
** Handler**簡(jiǎn)單說(shuō)Handler用于同一個(gè)進(jìn)程的線程間通信。Google 為了統(tǒng)一添加消息和消息的回調(diào)處理聋亡,又專門(mén)構(gòu)建了Handler類肘习,你只要在主線程構(gòu)建Handler類,那么這個(gè)Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;)坡倔,Handler 在sendMessage的時(shí)候就通過(guò)這個(gè)引用往消息隊(duì)列里插入新消息漂佩。Handler 的另外一個(gè)作用,就是能統(tǒng)一處理消息的回調(diào)罪塔。這樣一個(gè)Handler發(fā)出消息又確保消息處理也是自己來(lái)做仅仆,這樣的設(shè)計(jì)非常的贊。具體做法就是在隊(duì)列里面的Message持有Handler的引用(哪個(gè)handler 把它放到隊(duì)列里垢袱,message就持有了這個(gè)handler的引用)墓拜,然后等到主線程輪詢到這個(gè)message的時(shí)候,就來(lái)回調(diào)我們經(jīng)常重寫(xiě)的Handler的handleMessage(Message msg)方法请契。
- ** MessageQueue**MessageQueue 存在的原因很簡(jiǎn)單咳榜,就是同一線程在同一時(shí)間只能處理一個(gè)消息,同一線程代碼執(zhí)行是不具有并發(fā)性爽锥,所以需要隊(duì)列來(lái)保存消息和安排每個(gè)消息的處理順序涌韩。多個(gè)其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個(gè)列表(它同一時(shí)間不能處理那么多任務(wù)),然后挨個(gè)拿出來(lái)處理氯夷,這種設(shè)計(jì)很簡(jiǎn)單臣樱,我們平時(shí)寫(xiě)代碼其實(shí)也經(jīng)常這么做。每一個(gè)Looper線程都會(huì)維護(hù)這樣一個(gè)隊(duì)列腮考,而且僅此一個(gè)雇毫,這個(gè)隊(duì)列的消息只能由該線程處理。
- ** Message** UI線程和子線程通信的載體踩蔚,主要存儲(chǔ)和傳遞一些子線程想讓UI線程處理的內(nèi)容
** 關(guān)于Looper中loop()
方法的疑問(wèn)**
1.UI線程一直在這個(gè)循環(huán)里跳不出來(lái)棚放,主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死嗎,那還怎么執(zhí)行其他的操作呢馅闽?
在looper啟動(dòng)后飘蚯,主線程上執(zhí)行的任何代碼都是被looper從消息隊(duì)列里取出來(lái)執(zhí)行的。也就是說(shuō)主線程之后都是通過(guò)其他線程給它發(fā)消息來(lái)實(shí)現(xiàn)執(zhí)行其他操作的福也。生命周期的回調(diào)也是如此的局骤,系統(tǒng)服務(wù)ActivityManagerService通過(guò)Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后暴凑,通過(guò)App進(jìn)程的Binder線程給主線程的消息隊(duì)列插入一條消息來(lái)實(shí)現(xiàn)的峦甩。
2.主線程是UI線程和用戶交互的線程,優(yōu)先級(jí)應(yīng)該很高搬设,主線程的死循環(huán)一直運(yùn)行是不是會(huì)特別消耗CPU資源嗎穴店?App進(jìn)程的其他線程怎么辦撕捍?
這基本是一個(gè)類似生產(chǎn)者消費(fèi)者的模型,簡(jiǎn)單說(shuō)如果在主線程的MessageQueue沒(méi)有消息時(shí)泣洞,就會(huì)阻塞在loop的queue.next()方法里忧风,這時(shí)候主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個(gè)消息進(jìn)來(lái)時(shí)候就會(huì)喚醒主線程球凰,在2.2 版本以前狮腿,這套機(jī)制是用我們熟悉的線程的wait和notify 來(lái)實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制呕诉,通過(guò)往pipe管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線程工作缘厢。原理類似于I/O,讀寫(xiě)是堵塞的,不占用CPU資源甩挫。
** 關(guān)于Handler實(shí)例的創(chuàng)建**
- Handler實(shí)例的創(chuàng)建贴硫,是需要在創(chuàng)建之前調(diào)用Looper.prepare()將當(dāng)前線程變成Looper線程的,同時(shí)調(diào)用Looper.loop()伊者。那為什么我們平時(shí)在UI線程中創(chuàng)建Handler實(shí)例的時(shí)候?yàn)槭裁礇](méi)有調(diào)用Looper.prepare()和Looper.loop()英遭,那時(shí)因?yàn)閁I線程在創(chuàng)建的時(shí)候就變成了Looper線程,所以不再需要調(diào)用Looper.prepare()和Looper.loop()亦渗。如果再調(diào)用這兩個(gè)方法的話就會(huì)拋出
Only one Looper may be created per thread
異常挖诸。就是告訴你Looper實(shí)例已經(jīng)存在了,并且只能有一個(gè)法精。
我們?cè)贏ctivity中創(chuàng)建的Handler實(shí)例IDE常常提示出一些警告多律,這些警告一半都是提示這種定義方式可能出現(xiàn)內(nèi)存泄露。那么如何正確創(chuàng)建Handler實(shí)例搂蜓??可以采用static方式狼荞,定義一個(gè)內(nèi)部類繼承Handler,利用軟引用WeakReference洛勉,接收外部類的引用粘秆。具體可以參照下面這兩篇文章
** Android消息處理相關(guān)機(jī)制相關(guān)的博文**
- Android的Handler消息機(jī)制
- Android 消息機(jī)制分析
- Android 消息處理機(jī)制(Looper、Handler收毫、MessageQueue,Message)
- Android Handler原理分析
- Handler 的使用與原理
AsyncTask
1.AsyncTask機(jī)制
AnsycTask執(zhí)行任務(wù)時(shí),內(nèi)部會(huì)創(chuàng)建一個(gè)進(jìn)程作用域的線程池來(lái)管理要運(yùn)行的任務(wù)殷勘,也就就是說(shuō)當(dāng)你調(diào)用了AsyncTask.execute()后此再,AsyncTask會(huì)把任務(wù)交給線程池,由線程池來(lái)管理創(chuàng)建Thread和運(yùn)行Therad玲销。最后和UI打交道就交給Handler去處理了输拇。
這是從API 23的AsyncTask源碼中截取的一部分源碼:
從上面的代碼中我們可以看出,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ù)目带族,即使線程池沒(méi)有任務(wù),核心線程也不會(huì)終止(除非設(shè)置了allowCoreThreadTimeOut參數(shù))可以理解為“常駐線程”
- maximumPoolSize: 線程池中允許的最大線程數(shù)目蟀给;一般來(lái)說(shuō)蝙砌,線程越多,線程調(diào)度開(kāi)銷越大跋理;因此一般都有這個(gè)限制择克。
- keepAliveTime: 當(dāng)線程池中的線程數(shù)目比核心線程多的時(shí)候,如果超過(guò)這個(gè)keepAliveTime的時(shí)間前普,多余的線程會(huì)被回收肚邢;這些與核心線程相對(duì)的線程通常被稱為緩存線程
- unit: keepAliveTime的時(shí)間單位
- workQueue: 任務(wù)執(zhí)行前保存任務(wù)的隊(duì)列;這個(gè)隊(duì)列僅保存由execute提交的Runnable任務(wù)
- threadFactory: 用來(lái)構(gòu)造線程池的工廠拭卿;一般都是使用默認(rèn)的道偷;
- handler: 當(dāng)線程池由于線程數(shù)目和隊(duì)列限制而導(dǎo)致后續(xù)任務(wù)阻塞的時(shí)候,線程池的處理方式记劈。
2.多個(gè)AsyncTask任務(wù)是串行還是并行勺鸦?
從Android 1.6到2.3(Gingerbread) AsyncTask是并行的,即上面我們提到的有5個(gè)核心線程的線程池(ThreadPoolExecutor)負(fù)責(zé)調(diào)度任務(wù)目木。從Android 3.0開(kāi)始换途,Android團(tuán)隊(duì)又把AsyncTask改成了串行,默認(rèn)的Executor被指定為SERIAL_EXECUTOR刽射。
3.AsyncTask容易引發(fā)的Activity內(nèi)存泄露
如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類军拟,那么AsyncTask會(huì)保留一個(gè)對(duì)創(chuàng)建了AsyncTask的Activity的引用。如果Activity已經(jīng)被銷毀誓禁,AsyncTask的后臺(tái)線程還在執(zhí)行懈息,它將繼續(xù)在內(nèi)存里保留這個(gè)引用,導(dǎo)致Activity無(wú)法被回收摹恰,引起內(nèi)存泄露辫继。
4.博文推薦
總結(jié)
AsyncTask的運(yùn)行機(jī)制
AsyncTask分為兩個(gè)部分俗慈,一部分和主線程交互姑宽,一部分負(fù)責(zé)線程調(diào)度。雖然可能會(huì)存在多個(gè)AsyncTask的子類實(shí)例闺阱,但是其內(nèi)部的Handler和ThreadPoolExecutor都是靜態(tài)的炮车,是進(jìn)程范圍內(nèi)共享的。所以AsyncTask控制著進(jìn)程范圍內(nèi)所有其子實(shí)例。
與主線程交互 AsyncTask和主線程交互是通過(guò)Handler來(lái)完成的瘦穆;
?線程調(diào)度 關(guān)于AsyncTask內(nèi)部的線程調(diào)度纪隙,其內(nèi)部會(huì)創(chuàng)建一個(gè)進(jìn)程作用域內(nèi)的線程池;也就是說(shuō)當(dāng)你調(diào)用了AsyncTask的execute(...)方法后扛或,AsyncTask就會(huì)把任務(wù)交給線程池绵咱,由線程池來(lái)管理創(chuàng)建和運(yùn)行Thread。對(duì)于內(nèi)部線程池告喊,不同版本的Android內(nèi)部實(shí)現(xiàn)方式是不一樣的麸拄。
在Android2.3(API 10)
之前,線程池限制數(shù)為5個(gè)黔姜,因?yàn)锳ndroid2.3之前的AsyncTask是并行的拢切,所以同時(shí)只能有5個(gè)線程在運(yùn)行,超過(guò)的只能等待秆吵,等待前面5個(gè)完成才能繼續(xù)執(zhí)行淮椰。
這種情況在Android3.0(API 11)
之后的版本得到了改善,Google工程師把AsyncTask的execute(...)方法由之前的并行方式纳寂,改成了串行方式主穗,按先后順序每次只允許一個(gè)線程執(zhí)行。除此之外毙芜,還添加了一個(gè)executeOnExecutor(...)方法忽媒,這個(gè)方法是并行執(zhí)行方法,同時(shí)也允許開(kāi)發(fā)者提供自定義的線程池來(lái)運(yùn)行和調(diào)度Thread腋粥,如果你想讓所有的任務(wù)都能并發(fā)同時(shí)運(yùn)行晦雨,那就創(chuàng)建一個(gè)沒(méi)有限制的線程池(Executors.newCachedThreadPool()),并提供給AsyncTask隘冲。這樣這個(gè)AsyncTask實(shí)例就有了自己的線程池而不必使用AsyncTask默認(rèn)的闹瞧。當(dāng)然AsyncTask中還有兩個(gè)預(yù)設(shè)的線程池SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR。SERIAL_EXECUTOR表示串行執(zhí)行;THREAD_POOL_EXECUTOR表示并行執(zhí)行展辞。SERIAL_EXECUTOR作用是保證任務(wù)執(zhí)行的順序奥邮,也就是它可以保證提交的任務(wù)確實(shí)是按照先后順序執(zhí)行的。它的內(nèi)部有一個(gè)隊(duì)列用來(lái)保存所提交的任務(wù)罗珍,保證當(dāng)前只運(yùn)行一個(gè)洽腺,這樣就可以保證任務(wù)是完全按照順序執(zhí)行的,默認(rèn)的execute()使用的就是這個(gè)靡砌,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是一樣的已脓。?對(duì)于THREAD_POOL_EXECUTOR,可以通過(guò)調(diào)用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)通殃,這樣起碼不用等到前面的都結(jié)束了再執(zhí)行。
AsyncTask帶來(lái)的問(wèn)題
1.生命周期,AsyncTask不會(huì)?隨著Activity的?銷毀而銷毀画舌。正常情況下Activity銷毀了堕担,此時(shí)如果有AsyncTask在執(zhí)行,AsyncTask并不會(huì)隨著Activity的?銷毀而銷毀曲聂。而是會(huì)繼續(xù)執(zhí)行霹购,如果我們?cè)?code>onPostExecute()方法中做了更新UI的操作,就有可能出現(xiàn)crash現(xiàn)象朋腋。解決方法就是在Activity銷毀的時(shí)候齐疙,檢測(cè)當(dāng)前是否存在正在運(yùn)行的AsyncTask(通過(guò)isCancelled()
),如果有的話旭咽,就調(diào)用AsyncTask的cancel(true)
方法贞奋,取消任務(wù),注意不要再AsyncTask的doBackground()
方法中執(zhí)行不可中斷的操作(如BitmapFactory.decodeStream())穷绵,否則無(wú)法立即cancel掉轿塔。除此,還可以在onPostExecute()
方法中仲墨,在執(zhí)行前判斷當(dāng)前Activity是否已存活勾缭,如果是活著的,就繼續(xù)執(zhí)行目养,反之則return取消掉俩由。
2.內(nèi)存泄露,如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類癌蚁,那么AsyncTask會(huì)保留一個(gè)對(duì)創(chuàng)建了AsyncTask的Activity的引用幻梯。如果Activity已經(jīng)被銷毀,AsyncTask的后臺(tái)線程還在執(zhí)行匈勋,它將繼續(xù)在內(nèi)存里保留這個(gè)引用礼旅,導(dǎo)致Activity無(wú)法被回收,引起內(nèi)存泄露洽洁。解決辦法痘系,就是在Activity銷毀的時(shí)候,檢查是否有還在運(yùn)行的AsyncTask饿自,如果有就cancel掉汰翠。
3.結(jié)果丟失,屏幕旋轉(zhuǎn)或Activity在后臺(tái)被系統(tǒng)殺掉等情況會(huì)導(dǎo)致Activity的重新創(chuàng)建昭雌,之前運(yùn)行的AsyncTask會(huì)持有一個(gè)之前Activity的引用复唤,這個(gè)引用已經(jīng)無(wú)效,這時(shí)調(diào)用onPostExecute()再去更新界面將不再生效烛卧。解決方法和上面一樣佛纫,也是在銷毀之前cancel掉