launcher3-布局load与bind

一、配置好InvariantDeviceProfile列表信息

Launcher.java

public void onCreate() {
    LauncherAppState app = LauncherAppState.getInstance();
}

LauncherAppState.java

private LauncherAppState() {
    mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);

InvariantDeviceProfile.java

public InvariantDeviceProfile(Context context) {
		//closestProfiles 和 interpolatedDeviceProfileOut是什么?
		
		//1.getPredefinedDeviceProfiles--获取所有的profile信息
		//2.findClosestDeviceProfiles---找到最适合的配置信息,计算后获取的第一个最接近
        ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(
                minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
        InvariantDeviceProfile closestProfile = closestProfiles.get(0); 
                
        InvariantDeviceProfile interpolatedDeviceProfileOut =
                invDistWeightedInterpolate(minWidthDps,  minHeightDps, closestProfiles);
        
        //通过closestProfile拿到桌面的一系列配置,行、hotseat个数等
        numRows = closestProfile.numRows;
        numColumns = closestProfile.numColumns;
        numHotseatIcons = closestProfile.numHotseatIcons;
        numFolderRows = closestProfile.numFolderRows;
        numFolderColumns = closestProfile.numFolderColumns;
		//通过interpolatedDeviceProfileOut拿到图标大小等
        iconSize = interpolatedDeviceProfileOut.iconSize;
        iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
    }
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
        ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
        try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
            final int depth = parser.getDepth();
			//解析device_profiles,里面有各种分辨率的profile,配置名称,宽高,行数,列数等等
		}
        return profiles;
    }

device_profiles.xml中的一部分

    <profile
        launcher:name="Nexus 4"
        launcher:minWidthDps="359"
        launcher:minHeightDps="567"
        launcher:numRows="4"
        launcher:numColumns="4"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="4"
        launcher:iconSize="54"
        launcher:iconTextSize="13.0"
        launcher:numHotseatIcons="5"
        launcher:defaultLayoutId="@xml/default_workspace_4x4"
        />
// width代表设备的宽;minWidthDps代表profile中宽
ArrayList<InvariantDeviceProfile> findClosestDeviceProfiles(
            final float width, final float height, ArrayList<InvariantDeviceProfile> points) {

        // Sort the profiles by their closeness to the dimensions
        ArrayList<InvariantDeviceProfile> pointsByNearness = points;
        Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() {
            public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) {
                return Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                        dist(width, height, b.minWidthDps, b.minHeightDps));
            }
        });
        return pointsByNearness;
    }
//计算方法:hypot表示先求所有参数的和,再返回该和的平方跟
//1.(minWidthDps-width)的平方+(minHeightDps-height)的平方 =和
//2.在 求该和的平方跟(类似三角形斜边)
@Thunk float dist(float x0, float y0, float x1, float y1) {
     return (float) Math.hypot(x1 - x0, y1 - y0);
}

二、加载流程

LoadTask.java

public void run() {
	 //1.加载数据
     loadWorkspace();
     //2.绑定数据
     mResults.bindWorkspace();
    }
private void loadWorkspace() {
     Log.d(TAG, "loadWorkspace: loading default favorites");
     LauncherSettings.Settings.call(contentResolver,
                LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); //"load_default_favorites"
     //2.从数据库中读取配置文件信息            
     final LoaderCursor c = new LoaderCursor(contentResolver.query(
       LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
     //3.根据itemtype,走不同的case
     ShortcutInfo info;
     LauncherAppWidgetInfo appWidgetInfo;
     switch (c.itemType) {
           case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
           case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
           case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
           		c.checkAndAddItem(info, mBgDataModel);
           case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 
           		c.checkAndAddItem(folderInfo, mBgDataModel);      
           case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:       
           case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
           		c.checkAndAddItem(appWidgetInfo, mBgDataModel); 
    }

LauncherProvider.java

            case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
                loadDefaultFavoritesIfNecessary();
                return null;
            }

LauncherProvider.java

/**
     * Loads the default workspace based on the following priority scheme:
     *   1) From the app restrictions
     *   2) From a package provided by play store
     *   3) From a partner configuration APK, already in the system image
     *   4) The default configuration for the particular device
     */
    synchronized private void loadDefaultFavoritesIfNecessary() {
        SharedPreferences sp = Utilities.getPrefs(getContext());

        if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
            Log.d(TAG, "loading default workspace");
			//1.应用约束
            AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
            //2.从带有 android.autoinstalls.config.action.PLAY_AUTO_INSTALL Action的应用里获取workspace默认配置资源
            if (loader == null) {
                loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
            }
            //3.从系统内置的partner应用里获取workspace默认配置
            if (loader == null) {
                final Partner partner = Partner.get(getContext().getPackageManager());
                if (partner != null && partner.hasDefaultLayout()) {
                    final Resources partnerRes = partner.getResources();
                    int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                            "xml", partner.getPackageName());
                    if (workspaceResId != 0) {
                        loader = new DefaultLayoutParser(getContext(), widgetHost,
                                mOpenHelper, partnerRes, workspaceResId);
                    }
                }
            }
			//4.调用getDefaultLayoutParser() 获取配置的InvariantDeviceProfile资源,默认会执行第四步
            final boolean usingExternallyProvidedLayout = loader != null;
            if (loader == null) {
                loader = getDefaultLayoutParser(widgetHost);
            }
			//然后创建数据库,建表favorites和workspaceScrenns,加载数据
            mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
            // Populate favorites table with initial favorites
            if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
                    && usingExternallyProvidedLayout) {
                // Unable to load external layout. Cleanup and load the internal layout.
                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                //最后加载桌面资源
                mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
                        getDefaultLayoutParser(widgetHost));
            }
            clearFlagEmptyDbCreated();
        }
    }

