RemoteViews 内存溢出
1、RemoteViews内存溢出原因
我们使用大致是这几种方法来设置图片。
Android\Sdk\sources\android-25\android\widget\RemoteViews.java
/**
* Equivalent to calling ImageView.setImageResource
*
* @param viewId The id of the view whose drawable should change
* @param srcId The new resource id for the drawable
*/
public void setImageViewResource(int viewId, int srcId) {
setInt(viewId, "setImageResource", srcId);
}
/**
* Equivalent to calling ImageView.setImageURI
*
* @param viewId The id of the view whose drawable should change
* @param uri The Uri for the image
*/
public void setImageViewUri(int viewId, Uri uri) {
setUri(viewId, "setImageURI", uri);
}
/**
* Equivalent to calling ImageView.setImageBitmap
*
* @param viewId The id of the view whose bitmap should change
* @param bitmap The new Bitmap for the drawable
*/
public void setImageViewBitmap(int viewId, Bitmap bitmap) {
setBitmap(viewId, "setImageBitmap", bitmap);
}
/**
* Equivalent to calling ImageView.setImageIcon
*
* @param viewId The id of the view whose bitmap should change
* @param icon The new Icon for the ImageView
*/
public void setImageViewIcon(int viewId, Icon icon) {
setIcon(viewId, "setImageIcon", icon);
}
/**
* Equivalent to calling AdapterView.setEmptyView
*
* @param viewId The id of the view on which to set the empty view
* @param emptyViewId The view id of the empty view
*/
public void setEmptyView(int viewId, int emptyViewId) {
addAction(new SetEmptyView(viewId, emptyViewId));
}
单个RemoteViews对象通过setImageViewBitmap()方法设置图片不是替换,而是不断add。而且没有remove的方法,当这个list被add满之后,就会出现溢出的问题,甚至达到一定限额后就会让你的应用crash。
2、解决办法
所以解决办法是什么?
那就是不要复用RemoteViews,每次需要跟新RemoteViews的时候,new一个新的,然后该设置PendingIntent就再设置一遍,这是一个新的RemoteViews,当然得重新设置一遍。自己压力测试过了,没问题,回收的问题,开始复用RemoteViews的时候还手动recycle掉bitmap,结果报使用recycled bitmap。。。。其实没必要,new一个新的RemoteViews,然后交给垃圾回收器GC的时候回收就行了。
2.1贴一下我的代码
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"音乐服务onCreate");
remoteViews = new RemoteViews(getPackageName(), R.layout.view_remote);
initNotificationSon();
//省略其他代码
}
private void initNotificationSon() {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(MusicApplication.getContext());
mBuilder.setSmallIcon(R.drawable.play_background02); // 设置顶部图标
mBuilder.setOngoing(true);
notification = mBuilder.build();//构建通知
setNotification();
notification.contentView = remoteViews; // 设置下拉图标
notification.bigContentView = remoteViews; // 防止显示不完全,需要添加apiSupport
notification.flags = Notification.FLAG_ONGOING_EVENT;
notification.icon = R.drawable.anim_log;
startForeground(123, notification);//启动为前台服务
}
/**
* PendingIntent是一种特殊的intent,设置之后并不会马上使用,而是在真正点击后只会调用。
*/
private void setNotification(){
Log.d(TAG, "MediaUtils.currentState == " + MediaUtils.currentState);
if (MediaUtils.currentState == MediaStateCode.PLAY_PAUSE ||
MediaUtils.currentState == MediaStateCode.PLAY_STOP) {
/*play -》 pause*/
remoteViews.setImageViewResource(R.id.notification_play_pause, R.drawable.ic_pause_black);
}else {
remoteViews.setImageViewResource(R.id.notification_play_pause, R.drawable.ic_play_black);
}
// 点击音乐image跳转到主界面
Intent intentGo = new Intent(this, LocalListActivity.class);
PendingIntent pendingIntentGo = PendingIntent.getActivity(
this, 0, intentGo, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_album, pendingIntentGo);
// 关闭
Intent intentClose = new Intent(MediaStateCode.ACTION_CLOSE);
PendingIntent pendingIntentClose = PendingIntent.getBroadcast(
this, 0, intentClose, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_close_this, pendingIntentClose);
// 设置上一曲
Intent intentLast = new Intent(MediaStateCode.ACTION_LAST);
PendingIntent pendingIntentLast = PendingIntent.getBroadcast(
this, 0, intentLast, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_last, pendingIntentLast);
// 设置播放&暂停
Intent intentPlayOrPause = new Intent(MediaStateCode.ACTION_PLAY_OR_PAUSE);
PendingIntent pendingIntentPlayOrPause =
PendingIntent.getBroadcast(this, 0,
intentPlayOrPause, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_play_pause, pendingIntentPlayOrPause);
// 下一曲
Intent intentNext = new Intent(MediaStateCode.ACTION_NEXT);
PendingIntent pendingIntentNext = PendingIntent.getBroadcast(
this, 0, intentNext, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_next, pendingIntentNext);
// 设置收藏
Intent intentLove = new Intent(MediaStateCode.ACTION_LOVE);
PendingIntent pendingIntentLove = PendingIntent.getBroadcast(
this, 0, intentLove, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notification_love, pendingIntentLove);
}
public Handler remoteViewsHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.d(TAG, "remoteViewsHandler" + ", msg.what=" + msg.what);
int position = MediaUtils.currentSongPosition;
Log.d(TAG, "remoteViewsHandler" + ", 全局播放position=" + position);
switch (msg.what){
//上一曲/下一曲;需要跟新专辑图和歌手名,歌曲信息
case 1:
/*专辑图片,歌曲名,歌手名*/
RefreshAlbumPicTitleArtist(position);
RefreshPlayOrPauseButton();
break;
//播放/暂停同一首音乐;只需要更新:播放/暂停图标
case 2:
RefreshPlayOrPauseButton();
break;
}
notification.contentView = remoteViews; // 设置下拉图标
notification.bigContentView = remoteViews; // 防止显示不完全,需要添加apiSupport
startForeground(123, notification);//启动为前台服务
Log.d(TAG, "remoteViewsHandler end");
}
};
/**
* 更新 "专辑图片,歌曲名,歌手名"
*/
private void RefreshAlbumPicTitleArtist(int position) {
remoteViews = new RemoteViews(getPackageName(), R.layout.view_remote);
setNotification();
try {
String path = MusicResources.getAlbumArt((int) musicList.get(position).getAlbum_id());
Log.d(TAG,"RefreshAlbumPicTitleArtist" + "专辑图path="+path);
Bitmap bitmap;
if (null == path) {
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.play_background02);
} else {
bitmap = BitmapFactory.decodeFile(path);
}
remoteViews.setImageViewBitmap(R.id.notification_album, bitmap);
/*歌曲名称 & 歌手名*/
remoteViews.setTextViewText(R.id.notification_title, musicList.get(position).getTitle());
remoteViews.setTextViewText(R.id.notification_artist, musicList.get(position).getArtist());
} catch (Exception e) {
e.printStackTrace();
}
}
就这样,如果想看看完整的例子,可以来我的GitHub上看,
对应类的地址:https://github.com/stoneWangL/StoneMusic/blob/master/app/src/main/java/com/stone/stonemusic/service/MusicService.java
项目地址:https://github.com/stoneWangL/StoneMusic
欢迎follow,一起交流技术吱吱(: