Android 源码 <package> 了解 [4]

本文深入剖析Android源码,从启动过程到系统应用如Launcher3的工作原理。详细讲解了Launcher3的数据加载过程,包括应用信息获取、数据适配与显示。同时,概述了其他系统组件如蓝牙、相机、日历等的功能实现。通过对源码的解析,揭示了Android系统的内部工作机制。

在这里插入图片描述

Read The Fucking Source Code. —— Linus

站在’巨人’的肩膀上开始自己的旅途。—— 佚名

愉快的周末,从打开💻开始,到骑行归来结束。—— 佚名

忙忙碌碌又一周,城市的快节奏似乎是时间的轮盘上了润滑剂比以往转得更快,早上进入地铁看天空还是那么明亮,晚上踏出地铁口已是昏暗。

工作日确实忙绿,然而周末呢?

上午还在养精蓄锐 (懒床)
中午便是一天的开始
下午又能干嘛 (摆烂一下午)
晚上呢 (继续摆烂)
你说这一周能过得不快吗 🤔

文章系列

注: 本系列文章源码基于 Android 11-r21 master 分支

Warm-up

源码阅读可是一项耗时、费力的‘大工程’,对于大多数人来说可能是多么的枯燥无味,为了消除枯燥感,尝试接触新鲜事物可是一个良好的治疗药方,前三篇都是在看 Android 启动,现在不妨在下一次开始之前看看别的东西 ——— package,看看这里面都是些什么?

package

对于系统应用就就不过多展开,这种更适合当前从事应用开发的同学,我们就挑拣几个感兴趣的模块稍微展开。

