直接调用 onResume() 的替代方法

Alternative to direct onResume() call

我正在重写我的 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
}

这种方法可以接受吗?如果没有,任何 "rewrite the whole thing" 以外的建议都会对我很有帮助。我广泛搜索了一个干净的解决方案,但没有找到我能理解的解决方案。谢谢。

Yet it works well as far as I have determined, I honestly do not understand the problem with it.

您假设在手动调用 onResume() 的情况下调用 super.onResume() 是合适的。这不是一个安全的假设。

Is this method acceptable

这当然是一个改进,非常值得一做。

600 行代码是一个真的 长方法。许多代码审查都会失败,审查人员会要求您重构该代码以使其更易于维护。此外,根据您在这 600 行中所做的事情,最好将该逻辑移至后台线程。

考虑每次调用 super.onResume() 时执行的 Activity's onResume() source code

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

其中 mActivityTransitionState.onResume() invokes resetViews() 对 window 中的视图执行进一步操作。因此,当您体验到它有效时,所有方法调用都在浪费 CPU 时间,而且实际上是多余的,这得出第一种方法效率低下的结论。

另一方面,使用myOnResumeCode()而不调用super.onResume()的意图避免了不必要的方法调用,是一个更优化的解决方案。

而且,600行代码是一个很大的数目。如果主线程上的那些行 运行 它会冻结 UI 使应用程序看起来响应速度较慢。最好在后台线程上进行计算,post 在主线程上更改视图。

I don't hear a fear of NOT calling super in my code.

这似乎是对 Activity 生命周期方法的用途的误解。这些方法是系统用来通知侦听器其中发生的事件的回调。一旦 onResume() 被系统调用,你就会收到这样的通知。如果你删除 super.onResume() 你会得到一个异常,它在链接的源代码中有明确说明,并且是系统 "wants" 和 Ativity 在调用其 [=12= 时执行的唯一请求].它适用于所有其他生命周期方法 - 由 OS 通知。系统不关心Activity是否再次调用它们,"manually"。当 Activity 处于前台时,您可以尽可能多地调用 onResume() 浪费 CPU 时间并使您的应用程序看起来对用户响应较慢。

同样,被覆盖的 onResume() 是一个回调("listener")并且不会影响系统行为,例如finish() 影响系统行为的方法说:"Hey, system, I'm all done and wanna be killed by you"。这类方法可能会被视为对系统的请求。

更新

Do they even tell you where to put your application code?

您可以随意将代码放在任何地方。当您的内容对用户可见或隐藏时,系统只是通过生命周期方法调用通知您。所以问题是根据生命周期事件把你的代码放在一个合理的"place"

Does this state it is OK to directly call onResume()? There is such strongly stated prohibitions expressed against that.

这毫无意义,但正如您所见。 "You shall not eat meat on Friday",但谁说你不行? :)

I honestly do not understand the problem with it.

您的 onResume() 方法实施本身是无害的。但是调用它的super方法super.onResume();会让系统认为这是resume事件的又一次发生。这将导致不必要的资源使用用于刷新视图和类似的内部工作。所以在任何情况下都必须避免显式调用生命周期回调方法。

Is this method acceptable?

代码行数不能接受或不能接受。 这是一个你需要问自己的问题。如果您认为整个代码都将在该事件中执行,那么您应该这样做。否则你可以节省一些资源。

如果你正在做这样的事情

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();
}

现在,进入主题的核心

看了你的回复,我仔细看了你的问题,这让我眼前一亮。

My plan is to move those 600 lines to a separate class file. That will keep them away from damage while I work on the command decoder in the activity source file

实际上不是。但你真的很亲密。忘记所有复杂性,例如 activity 生命周期、方法、类 等等,只关注计算机程序的最基本执行级别。

程序总是逐行执行的。您如何安排代码没有任何区别。将程序适当地构造成方法,类 等是为了程序员的方便。对于系统来说,它总是一系列的线。因此,在执行繁重的任务时,UI 可能会变得反应迟钝,因为它必须等到轮到它。

那么如何才能并行工作呢?

多线程...!

