Альтернатива прямому вызову onResume () - PullRequest
0 голосов
/ 01 июня 2018

Я переписываю свое приложение Android, чтобы исключить прямые вызовы на onResume().

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

@Override
public void onResume() {
    super.onResume();
    // get current date and time,
    //  and determine if daylight savings time is in effect.
    //...600 lines of code
    // output both the chart and report
    //
    image.setImageBitmap(heightChart);
    report.setImageBitmap(reportBitmap);
}

Следующим шагом является сбор пользовательского ввода, который сообщает мне, какие изменения в отчете желает пользователь.(Это может быть новое местоположение, новая дата или новый стиль отображения и т. Д.).Это делается следующим образом:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    final int STATION_REQUEST = 1;
    int test = 1;
    switch (item.getItemId()) {
        case R.id.refresh: {
            userDateSet = false;
            onResume();
            return true;
        } // come to the present.

        //...200 lines of code
        default:
            return super.onOptionsItemSelected(item);
    }
}

Как показано в примере, выход восстанавливается путем вызова onResume() после определения новой пользовательской команды.ЭТО ПЛОХАЯ ПРАКТИКА, Я УЖЕ ЗНАЮ !!Тем не менее, насколько я определил, это работает хорошо, я, честно говоря, не понимаю проблемы с ним.

Мое решение состоит в том, чтобы собрать 600 строк кода в отдельную подпрограмму и вместо этого вызывать оба изв пределах onResume() и многочисленных точках в пределах onOptionsItemSelected()

@Override
public void onResume() {
    super.onResume();
    myOnResumeCode();
}

И внутри onOptionsItemSelected() сделать это

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    final int STATION_REQUEST = 1;
    int test = 1;
    switch (item.getItemId()) {
        case R.id.refresh: {
            userDateSet = false;
            myOnResumeCode();
            return true;
        } // come to the present.

    ... // Other statements
}

Приемлем ли этот метод?Если нет, то любые предложения, кроме «переписать все это», будут очень полезны для меня.Я интенсивно искал чистое решение, но не нашел того, которое смогу понять.Спасибо.

Ответы [ 7 ]

0 голосов
/ 08 июня 2018

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

Я думаю, @CommonsWare и другие уже указали на проблему, которую вы будете решатьв случае вызова функции onResume при повторном обновлении элементов пользовательского интерфейса на основе взаимодействия с пользователем.

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

600 строк кода не всегда поддерживаются.Вы можете разбить их на несколько функций.Однако, с моей точки зрения, все еще сложно собрать все вещи из одного места.

Я настоятельно рекомендую вам использовать ViewModel в вашем случае.Внедрение и управление станут намного проще.Я прилагаю пример реализации из документации разработчика .

public class UserActivity extends Activity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.user_activity_layout);
         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
         viewModel.userLiveData.observer(this, new Observer() {
            @Override
             public void onChanged(@Nullable User data) {
                 // update ui.
             }
         });
         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                  viewModel.doAction();
             }
         });
     }
 }

ViewModel было бы так.

 public class UserModel extends ViewModel {
     public final LiveData<User> userLiveData = new LiveData<>();

     public UserModel() {
         // trigger user load.
     }

     void doAction() {
         // depending on the action, do necessary business logic calls and update the
         // userLiveData.
     }
 }

При вашем действии с некоторыми данными, находящимися в вашем ViewModel, обновится пользовательский интерфейс, поскольку мы реализовали функцию обратного вызова, которая onChange.

Идея реализации функций обратного вызова может быть реализована несколькими способами (например, определение interface и затем переопределение функции).Это делает код намного чище, если его можно правильно реализовать.

А из документации ViewModel ...

ViewModel также можно использовать в качестве уровня связи между различными Fragments Activity.Каждый Fragment может получить ViewModel с помощью одного и того же ключа через Activity.Это позволяет осуществлять связь между фрагментами в разобщенном виде, так что им никогда не нужно напрямую общаться с другими Fragment.

public class MyFragment extends Fragment {
     public void onStart() {
         UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
     }
}

Теперь я думаю, что ваша проблема становится намного проще.Вы также можете разбить элементы пользовательского интерфейса на несколько Fragment и обрабатывать обновления там с различными элементами жизненного цикла.

Надеюсь, это поможет!

0 голосов
/ 15 июня 2018

Вот мой последний код, спасибо Ахамаду за прекрасное руководство.Я никогда не вызываю onResume () сейчас.В разных местах я вызываю myOnResume ().Код part1 () - это все расширенные вычисления.Код part2 () - это все операции вывода.

@Override
    public void onResume()
    {   super.onResume();//
        myOnResume();
    }// end: onResume.

    void myOnResume()
    {   new Thread(new Runnable()
        {   public void run()                   //run #1
            {   part1();                        // do all the calculations in background.
                runOnUiThread(new Runnable()
                {   public void run()           //run #2
                    {   part2();                // do the display in the UI thread.
                    }                           //end of run#2
                });                             //end of runOnUiThread
            }                                   //end of run#1
        }).start();
    }                                           //end: myOnResume()