packages 
    —— apps           系统应用
        —— basicSmsReceiver
            1.1 注册一个广播接收器 sms reciever,接受回调中通过解析 intent -> bundle -> puts 获得短信数据
            1.2 收到的每一条短信都需要一个唯一的通知ID,notificationId(先猜测这是通知渠道ID),通知点击事件将通过 pendingIntent 跳转到短信内容页面
            1.3 这个通知ID使用 SP 存储,下一次接收到通知时候自增,最大值不能超过最大阈值 32765(超过将重置)

        —— bluetooth 
            1.1 蓝牙实现还是比较底层的,最终会接触到硬件设备,那么这里需要 jni 调用,此前需要初始化 native
            1.2 蓝牙数据使用 room 框架做数据的 CURD 操作,主要数据是蓝牙地址、上一次激活时间
            1.3 设备管理,负责监听设备和蓝牙之间的连接、断开事件
            1.4 蓝牙服务是由多个子服务、通讯规范整合形成的,比如我们知道的有:蓝牙音频传输协议服务(A2PD)、音量控制服务、音视频远程控制规范(AVRCP)等等
            1.5 蓝牙是如何通讯的?a2dp —— a2dpshink 简单理解是 C——S 通讯(物联网应该接触到比较多,我们就略过吧)
            1.6 关键代码还是在 jni,这里就不展开了

        —— browser2
            1.1 【略】一个用于测试的 webview,构建生产环境使用的浏览器请参考链接;浏览器可又是一个大型项目——Chromium

        —— calendar
            1.1 好家伙,此模块用 kotlin 写,差点看不懂了 kt 的语法了解的还是太少,不影响我们粗略阅读
            1.2 日历除了看日期基本功能,还有其他的附加功能,比如闹钟提醒需要 AlarmScheduler,事件通知需要 notification
            1.3 日历在此前是我们遇到的第一个 UI 界面比较多的应用,对于应用开发者来说看看官方如何实现‘日历’可有一定参考;如何巧用列表控件、数据适配器。
 
        —— camera2
            1.1 相机主要的是图片,图片到底就是像素——一个多维的字节数组,对图片的处理最终既是对每一个像素的处理、转变,实际处理还是在 native
            1.2 【略】主要涉及对图片和音频的处理,如果当前从事相关开发我觉得可以继续往下看看,目前先略过(主要是代码不少啊😭)

        —— car
        —— carrierConfig
        —— cellBroadcastReceiver
        —— certInstaller
        —— contacts
        —— deskClock
        —— DevCamera
        —— dialer
        —— documentUI
        —— emergencyInfo
        —— Gallery2
        —— Gallery
        —— HTMLViewer
        —— keyChain
        —— Launcher3 【重点!重点!重点!】
            1.1 桌面应用,不就是我们看到的手机桌面嘛!看名称是 Launcher3 这个是原声系统的,如果你在友商手机看,这个名字可能是 ******.Launcher;
                原生的桌面应用可能太丑了,厂商通常都会开发自己的桌面应用程序,美化桌面,吸引用户————爱美之心。
            1.2 看 Androidmanifest 只有一个activity,便是桌面程序的主界面 com.android.launcher3.Launcher,这是一个可以拖拽的、有状态的 activity
            1.3 UI 界面我们不关心如何,关注的是应用数据 AppInfo 来源,数据是如何获取到的?数据都包含了哪些内容(应用程序直接找 model 模块——MVC、MVVM、MVP)?
                ——【ItemInfo】 
                    ID:每一条数据在数据库的ID  
                    type:应用程序、文件夹、部件、自定义部件、快捷方式
                    screenID:桌面ID,比如应用在桌面快速启动可以通过这个ID找到对应的应用程序启动数据
                    cellX/Y:坐标(桌面可以看作是一个二维网格)
                    title:最明显的应用图标或者文件夹名称、文件名称(桌面程序不仅仅是我们理解的那个只有只用应用图标的桌面)
                    componentName:这个就比较重要了,通常携带目标界面的启动类和必要数据
                    userHandle:通过 framework/base/core/java/android/os/Process 的 myUserHandle 方法赋值;
                                userHandle —— 当前进程的用户句柄,系统缓存的实例,是在 zygote 进程中被创建。
                                user 即是用户,这里的用户划分是什么:系统用户、所有用户、当前活动用户(大概意思就是根据 uid 获取特定的当前进程合适的 handle 来发送消息)

                    ——【FloderInfo】
                        lableState:标签
                        workspaceItemInfo:工作目录下可启动的应用程序(父类是 ItemInfoWithIcon)

                    ——【LauncherAppWidgetInfo】
                        intent:
                        compotentName:能够启动的,基本都包含着两个

                    ——【ItemInfoWithIcon】 
                        bitmap:作为应用程序,应用图标必不可少
                        process:应用下载进度,最大 100
                        ——【AppInfo】
                            intent:启动程序的常规方式就是通过 intent
                        ——【PackageItemInfo】
                            packagename:应用层开发者也知道 Android 在应用层有提供通过包名启动程序的接口
              
            1.4 说到底,数据从那里来?【重点!这才是本次真正关注的点】
                packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java 就是线程需要执行的任务;直接看 run 方法,加载任务大概做了这些事:
                1.4.1 loadWorkspace:默认从当前应用收藏夹 favorites uri 通过 contentResolve 解析数据(数据解析部分有点复杂不展开了)
                1.4.2 loadAllApps:需要的数据是 LauncherActivityInfo,实际获取数据的转到了 frameworks/base/core/java/android/content/pm/LauncherApps.java;
                      这是一个 AIDL 接口,ILauncherApps.aidl 实现类是 LauncherAppsService;获取到的事 resolveInfo 再通过 convertToActivitList 获得最终数据
                     (流程比较长点到为止,其实就是查询应用信息在组装转换为最后需要的)
		1.4.3 loadDeepShortcuts: shortcut 快捷方式;通过 usermanager getUserProfiles 解析获得
                     (framework/base/core/java/android/os/UserManager.java),实际上就是当前进程的 UserHandle
		1.4.4 widgetsModel.update(mApp, null):从应用中更新或者解析创建桌面部件

