Launcher2分析——系列之一

今天开始launcher2分析系列,Launcher2的代码路径为:$ANDROID_SRC/packages/apps/Launcher2

项目构成:
AndroidManifest.xml         项目Launcher2的描述文件
CleanSpec.mk                android项目授权文件?
NOTICE                      apache授权协议
Android.mk                  Launcher2编译的makefile
MODULE_LICENSE_APACHE2  空文件
proguard.flags          -keep clashh
res目录                 描述文件以及icon资源的位置
src目录                     源代码目录
先看AndroidManifest.xml文件,该文件是对Launcher2的配置文件

<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.android.launcher"
    android:sharedUserId="@string/sharedUserId"
    >
<!--package配置我们应用程序的包名 -->
    <original-package android:name="com.android.launcher2" />
<!--对系统资源访问的权限控制 -->
    <permission
        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="normal"
        android:label="@string/permlab_install_shortcut"
        android:description="@string/permdesc_install_shortcut" />
    <permission
        android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="normal"
        android:label="@string/permlab_uninstall_shortcut"
        android:description="@string/permdesc_uninstall_shortcut"/>
    <permission
        android:name="com.android.launcher.permission.READ_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="normal"
        android:label="@string/permlab_read_settings"
        android:description="@string/permdesc_read_settings"/>
    <permission
        android:name="com.android.launcher.permission.WRITE_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="normal"
        android:label="@string/permlab_write_settings"
        android:description="@string/permdesc_write_settings"/>

    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.SET_WALLPAPER" />
    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
    <!--对我们应用程序的配置 -->
    <application
        android:name="com.android.launcher2.LauncherApplication"
        android:process="@string/process"
        android:label="@string/application_name"
        android:icon="@drawable/ic_launcher_home">

    <!--配置应用程序额的名字,进程,标签,和图标

        label的值为values/strings.xml中application_name 键值对的值

        icon为drawable目录下名为的ic_launcher_home的图片

        实际上该图片的位置位于drawable-hdpi(高分辨率)目录下,是个小房子

    -->

        <activity
            android:name="com.android.launcher2.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:screenOrientation="nosensor"
            android:windowSoftInputMode="stateUnspecified|adjustPan">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
            </intent-filter>
        </activity>
 <!--一个项目可能有很多activity,设置intent-filter可以先启动该activity -->
        <activity
            android:name="com.android.launcher2.WallpaperChooser"
            android:label="@string/pick_wallpaper"
            android:icon="@drawable/ic_launcher_wallpaper"
            android:screenOrientation="nosensor"
            android:finishOnCloseSystemDialogs="true">
            <intent-filter>
                <action android:name="android.intent.action.SET_WALLPAPER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
 <!--设置wallpapaer的activity -->
        <!-- Intent received used to install shortcuts from other applications -->
        <receiver
            android:name="com.android.launcher2.InstallShortcutReceiver"
            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
            <intent-filter>
                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
            </intent-filter>
        </receiver>
 <!--安装快捷方式的intent -->
        <!-- Intent received used to uninstall shortcuts from other applications -->
        <receiver
            android:name="com.android.launcher2.UninstallShortcutReceiver"
            android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
            <intent-filter>
                <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT" />
            </intent-filter>
        </receiver>
 <!--设置删除快捷方式的intent -->
        <!-- The settings provider contains Home -->


好吧,现在我们来看res目录里的布局文件,布局文件都放在layout*目录里。
本以为launcher的layout都放在layout目录里,由于屏幕放置方式的不同会对桌面造成一定的影响,所以google的android项目组就决定因地制宜。比如当你横着放置屏幕的时候就会使用layout-land目录里的文件来对系统launcher进行布局,竖着屏幕的时候会使用layout-port内的布局文件来对launcher来布局。
横竖屏幕切换之际,会重新进行布局。那我们就以layout-land目录为例来看吧。
layout-land/launcuer.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<com.android.launcher2.DragLayer
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"

    android:id="@+id/drag_layer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/all_apps" />

    <!-- The workspace contains 3 screens of cells -->
    <com.android.launcher2.Workspace
        android:id="@+id/workspace"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="horizontal"
        android:fadeScrollbars="true"
        launcher:defaultScreen="2">
    <!-- 上面这行可以确定屏幕横放时默认的桌面号 -->
        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
    </com.android.launcher2.Workspace>
<!-- 上面这几行描述了几个工作区的屏幕,描述代码为layout-land/workspace_screen

      而模拟器上能看到左右各两个小白点可以控制工作区的移动

-->

<!-- 应该对应的是ClippedImageView类-->
    <com.android.launcher2.ClippedImageView
        android:id="@+id/previous_screen"
        android:layout_width="93dip"
        android:layout_height="@dimen/button_bar_height"
        android:layout_gravity="bottom|left"
        android:layout_marginLeft="6dip"

        android:scaleType="center"
        android:src="@drawable/home_arrows_left"
        
        android:onClick="previousScreen"

        launcher:ignoreZone="56dip"

        android:focusable="true"
        android:clickable="true" />
