Launcher2分析-加载Workspace数据和绑定

通过调用LoaderTask#startLoader()数据加载和绑定,该方法在Launcher#oncreate()中被调用:

if (sPausedFromUserAction) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(true, -1);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(true, mWorkspace.getCurrentPage());
            }

其中第二个参数不为-1时,是同步加载,只有桌面正在显示且屏幕发生旋转时才会同步加载。

看startLoader()这个方法源码:

public void startLoader(boolean isLaunching, int synchronousBindPage) {
        synchronized (mLock) {
            if (DEBUG_LOADERS) {
                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
            }

            // Clear any deferred bind-runnables from the synchronized load process
            // We must do this before any loading/binding is scheduled below.
            mDeferredBindRunnables.clear();

            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                // If there is already one running, tell it to stop.
                // also, don't downgrade isLaunching if we're already running
                isLaunching = isLaunching || stopLoaderLocked();
                mLoaderTask = new LoaderTask(mApp, isLaunching);
                if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);//同步加载时调用runBindSynchronousPage,而不是run()
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask);//异步加载,在给线程池运行LoaderTask的run()
                 }
            }
        }
    }


下面来分析异步加载时的数据加载和绑定过程。






数据加载

LauncherModel$LoaderTask#run()

public void run() {
            synchronized (mLock) {
                mIsLoaderTaskRunning = true;
            }

            keep_running: {//一直循环运行
                // Elevate priority when Home launches for the first time to avoid
                // starving at boot time. Staring at a blank home is not cool.
               //... ...

                // First step. Load workspace first, this is necessary since adding of apps from
                // managed profile in all apps is deferred until onResume. See http://b/17336902.
                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                loadAndBindWorkspace();//加载桌面数据和绑定

                if (mStopped) {
                    break keep_running;//在onStop调用后跳出循环
                }

                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
                // settled down.
               //... ...
                waitForIdle();

                // Second step. Load all apps.
                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                loadAndBindAllApps();加载应用列表数据和绑定

                // Restore the default thread priority after we are done loading items
                //... ...
            }


            // Update the saved icons if necessary
            //... ...

            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;

            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
                mIsLoaderTaskRunning = false;
            }
        }
其中loadAndBindWorkspace会调用loadWorkspace()和bindWorkspace(),下面先分析loadWorkspace()。

LauncherModel$LoaderTask#loadWorkspace():

1. mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0, false);

从xml文件填充数据库favorite表,如果没有填充过。默认是default_workspace.xml,源码如下:

/**
     * @param workspaceResId that can be 0 to use default or non-zero for specific resource
     */
    synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId,
            boolean overridePrevious) {
        String spKey = LauncherApplication.getSharedPreferencesKey();
        SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
//如果manifest中没有写上对应的键值对,那么resId返回0
        int restrictionLayoutId = getWorkspaceLayoutIdFromAppRestrictions();
        boolean restrictionLayoutChanged = didRestrictionLayoutChange(sp, restrictionLayoutId);
        overridePrevious |= restrictionLayoutChanged;
        boolean dbCreatedNoWorkspace =
                sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false);
        if (dbCreatedNoWorkspace || overridePrevious) {//如果需要初始化favorite表,进入
            SharedPreferences.Editor editor = sp.edit();

            // First try layout from app restrictions if it was found
            int workspaceResId = restrictionLayoutId;

            // If the restrictions are not set, use the resource passed to this method
            if (workspaceResId == 0) {//如果restrictionLayoutId为0,就使用调用方法传进来的resId
                workspaceResId = origWorkspaceResId;
            }

            // Use default workspace resource if none provided
            if (workspaceResId == 0) {//如果调用返回传进来的resId为0,去sharedpreference拿,默认是default_workspace
                workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);
            } else {
                editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, workspaceResId);
            }

            // Populate favorites table with initial favorites
            editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
            if (!dbCreatedNoWorkspace && overridePrevious) {
                if (LOGD) Log.d(TAG, "Clearing old launcher database");
                // Workspace has already been loaded, clear the database.
                deleteDatabase();
            }
            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);//将xml中的内容写到数据库
            editor.commit();
        }
    }

default_workspace.xml的内容:





    

    
    

    
    
    

    
    
    

    

    
    
    
    
    


2. final Cursor c = contentResolver.query(LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);

查询数据,Uri展开就是:content://com.android.launcher2.settings/favorites?notify=true