/*到这里,桌面需要显示的应用信息已经获得,后面就是通过适配器把数据填充、显示,最后就是我们能看到的设备桌面*/

        —— legacyCamera
        —— managedProvisioning
        —— messaging
        —— music
        —— musicFX
        —— nfc
        —— onDeviceAppPrediction
        —— oneTimeInitializer
        —— phoneCommon
        —— protips
        —— provision
        —— quickAccessWallet
        —— quickSearchBox
        —— safetyRegulartoryInfo
        —— sampleLocationAttribution
        —— secureElement
        —— settings
        —— settingsIntelligence
        —— soundRecorder
        —— stk
        —— storageManager
        —— tag
        —— terminal
        —— themepicker
        —— timeZoneData
        —— TimeZoneUpdate
        —— traceur 
        —— tv
        —— tvSettings
        —— universalMediaPlayer
        —— wallpaperPicker
        —— wallpaperPicker2
    
    —— inputmethods   输入法

    —— modules

    —— providers      数据来源(其实就是对操作数据库,只是 CURD 操作系统封装为内容提供者——contentprovider,有一部分系统应用的数据源内容提供者,比如:联系人、日历)
        —— blocedNumberProvider   被屏蔽的电话提供者(数据库:blockednumbers.db)
        —— bookmarkProvider       书签提供者(一看代码无任何实现,留着估计是为了兼容考虑)
        —— calenderProvider       日历(数据库:calender.db;包含日历闹钟提醒、日历信息更新等)
        —— contactsProvider       联系人
        —— downloadProvider       接收器——通过系统广播接受,通知——notificationManager,扫描——mediaScannerConnectionClient,
                                  存储——fileSystemProvider,线程——发起下载请求、文件磁盘持久化
                                 (数据库:downloads.db;JobService——特定条件下执行的后台服务,比如:下载并安装完毕需删除安装包,删除的不仅是安装包文件还有数据库中的记录)
        —— mediaProvider          多媒体(媒体访问往往需要权限,提供音频播放、图片视频选择界面)
        —— partnerBookmarksProvider 合作伙伴书签?不太了解
        —— telephonyProvider      电话(运营商信息:CarrierInformation.db   运营商标识:carrierIdentification.d   短信内容:mmssms.db  电话内容:telephony.db)
        —— tvProvider             电视(机顶盒?  数据库:tv.db)
        —— userDictionaryProvider 用户字典

    —— screensavers   屏保
        —— basic          基础工具(只有一个颜色选择)
        —— phototable     界面 activity、数据源 source、适配器 adapter

    —— services
        —— alternativeNetworkAccess  网络相关
        —— builtinPrint              打印服务
        —— car                       车载服务
        —— mms                       多媒体消息(multimedia messaging service)
        —— mtp                       媒体传输协议(media transfer protocol)
        —— telecomm                  通讯工具
            —— bluetooth       蓝牙
            —— callfiltering   电话过滤
            —— callrederiction 电话重定向
        —— telephone                 电话服务

    —— wallpapers     壁纸
        —— live wallpapers           实时壁纸

附加

参考链接

  • A2PD:Advanced Audio Distribution Profile 蓝牙音频传输模型规范
  • AVRCP:Audio/Video Remote Control Profile 音视频远程控制规范
  • 浏览器:https://www.chromium.org/developers/how-tos/android-build-instructions
  • Chromium:https://github.com/chromium/chromium
