android桌面小部件appwidget使用ListView或者StackView如何刷新

来源:互联网 发布:hadoop能用python吗 编辑:程序博客网 时间:2024/05/13 05:51

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<Pair<Integer, FilterComparison>> keys = mRemoteViewsServicesAppWidgets.keySet();                for (Pair<Integer, FilterComparison> 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的更新。