3.将数据库中的每一行包装成一个ItemInfo对象。根据列itemType中的值决定这个ItemInfo的实例是ShortcutInfo,FolderInfo,LauncherAppWidgetInfo。跟缓存这些ItemInfo有关的结构:

    // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
    // LauncherModel to their ids
    static final HashMap sBgItemsIdMap = new HashMap();

    // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
    //       created by LauncherModel that are directly on the home screen (however, no widgets or
    //       shortcuts within folders).
    static final ArrayList sBgWorkspaceItems = new ArrayList();

    // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
    static final ArrayList sBgAppWidgets =
        new ArrayList();

    // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
    static final HashMap sBgFolders = new HashMap();

所有ItemInfo都会存到sBgItemsIdMap 。然后ItemInfo根据它的属性container,判断该ItemInfo的直接容器是文件夹还是桌面,分别放到sBgFolders 和 sBgWorkspaceItems,而widget不能包含在folder中,直接容器只能是桌面,但是widget不会被放到sBgFolders 或者 sBgWorkspaceItems,而会被放到sBgAppWidgets 。

对于ShortcutInfo,除了xml中的那属性,还有一些属性是用代码去获取的,通过LoaderTask#getShortcutInfo()

public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, UserHandle user,
            Context context,
            Cursor c, int iconIndex, int titleIndex, HashMap labelCache) {
        Bitmap icon = null;
        final ShortcutInfo info = new ShortcutInfo();
        info.user = user;

        ComponentName componentName = intent.getComponent();
        if (componentName == null) {
            return null;
        }

        LauncherActivityInfo lai = mLauncherApps.resolveActivity(intent, user);//根据intent获取ActivityInfo
        if (lai == null) {
            return null;
        }

        icon = mIconCache.getIcon(componentName, lai, labelCache);//尝试从缓存获取icon
        // the db
        if (icon == null) {
            if (c != null) {
                icon = getIconFromCursor(c, iconIndex, context);//缓存没有则根据数据库查询到的uri获取icon文件
            }
        }
        // the fallback icon
        if (icon == null) {
            icon = getFallbackIcon();//如果通过数据库也没法获取icon则拿默认的
            info.usingFallbackIcon = true;
        }
        info.setIcon(icon);

        // from the resource
        ComponentName key = lai.getComponentName();
        if (labelCache != null && labelCache.containsKey(key)) {
            info.title = labelCache.get(key);
        } else {
            info.title = lai.getLabel();
            if (labelCache != null) {
                labelCache.put(key, info.title);
            }
        }
        // from the db
        if (info.title == null) {
            if (c != null) {
                info.title =  c.getString(titleIndex);
            }
        }
        // fall back to the class name of the activity
        if (info.title == null) {
            info.title = componentName.getClassName();
        }

        info.contentDescription = mApp.getPackageManager().getUserBadgedLabel(info.title, user);
        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
        return info;
    }

favorite表有那些列,列名和index在LauncherSettings$Favorite中。而LauncherProvider是可以供其他应用调用的,可以通过LauncherProvider对favorite表中的数据进行增删改查。

构建AppWidgetInfo:

AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final AppWidgetProviderInfo provider =
                                widgets.getAppWidgetInfo(appWidgetId);
 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                            provider.provider);
appWidgetInfo.id = id;
appWidgetInfo.screen = c.getInt(screenIndex);
appWidgetInfo.cellX = c.getInt(cellXIndex);
appWidgetInfo.cellY = c.getInt(cellYIndex);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
appWidgetInfo.minSpanX = minSpan[0];
appWidgetInfo.minSpanY = minSpan[1];

widgetinfo主要持有appWidgetId和从AppWidgetManager获取的Provider。


绑定数据

bindWorkspace()

