我有一个Activity
和一个ConstraingLayout
,其中有ImageView
个(每张卡一张)。
获胜后,通过单击将出现的ImageView,Activity
将被“重新加载”,显示一组新的卡牌。
问题在于,每次获胜后,Activity
使用的内存就会增加,而不是返回到初始使用的金额。
这会在某些内存不足的设备(例如Nexus 7)上导致OutOfMemory Exception
。 :(
逻辑是:
onCreate
方法中,我将ConstraintLayout
设置为30 ImageView
s(rhe卡的正面)和其他30 ImageView
s(卡)ImageView
(正面和背面),我通过缩放可绘制资源来设置OnClickListener
和图像ImageView
时,我都会在卡片的两面设置Alpha以仅显示正确的一面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
的第一次运行中,AndroidStudio
的{{1}}是:
获胜后(因此在第二个profiler
之后),已使用的内存为:
现在onCreate
的内存使用量为Java
,而不是返回到28,1 MB
的初始值。
屏幕截图指的是带有16个框的布局。 采用30盒式布局,使用的内存增加了很多。 (例如,从49 MB到83 MB)
我可以说图像的大小已足够调整,以便使用较少的内存,因此也许不是问题。请告诉我我是否错。
25,2 MB
使用的MB会增加?Java
活动的方法是正确的,还是有另一种方法可以让我释放更多资源?我很难找到它们,因为我对Android编程还比较陌生,尤其是因为我几乎从未面临过分使用内存的问题。
修改:
以下是一些使用LeakCanary的信息:
通过单击3个“ GiocaMemory Leaked 21 Agosto 13:35”之一(所有3个都是相同的,只更改跟踪末尾的GiocaMemory
)
key =
official LeakCanary's documentation说:
如果节点未泄漏,则指向该节点的任何先前引用都不是泄漏的来源,也不会泄漏。同样,如果某个节点正在泄漏,则泄漏跟踪下的任何节点也将泄漏。由此,我们可以推断出泄漏是由最后一个
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)
之后和第一个Leaking: NO
之前的引用引起的。
但是在我的泄漏跟踪中,只有Leaking: YES
个泄漏(最后一个UNKNOWN
除外)。
如何在我的代码中发现YES
泄漏(如果可能是泄漏)?
非常感谢您的帮助!
答案 0 :(得分:2)
您是否从AudioManager正确放弃了焦点监听器?
AudioManager#abandonAudioFocus(OnAudioFocusChangeListener listener)
实际的OOM可能是如上所述的非回收列表的结果,但这可能是导致内存泄漏的原因。