Android 9.0 Launcher Workspace加载

简介: 加载Workspace入口在/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java,想了解Launcher app的启动流程,可以先看看这篇文章,https://www.

加载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文件。

目录
相关文章
|
6月前
|
Android开发
Android Launcher研究(二)-----------Launcher为何物,究竟是干什么
Android Launcher研究(二)-----------Launcher为何物,究竟是干什么
202 2
|
4月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
140 2
|
27天前
|
Android开发 UED
Android 中加载 Gif 动画
【10月更文挑战第20天】加载 Gif 动画是 Android 开发中的一项重要技能。通过使用第三方库或自定义实现,可以方便地在应用中展示生动的 Gif 动画。在实际应用中,需要根据具体情况进行合理选择和优化,以确保用户体验和性能的平衡。可以通过不断的实践和探索,进一步掌握在 Android 中加载 Gif 动画的技巧和方法,为开发高质量的 Android 应用提供支持。
|
6月前
|
Android开发
Android 如何将定制的Launcher成为系统中唯一的Launcher
Android 如何将定制的Launcher成为系统中唯一的Launcher
235 2
|
3月前
|
存储 缓存 Java
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
46 0
|
3月前
|
Java Android开发 Kotlin
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
37 0
|
5月前
|
XML API 开发工具
Android Bitmap 加载与像素操作
Android Bitmap 加载与像素操作
48 2
|
6月前
|
存储 Android开发
android launcher总体分析
android launcher总体分析
93 1
|
6月前
|
Java Android开发
Android桌面快捷方式图标生成与删除 使用Intent与launcher交互
Android桌面快捷方式图标生成与删除 使用Intent与launcher交互
112 1
|
6月前
|
Android开发
Android launcher development resources
Android launcher development resources
29 1