安卓小部件(APPWidget)刷新源码解析一列表

一、刷新流程

1、system_process 发送广播
2、应用widget收到广播,执行一系列的业务逻辑后,调用AppWidgetManager的updateAppWidget()方法
3、AppWidgetManager 通过AIDL 通知 system_process更新,system_process收到回调后做一些列操作,回调host 进程
4、host 进程绑定service,回调应用进程onDataSetChanged,应用进程修改数据
5、host 进程根据remoteView 更新视图

二、详情刷新流程

list widget 更新 完整 -2-.png
1、system_process 发送广播
更新广播的action 为android.appwidget.action.APPWIDGET_UPDATE
2、调用 notifyAppWidgetViewDataChanged更新
调用AppWidgetManager.notifyAppWidgetViewDataChanged(),在此之前一般都会设置setRemoteAdapter
3、notifyAppWidgetViewDataChanged()
这里通过AIDL跨进程技术调用system_progress进程的AppWidgetServiceImpl对象。
3.1、enforceCallFromPackage
安全性校验,确定请求的包命和uid 是一致的
3.2、ensureGroupStateLoadedLocked
若已经加载过了则return,若没有加载过则根据uid 获取widgetProvider(过滤带刷新action 的广播),根据uid 获取相应的配置文件,根据配置文件设置widget,并绑定相应的host。放入mWidgets中。
3.3、lookupWidgetLocked
根据widgetId在mWidgets 找到对应的widget,通过uid验证权限
3.4、scheduleNotifyAppWidgetViewDataChanged 发送DataChange 的message
4、Looper 执行DataChange Message 调用 handleNotifyAppWidgetViewDataChanged
- 通过AIDL 回调 AppWidgetHost 的 viewDataChanged 方法
- 若viewDataChanged回调异常,则重新bindService,连接成功后回调 RemoteViewService 的 onDataSetChangedAsync方法。
5、AppWidgetHost 收到 viewDataChanged 回调,发DataChange Message
6、Looper 执行DataChange Message 调用viewDataChanged找到对应的 AppWidgetHostView 并执行刷新
6.1、获取AppWidgetHostView 的adapter 并执行notifyDataSetChanged
6.2、RemoteViewAdapter.notifyDataSetChanged 取消解绑message 发送dataChangeMessage
7、处理dataChangeMessage
7.1、enqueueDeferredUnbindServiceMessage 移除解绑message 并设置5s 后解绑
7.2、sendNotifyDataSetChange 回调RemoteViewFactory的onDataSetChanged ,这块通常用来获取新的数据
7.3、adapter.updateRemoteViews更新item
7.3.1、回调RemoteViewFactory的getViewAt 获取item 的RemoteView
7.3.2、发送remoteView load Message
8、调用notifyOnRemoteViewsLoaded 通过position找到对应的item 进行加载
8.1、调用applyRemoteViews 将RemoteView 的action 进行应用

三、详细流程

1、收到广播,更新广播的action 为android.appwidget.action.APPWIDGET_UPDATE。

// AppWidgetProvider 继承于广播,system_process发送广播是会回调onReceive方法
// 如果是更新广播的话会回调onUpdate()方法
public class AppWidgetProvider extends BroadcastReceiver {
   ...
   public void onReceive(Context context, Intent intent) {
    // Protect against rogue update broadcasts (not really a security issue,
    // just filter bad broacasts out so subclasses are less likely to crash).
    String action = intent.getAction();
    if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
        Bundle extras = intent.getExtras();
        if (extras != null) {
            int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
            if (appWidgetIds != null && appWidgetIds.length > 0) {
                this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
            }
        }
    }
   ...
}

2、应用widget收到广播,准备数据构建RemoteView,并调用AppWidgetManager的notifyAppWidgetViewDataChanged()方法

