понимание утечки в андроид MVP - PullRequest
0 голосов
/ 10 ноября 2018

Я читал некоторые подобные вопросы здесь, но из-за отсутствия представленного кода я не уверен, что мой вопрос описывает те же сценарии.

Я надеюсь, что следующие фрагменты и вопросы помогут другим прояснить, что и когда что-то просочилось в этой реализации MVP: https://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a

BaseActivity.java:

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupActivityComponent();
    }

    protected abstract void setupActivityComponent();
}

SplashActivityModule.java:

@Module
public class SplashActivityModule {
    private SplashActivity splashActivity;

    public SplashActivityModule(SplashActivity splashActivity) {
        this.splashActivity = splashActivity;
    }

    @Provides
    @ActivityScope
    SplashActivity provideSplashActivity() {
        return splashActivity;
    }

    @Provides
    @ActivityScope
    SplashActivityPresenter
    provideSplashActivityPresenter(Validator validator, UserManager 
    userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
        return new SplashActivityPresenter(splashActivity, validator, 
                                           userManager, heavyLibraryWrapper);
    }
}

SplashActivityPresenter внедряется внутри SplashActivity.java:

public class SplashActivity extends BaseActivity {
    ...

    @Inject
    SplashActivityPresenter presenter;

    @Override
    protected void setupActivityComponent() {
        GithubClientApplication.get(this)
                .getAppComponent()
                .plus(new SplashActivityModule(this))
                .inject(this);
    }

SplashActivityPresenter.java:

public class SplashActivityPresenter {
    public String username;

    private SplashActivity splashActivity;
    private Validator validator;
    private UserManager userManager;
    private HeavyLibraryWrapper heavyLibraryWrapper;

    public SplashActivityPresenter(SplashActivity splashActivity, 
        Validator validator, UserManager userManager, 
        HeavyLibraryWrapper heavyLibraryWrapper) {
        this.splashActivity = splashActivity;
        this.validator = validator;
        this.userManager = userManager;
            this.heavyLibraryWrapper = heavyLibraryWrapper;

        //This calls should be delivered to ExternalLibrary right after it will be initialized
        this.heavyLibraryWrapper.callMethod();
        this.heavyLibraryWrapper.callMethod();
        this.heavyLibraryWrapper.callMethod();
        this.heavyLibraryWrapper.callMethod();
    }

    public void onShowRepositoriesClick() {
        if (validator.validUsername(username)) {
            splashActivity.showLoading(true);
            userManager.getUser(username).subscribe(new 
SimpleObserver<User>() {
                @Override
                public void onNext(User user) {
                    splashActivity.showLoading(false);
                    splashActivity.showRepositoriesListForUser(user);
                }

                @Override
                public void onError(Throwable e) {
                    splashActivity.showLoading(false);
                    splashActivity.showValidationError();
                }
            });
        } else {
            splashActivity.showValidationError();
        }
    }
}
  1. Если пользовательповорачивает экран во время извлечения имени пользователя, и мы просочились в экземпляр активности, на который ссылаются в обратных вызовах наблюдателя, поскольку действие уничтожено.
  2. Если пользователь поворачивает экран без выполняемой выборки, экземпляр действияне утечка.
  3. Чтобы исправить эту утечку (1), нам нужно сохранить подписку и отписаться от нее в Presenter.onDestroy () (вызывается из SplashActivity onDestroy ()).
  4. Кто-тосказал мне, что делать (3) недостаточно, и что внутри onDestroy() мы также должны установить экземпляр активности на null.Я не согласен, потому что отмена подписки отменяет запрос, предотвращая обратные вызовы (например, onNext(User)), которые ссылаются на активность, от вызова.
  5. Он также сказал мне, что в то время как (3) и (4) предотвращают утечку активности, PRESENTER просочился во время ротации ТАКЖЕ, так как активность ссылается на него.Я не согласен, потому что новый презентатор создается для каждого поворота и инициализируется для внедренного презентатора, когда BaseActivity onCreate вызывает setupActivityComponent.Старый докладчик автоматически собирает мусор как участник SplashActivity.

Может ли кто-нибудь ответить на вопросы, изложенные выше, чтобы я мог подтвердить свое понимание или узнать, где я могу ошибаться?Спасибо

1 Ответ

0 голосов
/ 11 ноября 2018

Я сделаю все возможное, чтобы ответить на них как можно точнее (добро пожаловать редактировать, если неточно), но этот ответ хорошо читать: https://stackoverflow.com/a/10968689/4252352

  1. Если пользователь поворачивает экран во время извлечения имени пользователя, мы пропускаем экземпляр действия, на который ссылаются внутри обратные вызовы наблюдателя, поскольку активность уничтожена.

A: Это правильно, однако будет кратковременная утечка памяти, пока ресурсы не будут очищены после утилизации. Однако на это не следует полагаться, если у вас есть Flowable / Observable, он никогда не сможет распоряжаться и очищать ресурсы. Важно отметить, что все лямбды в цепочке Rx (обычно такие операторы, как map, filter и т. Д.), Которые не ссылаются (захватывают) на окружающий класс, не имеют утечек.

  1. Если пользователь поворачивает экран без выполнения выборки, экземпляр действия не пропускается.

A. Правильно, у вас никогда нет активной подписки.

  1. Чтобы устранить эту утечку (1), нам нужно сохранить подписку и отписаться от нее в Presenter.onDestroy () (вызывается из SplashActivity. OnDestroy ())

A Это должно остановить проблему. Однако лучше подходить, и в MVP View должен быть абстракцией / интерфейсом, а ваш Presenter должен иметь точки входа и выхода для представления, а не для конструктора, т.е. bind(view : View) и unbind() <- очистка здесь докладчик не должен знать о конкретных обратных вызовах хуков Android. Это имеет огромные преимущества не только с точки зрения ООП (программы для интерфейсов, а не реализаций), но и с точки зрения тестирования. </p>

  1. Кто-то сказал мне, что делать (3) недостаточно, и что внутри onDestroy () мы также должны установить экземпляр действия на null. я не согласен потому что отмена подписки отменяет запрос, предотвращая обратные вызовы (например, onNext (пользователь)), которые ссылаются на активность из вызывается.

A. Сначала я бы попросил уточнить их аргументацию. Поскольку ваша Presenter относится к Activity (они оба имеют одинаковый жизненный цикл), отписки должно быть достаточно. Однако, если ваш Presenter имеет более длинный жизненный цикл, чем Activity, необходимо удалить ссылку (это могло быть обоснованием для человека, с которым вы говорили).

  1. Он также сказал мне, что, хотя (3) и (4) предотвращают утечку ДЕЯТЕЛЬНОСТИ, ПРЕЗЕНТЕР протекает во время вращения ТАКЖЕ так как активность ссылается на это. Я не согласен, потому что новый докладчик создан за вращение и инициализируется введенному презентеру, когда BaseActivity onCreate вызывает setupActivityComponent. Старый ведущий автоматически собирает мусор в качестве члена SplashActivity.

A. Утечка Presenter, если утечка Activity (вместе со всеми ссылками на действия!)

...