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

一、刷新流程

1撒汉、system_process 發(fā)送廣播
2己单、應(yīng)用widget收到廣播,執(zhí)行一系列的業(yè)務(wù)邏輯后塞淹,調(diào)用AppWidgetManager的updateAppWidget()方法
3、AppWidgetManager 通過(guò)AIDL 通知 system_process更新栖茉,system_process收到回調(diào)后做一些列操作篮绿,回調(diào)host 進(jìn)程
4、host 進(jìn)程綁定service,回調(diào)應(yīng)用進(jìn)程onDataSetChanged,應(yīng)用進(jìn)程修改數(shù)據(jù)
5吕漂、host 進(jìn)程根據(jù)remoteView 更新視圖

二亲配、詳情刷新流程

list widget 更新 完整 -2-.png
1、system_process 發(fā)送廣播
更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE
2惶凝、調(diào)用 notifyAppWidgetViewDataChanged更新
調(diào)用AppWidgetManager.notifyAppWidgetViewDataChanged(),在此之前一般都會(huì)設(shè)置setRemoteAdapter
3吼虎、notifyAppWidgetViewDataChanged()
這里通過(guò)AIDL跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對(duì)象。
3.1梨睁、enforceCallFromPackage
安全性校驗(yàn)鲸睛,確定請(qǐng)求的包命和uid 是一致的
3.2、ensureGroupStateLoadedLocked
若已經(jīng)加載過(guò)了則return,若沒有加載過(guò)則根據(jù)uid 獲取widgetProvider(過(guò)濾帶刷新action 的廣播)坡贺,根據(jù)uid 獲取相應(yīng)的配置文件官辈,根據(jù)配置文件設(shè)置widget,并綁定相應(yīng)的host遍坟。放入mWidgets中拳亿。
3.3、lookupWidgetLocked
根據(jù)widgetId在mWidgets 找到對(duì)應(yīng)的widget,通過(guò)uid驗(yàn)證權(quán)限
3.4愿伴、scheduleNotifyAppWidgetViewDataChanged 發(fā)送DataChange 的message
4肺魁、Looper 執(zhí)行DataChange Message 調(diào)用 handleNotifyAppWidgetViewDataChanged
- 通過(guò)AIDL 回調(diào) AppWidgetHost 的 viewDataChanged 方法
- 若viewDataChanged回調(diào)異常,則重新bindService,連接成功后回調(diào) RemoteViewService 的 onDataSetChangedAsync方法隔节。
5鹅经、AppWidgetHost 收到 viewDataChanged 回調(diào),發(fā)DataChange Message
6怎诫、Looper 執(zhí)行DataChange Message 調(diào)用viewDataChanged找到對(duì)應(yīng)的 AppWidgetHostView 并執(zhí)行刷新
6.1瘾晃、獲取AppWidgetHostView 的adapter 并執(zhí)行notifyDataSetChanged
6.2、RemoteViewAdapter.notifyDataSetChanged 取消解綁message 發(fā)送dataChangeMessage
7幻妓、處理dataChangeMessage
7.1蹦误、enqueueDeferredUnbindServiceMessage 移除解綁message 并設(shè)置5s 后解綁
7.2、sendNotifyDataSetChange 回調(diào)RemoteViewFactory的onDataSetChanged ,這塊通常用來(lái)獲取新的數(shù)據(jù)
7.3、adapter.updateRemoteViews更新item
7.3.1强胰、回調(diào)RemoteViewFactory的getViewAt 獲取item 的RemoteView
7.3.2舱沧、發(fā)送remoteView load Message
8、調(diào)用notifyOnRemoteViewsLoaded 通過(guò)position找到對(duì)應(yīng)的item 進(jìn)行加載
8.1偶洋、調(diào)用applyRemoteViews 將RemoteView 的action 進(jìn)行應(yīng)用

三熟吏、詳細(xì)流程

1、收到廣播涡真,更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE分俯。

// AppWidgetProvider 繼承于廣播肾筐,system_process發(fā)送廣播是會(huì)回調(diào)onReceive方法
// 如果是更新廣播的話會(huì)回調(diào)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哆料、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteView,并調(diào)用AppWidgetManager的notifyAppWidgetViewDataChanged()方法