public abstract class TestWidgetProvider extends AppWidgetProvider {
    ...
    /**
    *onReceive会回调该方法
    *
    **/
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds{
    // AppWidgetProvider 这里通常会设置new RemoteView,并设置,可设置点击时间、文字、图片等最后调用
    // appWidgetManager.updateAppWidget()
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        for (int widgetId : appWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),         
            R.layout.widget_test);
             ...
            appWidgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.lv_list_test);
    }
    }
  ...
}

3、notifyAppWidgetViewDataChanged()

这里通过AIDL跨进程技术调用system_progress进程的AppWidgetServiceImpl对象。

public class AppWidgetManager {
    ...
    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
        if (mService == null) {
            return;
        }
        try {
            mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
   ...
}

/***************************************************************/
class AppWidgetServiceImpl {
…
    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.
        // AppWidgetServiceImpl 运行在system_process ,包名为字符串传入,
        // 安全性校验,确定请求的包命和uid 是一致的
        mSecurityPolicy.enforceCallFromPackage(callingPackage);


        if (appWidgetIds == null || appWidgetIds.length == 0) {
            return;
        }


        synchronized (mLock) {
            // 是否解锁状态,处于解锁状态,若第一次加载则构建widget,后面会详细解析
            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.
            //根据widgetId在mWidgets 找到对应的widget,通过uid验证权限
            Widget widget = lookupWidgetLocked(appWidgetId,
                    Binder.getCallingUid(), callingPackage);

            if (widget != null) {
                scheduleNotifyAppWidgetViewDataChanged(widget, viewId);
            }
        }
    }
...
}

3.1、enforceCallFromPackage()

安全性校验,确定请求的包命和uid 是一致的

public void enforceCallFromPackage(String packageName) {
    mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
}
@Deprecated
public void checkPackage(int uid, @NonNull String packageName) {
    try {
        // 检查请求的 uid 和 packageName  是否一致
        if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
            throw new SecurityException(
                    "Package " + packageName + " does not belong to " + uid);
        }
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

3.2、ensureGroupStateLoadedLocked

若已经加载过了则return,若没有加载过则根据uid 获取widgetProvider(过滤带刷新action 的广播),根据uid 获取相应的配置文件,根据配置文件设置widget,并绑定相应的host。放入mWidgets中

class AppWidgetServiceImpl{
    private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
        // 判断该应用是否处在解锁状态,设备锁
        if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
            throw new IllegalStateException(
                "User " + userId + " must be unlocked for widgets to be available");
        }
        // 判断该应用文件配置是否处在解锁状态
        if (enforceUserUnlockingOrUnlocked && isProfileWithLockedParent(userId)) {
            throw new IllegalStateException(
                "Profile " + userId + " must have unlocked parent");
        }    
        // 获取能用的配置配置id
        final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);


        // 查看是否有未加载的user
        // Careful lad, we may have already loaded the state for some
        // group members, so check before loading and read only the
        // state for the new member(s).
        int newMemberCount = 0;
        final int profileIdCount = profileIds.length;
        for (int i = 0; i < profileIdCount; i++) {
            final int profileId = profileIds[i];
            // >=0代表已经加载过,标记数组
            if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
                profileIds[i] = LOADED_PROFILE_ID;
            } else {
                newMemberCount++;
            }
        }
        // 没有新加的 便会return
        if (newMemberCount <= 0) {
            return;
        }    
         // 构建新增加的ProfileId 数组,后续通常在第一次加载的时候执行
        int newMemberIndex = 0;
        final int[] newProfileIds = new int[newMemberCount];
        for (int i = 0; i < profileIdCount; i++) {
            final int profileId = profileIds[i];
            if (profileId != LOADED_PROFILE_ID) {
                mLoadedUserIds.put(profileId, profileId);
                newProfileIds[newMemberIndex] = profileId;
                newMemberIndex++;
            }
        }
        // 清除provider 和 host 的tag 设置为 TAG_UNDEFINED
        clearProvidersAndHostsTagsLocked();
         // 根据加载ProfileId 获取系统 ResolveInfo 列表, 根据ResolveInfo 构建
         provider;
        loadGroupWidgetProvidersLocked(newProfileIds);    
        // 从系统配置文件/data/system/users/0/appwidgets.xml 加载状态、
        loadGroupStateLocked(newProfileIds);
    }
}