private void bindWorkspace(int synchronizeBindPage) {
            final long t = SystemClock.uptimeMillis();
            Runnable r;

            // Don't use these two variables in any of the callback runnables.
            // Otherwise we hold a reference to them.
            final Callbacks oldCallbacks = mCallbacks.get();//下面会检查是否改变了
            if (oldCallbacks == null) {
                // This launcher has exited and nobody bothered to tell us.  Just bail.
                Log.w(TAG, "LoaderTask running with no launcher");
                return;
            }

            final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
            final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
            oldCallbacks.getCurrentWorkspaceScreen();

            // Load all the items that are on the current page first (and in the process, unbind
            // all the existing workspace items before we call startBinding() below.
            unbindWorkspaceItemsOnMainThread();
            ArrayList workspaceItems = new ArrayList();
            ArrayList appWidgets =
                    new ArrayList();
            HashMap folders = new HashMap();
            HashMap itemsIdMap = new HashMap();
            synchronized (sBgLock) {
                workspaceItems.addAll(sBgWorkspaceItems);
                appWidgets.addAll(sBgAppWidgets);
                folders.putAll(sBgFolders);
                itemsIdMap.putAll(sBgItemsIdMap);
            }
//将当前页面和其他页面的ItemInfo分开。让当前页面的ItemInfo先绑定
            ArrayList currentWorkspaceItems = new ArrayList();
            ArrayList otherWorkspaceItems = new ArrayList();
            ArrayList currentAppWidgets =
                    new ArrayList();
            ArrayList otherAppWidgets =
                    new ArrayList();
            HashMap currentFolders = new HashMap();
            HashMap otherFolders = new HashMap();

            // Separate the items that are on the current screen, and all the other remaining items
            filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
                    otherWorkspaceItems);
            filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
                    otherAppWidgets);
            filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
                    otherFolders);
            sortWorkspaceItemsSpatially(currentWorkspaceItems);
            sortWorkspaceItemsSpatially(otherWorkspaceItems);

            // Tell the workspace that we're about to start binding items
            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.startBinding();//告诉Launcher开始绑定了,做一些必要但不是很重要的工作。Launcher实现了这个callback
                    }
                }
            };
            runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);//让上面的Runnable运行在主线程

            // Load items on the current page
            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
                    currentFolders, null);//绑定当前页的ItemInfo。这个是关键的方法,下面是详细分析
            if (isLoadingSynchronously) {//只有在同步加载时才会进入
                r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.onPageBoundSynchronously(currentScreen);
                        }
                    }
                };
                runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
            }

            // Load all the remaining pages (if we are loading synchronously, we want to defer this
            // work until after the first render)
            mDeferredBindRunnables.clear();
            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
                    (isLoadingSynchronously ? mDeferredBindRunnables : null));//绑定其他页面的ItemInfo

            // Tell the workspace that we're done binding items
            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.finishBindingItems();//告诉Launcher绑定结束了
                    }

                    // If we're profiling, ensure this is the last thing in the queue.
                    if (DEBUG_LOADERS) {
                        Log.d(TAG, "bound workspace in "
                            + (SystemClock.uptimeMillis()-t) + "ms");
                    }

                    mIsLoadingAndBindingWorkspace = false;
                }
            };
            if (isLoadingSynchronously) {
                mDeferredBindRunnables.add(r);
            } else {
                runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
            }
        }

看看LoaderTask#bindWorkspaceItems():

private void bindWorkspaceItems(final Callbacks oldCallbacks,
                final ArrayList workspaceItems,
                final ArrayList appWidgets,
                final HashMap folders,
                ArrayList deferredBindRunnables) {

            final boolean postOnMainThread = (deferredBindRunnables != null);

            // Bind the workspace items
            int N = workspaceItems.size();
//其中ITEMS_CHUNK类似是网络传输的窗口大小吧。决定了一个Runnable加载多少个ItemInfo。太大cpu利用不够,太小导致开过多的子线程
             for (int i = 0; i < N; i += ITEMS_CHUNK) {//循环绑定ItemInfo
                final int start = i;
                final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
                final Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.bindItems(workspaceItems, start, start+chunkSize);//在Launcher中绑定shortcut和folder
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
                }
            }

            // Bind the folders
            if (!folders.isEmpty()) {
                final Runnable r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.bindFolders(folders);//不是真正绑定folder的地方,只是处理了其他东西
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);//将上面的Runnable放到主线程运行
                }
            }

            // Bind the widgets, one at a time
            N = appWidgets.size();
            for (int i = 0; i < N; i++) {
                final LauncherAppWidgetInfo widget = appWidgets.get(i);
                final Runnable r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
//绑定appWidget,由于涉及remoteview,即需要通过provider跨进程通信,所以比较复杂,后面会另起一篇分析
                       callbacks.bindAppWidget(widget);
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);//将上面的Runnable放到主线程运行
                }
            }
        }


看Launcher#bindItems()