public abstract class TestWidgetProvider extends AppWidgetProvider {
    ...
    /**
    *onReceive會(huì)回調(diào)該方法
    *
    **/
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds{
    // AppWidgetProvider 這里通常會(huì)設(shè)置new RemoteView,并設(shè)置吗铐,可設(shè)置點(diǎn)擊時(shí)間东亦、文字、圖片等最后調(diào)用
    // 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()

這里通過(guò)AIDL跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對(duì)象典阵。

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 運(yùn)行在system_process ,包名為字符串傳入镊逝,
        // 安全性校驗(yàn)壮啊,確定請(qǐng)求的包命和uid 是一致的
        mSecurityPolicy.enforceCallFromPackage(callingPackage);


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


        synchronized (mLock) {
            // 是否解鎖狀態(tài),處于解鎖狀態(tài)撑蒜,若第一次加載則構(gòu)建widget歹啼,后面會(huì)詳細(xì)解析
            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.
            //根據(jù)widgetId在mWidgets 找到對(duì)應(yīng)的widget,通過(guò)uid驗(yàn)證權(quán)限
            Widget widget = lookupWidgetLocked(appWidgetId,
                    Binder.getCallingUid(), callingPackage);

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

3.1、enforceCallFromPackage()

安全性校驗(yàn)座菠,確定請(qǐng)求的包命和uid 是一致的

public void enforceCallFromPackage(String packageName) {
    mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
}
@Deprecated
public void checkPackage(int uid, @NonNull String packageName) {
    try {
        // 檢查請(qǐng)求的 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

若已經(jīng)加載過(guò)了則return,若沒有加載過(guò)則根據(jù)uid 獲取widgetProvider(過(guò)濾帶刷新action 的廣播),根據(jù)uid 獲取相應(yīng)的配置文件浴滴,根據(jù)配置文件設(shè)置widget拓萌,并綁定相應(yīng)的host。放入mWidgets中

class AppWidgetServiceImpl{
    private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
        // 判斷該應(yīng)用是否處在解鎖狀態(tài)升略,設(shè)備鎖
        if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
            throw new IllegalStateException(
                "User " + userId + " must be unlocked for widgets to be available");
        }
        // 判斷該應(yīng)用文件配置是否處在解鎖狀態(tài)
        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代表已經(jīng)加載過(guò),標(biāo)記數(shù)組
            if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
                profileIds[i] = LOADED_PROFILE_ID;
            } else {
                newMemberCount++;
            }
        }
        // 沒有新加的 便會(huì)return
        if (newMemberCount <= 0) {
            return;
        }    
         // 構(gòu)建新增加的ProfileId 數(shù)組,后續(xù)通常在第一次加載的時(shí)候執(zhí)行
        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 設(shè)置為 TAG_UNDEFINED
        clearProvidersAndHostsTagsLocked();
         // 根據(jù)加載ProfileId 獲取系統(tǒng) ResolveInfo 列表微王, 根據(jù)ResolveInfo 構(gòu)建
         provider;
        loadGroupWidgetProvidersLocked(newProfileIds);    
        // 從系統(tǒng)配置文件/data/system/users/0/appwidgets.xml 加載狀態(tài)、
        loadGroupStateLocked(newProfileIds);
    }
}

3.3品嚣、lookupWidgetLocked

根據(jù)widgetId在mWidgets 找到對(duì)應(yīng)的widget,通過(guò)uid驗(yàn)證權(quán)限

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 發(fā)送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 執(zhí)行DataChange Message 調(diào)用 handleNotifyAppWidgetViewDataChanged

class AppWidgetServiceImpl{
    private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
        int appWidgetId, int viewId, long requestId) {
        try {
            //通過(guò)AIDL 回調(diào) 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
        // 回調(diào)失敗了會(huì)重新綁定service ,連接成功后RemoteViewsFactory.onDataSetChanged()進(jìn)行刷新
        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));
                }
            }
        }
    }
}

