Android 9.0 Launcher Workspace详解

作者: Albert0211 | 来源:发表于2020-04-06 19:38 被阅读0次
    0.jpg 加载Workspace入口在/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java,想了解Launcher app的启动流程,可以先看看这篇文章,https://www.jianshu.com/p/0b273112cd7e

    1、Workspace加载调用过程,如图

    progrss.png

    代码入口:
    /packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java,

     public void run() {
            synchronized (this) {
                // Skip fast if we are already stopped.
                if (mStopped) {
                    return;
                }
            }
    
            TraceHelper.beginSection(TAG);
            try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
                TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
                loadWorkspace();
                ...
                
                transaction.commit();
            } catch (CancellationException e) {
                // Loader stopped, ignore
                TraceHelper.partitionSection(TAG, "Cancelled");
            }
            TraceHelper.endSection(TAG);
        }
        
    }
    ···
    

    接下来是总体调用过程:

     private void loadWorkspace() {
                 ...    
     
            LauncherSettings.Settings.call(contentResolver,
                    LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
               ...
                        
                }
    
                // If there are any empty screens remove them, and update.
                if (unusedScreens.size() != 0) {
                    mBgDataModel.workspaceScreens.removeAll(unusedScreens);
                    LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
                }
            
        }
    

    /packages/apps/Launcher3/src/com/android/launcher3/LauncherSettings.java

        public static final class Settings {
          ...
         
          public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
           ...
           
         public static Bundle call(ContentResolver cr, String method) {
                return cr.call(CONTENT_URI, method, null, null);
            }   
             ...    
    }        
    

    /packages/apps/Launcher3/src/com/android/launcher3/LauncherProvider.java

         @Override
        public Bundle call(String method, final String arg, final Bundle extras) {
            Log.e(TAG,"method-- "+method);
            if (Binder.getCallingUid() != Process.myUid()) {
                return null;
            }
            createDbIfNotExists();
            switch (method) {
            
            ...
            
              case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
                    loadDefaultFavoritesIfNecessary();
                    return null;
                }
    
              ...
              
               }
              
         }
             
    

    2、具体加载流程,是这个方法 loadDefaultFavoritesIfNecessary(),

    WorkSpace.png

    /**
    * 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());
            boolean aBoolean = sp.getBoolean(EMPTY_DATABASE_CREATED, false);
            if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
                    AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
                AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
                if (loader == null) {            
                    loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
                }
                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);
                        }
                    }
                }
    
                final boolean usingExternallyProvidedLayout = loader != null;
                if (loader == null) {
                    loader = getDefaultLayoutParser(widgetHost);
                }
              
                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());  
                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();
            }
        }
    
    1) From the app restrictions
    private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
            Context ctx = getContext();
            UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
            Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName());
            if (bundle == null) {
                return null;
            }
    
            String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME);
            if (packageName != null) {
                try {
                    Resources targetResources = ctx.getPackageManager()
                            .getResourcesForApplication(packageName);
                    return AutoInstallsLayout.get(ctx, packageName, targetResources,
                            widgetHost, mOpenHelper);
                } catch (NameNotFoundException e) {
                    Log.e(TAG, "Target package for restricted profile not found", e);
                    return null;
                }
            }
            return null;
        }
    
    2) From a package provided by play store
     static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
                LayoutParserCallback callback) {
            Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
                    ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
            if (customizationApkInfo == null) {
                return null;
            }
            return get(context, customizationApkInfo.first, customizationApkInfo.second,
                    appWidgetHost, callback);
        }
        
        /** Marker action used to discover a package which defines launcher customization */
        static final String ACTION_LAUNCHER_CUSTOMIZATION =
                "android.autoinstalls.config.action.PLAY_AUTO_INSTALL";
    
    3) From a partner configuration APK, already in the system image
         ...
     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);
                        }
                    }
                } 
         
         public static synchronized Partner get(PackageManager pm) {
            if (!sSearched) {
                Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
                if (apkInfo != null) {
                    sPartner = new Partner(apkInfo.first, apkInfo.second);
                }
                sSearched = true;
            }
            return sPartner;
        }
    
        ...
         
          /** Marker action used to discover partner */
        private static final String
                ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
    
    4) The default configuration for the particular device
         ...
         
         if (loader == null) {
                   loader = getDefaultLayoutParser(widgetHost);
               } 
    
         ... 
    
      private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost){
           InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
           int defaultLayout = idp.defaultLayoutId;
    
           UserManagerCompat um = UserManagerCompat.getInstance(getContext());
           if (um.isDemoUser() && idp.demoModeLayoutId != 0) {
               defaultLayout = idp.demoModeLayoutId;
           }
    
           return new DefaultLayoutParser(getContext(), widgetHost,
                   mOpenHelper, getContext().getResources(), defaultLayout);
       }
               
    public class DefaultLayoutParser extends AutoInstallsLayout {
       private static final String TAG = "DefaultLayoutParser";
    
         ...
         
        public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
               LayoutParserCallback callback, Resources sourceRes, int layoutId) {
           super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
       }
       
       ...
    }
    

    第一次加载,执行最后一个 loader = getDefaultLayoutParser(widgetHost)。 接下来,便是加载布局文件,解析数据,该文件在/packages/apps/Launcher3/res/xml目录下。具体是解析defaultLayout,对应default_workspace_3x3,default_workspace_4x4,default_workspace_5x6等的某一个文件,这个文件是从在InvariantDeviceProfile中获取的。

    3、加载,解析代码

    packages/apps/Launcher3/src/com/android/launcher3/LauncherProvider.java:

      ...
     mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) 
     ...
    
     @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
                ArrayList<Long> screenIds = new ArrayList<Long>();
                // TODO: Use multiple loaders with fall-back and transaction.
                int count = loader.loadLayout(db, screenIds);
                Log.e(TAG, "loadFavorites count : "+count);
                // 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");
                    }
                    Log.e(TAG,"loadFavorites id: "+id+",Screenrank: "+rank);
                    rank++;
    
                }
    
                // Ensure that the max ids are initialized
                mMaxItemId = initializeMaxItemId(db);
                mMaxScreenId = initializeMaxScreenId(db);
                Log.e(TAG,"loadFavorites favorite  mMaxItemId: "+mMaxItemId+",workSpaceScreen mMaxScreenId: "+mMaxScreenId);
                return count;
            }
        }
    

    /packages/apps/Launcher3/src/com/android/launcher3/AutoInstallsLayout.java

     public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
             mDb = db;
            try {
                return parseLayout(mLayoutId, screenIds);
            } catch (Exception e) {
                Log.e(TAG, "Error parsing layout: " + e);
                return -1;
            }
        }
     protected int parseLayout(int layoutId, ArrayList<Long> screenIds)
                throws XmlPullParserException, IOException {
            XmlResourceParser parser = mSourceRes.getXml(layoutId);
            beginDocument(parser, mRootTag);
            final int depth = parser.getDepth();
            int type;
            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;
                }
                Log.e(TAG,"parseLayout layoutId-- "+layoutId+",screenIds-- "+screenIds+",count-- "+count);
                count += parseAndAddNode(parser, tagParserMap, screenIds);
            }
            return count;
        }
     protected int parseAndAddNode(
            XmlResourceParser parser,
            ArrayMap<String, TagParser> tagParserMap,
            ArrayList<Long> screenIds)
            throws XmlPullParserException, IOException {
            if (TAG_INCLUDE.equals(parser.getName())) {
                final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
                if (resId != 0) {
                    // recursively load some more favorites, why not?
                    return parseLayout(resId, screenIds);
                } else {
                    return 0;
                }
            }
    
            mValues.clear();
            parseContainerAndScreen(parser, mTemp);
            final long container = mTemp[0];
            final long screenId = mTemp[1];
    
            mValues.put(Favorites.CONTAINER, container);
            mValues.put(Favorites.SCREEN, screenId);
    
            mValues.put(Favorites.CELLX,
                    convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
            mValues.put(Favorites.CELLY,
                    convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
    
            TagParser tagParser = tagParserMap.get(parser.getName());
            if (tagParser == null) {
                if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
                return 0;
            }
            long newElementId = tagParser.parseAndAdd(parser);
              if (newElementId >= 0) {
                // Keep track of the set of screens which need to be added to the db.
                if (!screenIds.contains(screenId) &&
                        container == Favorites.CONTAINER_DESKTOP) {
                    screenIds.add(screenId);
                }
                return 1;
            }
            return 0;
        }
    

    如图:


    parse.png

    总结:

    1、 第一次启动Launcher,加载器是由getDefaultLayoutParser生成。如果看执行效果,可以删除launcher.db ,重启ActivityManager。
      rm /data/data/com.android.launcher3/databases/launcher.db
      adb shell am restart

    2、 非初次启动,EMPTY_DATABASE_CREATED=false, 不会初始化loader。

    3、定制化需求,可以考虑添加对应的default_workspace.xml文件。

    相关文章

      网友评论

        本文标题:Android 9.0 Launcher Workspace详解

        本文链接:https://www.haomeiwen.com/subject/fhzbphtx.html