public void bindItems(final ArrayList shortcuts, final int start, final int end) {
//waitUntilResume方法是如果调用了onResume就返回false,否则返回true并且将Runnable放入一个结构中,在onResume时调用。
          if (waitUntilResume(new Runnable() {
                public void run() {
                    bindItems(shortcuts, start, end);
                }
            })) {
            return;
        }

        // Get the list of added shortcuts and intersect them with the set of shortcuts here
        Set newApps = new HashSet();
//在installreceiver和uninstallreceiver中更新
       newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);

        Workspace workspace = mWorkspace;
        for (int i = start; i < end; i++) {//循环绑定ItemInfo
            final ItemInfo item = shortcuts.get(i);

            // Short circuit if we are loading dock items for a configuration which has no dock
            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                    mHotseat == null) {
                continue;
            }

            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                    ShortcutInfo info = (ShortcutInfo) item;
                    String uri = info.intent.toUri(0).toString();
                    View shortcut = createShortcut(info);//创建一个Shortcut对象
 //指定哪一页,哪个格子,占位多少,shortcut和folder占位都为1x1,把shortcut添加到workspace
                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
                            item.cellY, 1, 1, false);
                    boolean animateIconUp = false;
                    synchronized (newApps) {
                        if (newApps.contains(uri)) {//判断当前的Item是否一个刚安装的app,是的话就需要动画一下
                            animateIconUp = newApps.remove(uri);
                        }
                    }
                    if (animateIconUp) {
                        // Prepare the view to be animated up
                        shortcut.setAlpha(0f);
                        shortcut.setScaleX(0f);
                        shortcut.setScaleY(0f);
                        mNewShortcutAnimatePage = item.screen;
                        if (!mNewShortcutAnimateViews.contains(shortcut)) {
                            mNewShortcutAnimateViews.add(shortcut);
                        }
                    }
                    break;
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                    FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item, mIconCache);//创建folder的view
                    workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
                            item.cellY, 1, 1, false);//绑定folder
                    break;
            }
        }

        workspace.requestLayout();//请求重新布局
    }

folder_icon.xml


    
    


分析Shortcut的创建,看Launcher#createShortcut()
View createShortcut(ShortcutInfo info) {
        return createShortcut(R.layout.application,
                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
    }
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
        favorite.applyFromShortcutInfo(info, mIconCache);
        favorite.setOnClickListener(this);
        return favorite;
    }

application.xml:

可以看到shortcut的实例时BubbleTextView类型的。

看看folder的创建,FolderIcon#fromXml():

static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
            FolderInfo folderInfo, IconCache iconCache) {
        @SuppressWarnings("all") // suppress dead code warning
        final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
        if (error) {
            throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
                    "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
                    "is dependent on this");
        }

        FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);

        icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
        icon.mFolderName.setText(folderInfo.title);
        icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);

        icon.setTag(folderInfo);
        icon.setOnClickListener(launcher);
        icon.mInfo = folderInfo;
        icon.mLauncher = launcher;
        icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
                folderInfo.title));
        Folder folder = Folder.fromXml(launcher);
        folder.setDragController(launcher.getDragController());
        folder.setFolderIcon(icon);
        folder.bind(folderInfo);
        icon.mFolder = folder;

        icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
        folderInfo.addListener(icon);

        return icon;
    }

分析bindAppWidget()

public void bindAppWidget(final LauncherAppWidgetInfo item) {
        if (waitUntilResume(new Runnable() {
                public void run() {
                    bindAppWidget(item);
                }
            })) {
            return;
        }

        final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
        if (DEBUG_WIDGETS) {
            Log.d(TAG, "bindAppWidget: " + item);
        }
        final Workspace workspace = mWorkspace;

        final int appWidgetId = item.appWidgetId;
        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
        if (DEBUG_WIDGETS) {
            Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
        }
//mAppWidgetHost是持有所有appwidgetview和管理他们的对象,这个hostview创建过程由LauncherAppWidgetHost实现,比较复杂。
        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);

        item.hostView.setTag(item);
        item.onBindAppWidget(this);

        workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
                item.cellY, item.spanX, item.spanY, false);//将这个widget添加到mWorkspace
        addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);

        workspace.requestLayout();

        if (DEBUG_WIDGETS) {
            Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
                    + (SystemClock.uptimeMillis()-start) + "ms");
        }
    }

LauncherAppWidgetHost#createView()

public final AppWidgetHostView createView(Context context, int appWidgetId,
            AppWidgetProviderInfo appWidget) {
//这是appWidget在桌面的代表
        AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
        view.setOnClickHandler(mOnClickHandler);
        view.setAppWidget(appWidgetId, appWidget);
        synchronized (mViews) {
            mViews.put(appWidgetId, view);
        }
        RemoteViews views;
        try {
//获取一个和目标进程通信的对象,这是appWidget在目标进程的代表
        views = sService.getAppWidgetViews(mContextOpPackageName, appWidgetId);
        } catch (RemoteException e) {
            throw new RuntimeException("system server dead?", e);
        }
        view.updateAppWidget(views);//绑定目标进程的代表

        return view;
    }
protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
            AppWidgetProviderInfo appWidget) {
        return new AppWidgetHostView(context, mOnClickHandler);
    }


你可能感兴趣的:(android系统相关)