compile 'com.anthonycr.grant:permissions:1.1.2'
版本更新后,作者為了避免內(nèi)存泄漏,在源碼中添加了一個(gè)弱引用來(lái)存儲(chǔ)一個(gè)抽象類。
但在用戶有多個(gè)權(quán)限需要選擇的時(shí)候,抽象類有時(shí)候會(huì)被回收。當(dāng)用戶確認(rèn)權(quán)限之后,沒(méi)有調(diào)用到回調(diào)函數(shù)。
android內(nèi)存空間分配
首先, 讓我們快速看下Android啟動(dòng)流程. 與眾多基于Linux內(nèi)核的系統(tǒng)類似, 啟動(dòng)系統(tǒng)時(shí), bootloader啟動(dòng)內(nèi)核和init進(jìn)程. init進(jìn)程分裂出更多名為"daemons(守護(hù)進(jìn)程)"的底層的Linux進(jìn)程, 諸如android debug deamon, USB deamon等. 這些守護(hù)進(jìn)程處理底層硬件相關(guān)的接口.
隨后, init進(jìn)程會(huì)啟動(dòng)一個(gè)非常有意思的進(jìn)程---"Zygote". 顧名思義, 這是一個(gè)Android平臺(tái)的非常基礎(chǔ)的進(jìn)程. 這個(gè)進(jìn)程初始化了第一個(gè)VM, 并且預(yù)加載了framework和眾多App所需要的通用資源. 然后它開(kāi)啟一個(gè)Socket接口來(lái)監(jiān)聽(tīng)請(qǐng)求, 根據(jù)請(qǐng)求孵化出新的VM來(lái)管理新的App進(jìn)程. 一旦收到新的請(qǐng)求, Zygote會(huì)基于自身預(yù)先加載的VM來(lái)孵化出一個(gè)新的VM創(chuàng)建一個(gè)新的進(jìn)程.
啟動(dòng)Zygote之后, init進(jìn)程會(huì)啟動(dòng)runtime進(jìn)程. Zygote會(huì)孵化出一個(gè)超級(jí)管理進(jìn)程---System Server. SystemServer會(huì)啟動(dòng)所有系統(tǒng)核心服務(wù), 例如Activity Manager Service, 硬件相關(guān)的Service等. 到此, 系統(tǒng)準(zhǔn)備好啟動(dòng)它的第一個(gè)App進(jìn)程---Home進(jìn)程了.
當(dāng)啟動(dòng)一個(gè)Android程序時(shí),會(huì)啟動(dòng)一個(gè)Dalvik VM進(jìn)程,系統(tǒng)會(huì)給它分配固定的內(nèi)存空間(16M,32M不定),這塊內(nèi)存空間會(huì)映射到RAM上某個(gè)區(qū)域。然后這個(gè)Android程序就會(huì)運(yùn)行在這塊空間上。Java里會(huì)將這塊空間分成Stack棧內(nèi)存和Heap堆內(nèi)存。stack里存放對(duì)象的引用,heap里存放實(shí)際對(duì)象數(shù)據(jù)。
在程序運(yùn)行中會(huì)創(chuàng)建對(duì)象,如果未合理管理內(nèi)存,比如不及時(shí)回收無(wú)效空間就會(huì)造成內(nèi)存泄露,嚴(yán)重的話可能導(dǎo)致使用內(nèi)存超過(guò)系統(tǒng)分配內(nèi)存,即內(nèi)存溢出OOM,導(dǎo)致程序卡頓甚至直接退出。
也就是帶有回調(diào)函數(shù)的對(duì)象會(huì)放到內(nèi)存堆中。當(dāng)然,一般處理內(nèi)存泄漏都是處理內(nèi)存堆,這里只是提一下。
弱引用
在Java里, 當(dāng)一個(gè)對(duì)象o被創(chuàng)建時(shí), 它被放在Heap里. 當(dāng)GC運(yùn)行的時(shí)候, 如果發(fā)現(xiàn)沒(méi)有任何引用指向o, o就會(huì)被回收以騰出內(nèi)存空間. 或者換句話說(shuō), 一個(gè)對(duì)象被回收, 必須滿足兩個(gè)條件: 1)沒(méi)有任何引用指向它 2)GC被運(yùn)行
private synchronized void addPendingAction(@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
if (action == null) {
return;
}
action.registerPermissions(permissions);
mPendingActions.add(new WeakReference<>(action));
}
public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
int size = permissions.length;
if (results.length < size) {
size = results.length;
}
Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
while (iterator.hasNext()) {
PermissionsResultAction action = iterator.next().get();
for (int n = 0; n < size; n++) {
if (action == null || action.onResult(permissions[n], results[n])) {
iterator.remove();
break;
}
}
}
for (int n = 0; n < size; n++) {
mPendingRequests.remove(permissions[n]);
}
}
在源碼中執(zhí)行到這兒的時(shí)候,action有時(shí)候變成了null 。
在addPendingAction操作中有PermissionsResultAction(強(qiáng)引用)引用指向,但到notifyPermissionsChange()的時(shí)候PermissionsResultAction依然被系統(tǒng)回收了,回調(diào)函數(shù)不被執(zhí)行。
這是因?yàn)榫幾g器在發(fā)現(xiàn)進(jìn)入while循環(huán)之后, PermissionsResultAction已經(jīng)沒(méi)有被使用, 所以進(jìn)行了優(yōu)化(將其置空).
寫了一段測(cè)試代碼,對(duì)象最后的確被回收了。
public static void main(String[] args) {
List<WeakReference<PermissionAction>> mPendingActions = new ArrayList<>(1);
mPendingActions.add(new WeakReference<>(new PermissionAction()));
int i = 0;
WeakReference<PermissionAction> actionPermission = null;
Iterator<WeakReference<PermissionAction>> iterator = mPendingActions.iterator();
if(iterator.hasNext()){
actionPermission = iterator.next();
}
while (true) {
PermissionAction action = actionPermission.get();
if (action != null) {
i++;
System.out.println("Object is alive for " + i + " loops - " + action);
} else {
System.out.println("Object has been collected.");
break;
}
}
}
- WeakReference的一個(gè)特點(diǎn)是它何時(shí)被回收是不可確定的, 因?yàn)檫@是由GC運(yùn)行的不確定性所確定的. 所以, 一般用weak reference引用的對(duì)象是有價(jià)值被cache, 而且很容易被重新被構(gòu)建, 且很消耗內(nèi)存的對(duì)象.
雖然弱引用能讓app避免了內(nèi)存溢出的問(wèn)題,但也帶來(lái)了不確定性。
弱引用可以用于Handler,一般的Handler寫法可能會(huì)導(dǎo)致內(nèi)存泄漏。因?yàn)榉庆o態(tài)的內(nèi)部類持有外部類的對(duì)象,而handler又會(huì)由于msg的處理而可能常駐在進(jìn)程中,在activity或者service destroy后,不能及時(shí)被系統(tǒng)回收,導(dǎo)致內(nèi)存泄漏。
建議寫法:
private static class OuterHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public OuterHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
if (activity != null) {
// do something...
}
}
}