apk简单介绍
- APK 的组成
- apk安装流程
- app的启动过程
- apk打包流程
- AIDL
- AIDL介绍
- 为什么要设计这门语言
- 它有哪些语法?
- 默认支持的数据类型包括
- 什么是apk打包流程
- 了解打包流程能做什么操作
APK 的组成
APK 其实是一个 zip 类型的压缩包,而一个典型的 APK 通常都会包含了以下七部分的内容:
- AndroidManifest.xml:如果 App 是一本书,那么这个文件就是它的 “封面” 和 “目录” 。它记载了 App 的名称、权限声明、所包含的组件等一系列信息。
- classes.dex:它是由项目源码生成的 .class 文件经过进一步地转换而生成的 Android 系统可识别的 Dalvik Byte Code。并且,由于 Android 系统中的字节码和标准 JVM 中的字节码是有区别的,所以如果 App 中引用了第三方 jar 包的话,那么通常情况下它也会被包含在 classes.dex 中。
- resources.arsc:资源索引表,包含编译后的二进制资源文件。每当在 res 文件夹下放一个文件时,aapt 就会自动生成对应的 id 并保存在 .R 文件中,但 .R 文件仅仅只是保证编译程序不会报错,实际上在应用运行时,系统会根据 ID 寻找对应的资源路径,而 resources.arsc 文件就是用来记录这些 ID 和 资源文件位置对应关系 的文件。
- res 目录:未编译的资源文件。
- asserts:额外建立的资源文件夹。res 和 assets 的不同在于 res 目录下的文件会在 .R 文件中生成对应的资源 ID,而 assets 不会自动生成对应的 ID,而是通过 AssetManager 类的接口来获取。
- libs 目录:如果存在的话,存放的是 ndk 编出来的 so 库 。
- META-INF 目录:用于保存 App 的签名和校验信息,以保证程序的完整性。当生成 APK 包时,系统会对包中的所有内容做一次校验,然后将结果保存在这里。而手机在安装这一 App 时还会对内容再做一次校验,并和 META-INF 中的值进行比较,以避免 APK 被恶意篡改。其中包含如下三个文件:
- MANIFEST.MF:其中每一个资源文件都有一个对应的 SHA-256-Digest(SHA1) 签名,MANIFEST.MF 文件的 SHA256(SHA1) 经过 base64 编码的结果即为 CERT.SF 中的 SHA256(SHA1)-Digest-Manifest 值。
- CERT.SF:除了开头处定义的 SHA256(SHA1)-Digest-Manifest 值,后面几项的值是对 MANIFEST.MF 文件中的每项再次 SHA256(SHA1) 经过 base64 编码后的值。
- CERT.RSA:其中包含了公钥、加密算法等信息。首先,对前一步生成的 CERT.SF 使用了 SHA256(SHA1)生成了数字摘要并使用了 RSA 加密,接着,利用了开发者私钥进行签名。然后,在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。
apk安装流程
Android apk的安装过程主要氛围以下几步:
- 复制apk到/data/app目录下,解压并扫描安装包
- 资源管理器解析apk里的资源文件
- 解析AndroidManifest文件,并在/data/data/目录下创建对应的应用数据目录
- 然后对.dex文件进行优化,并保存在dalvik-cache目录下
- 将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中
- 安装完成后,发送广播
app的启动过程
- 当点击桌面app时,launch进程中的startActvity方法通过binder通信调用system_server进程管理的AMS中的startActivity,而AMS又继续调用ATMS(ActivityTaskManagerService)的startActivity方法进行真正的启动
- system_server进程收到消息后向Zygote进程发送创建进程的请求(通过socket通信)
- Zygote进程fork出app进程,并执行ActivityThread的main方法
- 从main方法中可以看到创建ActivityThread之后调用了thread.attach(false, startSeq)方法,并同时初始化ApplicationThread用于和AMS通信
- App进程,通过Binder向sytem_server进程发起attachApplication请求,这里实际上就是APP进程通过Binder调用sytem_server进程中AMS的attachApplication方法,AMS的attachApplication方法的作用是将ApplicationThread对象与AMS绑定
- system_server进程在收到attachApplication的请求,进行一些准备工作后,再通过binder IPC向App进程发送handleBindApplication请求(初始化Application并调用onCreate方法)和scheduleLaunchActivity请求(创建启动Activity)
- App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送BIND_APPLICATION和LAUNCH_ACTIVITY消息,这里注意的是AMS和主线程并不直接通信,而是AMS和主线程的内部类ApplicationThread通过Binder通信,ApplicationThread再和主线程通过Handler消息交互
- 主线程在收到Message后,创建Application并调用onCreate方法,再通过反射机制创建目标Activity,并回调Activity.onCreate()等方法
- 到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染后显示APP主界面
apk打包流程
生成一个Apk包需要经历的过程,大致可以分为9步
- aapt把resources目录下的资源生成R.java文件 ,并为AndroidManifest.xml生成Manifest.java类
- 通过AIDL工具处理AIDL文件,生成对应的java类
- JavaCompiler把所有的Java源文件编译成class文件,包括:aapt生成的、aidl生成的、项目中自有的java源文件
- 使用proguard混淆,并生成一个proguardMapping.xml文件(可选项:可以混淆也可以不混淆)
- 使用dex工具把所有的class文件生成.dex文件
- 使用aapt资源打包工具把resources、assets目录下的资源打包成一个_ap文件
- 使用apkbuilder把所有的dex、_ap文件、AndroidManifest.xml文件打包成一个未签名的apk
- 使用jarsinger生成一个签名过的apk包
- 使用zipalign工具对要发布的apk文件进行对齐操作,以便在运行时节约内存
AIDL
AIDL介绍
AIDL(Android 接口定义语言),可以使用它定义客户端与服务端进程间通信(IPC)的编程接口。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求。
AIDL是用于定义服务端和客户端通信接口的一种描述语言,可以拿来生产IPC代码,从某种意义上说AIDL其实就是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此生产的一个Interface的实例代码, AIDL其实是为了避免我们重复写代码而出现的一个模板。
为什么要设计这门语言
设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信
它有哪些语法?
AIDL这门语言比较简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别,毕竟它只是被创造出来简化Android程序员工作的,太复杂不好,所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点:
- 文件类型
用AIDL书写的文件的后缀是 .aidl,而不是 .java - 数据类型
AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下,在 Java 中这种情况是不需要导包的。比如现在我们编写了两个文件,一个叫做Book.java ,另一个叫做 BookManager.aidl,它们都在com.lypeer.aidldemo包下 ,现在我们需要在.aidl 文件里使用Book对象,那么我们就必须在 .aidl 文件里面写上 import com.lypeer.aidldemo.Book,哪怕 .java 文件和 .aidl 文件就在一个包下
默认支持的数据类型包括
- Java中的八种基本数据类型:byte,short,int,long,float,double,boolean,char
- String 类型
- CharSequence类型
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(List可以使用泛型)
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(Map是不支持泛型的)
什么是apk打包流程
app的打包流程是指通过把资源(图片、文本)、源代码等资源打包成一个apk的过程
了解打包流程能做什么操作
通过了解打包流程我们可以对app打包的过程进行干涉
比如:
- 在热更新项目中我们可以干涉R.java中资源id的生成来防止宿主app和插件app资源冲突
- 我们可以在编译前通过注解的形式生成辅助类从而优化代码量和代码结构(ButterKnife、Digger)
- 通过javac把源代码编译成class文件后,在生成dex前我们可以对class文件进行干涉,动态的生成class文件(无痕埋点)