3.3、lookupWidgetLocked

根据widgetId在mWidgets 找到对应的widget,通过uid验证权限

class AppWidgetServiceImpl{
    private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
        final int N = mWidgets.size();
        for (int i = 0; i < N; i++) {
            Widget widget = mWidgets.get(i);
            if (widget.appWidgetId == appWidgetId
                    && mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) {
                return widget;
            }
        }
        return null;
    }
}

3.4、scheduleNotifyAppWidgetViewDataChanged 发送DataChange 的message

class AppWidgetServiceImpl{
    ...
    private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
        if (viewId == ID_VIEWS_UPDATE || viewId == ID_PROVIDER_CHANGED) {
            // A view id should never collide with these constants but a developer can call this
            // method with a wrong id. In that case, ignore the call.
            return;
        }
        long requestId = UPDATE_COUNTER.incrementAndGet();
        if (widget != null) {
            widget.updateSequenceNos.put(viewId, requestId);
        }
        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.arg3 = requestId;
        args.argi1 = widget.appWidgetId;
        args.argi2 = viewId;

        mCallbackHandler.obtainMessage(
            CallbackHandler.MSG_NOTIFY_VIEW_DATA_CHANGED,
            args).sendToTarget();
    }
...
}

4、Looper 执行DataChange Message 调用 handleNotifyAppWidgetViewDataChanged

class AppWidgetServiceImpl{
    private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
        int appWidgetId, int viewId, long requestId) {
        try {
            //通过AIDL 回调 AppWidgetHost 的 viewDataChanged 方法
            callbacks.viewDataChanged(appWidgetId, viewId);
            host.lastWidgetUpdateSequenceNo = requestId;
        } 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
        // 回调失败了会重新绑定service ,连接成功后RemoteViewsFactory.onDataSetChanged()进行刷新
        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));
                }
            }
        }
    }
}

5、AppWidgetHost 收到 viewDataChanged 回调,发DataChange Message

class AppWidgetHost {
    ...
    static clase Callbacks{
        public void viewDataChanged(int appWidgetId, int viewId) {
            Handler handler = mWeakHandler.get();
            if (handler == null) {
                return;
            }
            Message msg = handler.obtainMessage(HANDLE_VIEW_DATA_CHANGED,
            appWidgetId, viewId);
            msg.sendToTarget();
        }
    }
   ...
}

6、Looper 执行DataChange Message 调用viewDataChanged找到对应的 AppWidgetHostView 并执行刷新

