Launcher学习总结

一、启动流程

Android launcher启动

AMS startHomeOnAllDisplays。

//查询符合条件的home 复制代码
/frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
                                          int flags, int userId, 
                                          boolean resolveForStart, 
                                          int filterCallingUid) {
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");

        if (!sUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                                                      false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                                                                      flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        final ResolveInfo bestChoice =
            chooseBestActivity(intent, resolvedType, flags, query, userId);
        return bestChoice;
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

二、编译

以展锐 Android10平台为例。编译时用 make Launcher3QuickStep -j4 ,而不是 make Launcher3 -j4

三、源码分析

Launcher源码浅析

Launcher.java是Launcher的启动页面,负责资源初始化和桌面UI创建。对应布局 launcher.xml。

3.1Launcher.java

protected 复制代码
    super.onCreate(savedInstanceState);
 
    // 获取 LauncherAppState 实例和模型
    LauncherAppState app = LauncherAppState.getInstance(this);
    mModel = app.getModel();
    
    // 初始化不变的设备配置文件
    InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
    initDeviceProfile(idp);
    idp.addOnChangeListener(this);
 
    // 获取共享首选项和图标缓存
    mSharedPrefs = LauncherPrefs.getPrefs(this);
    mIconCache = app.getIconCache();
 
    // 创建无障碍代理
    mAccessibilityDelegate = createAccessibilityDelegate();
 
    // 初始化拖动控制器
    initDragController();
    
    // 创建所有应用程序控制器
    mAllAppsController = new AllAppsTransitionController(this);
    
    // 创建状态管理器
    mStateManager = new StateManager<>(this, NORMAL);
 
    // 创建引导首选项
    mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
    // 设置视图
    setupViews();
 
    // 初始化Widget
    mAppWidgetManager = new WidgetManagerHelper(this);
    mAppWidgetHolder = createAppWidgetHolder();
    mAppWidgetHolder.startListening();
 
    // 设置内容视图
    setContentView(getRootView());
    ComposeInitializer.initCompose(this);
 
}

3.1.1setupViews方法

protected 复制代码
    // 创建根视图
    inflateRootView(R.layout.launcher);
 
    // 获取拖动层和焦点处理器
    mDragLayer = findViewById(R.id.drag_layer);
    mFocusHandler = mDragLayer.getFocusIndicatorHelper();
    
    // 获取工作区、总览面板和Hotseat
    mWorkspace = mDragLayer.findViewById(R.id.workspace);
    mWorkspace.initParentViews(mDragLayer);
    mOverviewPanel = findViewById(R.id.overview_panel);
    mHotseat = findViewById(R.id.hotseat);
    // 将工作区设置为Hotseat
    mHotseat.setWorkspace(mWorkspace);
 
    // 设置拖动层
    mDragLayer.setup(mDragController, mWorkspace);
 
    // 设置工作区
    mWorkspace.setup(mDragController);
    // 在工作区绑定之前,确保我们将壁纸偏移锁定到默认状态,否则在RTL中我们将更新错误的偏移量
    mWorkspace.lockWallpaperToDefaultPage();
    mWorkspace.bindAndInitFirstWorkspaceScreen();
    mDragController.addDragListener(mWorkspace);
 
    // 获取搜索/删除/卸载栏
    mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
 
    // 设置应用程序视图
    mAppsView = findViewById(R.id.apps_view);
    mAppsView.setAllAppsTransitionController(mAllAppsController);
 
    // 设置拖动控制器(拖动目标必须按优先级的相反顺序添加)
    mDropTargetBar.setup(mDragController);
    mAllAppsController.setupViews(mScrimView, mAppsView);
 
    // 如果启用了点分页,则设置工作区的分页指示器
    if (SHOW_DOT_PAGINATION.get()) {
        mWorkspace.getPageIndicator().setShouldAutoHide(true);
        mWorkspace.getPageIndicator().setPaintColor(
                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
                        ? Color.BLACK
                        : Color.WHITE);
    }
}
 

四、页面组件

  • Workspace:工作区,也是我们常说的桌面区域,包括搜索框,桌面,壁纸。 CellLayout 是单一页。
  • AppsView:应用程序列表
  • Widget:小组件

Workspace(工作区)

AppsView(应用程序视图)

Widget(小组件)

4.4桌面图标排列布局

LauncherProvider.java处控制桌面图标的加载。