它并不像听起来那么复杂。

您必须找到代码中使用资源较多的最关键部分,并将其移至其他线程。

我在这里说明了如何进行多线程。

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)。您可以轻松地在线找到更多相关信息(尝试搜索 "multi threading in Android")。欢迎提问。

调用生命周期方法来执行某些任务是开发人员非常普遍的习惯,因为它方便

但实际代码必须适当模块化。

由于您正在重写代码,我建议您迁移到 MVVM 或 MVP 架构,因为使用这些架构可以更好地管理您提到的案例。

无论你是否使用和架构,根据目的拆分代码是好的。

例如 onResume() 表示当 Activity/Fragment 恢复时您执行的操作。同样适用于 onCreate()onDestroy()

一般来说,
1.我们在onCreate中初始化不变的变量,在onDestroy中释放它们。
2.我们从后台重新取数据或者刷新onResume()
中的UI 3. 我们暂停任何正在进行的过程,例如 onPause()
etc

中的媒体播放

根据您的代码示例,您提到了大约 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);
}

使用这种方法可以使您的代码保持整洁。主要是你不会破坏应用程序的生命周期。您还可以控制从应用程序的哪个部分调用哪个操作。

始终建议使用任何可访问/可维护的代码。

我宁愿建议不仅 将您的代码移至可维护的部分 ,而且 重构实现 通过遵循任何方法将表示层与逻辑分开。

比如Android Model View Presenter / MVP with working example

现在如果你能通过更简单的解释(Simpler explanation for MVP),肯定有可能更容易调试单元测试可维护代码 显而易见。

谈到你的观点(已经@CommonsWare)已经解释了很多,将所有 600 行代码移动到 Background Thread or Async task 将提高你的应用程序的性能。现在您将不会再看到如下所示的消息。

I honestly do not understand the problem with it


声明引用 developer.android

中的 onResume() 中可能包含的内容

Therefore, any method that runs in the UI thread should do as little work as possible on that thread. In particular, activities should do as little as possible to set up in key life-cycle methods such as onCreate() and onResume(). Potentially long running operations such as network or database operations, or computationally expensive calculations such as resizing bitmaps should be done in a worker thread (or in the case of databases operations, via an asynchronous request)


Android life methods 应该由系统调用,通过调用超级 onResume / 超级 onPause。这允许系统 allocate/deallocate 资源。当然,我们可以通过在 onResume()onPause() 等内部调用子方法来扩展可用性。但是在这些方法中保留业务逻辑并调用它们是完全不可取的。

MVP的情况下,以下是可以遵循的行会路线。 截至目前还没有 standard/solid 定义,如何实现相同。但我提供的样本仍然是一个很好的起点。

  1. 视图的单独代码。使视图尽可能愚蠢
  2. 将所有业务逻辑保留在 Presenter 中类
  3. 模型是负责管理数据的接口

Yet it works well as far as I have determined, I honestly do not understand the problem with it.

我认为@CommonsWare 和其他人已经指出了在调用 onResume 函数的情况下您将遇到的问题,同时根据用户交互再次更新 UI 元素。

Is this method acceptable? If not, any suggestions short of "rewrite the whole thing" will be very helpful to me.

600 行代码并不总是可维护的。您可以考虑将它们分解成几个函数。然而,从我的角度来看,从一个地方把所有的东西都拉到一起仍然是一项困难的工作。

我强烈建议您使用 ViewModel in your case. The implementation and management will become a lot simpler. I am attaching the sample implementation from the developer documentation

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 中的某些数据执行的操作将更新 UI,因为我们已经实施了回调函数 onChange

实现回调函数的想法也可以通过多种方式实现(例如,定义一个 interface 然后覆盖该函数)。如果可以正确地实现它们,它会使代码更清晰。

并从 ViewModel 的文档...

ViewModel can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.

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

现在我想,你的问题变得简单多了。您还可以考虑将 UI 元素分解为几个 Fragment 并使用不同的生命周期元素处理那里的更新。

希望对您有所帮助!

这是我的最终编码,感谢 Ahamad 的出色指导。 我现在从不调用 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()