Android内存泄漏accountmanager依赖
Posted
技术标签:
【中文标题】Android内存泄漏accountmanager依赖【英文标题】:Android memory leak accountmanager dependency 【发布时间】:2018-02-22 12:25:44 【问题描述】:对于我的项目,我使用 Retrofit 进行 API 调用。 对于身份验证,我使用的是 JWT 令牌。 对于该令牌的存储和刷新流程,我使用Google's AccountManager。
ApiService 类是一个单例,我构建如下:
private ApiService(AccountManager accountManager)
mAccountManager = accountManager;
build();
public static synchronized ApiService getInstance(AccountManager accountManager)
if (mInstance == null)
mInstance = new ApiService(accountManager);
return mInstance;
在这个课程中,我正在构建 OkHttp3 客户端和 Retrofit 服务。
当服务器响应挑战时,Okhttp3 有一个很好的方法,所以我这样设置:
OkHttpClient client = new OkHttpClient.Builder()
.authenticator(new ApiAuthenticator(mAccountManager))
如您所见,我正在设置 Okhttp3 Authenticator 的自定义实现,如下所示:
/**
* 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 依赖引起的内存泄漏,我再也看不到解决方案了...
【问题讨论】:
【参考方案1】:希望它能帮助您解决问题:-
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;
欲了解更多信息,请访问以下链接:-
https://github.com/square/leakcanary/issues/97
Binder preventing garbage collection
【讨论】:
以上是关于Android内存泄漏accountmanager依赖的主要内容,如果未能解决你的问题,请参考以下文章