代码之家  ›  专栏  ›  技术社区  ›  Daan Geurts

Android内存泄漏accountmanager依赖项

  •  0
  • Daan Geurts  · 技术社区  · 7 年前

    对于我的项目,我正在对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客户端和改装服务。

    当服务器响应质询时,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依赖性导致内存泄漏,我再也看不到解决方案了。。。

    1 回复  |  直到 7 年前
        1
  •  0
  •   duggu Neeraj Nama    7 年前

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

    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