替代直接onResume()调用

时间:2018-06-01 12:34:29

标签: java android android-activity android-lifecycle onresume

我正在重写我的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 :(得分:3)

  

然而就我所确定的而言,它运作良好,老实说,我不明白它的问题。

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

  

这种方法是否可以接受

这肯定是一种改进,值得做。

600行代码是真正的长方法。您将无法进行许多代码审查,审阅者会要求您重构该代码以使其更易于维护。此外,根据您在这600行中的操作,将该逻辑移动到后台线程可能更好。

答案 1 :(得分:3)

考虑每次调用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()通过对窗口中的视图采取进一步操作来调用resetViews()。因此,当您体验到它的工作原理时,所有方法调用都会浪费CPU时间,实际上是多余的,从而得出结论第一种方法效率低下。

另一方面,在不调用myOnResumeCode()的情况下使用super.onResume()的意图避免了不必要的方法调用,并且是一种更优化的解决方案。

此外,600行代码相当多。如果这些行在主线程上运行,它会冻结UI,使应用程序看起来响应性降低。最好在后台线程上进行计算,并在主线程上发布视图更改。

  

我没有听到过在我的代码中没有调用super的担心。

似乎是对Activity生命周期方法意图的误解。这些方法是系统用来向听众通知其中发生的事件的回调。一旦系统调用了onResume(),您就会收到这样的通知。如果您删除super.onResume(),您将收到例外情况,它会在链接的源代码中明确说明,并且是系统在调用Ativity时“希望”onResume()执行的唯一请求。它适用于所有其他生命周期方法 - 由操作系统提供信息。系统无需关心Activity是否“手动”再次调用它们。虽然Activity位于前台,但您可以根据需要尽可能多地调用onResume()浪费CPU时间,并使您的应用对用户的响应能力降低。

同样,被覆盖的onResume()是一个回调(“监听器”)并且不能影响系统行为,例如,影响系统行为的finish()方法说:“嘿,系统,我已经完成并想要被你杀死”。这些方法可以被视为对系统的请求。

<强>更新

  

他们甚至会告诉您将应用程序代码放在何处?

您可以随意将代码放在任何地方。例如,当您的内容对用户可见或隐藏时,系统会通知您生命周期方法调用。所以这是一个根据生命周期事件将代码放在合理“位置”的问题。

  

这是否可以直接调用onResume()?有强烈声明禁止表达的禁令。

这是毫无意义的,但正如你所目睹的那样有效。 “你不应该在星期五吃肉”,但谁说你不能? :)

答案 2 :(得分:3)

  

老实说,我不明白它的问题。

您的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行移到一个单独的类文件中。那会的   当我使用命令解码器时,让它们远离损坏   活动源文件

实际上并非如此。但你真的很亲密。忘掉活动生命周期,方法,类等所有复杂性,只关注计算机程序的最基本执行级别。

程序总是逐行执行。如何安排代码并没有什么不同。将程序正确地构造成方法,类等是为了方便程序员。对于系统来说,它始终是一系列的线条。因此,在执行繁重的任务时,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 )。您可以在线轻松找到更多相关信息(尝试搜索&#34; Android中的多线程和#34;)。随意问更多。

答案 3 :(得分:1)

  

然而就我所确定的而言,它运作良好,老实说,我没有   了解它的问题。

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

  

这种方法可以接受吗?如果没有,任何建议都缺少&#34;重写   整件事&#34;对我很有帮助。

总是无法维护600行代码。您可以考虑将它们分解为多个功能。但是,从我的观点来看,将所有事物从一个地方拉到一起仍然是一项艰巨的任务。

我强烈建议您在案例中使用ViewModel。实施和管理将变得更加简单。我正在附加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中的某些数据的操作,我们已经实现了回调函数onChange,因此会更新UI。

实现回调函数的想法可以通过多种方式完成(例如,定义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中分解您的UI元素,并使用不同的生命周期元素处理更新。

希望有所帮助!

答案 4 :(得分:1)

这是我的最终编码,感谢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()

答案 5 :(得分:0)

这是开发人员习惯使用生命周期方法执行某些任务的一种常见习惯,因为它方便

但实际代码必须正确模块化。

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

无论您是否使用和体系结构,最好根据目的拆分代码。

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

一般而言,
1.我们在onCreate中初始化非变化变量,并在onDestroy中释放它们 2.我们从后端获取数据或刷新onResume()中的UI 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);
}

使用此方法可以保持代码清洁。主要是你最终不会打破应用程序的生命周期。您可以控制从应用程序的哪个部分调用哪个操作。

答案 6 :(得分:0)

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

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

例如Android Model View Presenter / MVP with working example

现在,如果您可以进行更简单的解释(Simpler explanation for MVP),那么更容易调试单元测试可维护代码< / em>很明显。

来到你的观点(已经是@CommonsWare)已经解释了很多,将所有600行代码移到Background ThreadAsync task将提高你的应用程序的性能。现在你不会再看到如下信息了。

enter image description here

  

老实说,我不明白它的问题

语句参考onResume()

中可能包含的内容onResume
  

因此,在UI线程中运行的任何方法都应该尽可能少   尽可能在该线程上工作。特别是,活动应该做   尽可能少地设置关键的生命周期方法,如   onCreate()和onResume()。可能长时间运行的操作如   网络或数据库操作,或计算上昂贵   调整位图大小等计算应该在worker中完成   线程(或者在数据库操作的情况下,通过异步   请求)

系统应该通过调用超级onPause / super onResume()来调用

developer.android。这允许系统分配/释放资源。当然,我们可以通过在onPause()existing package by the same name with a conflicting signature is already installed等内部调用子方法来扩展可用性。但是在这些方法中保留业务逻辑并调用这些方法根本不可取。

如果是Android life methods,下面是可以遵循的公会线。 截至目前尚无标准/固定定义,如何实现相同。但我提供的样本仍然是一个很好的起点。

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