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,小细节备忘)