public class AppWidgetHost {
    ...
    void viewDataChanged(int appWidgetId, int viewId) {
        AppWidgetHostView v;
        synchronized (mViews) {
            v = mViews.get(appWidgetId);
        }
        if (v != null) {
            v.viewDataChanged(viewId);
        }
     ...
}

6.1、获取AppWidgetHostView 的adapter 并执行notifyDataSetChanged

public class AppWidgetHostView{
     ...
     void viewDataChanged(int viewId) {
        View v = findViewById(viewId);
        if ((v != null) && (v instanceof AdapterView)) {
        AdapterView adapterView = (AdapterView) v;
        Adapter adapter = adapterView.getAdapter();
        if (adapter instanceof BaseAdapter) {
            BaseAdapter baseAdapter = (BaseAdapter) adapter;
            baseAdapter.notifyDataSetChanged();
        }  else if (adapter == null && adapterView instanceof RemoteAdapterConnectionCallback) {
            // If the adapter is null, it may mean that the RemoteViewsAapter has not yet
            // connected to its associated service, and hence the adapter hasn't been set.
            // In this case, we need to defer the notify call until it has been set.
             adapter 为空,说明还没有连接成功,阻挡数据过滤
            ((RemoteAdapterConnectionCallback) adapterView).deferNotifyDataSetChanged();
        }
    }
...
}

6.2、RemoteViewAdapter.notifyDataSetChanged 取消解绑message 发送dataChangeMessage

public class RemoteViewsAdapter {
    ...
    public void notifyDataSetChanged() {
        mServiceHandler.removeMessages(MSG_UNBIND_SERVICE);
        mServiceHandler.sendEmptyMessage(MSG_NOTIFY_DATA_SET_CHANGED);
    ...
    }
}

7、处理dataChangeMessage

private static class RemoteServiceHandler{    
    …
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
          ...
          case MSG_NOTIFY_DATA_SET_CHANGED: {
            //移除解绑message 并设置5s 后解绑,后面详讲
            enqueueDeferredUnbindServiceMessage();
            if (adapter == null) {
                return;
            }
            if (mRemoteViewsFactory == null) {
                //设置延迟通知
                mNotifyDataSetChangedPending = true;
                adapter.requestBindService();
                return;
            }
            // 回调RemoteViewFactory的onDataSetChanged ,这块通常用来获取新的数据,后面详将
            if (!sendNotifyDataSetChange(true)) {
                return;
            }

            // Flush the cache so that we can reload new items from the service
            synchronized (adapter.mCache) {
                adapter.mCache.reset();
            }

            // Re-request the new metadata (only after the notification to the factory)
            // 更新缓存
            adapter.updateTemporaryMetaData(mRemoteViewsFactory);
            int newCount;
            int[] visibleWindow;
            synchronized (adapter.mCache.getTemporaryMetaData()) {
                newCount = adapter.mCache.getTemporaryMetaData().count;
                visibleWindow = adapter.getVisibleWindow(newCount);
            }

            // Pre-load (our best guess of) the views which are currently visible in the
            // AdapterView. This mitigates flashing and flickering of loading views when a
            // widget notifies that its data has changed.
            //更新每一个item,后面详讲
            for (int position : visibleWindow) {
                // Because temporary meta data is only ever modified from this thread
                // (ie. mWorkerThread), it is safe to assume that count is a valid
                // representation.
                if (position < newCount) {
                    adapter.updateRemoteViews(mRemoteViewsFactory, position, false);
                }
            }

            // Propagate the notification back to the base adapter
            adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_COMMIT_METADATA);
            adapter.mMainHandler.sendEmptyMessage(
                MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED);
            return;
        }
         }
    }
}

7.1、enqueueDeferredUnbindServiceMessage 移除解绑message 并设置5s 后解绑

private static class RemoteServiceHandler{
…
    private void enqueueDeferredUnbindServiceMessage() {
    removeMessages(MSG_UNBIND_SERVICE);
    sendEmptyMessageDelayed(MSG_UNBIND_SERVICE, UNBIND_SERVICE_DELAY);
    }