LauncherProvider.java

private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
        InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
        int defaultLayout = idp.defaultLayoutId;
        return new DefaultLayoutParser(getContext(), widgetHost,
                mOpenHelper, getContext().getResources(), defaultLayout);
    }

LauncherAppState.java

    public static InvariantDeviceProfile getIDP(Context context) {
        return LauncherAppState.getInstance(context).getInvariantDeviceProfile(); //设备信息
    }

LauncherAppState.java

    public InvariantDeviceProfile getInvariantDeviceProfile() {
        return mInvariantDeviceProfile; //回到launcherAppState的构造方法就看见了
    }

三、加载桌面数据资源配置

接上面的mOpenHelper.loadFavorites

LauncherProvider.java 中的内部类 DatabaseHelper.java

 @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
            ArrayList<Long> screenIds = new ArrayList<Long>();
            //通过loadlayout解析布局信息
            int count = loader.loadLayout(db, screenIds);

            // Add the screens specified by the items above
            Collections.sort(screenIds);
            int rank = 0;
            ContentValues values = new ContentValues();
            for (Long id : screenIds) {
                values.clear();
                values.put(LauncherSettings.WorkspaceScreens._ID, id);
                values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
                //保存到数据库
                if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
                    throw new RuntimeException("Failed initialize screen table"+ "from default layout");
                }
                rank++;
            }
            return count;
        }

AutoInstallsLayout.java

    public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
       return parseLayout(mLayoutId, screenIds);
    }

AutoInstallsLayout.java

protected int parseLayout(int layoutId, ArrayList<Long> screenIds) throws XmlPullParserException, IOException {
		//通过XmlResourceParser解析xml
        XmlResourceParser parser = mSourceRes.getXml(layoutId);
		//我们使用的是AutoInstallsLayout的子类DefaultLayoutParser,解析到信息后会保存到对应的数据库中。
        ArrayMap<String, TagParser> tagParserMap = getLayoutElementsMap();
        int count = 0;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            count += parseAndAddNode(parser, tagParserMap, screenIds);
        }
        //返回解析的个数
        return count;
    }

AutoInstallsLayout.java

//不同的标签通过不同的解析对象处理
    protected ArrayMap<String, TagParser> getLayoutElementsMap() {
        ArrayMap<String, TagParser> parsers = new ArrayMap<>();
        parsers.put(TAG_APP_ICON, new AppShortcutParser());
        parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
        parsers.put(TAG_FOLDER, new FolderParser()); //文件夹解析器
        parsers.put(TAG_APPWIDGET, new PendingWidgetParser());
        parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
        return parsers;
    }

LauncherProvider.java 中的内部类 DatabaseHelper.java

    @Thunk static long dbInsertAndCheck(DatabaseHelper helper,
            SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
        helper.checkId(table, values);
        return db.insert(table, nullColumnHack, values);
    }

四、介绍默认的配置文件

default_workspace_4x4.xml

<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto">

    <!-- Hotseat -->  //规则一样
    <include launcher:workspace="@xml/dw_phone_hotseat" />

    <!-- Bottom row -->
    <resolve   //通过ResolveParser解析,包含内嵌标签
        launcher:screen="0"	//第0屏
        launcher:x="0"	//坐标x
        launcher:y="-1" >	//坐标y
        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />	//favorite 一个app的信息,包含uri,或包名类名
        <favorite launcher:uri="mailto:" />
    </resolve>
    
</favorites>

五、bind worksapce 数据

以上loadworkspace就差不多了,接下来就该bind了,从上面的loadWorkSpace方法中知道,它把所有的数据都保存在了mBgDataModel里面。

LoaderResul.java

