在活动中使用内部BroadcastReceiver的内存问题

时间:2018-05-16 14:39:53

标签: java android performance

我已经有一个月的时间来编写Android应用程序作为业余爱好。我现在已经到了这样一个地步,我必须意识到现实世界并不像某些教程听起来那么容易,并且应用程序在多个设备上的表现确实不同。目前,我担心我的应用程序中的内存和CPU使用率。我从来没有真正花费任何时间来优化或甚至考虑我的应用程序的内存使用情况,我在这里,我意识到我可能真的搞砸了许多点,并有很多要清理。

不幸的是,我很难找到最新的教程和解释,对新手来说是可以理解的。许多教程正在处理旧的android工作室版本,非常好的教程是关于使用DDMS。我觉得新的Android Profiler(在Android Studio 3.0中)缺少一些重要的见解。 如果你可以帮助我消除一些误解,那将是很好的,我目前因长时间的夜间阅读而烦恼了很多操作方法和教程。

我目前想知道一项活动中的BroadcastReceiver,这是一种经常使用的做法。 所以,例如,我有一个音乐应用程序。 MainActivity正在启动Service来处理音乐播放。此Service向活动报告有关当前播放状态和已播放时间的活动。 通过将Service中的意图发送到指定BroadcastReceiver内的内部MainActivity来解决报告。 这是一个简单的例子,让你掌握我的意思:

public class MainActivity extends Activity {

    private class MusicPlayerBroadcast extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //update UI elements
            seekBar.post(() -> seekBar.setProgress(POSITION)); //using post for thread safeness? 

            //lots of other UI calls 

        }

    }
}

由于我不确定是否通过其他错误的实现引发了以下问题,我决定发布意图接收的完整代码:

                            int currentPos = intent.getIntExtra(KEY_POSITION, -1);

                            //check if large player is showing, if not it should display mini Player
                            if(playerRootView.getVisibility() != View.VISIBLE) {
                                mini_Player.setVisibility(View.VISIBLE);
                            }

                            //if the loading hasn't been triggered away yet, this one will help
                            if(relativeLayoutLoading.getVisibility() == View.VISIBLE) {
                                relativeLayoutLoading.setVisibility(View.GONE);
                                playerControls.setVisibility(View.VISIBLE);
                            }

                            TrackModel mCurrentTrack=MusicDataMng.getInstance().getCurrentTrackModel();
                            if (currentPos > 0 && mCurrentTrack != null) {
                                long duration = currentPos / 1000;
                                String minute = String.valueOf((int) (duration / 60));
                                String seconds = String.valueOf((int) (duration % 60));
                                if (minute.length() < 2) {
                                    minute = "0" + minute;
                                }
                                if (seconds.length() < 2) {
                                    seconds = "0" + seconds;
                                }
                                String timePassed = minute + ":" + seconds;

                                if(!seeking) {
                                    // will update the "progress" propriety of seekbar until it reaches progress
                                    seekBar.post(() -> {
                                        ObjectAnimator animation = ObjectAnimator.ofInt(seekBar, "progress", currentPos);
                                        animation.setDuration(1000); // 0.5 second
                                        animation.setInterpolator(new LinearInterpolator());
                                        animation.start();
                                    });

                                } else {
                                    seekBar.post(() -> seekBar.setProgress(currentPos));
                                }
                                mini_ProgressBar.post(() -> mini_ProgressBar.setProgress(currentPos));
                                trackDurationStart.post(() -> trackDurationStart.setText(timePassed));

                            }

在我开始播放歌曲后录制内存时,我现在可以在Android Profiler中看到的是: Screenshot of Android Profiler excerpt

我对此的疑问如下:

  1. 我觉得不应该为每个意图更新一个BroadcastReceiver的实例。 GC之后为什么它们仍然存储而未被删除?
  2. 实例在arg$1.this$0中保留对MainActivity的引用
    • 来自引用ID,我可以看到它们指向相同的MainActivity。这是否意味着它们只存储指向该引用的指针,或者是存储对MainActivity的完整引用的BroadcastReceiver的每个实例?如果是这样意味着会占用大量内存,对吧?
  3. 当我使BroadcastReceiver静态然后添加一个将设置WeakReferences的构造函数时,这种不当行为是否会消失,如here所述?
  4. 如果你觉得我真的错过了关于这个洞的表演主题,或者应该阅读这个或那个教程或书,我会非常感谢任何推荐。

    提前谢谢

1 个答案:

答案 0 :(得分:2)

通过将广播接收器声明为嵌套类,它保存对封闭类(MainActivity)的引用。您可以将其称为MainActivity实例中描述的“指针”(内存中只有一个实例)。

我没有通过您链接的博客,但将其设为静态将删除此实例“指针/引用”。

我的建议是通过https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html来更好地理解Java中的嵌套类以及何时/如何最好地使用它们。

至于你的具体问题,我建议你使用EventBus并在公交车上发送你的玩家更新,任何感兴趣的班级都可以订阅和收听。

http://square.github.io/otto

https://github.com/greenrobot/EventBus