一个Android应用,是怎么被系统认识的

一个Android应用,是怎么被系统认识的 你下载了一个App点击安装然后桌面上出现了一个图标。你点击图标App打开了。这个过程看起来理所当然。但在这背后Android系统做了一件很重要的事它需要先认识这个App。认识它叫什么认识它能做什么认识它需要什么权限认识它从哪里开始运行。这些信息全部写在一个文件里。这个文件叫做AndroidManifest.xml。它是每一个Android应用的身份证也是Android系统和应用之间的第一次握手。一、先把这个文件放在哪里说清楚AndroidManifest.xml住在每个Android项目的固定位置。如果你用Android Studio创建一个新项目它在这里你的项目/ ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── AndroidManifest.xml ← 就在这里 │ │ │ ├── java/ │ │ │ └── res/它不在java文件夹里不在res资源文件夹里它单独待在main目录下地位特殊。每个Android应用有且只有一个AndroidManifest.xml。当你构建应用的时候如果你用了多个模块每个模块可能有自己的Manifest文件但最终构建工具会把它们合并成一个打进APK包里。系统安装APK的时候读的就是这个合并后的文件。二、打开文件先看骨架一个最基础的AndroidManifest.xml长这样?xml version1.0 encodingutf-8?manifestxmlns:androidhttp://schemas.android.com/apk/res/androidpackagecom.example.myappuses-permissionandroid:nameandroid.permission.INTERNET/applicationandroid:allowBackuptrueandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:themestyle/AppThemeactivityandroid:name.MainActivityandroid:exportedtrueintent-filteractionandroid:nameandroid.intent.action.MAIN/categoryandroid:nameandroid.intent.category.LAUNCHER//intent-filter/activity/application/manifest看起来有点多但结构很清晰。最外层是manifest标签这是根元素整个文件都包在里面。里面有两类东西一类是直接挂在manifest下面的比如权限声明。另一类是包在application标签里的比如Activity、Service这些组件。这个层级关系不是随意的是有严格规定的。三、manifest标签应用的基本身份manifestxmlns:androidhttp://schemas.android.com/apk/res/androidpackagecom.example.myapp这一行包含了两个重要信息。xmlns:android是XML命名空间的声明告诉解析器android:这个前缀对应的是Android的属性定义。这行几乎是固定的每个Manifest文件都有不需要改。package是应用的包名这是应用在Android系统里的唯一标识符。包名就像你的身份证号码。两个应用可以有一样的名字可以有一样的图标但不能有一样的包名。如果你在Google Play上发布应用包名一旦确定就不能改了。改了包名系统会认为这是一个全新的应用用户需要重新安装之前的数据也不会自动迁移。包名的命名规范通常是反向域名格式com.公司名.应用名比如微信是com.tencent.mm支付宝是com.eg.osc.alipay。四、权限声明应用想要什么在manifest标签里application标签外面是权限声明的位置。uses-permissionandroid:nameandroid.permission.INTERNET/uses-permissionandroid:nameandroid.permission.CAMERA/uses-permissionandroid:nameandroid.permission.READ_CONTACTS/这些声明告诉系统这个应用需要用到哪些敏感能力。权限分两类。普通权限系统安装时自动授予不需要用户确认。比如INTERNET访问网络。几乎所有应用都需要系统直接给不会弹窗问用户。危险权限需要在运行时弹窗请求用户授权。比如CAMERA访问摄像头。READ_CONTACTS读取联系人。ACCESS_FINE_LOCATION获取精确位置。这些权限即使你在Manifest里声明了用户也可以拒绝。所以在代码里每次使用这些功能之前都需要先检查权限是否已经被授予。有一个常见的错误是在Manifest里忘记声明权限然后在代码里直接使用相关功能。结果是应用崩溃报SecurityException。系统的逻辑很简单你没有声明你要用这个权限我就不给你用不管你代码里怎么写。五、application标签应用的整体配置applicationandroid:allowBackuptrueandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:themestyle/AppThemeapplication标签是整个应用的全局配置。android:icon应用图标。mipmap/ic_launcher指向res/mipmap目录下的ic_launcher图片。为什么用mipmap而不是drawable因为mipmap目录专门为图标设计系统会根据设备屏幕密度自动选择合适分辨率的图标显示效果更好。android:label应用名称。string/app_name指向res/values/strings.xml里定义的字符串。这个名字会显示在桌面图标下面显示在应用列表里显示在最近任务列表里。android:theme应用的默认主题。这里设置的主题会应用到所有Activity除非某个Activity单独指定了自己的主题。android:allowBackup是否允许系统备份应用数据。设置为true用户换手机的时候应用数据可以通过Google备份恢复。但如果你的应用有敏感数据比如账号信息可能需要设置为false或者自定义备份规则。还有一个重要的属性android:name用来指定自定义的Application类。applicationandroid:name.MyApplication...如果你需要在应用启动时做一些全局初始化比如初始化第三方SDK就需要创建一个继承Application的类然后在这里声明它。六、Activity应用的每一个界面application标签里最常见的子标签是activity。activityandroid:name.MainActivityandroid:exportedtrueandroid:screenOrientationportraitandroid:windowSoftInputModeadjustResizeintent-filteractionandroid:nameandroid.intent.action.MAIN/categoryandroid:nameandroid.intent.category.LAUNCHER//intent-filter/activityandroid:nameActivity的类名。.MainActivity前面的点表示相对于package属性的包名完整的类名是com.example.myapp.MainActivity。android:exported这个Activity是否可以被其他应用启动。Android 12之后这个属性是必填的不写会报错。如果你的Activity有intent-filter通常需要设置为true因为系统需要能够启动它。如果是应用内部使用的Activity设置为false其他应用就无法直接启动它。android:screenOrientation屏幕方向。portrait是竖屏landscape是横屏sensor是跟随传感器自动旋转。android:windowSoftInputMode软键盘弹出时的行为。adjustResize键盘弹出时界面会压缩给键盘让出空间。adjustPan键盘弹出时界面整体上移不压缩。这个属性经常影响输入框的显示效果是一个容易踩坑的地方。七、intent-filter告诉系统我能响应什么intent-filter是Manifest里最有意思的部分之一。它告诉系统这个组件能响应哪些Intent。最常见的用法是声明启动Activityintent-filteractionandroid:nameandroid.intent.action.MAIN/categoryandroid:nameandroid.intent.category.LAUNCHER//intent-filteraction.MAIN表示这是应用的主入口。category.LAUNCHER表示这个Activity应该出现在桌面启动器里。这两个加在一起告诉系统这是应用的启动Activity在桌面上给它一个图标。一个应用可以有多个Activity有LAUNCHER的intent-filter桌面上就会出现多个图标每个图标启动不同的界面。intent-filter还有另一个重要用途深度链接intent-filteractionandroid:nameandroid.intent.action.VIEW/categoryandroid:nameandroid.intent.category.DEFAULT/categoryandroid:nameandroid.intent.category.BROWSABLE/dataandroid:schemehttpsandroid:hostwww.example.comandroid:pathPrefix/product//intent-filter这段配置告诉系统当用户点击https://www.example.com/product/...这样的链接时可以用这个Activity来打开。这就是为什么你在浏览器里点一个链接系统会弹出选择应用打开的对话框因为多个应用都声明了能处理这种链接。八、Service、BroadcastReceiver、ContentProvider除了Activityapplication里还可以声明其他三种组件。Service后台服务serviceandroid:name.MusicServiceandroid:exportedfalse/Service在后台运行没有界面。播放音乐、下载文件、处理网络请求这些适合放在Service里。BroadcastReceiver广播接收器receiverandroid:name.BootReceiverandroid:exportedtrueintent-filteractionandroid:nameandroid.intent.action.BOOT_COMPLETED//intent-filter/receiver这个配置告诉系统手机开机完成后通知我一声。BootReceiver收到这个广播可以做一些开机初始化的工作。ContentProvider内容提供者providerandroid:name.MyContentProviderandroid:authoritiescom.example.myapp.providerandroid:exportedfalse/ContentProvider用于在应用之间共享数据。比如系统的联系人数据就是通过ContentProvider暴露给其他应用的。android:authorities是这个Provider的唯一标识其他应用通过这个标识来访问数据。九、系统是怎么读这个文件的现在把整个流程串起来。你把APK发给用户用户点击安装。系统的包管理器PackageManager解压APK找到AndroidManifest.xml开始解析。它读取package属性记录这个应用的包名。它读取所有的uses-permission记录这个应用申请了哪些权限对于危险权限标记为待用户确认。它读取application标签记录应用图标、名称、主题。它读取所有的activity、service、receiver、provider把这些组件注册到系统里。特别是那些有LAUNCHER的intent-filter的Activity系统会在桌面上为它们创建图标。安装完成。用户点击桌面图标。系统查找哪个Activity有action.MAIN和category.LAUNCHER找到MainActivity。系统启动MainActivity所在的进程如果进程不存在先创建进程。进程创建后系统创建Application对象如果Manifest里声明了自定义Application类就创建那个类的实例否则创建默认的Application实例。Application的onCreate方法被调用全局初始化在这里完成。然后系统创建MainActivity的实例调用它的onCreate方法。界面出现在屏幕上。整个过程AndroidManifest.xml是起点是系统认识这个应用的第一份资料也是整个启动流程的地图。有一个细节很多初学者会忽略。你在项目里新建了一个Activity写好了代码运行崩溃了。报错信息说找不到这个Activity。原因几乎一定是你忘了在AndroidManifest.xml里声明它。Android系统不会自动发现你的组件你必须主动告诉它们的存在。这就是AndroidManifest.xml的本质不是配置文件是一份声明。你向系统声明这个应用存在它有这些组件它需要这些权限它能响应这些事件。系统读完这份声明才真正认识了你的应用。