Android 内存泄漏 accountmanager 依赖

Android memory leak accountmanager dependency

对于我的项目,我使用 Retrofit 进行 API 调用。 对于身份验证,我使用的是 JWT 令牌。 对于该令牌的存储和刷新流程,我使用 Google's AccountManager

ApiService class 是我按如下方式构建的单例:

 private ApiService(AccountManager accountManager) {
        mAccountManager = accountManager;
        build();
    }

    public static synchronized ApiService getInstance(AccountManager accountManager) {
        if (mInstance == null) {
            mInstance = new ApiService(accountManager);
        }
        return mInstance;
    }

在此 class 中,我正在构建 OkHttp3 客户端和 Retrofit 服务。

当服务器响应挑战时,Okhttp3 有一个很好的方法,所以我这样设置:

 OkHttpClient client = new OkHttpClient.Builder()
    .authenticator(new ApiAuthenticator(mAccountManager))

如您所见,我正在设置 Okhttp3 身份验证器的自定义实现,如下所示:

    /**
     * 2017 App-vise.
     * Created by daangeurts on 31/07/2017.
     */
    public class ApiAuthenticator implements Authenticator {

        private static final String TAG = "ApiAuthenticator";
        private AccountManager accountManager;


    ApiAuthenticator(AccountManager accountManager) {
        this.accountManager = accountManager;
    }

    private static int responseCount(Response response) {
        int result = 1;
        while ((response = response.priorResponse()) != null) {
            result++;
        }
        return result;
    }

    /**
     * Returns a request that includes a credential to satisfy an authentication challenge in {@code
     * response}. Returns null if the challenge cannot be satisfied.
     *
     * @param route
     * @param response
     */
    @Nullable
    @Override
    public Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException {
        if (response.request().url().encodedPath().startsWith("login_check") || response.request().url().encodedPath().startsWith("token"))
            return null;

        if (responseCount(response) >= 2) {
            // If both the original call and the call with refreshed token failed,
            // it will probably keep failing, so don't try again.
            return null;
        }

        for (Challenge challenge : response.challenges()) {
            if (challenge.scheme().equals("Bearer")) {
                Account[] accounts = accountManager.getAccountsByType(AuthConstants.ACCOUNT_TYPE);
                if (accounts.length != 0) {
                    String oldToken = accountManager.peekAuthToken(accounts[0], AuthConstants.AUTHTOKEN_TYPE_FULL_ACCESS);

                    if (oldToken != null) {
                        accountManager.invalidateAuthToken(AuthConstants.ACCOUNT_TYPE, oldToken);
                    }
                    try {
                        String token = accountManager.blockingGetAuthToken(accounts[0], AuthConstants.AUTHTOKEN_TYPE_FULL_ACCESS, false);

                        if (token == null) {
                            accountManager.removeAccount(accounts[0], null, null);
                        }

                        if (token != null) {
                            Request.Builder builder = response.request().newBuilder();
                            return builder.header("Authorization", "Bearer " + token).build();
                        }
                    } catch (OperationCanceledException | AuthenticatorException e) {
                        e.printStackTrace();
                        Log.d(TAG, e.getLocalizedMessage());
                    }
                }
            }
        }
        return null;
    }
}

我的某些函数的存储库如下所示:

public class ApiCheckinRepository implements CheckinRepository {

    private AccountManager accountManager;

    public ApiCheckinRepository(AccountManager accountManager) {
        this.accountManager = accountManager;
    }

    @Override
    public Observable<Response<Message>> checkin(QrCode qrCode) {
       return ApiService.getInstance(accountManager).checkin(qrCode);
    }
}

但现在我遇到了由 accountManager 依赖项引起的内存泄漏问题,我再也看不到解决方案了...

希望它能帮助您解决问题:-

public class AccountLeakHandler {

    public static AccountManagerFuture<Bundle> safeGetAuthToken(AccountManager accountManager,
                                                                Account account,
                                                                String authTokenType,
                                                                Bundle options,
                                                                Activity activity,
                                                                AccountManagerCallback<Bundle> callback,
                                                                Handler handler) {
        return accountManager.getAuthToken(
                account,
                authTokenType,
                options,
                null,
                new ResolveLeakAccountManagerCallback(callback, activity),
                handler);
    }


    public static class ResolveLeakAccountManagerCallback implements AccountManagerCallback<Bundle> {

        private AccountManagerCallback<Bundle> originalCallback = null;
        private Activity originalActivity = null;

        public ResolveLeakAccountManagerCallback(AccountManagerCallback<Bundle> callback, Activity activity) {
            originalCallback = callback;
            originalActivity = activity;
        }

        @Override
        public void run(AccountManagerFuture<Bundle> future) {
            Bundle bundle = null;
            try {
                bundle = future.getResult();
            } catch (Exception e) {
                // error happen
            }

            if (bundle == null) {
                callAndClear(future);
            } else {
                Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
                if (intent != null) {
                    if (originalActivity != null) {
                        try {
                            //make FutureTask callback again
                            new RInstance(FutureTask.class, future).setValue("state", 0);
                        } catch (Exception e) {}
                        originalActivity.startActivity(intent);
                    } else {
                        callAndClear(future);
                    }
                } else {
                    callAndClear(future);
                }
            }
        }

        private void callAndClear(AccountManagerFuture<Bundle> future) {
            if (originalCallback != null) {
                originalCallback.run(future);
                originalCallback = null;
            }
            originalActivity = null;
        }
    }
}

有关更多信息,请访问下面 link:-

https://github.com/square/leakcanary/issues/97

Binder preventing garbage collection