Предотвращение утечки памяти при вызове дооснащения 2 - PullRequest
0 голосов
/ 30 августа 2018

Следуя этой статье , я обнаружил, что вызов метода Retrofit enqueue () для метода onCreate () может вызвать утечку памяти.

Вот что говорится в статье, делая это:

Вызов Retrofit в основном потоке

public class MoviesActivity extends Activity {

    private TextView mNoOfMoviesThisWeek;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_movies_activity);
        mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);

        MoviesRepository repository = ((MoviesApp) getApplication()).getRepository();
        repository.getMoviesThisWeek()
                .enqueue(new Callback<List<Movie>>() {

                    @Override
                    public void onResponse(Call<List<Movie>> call,
                                           Response<List<Movie>> response) {
                        int numberOfMovies = response.body().size();
                        mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));
                    }

                    @Override
                    public void onFailure(Call<List<Movie>> call, Throwable t) {
                        // Oops.
                    }
                });
    }
}

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

Я пытался сделать то же самое в своем приложении. Я вызвал большой контент (240 объектов) usign enqueue () в методе onCreate (). Затем, во время загрузки контента, я несколько раз поворачивал устройство, и LeakCanary показывал мне утечку памяти в Activity, как сказано в статье.

Затем я попробовал два подхода, чтобы избежать утечки памяти:

Первый вариант

Вызов модифицированного метода execute () в фоновом потоке с использованием статического внутреннего класса.

Вызов Retrofit в фоновом потоке

private static class RetrofitCall extends AsyncTask<Void, Void, List<Show>> {
        private WeakReference<TextView> numberOfShows;

        public RetrofitCall(TextView numberOfShows) {
            this.numberOfShows = new WeakReference<>(numberOfShows);
        }

        @Override
        protected List<Show> doInBackground(Void... voids) {
            List<Show> showList = new ArrayList<>();

            if (!isCancelled()) {
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(TvMazeService.BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();

                TvMazeService service = retrofit.create(TvMazeService.class);
                try {
                    Response<List<Show>> response = service.getShows().execute();
                    if (response.isSuccessful()) {
                        showList = response.body();
                    }
                    return showList;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return null;
        }

        @Override
        protected void onPostExecute(List<Show> shows) {
            super.onPostExecute(shows);
            TextView textView = numberOfShows.get();
            if (textView != null) {
                String number = String.valueOf(shows.size());
                textView.setText(number);
            }
        }
    }

Затем я попытался снова получить утечку памяти, используя LeakCanary, и случилось, что утечка памяти исчезла.

Второй вариант

Использование ViewModel .

Как вы можете видеть из документации, при использовании ViewModel я вызывал асинхронную модификацию в классе ViewModel, и когда экран поворачивается (активность разрушается), ему не нужно загружать данные снова, поскольку они сохраняются.

Этот подход также не дал утечки памяти и был лучшим при разговоре о памяти.

Вопросы

1) Тогда использование ViewModel для вызова Retrofit - лучший вариант, и это действительно позволяет избежать утечки памяти?

2) Есть ли проблемы с вызовом модификации с помощью enqueue () в onCreate (), как это делает MoviesActivity?

3) При таком подходе, какой из них является лучшим, чтобы позвонить для аутентификации пользователя?

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Причиной утечки памяти при вызове enqueue() в onCreate() является то, что вызовы в очереди будут содержать ссылку на экземпляр вашей активности, поскольку переданный ему экземпляр обратного вызова (анонимный класс) содержит ссылку на экземпляр окружающего класса. Пока вы отмените его до onDestroy(), проблем не будет.

0 голосов
/ 14 сентября 2018

1) Правильное использование ViewModel не вызывает утечек памяти и является хорошим вариантом. Вы можете посмотреть видео объяснение Google , а также эту лекцию о разнице между MVP и MVVM . Эта вторая лекция дает действительно хорошее объяснение по теме.

2) Вызов функции enqueue () в методе onCreate () является проблемой и вызывает утечку памяти. Проблема заключается в том, что при первом запуске вашей деятельности она вызывает модификацию, а затем при повороте устройства все действия уничтожаются и воссоздаются заново. Если вы поворачиваете устройство до завершения загрузки данных, при повторном вызове onCreate () вызывается дооснащение во второй раз, и если вы продолжаете делать это 10 раз, то дооснащение будет вызываться 10 раз, а затем вы перестанете вращать устройство. Результат от вызовов начнет поступать, bzzz :( результат будет отображаться 10 раз, потому что вы вызывали его 10 раз. Это означает огромную утечку памяти. Если вы реализуете этот подход и используете LeakCanary , вы увидит утечку.

3) Каков наилучший подход?

  • Использование метода enqueue () в onCreate () определенно не годится.
  • Статические внутренние классы (с использованием AsyncTask) хороши, но они не сохраняются до изменений конфигурации, потому что вам нужно отменить их в onDestroy (). Вот почему это не приводит к утечке памяти, поскольку задача отменяется в onDestroy ().
  • MVP - это действительно хороший подход для осуществления модернизированных вызовов. Вы можете узнать больше в этой средней статье , а исходный код здесь .
  • Узнайте о различиях между MVP и MVVM, как в этой статье .
  • Наконец, Google рекомендует разработчикам использовать ViewModel в этих сценариях.

Вы можете следить за моим обсуждением в другом вопросе . Где мы говорим о той же теме, но во время входа пользователя на сервер.

...