OneDrive / onedrive-sdk-android

OneDrive SDK for Android!
https://dev.onedrive.com
Other
148 stars 52 forks source link

Memory leak in MSAAuthentication. #118

Closed isabsent closed 1 month ago

isabsent commented 7 years ago

It seems your MSAAuthenticator caches the activity in which authenticator was created and does not release it after authentication process is finalized. It leads to memory leak (thanks to Leak Canary): onedrive_leak Leak Canary log:

D/LeakCanary: * com.example.activity.OneDriveActivity has leaked:
D/LeakCanary: * GC ROOT static com.example.cloud.Clouds.ONE_DRIVE
D/LeakCanary: * references com.example.cloud.Clouds.cloud
D/LeakCanary: * references com.example.cloud.OneDriveCloud.service
D/LeakCanary: * references com.onedrive.sdk.extensions.OneDriveClient.mAuthenticator
D/LeakCanary: * references com.example.activity.OneDriveActivity$1.this$0 (anonymous subclass of com.onedrive.sdk.authentication.MSAAuthenticator)
D/LeakCanary: * leaks com.example.activity.OneDriveActivity instance

Activity used to authenticate a user is:

public class OneDriveActivity extends CloudActivity{
    public static String
            ACCESS_KEY_NAME = "ACCESS_KEY_ONEDRIVE",
            ACCESS_SECRET_NAME = "ACCESS_SECRET_ONEDRIVE";
    private static String
            CLIENT_ID = "****************",
            OAUTH_2 = "oauth2:";
    private static String[] SCOPES_ARRAY = new String[] {"onedrive.readwrite", "onedrive.appfolder", "wl.offline_access"};

    private final AtomicReference<IOneDriveClient> mClient = new AtomicReference<>();
    private Button mSubmit;

    private IClientConfig createConfig() {
        final MSAAuthenticator msaAuthenticator = new MSAAuthenticator() {

            @Override
            public String getClientId() {
                return CLIENT_ID;
            }

            @Override
            public String[] getScopes() {
                return SCOPES_ARRAY;
            }
        };
        return DefaultClientConfig.createWithAuthenticator(msaAuthenticator);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_onedrive);

        mSubmit = (Button) findViewById(R.id.auth_button);
        mSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubmit.setEnabled(false);
                final ICallback<Void> serviceCreatedCallback = new DefaultCallback<Void>(OneDriveActivity.this) {
                    @Override
                    public void success(final Void result) {
                        mSubmit.setEnabled(true);

                        storeAuth(mClient.get().getAuthenticator().getAccountInfo().getAccessToken(), OneDriveActivity.this);
                        ONE_DRIVE.getOneDrive().setService(mClient.get());
                        setResult(RESULT_OK);
                        finish();
                    }
                };
                try {
                    IOneDriveClient service = getOneDriveClient();
                    mSubmit.setEnabled(true);

                    storeAuth(service.getAuthenticator().getAccountInfo().getAccessToken(), OneDriveActivity.this);
                    ONE_DRIVE.getOneDrive().setService(service);
                    setResult(RESULT_OK);
                    finish();
                } catch (UnsupportedOperationException ignored) {
                    createOneDriveClient(OneDriveActivity.this, serviceCreatedCallback);
                }
            }
        });
    }

    /**
     * Get an instance of the service
     *
     * @return The Service
     */
    private synchronized IOneDriveClient getOneDriveClient() {
        if (mClient.get() == null) {
            throw new UnsupportedOperationException("Unable to generate a new service object");
        }
        return mClient.get();
    }

    /**
     * Used to setup the Services
     * @param activity the current activity
     * @param serviceCreated the callback
     */
    private synchronized void createOneDriveClient(final Activity activity, final ICallback<Void> serviceCreated) {
        final DefaultCallback<IOneDriveClient> callback = new DefaultCallback<IOneDriveClient>(activity) {
            @Override
            public void success(final IOneDriveClient service) {
                mClient.set(service);
                serviceCreated.success(null);

                storeAuth(service.getAuthenticator().getAccountInfo().getAccessToken(), OneDriveActivity.this);
                ONE_DRIVE.getOneDrive().setService(service);
                setResult(RESULT_OK);
                finish();
            }

            @Override
            public void failure(final ClientException error) {
                serviceCreated.failure(error);
            }
        };
        new OneDriveClient
                .Builder()
                .fromConfig(createConfig())
                .loginAndBuildClient(activity, callback);
    }

    private static void storeAuth(String accessToken, Context context) {// Store the OAuth 2 access token, if there is one.
        if (accessToken != null) {
            SharedPreferences prefs = FileManagerApplication.getAccountPreferences(context);
            SharedPreferences.Editor edit = prefs.edit();
            edit.putString(ACCESS_KEY_NAME, OAUTH_2);
            edit.putString(ACCESS_SECRET_NAME, accessToken);
            edit.commit();
        }
    }
}
baywet commented 1 month ago

Thank you for reaching out and for your patience. This SDK is being officially deprecated. See #172 for more information