compile 'com.anthonycr.grant:permissions:1.1.2'
版本更新后,作者為了避免內存泄漏,在源碼中添加了一個弱引用來存儲一個抽象類。
但在用戶有多個權限需要選擇的時候,抽象類有時候會被回收。當用戶確認權限之后,沒有調用到回調函數(shù)。
android內存空間分配
首先, 讓我們快速看下Android啟動流程. 與眾多基于Linux內核的系統(tǒng)類似, 啟動系統(tǒng)時, bootloader啟動內核和init進程. init進程分裂出更多名為"daemons(守護進程)"的底層的Linux進程, 諸如android debug deamon, USB deamon等. 這些守護進程處理底層硬件相關的接口.
隨后, init進程會啟動一個非常有意思的進程---"Zygote". 顧名思義, 這是一個Android平臺的非?;A的進程. 這個進程初始化了第一個VM, 并且預加載了framework和眾多App所需要的通用資源. 然后它開啟一個Socket接口來監(jiān)聽請求, 根據(jù)請求孵化出新的VM來管理新的App進程. 一旦收到新的請求, Zygote會基于自身預先加載的VM來孵化出一個新的VM創(chuàng)建一個新的進程.
啟動Zygote之后, init進程會啟動runtime進程. Zygote會孵化出一個超級管理進程---System Server. SystemServer會啟動所有系統(tǒng)核心服務, 例如Activity Manager Service, 硬件相關的Service等. 到此, 系統(tǒng)準備好啟動它的第一個App進程---Home進程了.

當啟動一個Android程序時,會啟動一個Dalvik VM進程,系統(tǒng)會給它分配固定的內存空間(16M,32M不定),這塊內存空間會映射到RAM上某個區(qū)域。然后這個Android程序就會運行在這塊空間上。Java里會將這塊空間分成Stack棧內存和Heap堆內存。stack里存放對象的引用,heap里存放實際對象數(shù)據(jù)。
在程序運行中會創(chuàng)建對象,如果未合理管理內存,比如不及時回收無效空間就會造成內存泄露,嚴重的話可能導致使用內存超過系統(tǒng)分配內存,即內存溢出OOM,導致程序卡頓甚至直接退出。

也就是帶有回調函數(shù)的對象會放到內存堆中。當然,一般處理內存泄漏都是處理內存堆,這里只是提一下。
弱引用
在Java里, 當一個對象o被創(chuàng)建時, 它被放在Heap里. 當GC運行的時候, 如果發(fā)現(xiàn)沒有任何引用指向o, o就會被回收以騰出內存空間. 或者換句話說, 一個對象被回收, 必須滿足兩個條件: 1)沒有任何引用指向它 2)GC被運行
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í)行到這兒的時候,action有時候變成了null 。
在addPendingAction操作中有PermissionsResultAction(強引用)引用指向,但到notifyPermissionsChange()的時候PermissionsResultAction依然被系統(tǒng)回收了,回調函數(shù)不被執(zhí)行。
這是因為編譯器在發(fā)現(xiàn)進入while循環(huán)之后, PermissionsResultAction已經(jīng)沒有被使用, 所以進行了優(yōu)化(將其置空).
寫了一段測試代碼,對象最后的確被回收了。
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的一個特點是它何時被回收是不可確定的, 因為這是由GC運行的不確定性所確定的. 所以, 一般用weak reference引用的對象是有價值被cache, 而且很容易被重新被構建, 且很消耗內存的對象.
雖然弱引用能讓app避免了內存溢出的問題,但也帶來了不確定性。
弱引用可以用于Handler,一般的Handler寫法可能會導致內存泄漏。因為非靜態(tài)的內部類持有外部類的對象,而handler又會由于msg的處理而可能常駐在進程中,在activity或者service destroy后,不能及時被系統(tǒng)回收,導致內存泄漏。
建議寫法:
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...
}
}
}