0 голосов
/ 08 июня 2018

Всегда желательно использовать любой доступный / поддерживаемый код.

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

Например, Android Model View Presenter / MVP с рабочим примером

Теперь, если бы вы могли пройти более простое объяснение ( Более простое объяснение для MVP ), определенно возможна более простая отладка , модульное тестирование и , сопровождаемыекод очевиден.

Подойдя к вашим точкам (которые уже @CommonsWare) многое объяснил, переместив все 600 строк кода в Фоновая нить или Асинхронная задача улучшит производительность вашего приложения.Теперь вы больше не увидите сообщение, как показано ниже.

enter image description here

Честно говоря, я не понимаю, проблема с ним


Ссылка на оператор, что может быть включено в onResume() от developer.android

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


Жизненные методы Android должны вызываться системой, вызывая super onResume / super onPause. Это позволяет системе распределять / освобождать ресурсы.Конечно, мы можем расширить удобство использования, вызывая дочерние методы внутри onResume(), onPause() и т. Д. Но хранить бизнес-логику в этих методах и вызывать их вовсе не рекомендуется.

В случае MVP, ниже приведены строки гильдии, которым можно следовать.На данный момент нет стандартного / твердого определения, как этого добиться.Но все же приведенный мною пример - хорошее место для начала.

  1. Отдельный код для View.Сохранение представления как можно более тупым
  2. Сохранение всей бизнес-логики в классах Presenter
  3. Модели - это интерфейсы, отвечающие за управление данными
0 голосов
/ 06 июня 2018

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

Но реальный код должен быть правильно модульным.

Поскольку вы переписываете код, я бы порекомендовал вам перейти на архитектуру MVVM или MVP, поскольку управление этими случаями, о которых вы говорили, лучше с этими архитектурами.

Независимо от того, используете вы архитектуру или нет, этохорошо разделить код по назначению.

Например, onResume() означает, что вы делаете, когда Activity/Fragment возобновляется.То же самое относится к onCreate(), onDestroy() и т. Д.

В общих чертах,
1. Мы инициализируем неизменяемые переменные в onCreate и освобождаем их в onDestroy.
2.Мы извлекаем данные из серверной части или обновляем интерфейс пользователя в onResume()
3. Мы приостанавливаем любой текущий процесс, например воспроизведение мультимедиа в onPause()
и т. Д.

В соответствии с примером кода, который вы упомянулиоколо 600 строк, которые, как я полагаю, не выполняют ту же задачу.

Таким образом, вы должны разделить код на основе задачи

private void refreshDayLightTime(){
    //refresh your time
}

private void performRefreshTasks(){
   //series of codes that are to be executed one after the other
   //ideally this method should call methods which perform specific tasks.
}

private void updateLaunchViews(Bitmap heightChart, Bitmap reportBitmap){
   image.setImageBitmap(heightChart);
   report.setImageBitmap(reportBitmap);
}

. При таком подходе ваш код остается чистым.И в основном вы не нарушаете жизненные циклы приложения.И вы можете контролировать, какое действие вызывать из какой части приложения.

0 голосов
/ 04 июня 2018

Честно говоря, я не понимаю проблемы с ним.

Ваша реализация метода onResume() сама по себе безвредна.Но вызов этого супер-метода super.onResume(); позволит системе подумать, что это еще одно возникновение события возобновления.Это приведет к ненужному использованию ресурсов для обновления представлений и аналогичных внутренних работ.Поэтому вы должны избегать явных вызовов методов обратного вызова жизненного цикла при любых обстоятельствах.

Является ли этот метод приемлемым?

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

Если вы делаете что-то подобное

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.mnuEnableSomething:
            {
                refreshTheWholeUi();
                return true;
            }
        case R.id.mnuClearList:
            {
                refreshTheWholeUi();
                return true;
            }
    }
}

public void onResume() {
    super.onResume();
    refreshTheWholeUi();
}

Тогда его замена на это будет стоить этого.

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.mnuEnableSomething:
            {
                enableIt();
                return true;
            }
        case R.id.mnuClearList:
            {
                justClearTheList();
                return true;
            }
    }
}

public void onResume() {
    super.onResume();
    refreshTheWholeUi();
}

Сейчас, в суть темы

После вашего ответа я внимательно посмотрел на ваш вопрос, и это бросилось в глаза.

Мой план состоит в том, чтобы переместить эти 600 строк вотдельный файл класса.Это защитит их от повреждений, пока я работаю с командным декодером в исходном файле активности

Не совсем.Но вы действительно близко.Забудьте о всех сложностях, таких как жизненный цикл деятельности, методы, классы и т. Д., И просто сосредоточьтесь на самом базовом уровне выполнения компьютерной программы.