scss 复制代码
  /**
     * 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(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
            Log.d(TAG, "loading default workspace");

            AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
            if (loader == null) {
                Log.d(TAG, "11111111111e");
                loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
            }
            if (loader == null) {
                Log.d(TAG, "11222222");
                final Partner partner = Partner.get(getContext().getPackageManager());
                if (partner != null && partner.hasDefaultLayout()) {
                Log.d(TAG, "3333333");
                    final Resources partnerRes = partner.getResources();
                    int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                            "xml", partner.getPackageName());
                    if (workspaceResId != 0) {
                        Log.d(TAG, "4444444");
                        loader = new DefaultLayoutParser(getContext(), widgetHost,
                                mOpenHelper, partnerRes, workspaceResId);
                    }
                }
            }

            final boolean usingExternallyProvidedLayout = loader != null;
            if (loader == null) {
                        Log.d(TAG, "555555");
                loader = getDefaultLayoutParser(widgetHost);
            }

            // There might be some partially restored DB items, due to buggy restore logic in
            // previous versions of launcher.
            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();
        }
    }
非GMS版本:

要看:

Launcher3/res/xml/default_workspace**.xml 的配置。

可在device_profiles.xml 中查看配置的是哪个布局文件;go版本在 go/res/xml/device_profiles.xml

以 default_workspace_4x3.xml 为例:

xml 复制代码
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">

    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
    <!-- Dialer, Messaging, Browser, Camera -->
    <resolve
        launcher:container="-101"
        launcher:screen="0"
        launcher:x="0"
        launcher:y="0" >
		<favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
    </resolve>

    <resolve
        launcher:container="-101"
        launcher:screen="1"
        launcher:x="1"
        launcher:y="0" >
        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
    </resolve>

    <resolve
        launcher:container="-101"
        launcher:screen="2"
        launcher:x="2"
        launcher:y="0" >
        <!-- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_SETTINGS;end" /> -->
		<favorite launcher:packageName="com.android.settings"   launcher:className="com.android.settings.Settings"  launcher:uri="android.settings.SETTINGS" />
    </resolve>

    <!-- Bottom row -->	
	<resolve
        launcher:screen="0"
        launcher:x="0"
        launcher:y="-1" >
        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_TURBOTEST;end" />
    </resolve>

    <resolve
        launcher:screen="0"
        launcher:x="1"
        launcher:y="-1" >
        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_STORE;end" />
        <favorite launcher:uri="#Intent;type=images/*;end" />
    </resolve>

    <resolve
        launcher:screen="0"
        launcher:x="2"
        launcher:y="-1" >
        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_AGINGTEST;end" />
    </resolve>

</favorites>

一个resolve中配置一个应用图标;同一个resolve 中不同的favorite是为了兼容不同版本的intent。

launcher:container="-101" 控制最下面的hotseat. 没有的是上面的快捷区。

GMS版本:

要看

vendor/google/apps/GmsSampleIntegration/***/xml/partner_default_layout.xml

中的覆盖情况。

控制应用图标隐藏

复写AppFilter 的实现类AppFilterImpl ,并配置 app_filter_class 为对应实现的类即可。

注意事项:

AppFilterImpl必须实现 带Context 参数的构造函数

scss 复制代码
AppFilterImpl(Context context)

参考 Android Launcher3 隐藏应用图标和widget的显示

五、部分事件处理

5.1点击App图标的事件响应

typescript 复制代码
/**
 * Class for handling clicks on workspace and all-apps items
 */
public class ItemClickHandler {
    private static void onClick(View v) {
        startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
    }
    
    // 通知launcher启动Activity
    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
        launcher.startActivitySafely(v, intent, item);
    }
}

5.2禁用 应用长按显示信息或拖拽事件

ItemLongClickListener.java中 canStartDrag函数中返回false 即可。

5.3禁用长按空白地方弹出widget等项

在 WorkspaceTouchListener 中的 canHandleLongPress 函数中返回false 即可。

相关推荐
Coffeeee2 小时前
Android功耗优化,哪些代码会让你的机子发热又耗电
android·架构·kotlin
xianrenli383 小时前
Android14自动时区更新
android·学习·framework·aosp14·自动时区
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端:零外部资源的应用打包策略
android·javascript·flutter·华为·harmonyos
恋猫de小郭3 小时前
Fluter 共享内存多线程正在落地,IsolateGroupBound 来了
android·前端·flutter
三少爷的鞋4 小时前
Kotlin 协程的七重境界:从 launch(IO) 到执行模型设计
android
-SOLO-13 小时前
Android Event 日志完全指南
android
壮哥_icon14 小时前
【Android 系统开发】从掉帧卡顿到丝滑:高频硬件中断(IRQ)的 CPU 亲和性性能优化实战
android·性能优化
千里马学框架14 小时前
Android Framework 新手学习踩坑建议指南
android·智能手机·性能优化·framework·aaos·车载开发·系统工程师
我命由我1234516 小时前
执行 Gradle 指令报错,无法将“grep”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
android·java·java-ee·android studio·android jetpack·android-studio·android runtime