踩坑-复用RemoteView导致内存泄漏总结
复用RemoteView导致内存泄漏总结
关于RemoteView
RemoteView是一个提供跨进程控制的View,主要用在通知栏或者小部件的开发上。例如音乐类APP自定义的通知栏样式就是通过RemoteView实现的。如果你之前没有听说过RemoteView,可以在这里简单了解一下:
https://www.jianshu.com/p/23041852bd85
RemoteView中可以使用的布局和控件是受限制的,能用的布局有:
AdapterViewFlipper
FrameLayout
GridLayout
GridView
LinearLayout
ListView
RelativeLayout
StackView
ViewFlipper
可以用的控件有:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextClock
TextView
遇到的问题
最近开发了一个音乐类的App,播放音乐时会在通知栏常驻一个自定义样式的通知,其中通知栏有一个头像是通过setImageViewBitmap(int viewId, Bitmap bitmap)
方法进行设置的:
1 | RemoteViews mRemoteView; // 全局变量 |
后来发现如果App连续放歌在3个小时左右时就会OOM崩掉,通过Profile检查内存后,发现是头像的bitmap没有销毁导致的,但是这里的bitmap对象每次使用完都会recycle掉,为什么还会内存泄漏呢?经过一番排查,发现是使用同一个RemoteVIew
对象setImageViewBitmap(R.id.cover, bitmap)
导致的。
在RemoteView
的源码中我们可以看到一个mActions
变量,这是一个Action
的列表:
1 | /** |
而Action是其内部定义的一个可序列化的类:
1 | private abstract static class Action implements Parcelable |
通过跟踪setImageViewBitmap(int viewId, Bitmap bitmap)中bitmap的去向,发现最终调用了setBitmap()方法:
1 | public void setBitmap(int viewId, String methodName, Bitmap value) { |
可以看出,每次setImageViewBitmap()
,都会将Bitmap做成一个BitmapReflectionAction
,并添加到mActions
列表里,这里的BitmapReflectionAction
是继承Action
的一个可序列化的类,Bitmap在里面作为被序列化成了一组值最终存到了mActions
列表中。在源码中,mActions
列表只看到有添加操作,并没有看到remove
或者clear
操作,导致了内存泄漏。
解决
不要复用RemoteView
,更新通知栏icon时,new一个新的RemoteView
给NotificationCompat.Builder
参考
So internally RemoteViews is simply a set of actions that are “serialized” and sent to another process. Each time you make a call to something like setDouble(), you’re adding an additional action to RemoteViews’ internal list.
Because there isn’t a way of clearing these actions from a RemoteViews object, all of your successive setImageViewBitmap() calls, along with their Bitmaps, remain in the internal list, and are actually “serialized” and applied each time your send it. :( In this case it’s best to just create a new RemoteViews object every time.
https://github.com/rojdes/AngryDict/blob/master/app/src/main/java/me/rds/angrydictionary/widget/BinaryClockWidget.java
https://blog.csdn.net/u013989732/article/details/78501462