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 内存溢出
AppWidget RemoteViews 内存溢出 这几天在开发AppWidget时遇到一个奇怪问题:AppWidget上有一个时钟,使用TIME_TICK每分钟更新一次时间,问题是在长时间待机后出现,AppWidget上的时间不能与系统时间同步(ERROR/JavaBinder(1847): !!! FAILED BINDER TRANSACTION !!!)。 通过AIDL通信机制的流程找到这个Log出现在android_os_Binder.cpp文件中android_os_BinderProxy_transact函数内部,原本是一个OutOfMemoryError,这是AIDL Client端访问时传递的Parcelable包过大而引起的错误,而RemoteViews就是一个实现了Parcelable接口的类,它作为AppWidget视图组件传递的数据结构通过AIDL在AppWidget应用向Launcher传递。 产生这个问题的原因主要在于,RemoteViews的set系列函数在其内部会转换成Action对象存放在在ArrayList<Action>中,而在RemoteViews中没有对这个ArrayList<Action>进行清理的操作。而由于更新频率比较高在设计这个应用时把updateAppWidget()操作放在了Service中,并且每次都在重复使用这个RemoteViews对象,这就造成RemoteViews中的ArrayList<Action>中的数据越来越大,造成内存溢出错误。 所以每个RemoteViews对象最好不要重复使用
至于为什么,为什么单用一个 RemoteViews 就会出现这样的问题呢,ArrayList <Action> 又是什么?
先看RemoteViews Source 里面都是 RemoteViews 的源代码:
/** 成员变量 */ // The package name of the package containing the layout resource. (Added to the parcel) private String mPackage; // The resource ID of the layout file. (Added to the parcel) private int mLayoutId; // An array of actions to perform on the view tree once it has been inflated private ArrayList<Action> mActions; // 要处理的 Action
看看 AddAction 方法, 其实就是 ArrayList 搞的鬼!
private void addAction(Action a) { if (mActions == null) { mActions = new ArrayList<Action>(); } mActions.add(a); }
看看 RemoteViews 的构造方法,列比较常用:
public RemoteViews(String packageName, int layoutId) { mPackage = packageName; mLayoutId = layoutId; }
看到把,他并没有初始化 ArrayList,也就是说你用同一对象的时候, ArrayList 底层引用还是同一个,所以会导致 ArrayList 不d断膨胀,最后内存溢出!至于为什么是同一个 ArrayList ,自己面壁一下!
而当你频繁使用 addView,removeAllViews 来实现动画,我们来看看这个代码:
public void addView(int viewId, RemoteViews nestedView) { addAction(new ViewGroupAction(viewId, nestedView)); }
public void removeAllViews(int viewId) { addAction(new ViewGroupAction(viewId, null)); }
都是调用到AddAction,并且每次 更新 AppWiget 的时候都会引起 GC(垃圾回收)工作,GC工作是很耗资源的,并且也需要事件,所以当你是线程在工作,源源不断的执行命令,又要等到 Android 设备反应,期间慢就正常了!
下面是自己的实验:使用单一 RemoteView 和每次新生成一个的区别
可以看到对比,GC 工作多么频繁啊~ 资源资源啊 -。-
下一篇补上 动态更新 Widget ...