Программа всегда выполняется построчно.Не имеет значения, как вы расположили код.Правильное структурирование программы по методам, классам и т. Д. Для удобства программиста.Для системы это всегда серия линий.Поэтому при выполнении тяжелых задач пользовательский интерфейс может перестать отвечать на запросы, поскольку ему приходится ждать до своей очереди.

Тогда как можно выполнять параллельную работу?

Многопоточность...!

Это не так сложно, как кажется.

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

Здесь я проиллюстрировал, как выполнять многопоточность.

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.mnuProceessImageAction:
            {
                //Let user know that a background operation is running
                //with a progressbar or something
                processImage(mImage);
                return true;
            }
    }
}

private void processImage(Object image) {
    new Thread(new Runnable(){
        public void run() {
        //Doing all the heavy duty here.
        //.............................
        //Now you have the result. Use it to update the UI.
        //(UI can be updated only from the UI thread)
        runOnUiThread(new Runnable(){
                public void run() {
                    updateTheUiWithTheImage(proccessedImage);
                }
        });
        }
    }).start();

}

private void updateTheUiWithTheImage(Object image) {
    try {
        //Hide progressbar if you have one
        //Now it wont make the UI to struggle to use it.
    } catch(NullPointerException e) {
        e.printStackTrace;
    }
}

Это самая базовая форма.Конечно, есть альтернативы (например, AsyncTask ).Вы можете найти больше информации об этом в Интернете (попробуйте поискать "многопоточность в Android")Не стесняйтесь спрашивать больше.

0 голосов
/ 04 июня 2018

Рассмотрим исходный код 1002 * onResume() , который выполняется каждый раз, когда вызывается super.onResume():

protected void onResume() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
    getApplication().dispatchActivityResumed(this);
    mActivityTransitionState.onResume();
    mCalled = true;
}

, где mActivityTransitionState.onResume() вызывает resetViews() с дальнейшими действиями над представлениями в окне.Таким образом, хотя вы испытали, что это работает, все вызовы методов тратят время процессора и, по сути, избыточны, что приводит к выводу, что 1-й подход неэффективен.

С другой стороны, намерение использоватьmyOnResumeCode() без вызова super.onResume() позволяет избежать ненужных вызовов методов и является более оптимизированным решением.

Более того, 600 строк кода - это довольно большой объем.Если эти строки выполняются в главном потоке, пользовательский интерфейс замораживается, что делает приложение менее отзывчивым.Лучше выполнять вычисления в фоновом потоке и публиковать изменения представления в основном потоке.

Я не слышу страха НЕ вызывать super в моем коде.

Кажется, это неправильное понимание того, для чего предназначены методы жизненного цикла Activity.Эти методы являются обратными вызовами, которые система использует для информирования слушателей о происходящих в ней событиях.И вы получили такое уведомление, как только onResume() был вызван системой.Если вы удалите super.onResume(), вы получите исключение, это четко указано в связанном исходном коде и является единственным запросом, который система «хочет» выполнить Ativity при вызове ее onResume().Это относится ко всем остальным методам жизненного цикла - для информирования ОС.Системе все равно, если Activity вызовет их снова, «вручную».Пока Activity находится на переднем плане, вы можете вызывать onResume() столько, сколько хотите, тратя процессорное время и делая ваше приложение менее отзывчивым для пользователей.

Опять переопределенный onResume() является обратным вызовом ("слушатель ") и не может влиять на поведение системы в отличие, например, от метода finish(), который воздействует на поведение системы, говоря:" Эй, система, я все сделал и хочу быть убитым тобой ".Такие методы могут рассматриваться как запросы к системе.

Обновление

Они даже говорят вам, куда поместить код вашего приложения?

Вы можете разместить свой код везде, где хотите.Система просто информирует вас через вызовы методов жизненного цикла, когда, скажем, ваш контент виден пользователю или скрыт.Таким образом, это вопрос размещения вашего кода в разумном «месте» в соответствии с событиями жизненного цикла.

Означает ли это состояние, что можно напрямую вызывать onResume ()?Есть такие строго установленные запреты, высказанные против этого.

Это бессмысленно, но работает, как вы засвидетельствовали. "Вы не должны есть мясо в пятницу" , но кто сказал, что вы не можете?:)

0 голосов
/ 01 июня 2018

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

Вы предполагаете, что вызов super.onResume() подходит в тех случаях, когда вывручную звонят onResume().Это не безопасное предположение.

Является ли этот метод приемлемым

Это, безусловно, улучшение и его стоит сделать.

600 строк кода действительно длинный метод.Вам не удастся провести много проверок кода, так как рецензенты попросят вас реорганизовать этот код, чтобы сделать его более понятным.Кроме того, в зависимости от того, что вы делаете в этих 600 строках, может быть лучше перенести эту логику в фоновый поток.

...