<think>首先,用户在询问关于ProGuard配置文件中`goals`或`goal`标签爆红的问题。意思是配置文件中有个错误导致红色错误提示出现。用户提到了“爆红”,这在中文语境中通常指在IDE或编译环境中显示的红色错误标记。用户提供了一些参考引用,我应该参考这些,因为它们来自系统指令。引用[1]、[2]、[3]、[4]是关于Android构建系统的信息,包括LOCAL_MODULE_PATH、LOCAL_PACKAGE_NAME等环境变量和宏。虽然与ProGuard相关,但不是直接关于ProGuard的问题。回顾系统指令:-使用$...$格式的行内数学表达式,但这里可能用不到数学表达式。-独立公式用$$...$$,但不太需要。-LaTeX语法正确。-用中文回答。-生成相关问题。-在引用的段落末尾添加引用标识。用户在说:“我想解决proguard配置文件中爆红的问题”,所以核心问题是解决ProGuard配置文件中的错误。用户的问题焦点是“proguardgoalsgoal标签爆红解决方案”。用户可能打错了字,可能是“goal标签”,可能指的是ProGuard配置文件中的元素报错。在ProGuard配置中,没有直接叫“goals”或“goal”的标签;但可能用户指的是配置文件中某个特定部分出现了错误,导致IDEAndroidStudio显示红线。在Android构建过程中,ProGuard配置文件(通常是proguard-rules.pro)中可能的错误包括:-无效的指令-语法错误-未识别的选项用户指定了“goalsgoal标签”,这可能是个误解。可能在文件中有类似`-keepclass*implementscom.example.Goal`的东西,但`Goal`应该是个接口名。可能是用户在配置ProGuard时遇到了错误,错误是关于某个goal或标签的。我应该回想ProGuard配置的标准。ProGuard用于混淆和压缩代码,配置文件使用特定的指令。常见错误包括:-缺少`-keep`指令导致类被删除。-未正确指定入口点。-语法错误。用户提到“爆红”,所以可能是在AndroidStudio中编辑文件时,IDE报告了语法错误。现在,解决方案:1.**检查语法**:确保配置文件遵循ProGuard语法。文件应以`.pro`扩展名结尾,使用指令如`-keep`、`-dontwarn`等。2.**验证指令**:如果使用了无效指令,IDE可能会爆红。确保指令正确拼写。3.**处理特定问题**:如果爆红是关于“goals”或“goal”,可能是类或包名错误。用户可能需要更新以匹配实际代码。4.**运行清理和重建**:在AndroidStudio中,尝试“File>InvalidateCaches/Restart”或清理项目。5.**查看ProGuard文档**:参考官方文档解决常见错误。由于用户提供了引用,但它们不直接相关,我应该只参考它们如果相关。引用是关于Android.mk和环境变量的,与ProGuard配置文件不直接关联。所以,可能只需一般性建议。结构回答:-先理解用户的问题。-提供逐步解决方案。-确保中文回答。-生成相关问题。-末尾添加引用标识如果引用了内容。用户说“上述引用仅供参考”,所以我可能需要参考引用但专注主题。最后,生成相关问题:-基于ProGuard配置错误主题。回答框架:1.确认问题:解释什么是“goalsgoal标签”爆红。2.提供原因和解决方案。3.给示例代码或步骤。4.添加引用标记如果需要。5.生成相关问题。</think> ### ProGuard配置文件中`goal`标签爆红问题的解决方案 您遇到的ProGuard配置文件中“`goal`标签爆红”问题,通常是配置文件中存在语法错误或无效指令导致的。在Android构建过程中,ProGuard(用于代码混淆和优化)的配置文件(如`proguard-rules.pro`)如果包含无效元素(如`goal`标签),IDE(如Android Studio)会显示红色错误标识(爆红)。以下我将逐步解释原因并提供解决方案。参考您提供的引用([^1]、[^2]、[^3]、[^4]),我注意到Android构建系统的环境变量和宏(如`LOCAL_MODULE_PATH`)可能间接影响构建行为,但核心问题源于ProGuard配置本身。我将结合通用Android开发实践为您解决。 #### 问题原因 - **为什么爆红?** ProGuard配置文件使用基于规则的语法(如`-keep`、`-dontwarn`),不是XML标签格式。`goal`标签可能是拼写错误或不支持的指令,导致语法验证失败。 - 常见触发场景: - 错误使用`goal`代替有效的ProGuard指令。 - 目标类名或包名错误(如混淆规则中引用了不存在的类)。 - Android构建环境问题(如Gradle版本冲突或缓存错误)。 - 引用相关上下文:在Android构建中,ProGuard规则由`LOCAL_PROGUARD_FLAG_FILES`指定(在`Android.mk`中定义[^2]),如果规则文件无效,会触发构建错误。 #### 逐步解决方案 按照以下步骤检查和修复问题,确保语法正确(适用于Android Studio或命令行构建): 1. **检查并修复ProGuard配置文件语法**: - ProGuard配置文件(如`proguard-rules.pro`)必须只使用有效的指令,例如: - 正确示例:`-keep class com.example.MyClass { *; }`(用于保留特定类)。 - 错误示例:`<goal>com.example.Goal</goal>`(无效XML标签,会爆红)。 - **解决方案**: - 打开您的`proguard-rules.pro`文件。 - 搜索所有`goal`或类似关键词。通常,应替换为标准的ProGuard指令: - 如果目标是保留某个接口或类,使用`-keep interface`或`-keep class`。 - 例如,原问题中的`goal`可能是为`com.example.Goal`接口设置混淆规则,正确语法应为: ```pro -keep interface com.example.Goal { public *; } ``` - 确保所有指令以短横线开头(如`-keep`),不包含XML标签。 - 如果引用外部JAR或库,添加`-libraryjars`指令(如`-libraryjars libs/example.jar`)。 2. **验证类名和路径是否正确**: - 爆红可能是配置文件引用了代码中不存在的类名。 - **解决方案**: - 在Android Studio中,使用"Find in Path"(Ctrl+Shift+F)搜索目标类名(如`com.example.Goal`)。 - 如果类名错误或包路径不一致,更新配置文件以匹配实际代码结构。 - 如果类不存在,删除或注释掉相关规则(使用`#`开头)。 3. **清理构建缓存并重启IDE**: - 缓存错误可能导致IDE误报爆红问题。 - **解决方案**: - 在Android Studio中选择:"File > Invalidate Caches / Restart",然后选择“Invalidate and Restart”。 - 或运行命令行清理: ```bash # 清理Gradle缓存 ./gradlew cleanBuildCache # 清理项目构建 ./gradlew clean ``` 4. **检查构建环境和依赖**: - 如果使用了Android.mk(如引用[^2]中`LOCAL_PROGUARD_FLAG_FILES`),确保配置文件路径正确。例如: ```makefile LOCAL_PROGUARD_FLAG_FILES := proguard-rules.pro ``` - 路径错误会导致规则未被加载。 - 确保ProGuard已启用:在`build.gradle`中设置`minifyEnabled true`(用于release构建): ```groovy android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } ``` - 如果`minifyEnabled`为false,爆红可能无关紧要。 5. **运行调试构建获取详细日志**: - 使用Gradle命令生成ProGuard调试输出,定位具体错误行。 - **解决方案**: ```bash # 运行release构建并打印ProGuard日志 ./gradlew assembleRelease --info | grep -i "proguard" ``` - 日志会显示错误位置(如`Error in proguard-rules.pro at line 10: Invalid token 'goal'`),据此修正。 #### 验证修复 - 修复后,在Android Studio中保存文件。 - 执行构建:`./gradlew clean assembleDebug`。 - 确认爆红消失,构建成功。 如果问题持续,可能是Gradle版本冲突(如AGP 8.x+有变化),建议升级依赖。参考环境变量(如`OUT_DIR`指定输出路径[^3]),构建错误日志通常位于`out/production/proguard`目录下。 ### 相关问题 1. 如何优化ProGuard规则以提高Android应用的压缩效果? (例如使用`-optimizations`指令减少冗余) 2. ProGuard和R8在Android构建中的区别及迁移注意事项? 3. 如何解决ProGuard构建过程中的类NotFoundException错误? (涉及`-dontwarn`和`-keep`规则) 4. 在Android.mk文件中如何正确配置ProGuard规则路径以避免构建失败? (参考`LOCAL_PROGUARD_FLAG_FILES`用法) 如您在实施中仍有问题,请提供proguard-rules.pro的具体片段,我可以帮助进一步分析。 [^1]: 引用[1]: 定义目标生成路径,默认在out的对应目录 LOCAL_MODULE_RELATIVE_PATH := $(LOCAL_PATH)/lib LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin. [^2]: 引用[2]: LOCAL_SRC_FILES,app的所有源码,可以调用all-java-files-under得到,如果是java源码的话。 LOCAL_PACKAGE_NAME,package的名字,这个名字在脚本中将标识这个app或package。 $(CLEAR_VARS)指的是clear_vars.mk,脚本会清空所有LOCAL_xxx的变量. [^3]: 引用[3]: 环境变量的意义: TARGET_BUILD_VARIANT:表示编译产品的类型,可能的值有eng、user和userdebug OUT_DIR:指定编译结果的输出目录. [^4]: 引用[4]: subdir_makefiles := \ $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk) include $(subdir_makefiles).
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值