关于 AppWidget 动态更新:RemoteViews 更新缓慢&内存溢出

来源:互联网 发布:阿里云ecs挂载数据盘 编辑:程序博客网 时间:2024/06/04 19:21


AppWidget 提供在 Launcher 中显示,诸如快捷操作,小工具等,如果只是提供快捷操作我想界面可以简洁,但如果 AppWidget 如果想提供类似广告和豆瓣电台那样效果的话,动态改变 AppWidget ,显示就显得很重要了,毕竟想在 Android 制胜的还是 UI 战争和用户体验~


Android 在 1.5 之后就取消了android:updatePeriodMillis="**" 的属性,有的说是30分钟,无从考证,除非看源代码,后续弄 ...


想要动态更新还是要自己写 Service 或者触发事件用 Receiver 更新都是可行的,后面文章会介绍 AppWidget 动态更新相关,现在比较澎湃想写关于 RemoteViews 在更新 AppWidget 中存在的问题!-。-,跟题目描述一样,我们都知道 AppWidget 更新是用 RemoteViews 来更新的,但你有没有发现一个问题:当你更新频繁的时候,你是不是用一个 RemoteViews 引用来动态更新,而不是每次在去栈中申请一个变量,如果是,这就对了这篇文章可以解决你的问题!


先说我的情况,为了节省 Android 资源,我用一个 RemoteViews 来动态绑定更新 AppWidget ,又为了动画效果,频繁使用 addView,removeAllViews,导致后面程序使用 Android 设备运行缓慢,出现 ANR, 或者更新图片缓慢... 


RemoteViews 介绍:

A class that describes a view hierarchy that can be displayed in another process. The hierarchy is inflated from a layout resource file, and this class provides some basic operations for modifying the content of the inflated hierarchy.

因为 RemoteViews 并不像 ViewsGroup 那样强大,所以动画效果就限制了, 可以用 layoutAnimation,网络也有介绍,-。- 后续补上


先看网络友友问题:多个 widget 换肤问题响应速度慢 

下面是网友最好提出的回答:AppWidget 内存溢出

[plain] view plaincopyprint?
  1. AppWidget RemoteViews 内存溢出  
  2. 这几天在开发AppWidget时遇到一个奇怪问题:AppWidget上有一个时钟,使用TIME_TICK每分钟更新一次时间,问题是在长时间待机后出现,AppWidget上的时间不能与系统时间同步(ERROR/JavaBinder(1847): !!! FAILED BINDER TRANSACTION !!!)。  
  3. 通过AIDL通信机制的流程找到这个Log出现在android_os_Binder.cpp文件中android_os_BinderProxy_transact函数内部,原本是一个OutOfMemoryError,这是AIDL Client端访问时传递的Parcelable包过大而引起的错误,而RemoteViews就是一个实现了Parcelable接口的类,它作为AppWidget视图组件传递的数据结构通过AIDL在AppWidget应用向Launcher传递。  
  4. 产生这个问题的原因主要在于,RemoteViews的set系列函数在其内部会转换成Action对象存放在在ArrayList<Action>中,而在RemoteViews中没有对这个ArrayList<Action>进行清理的操作。而由于更新频率比较高在设计这个应用时把updateAppWidget()操作放在了Service中,并且每次都在重复使用这个RemoteViews对象,这就造成RemoteViews中的ArrayList<Action>中的数据越来越大,造成内存溢出错误。  
  5. 所以每个RemoteViews对象最好不要重复使用  

至于为什么,为什么单用一个 RemoteViews 就会出现这样的问题呢,ArrayList <Action> 又是什么?


先看RemoteViews Source 里面都是 RemoteViews 的源代码:

[java] view plaincopyprint?
  1. /** 成员变量 */  
  2. // The package name of the package containing the layout resource. (Added to the parcel)  
  3. private String mPackage;  
  4. // The resource ID of the layout file. (Added to the parcel)  
  5. private int mLayoutId;  
  6. // An array of actions to perform on the view tree once it has been inflated  
  7. private ArrayList<Action> mActions; // 要处理的 Action  


看看 AddAction 方法, 其实就是 ArrayList 搞的鬼!

[java] view plaincopyprint?
  1. private void addAction(Action a) {  
  2.         if (mActions == null) {  
  3.              mActions = new ArrayList<Action>();  
  4.         }  
  5.          mActions.add(a);  
  6.  }  

看看 RemoteViews 的构造方法,列比较常用:

[java] view plaincopyprint?
  1. public RemoteViews(String packageName, int layoutId) {  
  2.         mPackage = packageName;  
  3.         mLayoutId = layoutId;  
  4. }  

看到把,他并没有初始化 ArrayList,也就是说你用同一对象的时候, ArrayList 底层引用还是同一个,所以会导致 ArrayList 不d断膨胀,最后内存溢出!至于为什么是同一个 ArrayList ,自己面壁一下!


而当你频繁使用 addView,removeAllViews 来实现动画,我们来看看这个代码:

[java] view plaincopyprint?
  1. public void addView(int viewId, RemoteViews nestedView) {  
  2.          addAction(new ViewGroupAction(viewId, nestedView));  
  3. }  
[java] view plaincopyprint?
  1. public void removeAllViews(int viewId) {  
  2.          addAction(new ViewGroupAction(viewId, null));  
  3. }  


都是调用到AddAction,并且每次 更新 AppWiget 的时候都会引起 GC(垃圾回收)工作,GC工作是很耗资源的,并且也需要事件,所以当你是线程在工作,源源不断的执行命令,又要等到 Android 设备反应,期间慢就正常了!


下面是自己的实验:使用单一 RemoteView 和每次新生成一个的区别




可以看到对比,GC 工作多么频繁啊~ 资源资源啊 -。-

下一篇补上 动态更新 Widget ...

0 0
原创粉丝点击