android桌面小部件appwidget使用ListView或者StackView如何刷新
android的桌面小部件使用ListView和StackView刷新数据,找了半天网上了没个说明白的,自己研究了一上午,大概明白了小部件的整体机制。
如何实现StackView的小部件使用RemoteService,这些网上一堆,可以找一下。
如何刷新adapter中的数据先把结果贴上来。
使用如下方式,StackView的远端Adapter就会调用onDataSetChanged()
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(entry.widgetId,R.id.appwidget_stack_view);
源码分析:
RemoteView是跑在系统进程中的,并没有在我们的App中,必须使用进程间通信才能通知真正的View来刷新。RemoteViews这个类只是个数据传输类同时还有些工具函数apply什么的,app调用upateWidget将数据传输到远端Service,之后远端就可以处理了。
OK,这里分析通知刷新,就不用反射一些方法了,直接远端调用onDataSetChanged()就可以了,看一下android是怎么调用的。
正文:
使用notifyAppWidgetViewDataChanged()方法,实际实现是在系统Service,AppWidgetServiceImpl.java中的notifyAppWidgetViewDataChanged()
@Override public void notifyAppWidgetViewDataChanged(String callingPackage, int[] appWidgetIds, int viewId) { final int userId = UserHandle.getCallingUserId(); if (DEBUG) { Slog.i(TAG, "notifyAppWidgetViewDataChanged() " + userId); } // Make sure the package runs under the caller uid. mSecurityPolicy.enforceCallFromPackage(callingPackage); if (appWidgetIds == null || appWidgetIds.length == 0) { return; } synchronized (mLock) { ensureGroupStateLoadedLocked(userId); final int N = appWidgetIds.length; for (int i = 0; i < N; i++) { final int appWidgetId = appWidgetIds[i]; // NOTE: The lookup is enforcing security across users by making // sure the caller can only access widgets it hosts or provides. Widget widget = lookupWidgetLocked(appWidgetId, Binder.getCallingUid(), callingPackage); if (widget != null) { scheduleNotifyAppWidgetViewDataChanged(widget, viewId); } } } }
在这个方法中查找到要刷新的widget,然后scheduleNotifyAppWidgetViewDataChanged(widget, viewId);看一下这个方法。
private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) { if (widget == null || widget.host == null || widget.host.zombie || widget.host.callbacks == null || widget.provider == null || widget.provider.zombie) { return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = widget.host; args.arg2 = widget.host.callbacks; args.argi1 = widget.appWidgetId; args.argi2 = viewId; mCallbackHandler.obtainMessage( CallbackHandler.MSG_NOTIFY_VIEW_DATA_CHANGED, args).sendToTarget(); }
将查找到的widget的一些成员变量,通过Handler发送出去了。
case MSG_NOTIFY_VIEW_DATA_CHANGED: { SomeArgs args = (SomeArgs) message.obj; Host host = (Host) args.arg1; IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; final int appWidgetId = args.argi1; final int viewId = args.argi2; args.recycle(); handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId); } break;
找到Handler处理的部分,调用handleNotifyAppWidgetViewDataChanged来处理。
case MSG_NOTIFY_VIEW_DATA_CHANGED: { SomeArgs args = (SomeArgs) message.obj; Host host = (Host) args.arg1; IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; final int appWidgetId = args.argi1; final int viewId = args.argi2; args.recycle(); handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId); } break;
最后绑定AppWidget的RemoteService这个Service就是为Widget的提供Adapter的Service,只要绑定成功就就回调方法onDataSetChangedAsync()代码如下
private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, int viewId) { try { callbacks.viewDataChanged(appWidgetId, viewId); } catch (RemoteException re) { // It failed; remove the callback. No need to prune because // we know that this host is still referenced by this instance. callbacks = null; } // If the host is unavailable, then we call the associated // RemoteViewsFactory.onDataSetChanged() directly synchronized (mLock) { if (callbacks == null) { host.callbacks = null; Set> keys = mRemoteViewsServicesAppWidgets.keySet(); for (Pair key : keys) { if (mRemoteViewsServicesAppWidgets.get(key).contains(appWidgetId)) { final ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IRemoteViewsFactory cb = IRemoteViewsFactory.Stub .asInterface(service); try { cb.onDataSetChangedAsync(); } catch (RemoteException e) { Slog.e(TAG, "Error calling onDataSetChangedAsync()", e); } mContext.unbindService(this); } @Override public void onServiceDisconnected(android.content.ComponentName name) { // Do nothing } }; final int userId = UserHandle.getUserId(key.first); Intent intent = key.second.getIntent(); // Bind to the service and call onDataSetChanged() bindService(intent, connection, new UserHandle(userId)); } } } } }
在RemoteViewsServices.java中
public synchronized void onDataSetChangedAsync() { onDataSetChanged(); }
回调了onDataSetChanged();刷新了数据。之后就是刷新了,没有继续分析stackView源码。
总结:appWidget,使用进程间通信的方式,来刷新其他进程的View,系统Service (AppWidgetServiceImpl)处理View更新,更新时将RemoteVeiws传输到系统Service中,通过WidgetId获得对应的实际View,调用RmoteView的apply,其实就是将RemoteView中的数据,使用反射的方式,应用到实际View的对应方法中。完成实际View的更新。
更多相关文章
- android sharedpreference保存boolean,int,float,long,String和图片
- 《Android开发艺术探索》笔记(五)
- androidP 系统集成时发现部分应用初次打开时提示此应用专为低版
- Android之AlertDialog的基础使用
- 【Android】网络通讯
- Android(安卓)native CursorWindow数据保存原理
- Mediaplayer中通过create函数获取资源时PreareAsync问题
- Android内存监测工具DDMS-->Heap
- Android(安卓)自定义View - 网络加载 ProgressBar 加载与隐藏