public void bindWorkspace() {
        // Save a copy of all the bg-thread collections,这里不是遍历而是copy,避免后续的某个线程修改全局变量影响到其他的工作线程。
        ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
        ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
        final ArrayList<Long> orderedScreenIds = new ArrayList<>();
		//1.把数据保存到集合中
        synchronized (mBgDataModel) {
            workspaceItems.addAll(mBgDataModel.workspaceItems);
            appWidgets.addAll(mBgDataModel.appWidgets);
            orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
            mBgDataModel.lastBindId++;
        }
        //2.filter和sort,将当前需要加载的页的数据,按screenid排序,保存在新的集合里
        filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
                otherWorkspaceItems);
        filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets,
                otherAppWidgets);
        sortWorkspaceItemsSpatially(currentWorkspaceItems);
        sortWorkspaceItemsSpatially(otherWorkspaceItems);
		
        callbacks.startBinding(); //改变workspace的状态,移除一些旧的View和数据
        
        callbacks.bindScreens(orderedScreenIds); //1.bind screen
        
        // Load items on the current page. //2.bind workspace和widgetbind
        bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, mainExecutor);
        
        //3.上述是bind 当前workspace,然后就是bind other Workspace,与上面的流程差不多一样
       

Launcher.java

public void startBinding() { 
		setWorkspaceLoading(true); 
        // Clear the workspace because it's going to be rebound
        mWorkspace.clearDropTargets();
        mWorkspace.removeAllWorkspaceScreens();
        mAppWidgetHost.clearViews();
    }

Launcher.java

public void bindScreens(ArrayList<Long> orderedScreenIds) {
        bindAddScreens(orderedScreenIds);
    }

Launcher.java

    private void bindAddScreens(ArrayList<Long> orderedScreenIds) { //1.bind screen
        int count = orderedScreenIds.size();
        for (int i = 0; i < count; i++) {
            long screenId = orderedScreenIds.get(i);
            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
                //根据screenid,调用此方法创建相对应的celllayout,并添加到workspace
                mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
            }
        }
    }

WorkSpace.java

    public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
        insertNewWorkspaceScreen(screenId, insertIndex);
    }

WorkSpace.java

public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
        CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                        R.layout.workspace_screen, this, false /* attachToRoot */);
        mWorkspaceScreens.put(screenId, newScreen);
        mScreenOrder.add(insertIndex, screenId);
        addView(newScreen, insertIndex);
        return newScreen;
    }

LoaderResult.java

//2.bind workspace和widget
 private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems,
            final ArrayList<LauncherAppWidgetInfo> appWidgets,
            final Executor executor) {

        // Bind the workspace items
        int N = workspaceItems.size();
        for (int i = 0; i < N; i += ITEMS_CHUNK) {
            callbacks.bindItems(workspaceItems.subList(start, start+chunkSize), false);  
        };
         
        // Bind the widgets, one at a time
        N = appWidgets.size();
        for (int i = 0; i < N; i++) {
          callbacks.bindItems(Collections.singletonList(widget), false); 
        };        
    }

Launcher.java

 @Override
    public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
        for (int i = 0; i < end; i++) {
            final ItemInfo item = items.get(i);
            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
                    ShortcutInfo info = (ShortcutInfo) item;
                    view = createShortcut(info);
                    break;
                }
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
                    view = FolderIcon.fromXml(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item);
                    break;
                }
                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
                    break;
                }
            }
        workspace.requestLayout();
    }

六、App的load与bind

上回分析了loadWorkspace和bindWorkspace,接着继续

LoaderTask.java

public void run() {
	 //1.加载数据
     loadWorkspace();
     //2.绑定数据
     mResults.bindWorkspace();
     //3.
     loadAllApps();
     //4.
     mResults.bindAllApps();
    }

LoaderTask.java

private void loadAllApps() {
        final List<UserHandle> profiles = mUserManager.getUserProfiles();

        // Clear the list of apps
        mBgAllAppsList.clear();
        for (UserHandle user : profiles) {
            //1.拿到所有app
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            boolean quietMode = mUserManager.isQuietModeEnabled(user);
            // Create the ApplicationInfos
            for (int i = 0; i < apps.size(); i++) {
                LauncherActivityInfo app = apps.get(i);
                // 2.遍历添加到AllAppsList
                mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
            }
        }
    }

LoaderResults.java

    public void bindAllApps() {
        // shallow copy
        @SuppressWarnings("unchecked")
        final ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();

        Runnable r = new Runnable() {
            public void run() {
                Callbacks callbacks = mCallbacks.get();
                if (callbacks != null) {
                	//与workspace的bind一样,调launcher的回调
                    callbacks.bindAllApplications(list); 
                }
            }
        };
        mUiExecutor.execute(r);
    }

Launcher.java

    public void bindAllApplications(ArrayList<AppInfo> apps) {
        mAppsView.getAppsStore().setApps(apps);
    }

参考文章:
https://www.cnblogs.com/yangjies145/p/14342541.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值