gpt4 book ai didi

java - 在 Activity 代码中查找内存泄漏以释放内存使用并避免 OutOfMemory Exception

转载 作者:行者123 更新时间:2023-11-29 18:28:53 25 4
gpt4 key购买 nike

我有一个 ActivityConstraingLayout有很多 ImageView s(每张卡一张)。

获胜后,通过单击将出现的 ImageView,Activity将被“重新加载”,显示一组新的牌来玩。

问题是每次获胜后 Activity 使用的内存提高而不是返回初始使用的金额。

这会导致 OutOfMemory Exception在一些内存不足的设备上(例如在 Nexus 7 上)。 :(

逻辑是:

  • onCreate方法我设置 ConstraintLayout由 30 ImageView 制成s(卡片正面)等 30 ImageView s(卡片背面)
  • 每个ImageView (正面和背面)我设置了 OnClickListener和图像通过缩放可绘制资源
  • 每次用户点击 ImageView , 我将卡片两侧的 alpha 设置为只显示正确的一面
  • 如果用户找到所有匹配项,将出现 win View :如果用户单击它,将调用 win 方法,即“重新加载 Activity ”

GiocaMemory.java:

package ...
import ...

public class GiocaMemory extends AppCompatActivity
{
...

MediaPlayer mediaPlayer;
MediaPlayer.OnCompletionListener onCompletionListenerReleaseMediaPlayer;

private AudioManager audioManager;
private AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener;

...

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

...

onCompletionListenerReleaseMediaPlayer = new MediaPlayer.OnCompletionListener()
{
@Override
public void onCompletion(MediaPlayer mp)
{
releaseMediaPlayer();
}
};

audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener()
{
@Override
public void onAudioFocusChange(int focusChange)
{
if(mediaPlayer != null)
{
switch (focusChange)
{
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mediaPlayer.pause();
mediaPlayer.seekTo(0);
break;
case AudioManager.AUDIOFOCUS_GAIN:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
mediaPlayer.start();
break;
case AudioManager.AUDIOFOCUS_LOSS:
releaseMediaPlayer();
break;
}
}
}
};

...
}

private void win()
{
showView(textViewWin);
}

public void reloadActivity()
{
finish();
startActivity(getIntent());
}

public void playSound(int idElement)
{
String name = idElement + "_name";

playAudio(name, onCompletionListenerReleaseMediaPlayer);
}

public void playAudioName(int idElement)
{
String name = idElement + "_sound";

playAudio(name, onCompletionListenerReleaseMediaPlayer);
}

public void onClickHome(View view)
{
finish();
}

public void stopAudio()
{
releaseMediaPlayer();
}

private void playAudio(String audioName, MediaPlayer.OnCompletionListener onCompletionListener)
{
stopAudio();

if(!audioName.isEmpty())
{
int result = audioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
{
int resID = res.getIdentifier(audioName, "raw", getPackageName());

if (resID == 0)
{
return;
}
releaseMediaPlayer();

startMediaPlayerWithRes(this, resID, audioName);
mediaPlayer.setOnCompletionListener(onCompletionListener);

}
}
}

private void startMediaPlayerWithRes(Context context, int resID, String audioName)
{
mediaPlayer = MediaPlayer.create(context, resID);

if(mediaPlayer != null) mediaPlayer.start();
else mediaPlayer = new MediaPlayer();
}

private void releaseMediaPlayer()
{
if(mediaPlayer != null) mediaPlayer.release();
mediaPlayer = null;
}

private void loadContents()
{
...
}

@Override
protected void onPause()
{
super.onPause();

releaseMediaPlayer();
}

public void freeRes()
{
...

mediaPlayer = null;
onCompletionListenerReleaseMediaPlayer = null;

audioManager = null;
onAudioFocusChangeListener = null;
res = null;

releaseMediaPlayer();
}

@Override
protected void onDestroy() {
super.onDestroy();

freeRes();
}
}

第一次运行 GiocaMemory AndroidStudioprofiler是:

enter image description here

获胜后(即第二次 onCreate 之后)使用的内存为:

enter image description here

现在 Java内存使用量为 28,1 MB , 而不是返回初始值 25,2 MB .

屏幕截图引用了 16 个框的布局。 在 30 盒布局中,使用的内存增加了很多。 (例如从 49 MB 到 83 MB)

我可能会说图像的大小调整得足够多,以便尽可能使用更少的内存,所以也许它们不是问题所在。如果我错了请告诉我。

  • 为什么每次获胜后 Java 使用的 MB会增加吗?
  • 你能帮我找到我留在代码中的一些内存泄漏吗?
  • 我用来“重新加载”GiocaMemory 的方式 Activity 是正确的还是有其他方式可以让我释放更多资源?

我发现很难找到它们,因为我对 Android 编程还比较陌生,尤其是因为我几乎从来没有遇到过与内存使用过多相关的问题。

编辑:

这些是使用 LeakCanary 的一些信息:

enter image description here

通过单击 3 个“GiocaMemory Leaked 21 Agosto 13:35”之一(所有 3 个都相同,仅更改跟踪末尾的 key =)

ApplicationLeak(className=app.myapp.GiocaMemory, leakTrace=

├─ android.media.AudioManager$1
│ Leaking: UNKNOWN
│ Anonymous subclass of android.media.IAudioFocusDispatcher$Stub
│ GC Root: Global variable in native code
│ ↓ AudioManager$1.this$0
│ ~~~~~~
├─ android.media.AudioManager
│ Leaking: UNKNOWN
│ ↓ AudioManager.mAudioFocusIdListenerMap
│ ~~~~~~~~~~~~~~~~~~~~~~~~
├─ java.util.HashMap
│ Leaking: UNKNOWN
│ ↓ HashMap.table
│ ~~~~~
├─ java.util.HashMap$HashMapEntry[]
│ Leaking: UNKNOWN
│ ↓ array HashMap$HashMapEntry[].[0]
│ ~~~
├─ java.util.HashMap$HashMapEntry
│ Leaking: UNKNOWN
│ ↓ HashMap$HashMapEntry.value
│ ~~~~~
├─ app.myapp.GiocaMemory$2
│ Leaking: UNKNOWN
│ Anonymous class implementing android.media.AudioManager$OnAudioFocusChangeListener
│ ↓ GiocaMemory$2.this$0
│ ~~~~~~
╰→ app.myapp.GiocaMemory
​ Leaking: YES (Activity#mDestroyed is true and ObjectWatcher was watching this)
​ key = dfa0d5fe-0c50-4c64-a399-b5540eb686df
​ watchDurationMillis = 380430
​ retainedDurationMillis = 375425
, retainedHeapByteSize=470627)

official LeakCanary's documentation说:

If a node is not leaking, then any prior reference that points to it is not the source of the leak, and also not leaking. Similarly, if a node is leaking then any node down the leak trace is also leaking. From that, we can deduce that the leak is caused by a reference that is after the last Leaking: NO and before the first Leaking: YES.

但在我的 leakTrace 中只有 UNKNOWN除了最后一次泄漏 YES .

我怎样才能找到 YES在我的代码中泄漏,如果它可能是泄漏?

非常感谢您的帮助!

最佳答案

您是否正确地放弃了 AudioManager 中的焦点监听器?

AudioManager#abandonAudioFocus(OnAudioFocusChangeListener 监听器)

实际的 OOM 可能是上述非回收列表的结果,但这可能是内存泄漏的原因。

关于java - 在 Activity 代码中查找内存泄漏以释放内存使用并避免 OutOfMemory Exception,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57462677/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com