    @Override
    public void handleMessage(Message msg) {
        RemoteViewsAdapter adapter = mAdapter.get();
        switch (msg.what) {
    ...
        switch (msg.what) {
            case MSG_UNBIND_SERVICE: {
            unbindNow();
            return;
        }    
    }

   protected void unbindNow() {
    if (mBindRequested) {
        mBindRequested = false;
        mContext.unbindService(this);
    }
    mRemoteViewsFactory = null;
    }
...
}

7.2、sendNotifyDataSetChange 回调RemoteViewFactory的onDataSetChanged ,这块通常用来获取新的数据

private boolean sendNotifyDataSetChange(boolean always) {
    try {
        if (always || !mRemoteViewsFactory.isCreated()) {
            mRemoteViewsFactory.onDataSetChanged();
        }
        return true;
    } catch (RemoteException | RuntimeException e) {
        Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
        return false;
    }
}

7.3、adapter.updateRemoteViews更新item

public class RemoteViewsAdapter {
    ...
    @WorkerThread
    private void updateRemoteViews(IRemoteViewsFactory factory, int position,
        boolean notifyWhenLoaded) {
        // Load the item information from the remote service
        final RemoteViews remoteViews;
        final long itemId;
        try {
            // 获取item 对应的RemoteView
            remoteViews = factory.getViewAt(position);
            itemId = factory.getItemId(position);

            if (remoteViews == null) {
                throw new RuntimeException("Null remoteViews");
            }
        } catch (RemoteException | RuntimeException e) {
            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
            // Return early to prevent additional work in re-centering the view cache, and
            // swapping from the loading view
            return;
        }

        if (remoteViews.mApplication != null) {
        // We keep track of last application info. This helps when all the remoteViews have
        // same applicationInfo, which should be the case for a typical adapter. But if every
        // view has different application info, there will not be any optimization.
            if (mLastRemoteViewAppInfo != null
                    && remoteViews.hasSameAppInfo(mLastRemoteViewAppInfo)) {
                // We should probably also update the remoteViews for nested ViewActions.
                // Hopefully, RemoteViews in an adapter would be less complicated.
                remoteViews.mApplication = mLastRemoteViewAppInfo;
            } else {
            mLastRemoteViewAppInfo = remoteViews.mApplication;
            }
        }


        int layoutId = remoteViews.getLayoutId();
        RemoteViewsMetaData metaData = mCache.getMetaData();
        boolean viewTypeInRange;
        int cacheCount;
        synchronized (metaData) {
            viewTypeInRange = metaData.isViewTypeInRange(layoutId);
            cacheCount = mCache.mMetaData.count;
        }
        synchronized (mCache) {
            if (viewTypeInRange) {
                int[] visibleWindow = getVisibleWindow(cacheCount);
                // Cache the RemoteViews we loaded
                mCache.insert(position, remoteViews, itemId, visibleWindow);
            if (notifyWhenLoaded) {
                // Notify all the views that we have previously returned for this index that
                // there is new data for it.
                Message.obtain(mMainHandler, MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED, position, 0,
                        remoteViews).sendToTarget();
            }
        } else {
            // We need to log an error here, as the the view type count specified by the
            // factory is less than the number of view types returned. We don't return this
            // view to the AdapterView, as this will cause an exception in the hosting process,
            // which contains the associated AdapterView.
            Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " +
                    " indicated by getViewTypeCount() ");
        }
    }
 ...
}

8、调用notifyOnRemoteViewsLoaded 通过position找到对应的item 进行加载

private class RemoteViewsFrameLayoutRefSet{
    /**
     * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
     * the associated RemoteViews has loaded.
     */
    public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
    if (view == null) return;

        // Remove this set from the original mapping
        final LinkedList refs = removeReturnOld(position);
        if (refs != null) {
            // Notify all the references for that position of the newly loaded RemoteViews
            for (final RemoteViewsFrameLayout ref : refs) {
                ref.onRemoteViewsLoaded(view, mRemoteViewsInteractionHandler, true);
            }
        }
    }
}

8.1、调用onRemoteViewsLoaded 将RemoteView 的action 进行应用

static class RemoteViewsFrameLayout extends AppWidgetHostView {
    /**
     * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
     * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
     *             successfully.
     * @param forceApplyAsync when true, the host will always try to inflate the view
     *                        asynchronously (for eg, when we are already showing the loading    
     *                        view)
     */
    public void onRemoteViewsLoaded(RemoteViews view, InteractionHandler handler,
        boolean forceApplyAsync) {
        setInteractionHandler(handler);
        applyRemoteViews(view, forceApplyAsync || ((view != null) && view.prefersAsyncApply()));
    }

}

你可能感兴趣的:(安卓小部件(APPWidget)刷新源码解析一列表)