<!-- 定义了previousScreen 按钮,即左下脚的白色小点,用来控制移动-->
    <com.android.launcher2.ClippedImageView
        android:id="@+id/next_screen"
        android:layout_width="93dip"
        android:layout_height="@dimen/button_bar_height"
        android:layout_gravity="bottom|right"
        android:layout_marginRight="6dip"

        android:scaleType="center"
        android:src="@drawable/home_arrows_right"
        
        android:onClick="nextScreen"
        
        launcher:ignoreZone="-56dip"
        
        android:focusable="true"
        android:clickable="true" />
<!-- 底部右边的两个白色小点 -->
    <com.android.launcher2.DeleteZone
        android:id="@+id/delete_zone"
        android:layout_width="@dimen/delete_zone_size"
        android:layout_height="@dimen/delete_zone_size"
        android:paddingLeft="@dimen/delete_zone_padding"
        android:layout_marginBottom="@dimen/half_status_bar_height"
        android:layout_gravity="right|center_vertical"

        android:scaleType="center"
        android:src="@drawable/delete_zone_selector"
        android:visibility="invisible"
        launcher:direction="vertical"
        />
<!-- 定义Trash放置的位置 右侧,中间平放-->
    <RelativeLayout
        android:id="@+id/all_apps_button_cluster"
        android:layout_height="fill_parent"
        android:layout_width="@dimen/button_bar_height_portrait"
        android:layout_gravity="right|center_vertical"
        android:layout_marginBottom="@dimen/half_status_bar_height"
        >
<!-- 定义右侧 靠近屏幕边缘的三个按钮,中间一个是all-apps

     之下是phone按钮,之上是浏览器按钮,绑定响应函数

-->
        <com.android.launcher2.HandleView
            style="@style/HotseatButton"
            android:id="@+id/all_apps_button"
            android:layout_centerVertical="true"
            android:layout_alignParentRight="true"

            android:src="@drawable/all_apps_button"
            launcher:direction="vertical"
            />

        <ImageView
            android:id="@+id/hotseat_left"
            style="@style/HotseatButton.Left"
            android:layout_below="@id/all_apps_button"

            android:src="@drawable/hotseat_phone"

            android:onClick="launchHotSeat"
            />
<!-- onClick的值为响应方法-->
        <ImageView
            android:id="@+id/hotseat_right"
            style="@style/HotseatButton.Right"
            android:layout_above="@id/all_apps_button"

            android:src="@drawable/hotseat_browser"

            android:onClick="launchHotSeat"
            />
<!-- 对浏览器进行的描述-->
    </RelativeLayout>
</com.android.launcher2.DragLayer>

### Android Launcher 的启动流程及时间点 #### 1. **Zygote 进程初始化** Android 系统中的 `Launcher` 是作为应用程序的一部分运行的,其启动过程始于系统的初始进程——`Zygote`。当设备完成引导加载程序 (Bootloader) 和 Linux 内核初始化之后,`init` 进程会创建并启动 `Zygote`[^3]。 `Zygote` 负责预加载通用类库和资源文件,并通过分叉机制快速生成新的应用程序进程。对于 `Launcher` 来说,在系统准备就绪后,它会被标记为默认的 Home 应用程序并通过特定广播触发启动。 --- #### 2. **SystemServer 初始化阶段** 在 `Zygote` 完成初始化后,`SystemServer` 进程被启动。此进程中包含了多个核心服务,其中包括 `ActivityManagerService` 和 `WindowManagerService`。这些服务负责管理应用程序生命周期以及窗口显示逻辑。 特别需要注意的是,`ActivityTaskManagerService` 中的方法 `startHomeOnAllDisplays()` 将会在适当的时间点尝试启动默认的 Home 应用 (`Launcher`): ```java // ActivityTaskManagerService.java public void startHomeOnAllDisplays() { // 遍历所有显示屏并启动对应的 Home 应用 } ``` 上述方法通常是在系统进入用户交互状态时调用,例如锁屏解锁完成后。 --- #### 3. **BroadcastReceiver 接收 ACTION_MAIN 类型 Intent** 一旦系统准备好接收用户输入操作,则会发送一个带有类别 `CATEGORY_HOME` 的隐式意图给当前设置好的默认 Home 应用。此时,`Launcher` 的入口组件(通常是定义在 `AndroidManifest.xml` 文件内的某个活动)将接收到该广播消息[^4]: ```xml <activity android:name=".Launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ``` 这一步骤标志着实际意义上的 `Launcher` 启动开始。 --- #### 4. **Application 创建与回调执行** 随着目标 Activity 实例化之前,框架层还会先实例化关联的应用对象(`Application`) 并调用其 `onCreate()` 方法。这是通过 `ActivityThread` 组件内部实现的: ```java mInstrumentation.callApplicationOnCreate(app); ``` 这里确保了全局上下文环境已经构建完毕,可以供后续业务模块使用[^2]。 随后便是具体的 UI 渲染工作,比如读取配置数据、绑定视图控件等动作均在此期间发生。 --- #### 5. **Launcher 特定行为分析** 具体到源码层面来看,《LauncherAppState》这个类承担着部分关键职责,像主题样式适配或者快捷方式数据库维护等功能都可能涉及其中[^1]。因此可以说它是整个桌面体验得以正常运作的基础之一。 最终呈现出来的效果就是我们熟悉的主屏幕界面,等待用户的进一步指令。 --- ### 总结 综上所述,从底层硬件加电到最后看到完整的图形化操作系统首页之间经历了一系列复杂而精密的过程。每一个环节都需要紧密配合才能顺利完成任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值