5腰根、AppWidgetHost 收到 viewDataChanged 回調(diào)激才,發(fā)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 執(zhí)行DataChange Message 調(diào)用viewDataChanged找到對(duì)應(yīng)的 AppWidgetHostView 并執(zhí)行刷新

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 并執(zhí)行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 為空瘸恼,說(shuō)明還沒有連接成功劣挫,阻擋數(shù)據(jù)過(guò)濾
            ((RemoteAdapterConnectionCallback) adapterView).deferNotifyDataSetChanged();
        }
    }
...
}

6.2、RemoteViewAdapter.notifyDataSetChanged 取消解綁message 發(fā)送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 并設(shè)置5s 后解綁压固,后面詳講
            enqueueDeferredUnbindServiceMessage();
            if (adapter == null) {
                return;
            }
            if (mRemoteViewsFactory == null) {
                //設(shè)置延遲通知
                mNotifyDataSetChangedPending = true;
                adapter.requestBindService();
                return;
            }
            // 回調(diào)RemoteViewFactory的onDataSetChanged ,這塊通常用來(lái)獲取新的數(shù)據(jù),后面詳將
            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.
            //更新每一個(gè)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 并設(shè)置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 回調(diào)RemoteViewFactory的onDataSetChanged ,這塊通常用來(lái)獲取新的數(shù)據(jù)

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 對(duì)應(yīng)的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愧膀、調(diào)用notifyOnRemoteViewsLoaded 通過(guò)position找到對(duì)應(yīng)的item 進(jìn)行加載

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<RemoteViewsFrameLayout> 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拦键、調(diào)用onRemoteViewsLoaded 將RemoteView 的action 進(jìn)行應(yīng)用

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()));
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市檩淋,隨后出現(xiàn)的幾起案子芬为,更是在濱河造成了極大的恐慌,老刑警劉巖蟀悦,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件媚朦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡日戈,警方通過(guò)查閱死者的電腦和手機(jī)询张,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浙炼,“玉大人份氧,你說(shuō)我怎么就攤上這事」呐。” “怎么了半火?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)季俩。 經(jīng)常有香客問(wèn)我钮糖,道長(zhǎng),這世上最難降的妖魔是什么酌住? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任店归,我火速辦了婚禮,結(jié)果婚禮上酪我,老公的妹妹穿的比我還像新娘消痛。我一直安慰自己,他們只是感情好都哭,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布秩伞。 她就那樣靜靜地躺著逞带,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纱新。 梳的紋絲不亂的頭發(fā)上展氓,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音脸爱,去河邊找鬼遇汞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛簿废,可吹牛的內(nèi)容都是我干的空入。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼族檬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歪赢!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起导梆,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤轨淌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后看尼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盟步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年藏斩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片却盘。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狰域,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黄橘,到底是詐尸還是另有隱情兆览,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布塞关,位于F島的核電站抬探,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏帆赢。R本人自食惡果不足惜小压,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望椰于。 院中可真熱鬧怠益,春花似錦、人聲如沸瘾婿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抢呆,卻和暖如春髓削,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镀娶。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工立膛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梯码。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓宝泵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親轩娶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子儿奶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 一、刷新流程 1鳄抒、system_process 發(fā)送廣播2闯捎、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteVi...
    OpenDreamer閱讀 625評(píng)論 0 1
  • 本文主要從系統(tǒng)層怎樣加載一個(gè)widget分析,不包含怎樣創(chuàng)建一個(gè)含有widget的app许溅。所謂widget瓤鼻,梗概流...
    福爾摩斯春卷閱讀 5,211評(píng)論 3 8
  • 一、引言 作為一個(gè)車機(jī)的Launcher開發(fā)贤重,總結(jié)了下自己認(rèn)為的難點(diǎn)技術(shù):ApppWidget茬祷、拖拽、Remote...
    眼中有碼閱讀 5,210評(píng)論 3 19
  • 轉(zhuǎn)自 1. 什么是Activity? 四大組件之一,一般的,一個(gè)用戶交互界面對(duì)應(yīng)一個(gè)activity setCon...
    joe1632閱讀 1,405評(píng)論 0 7
  • 前幾天整理了Java面試題集合,今天再來(lái)整理下Android相關(guān)的面試題集合.如果你希望能得到最新的消息,可以關(guān)注...
    Boyko閱讀 3,641評(píng)論 8 135