Android Framework

  • 1、Android 系统架构
  • 1.应用程序
  • 2、Java API 框架
  • 3、系统运行库
  • 1原生 C/C++ 库
  • 2Android Runtime
  • 4、硬件抽象层 (HAL)
  • 5、Linux 内核
  • 2、View 的事件分发机制?滑动冲突怎么解决?
  • 3、View 的绘制流程?
  • 4、跨进程通信
  • Android 中进程和线程的关系?区别?
  • 如何开启多进程?应用是否可以开启 N 个进程?
  • 为何需要 IPC?多进程通信可能会出现的问题?
  • Android中IPC方式、各种方式优缺点,为什么选择Binder?
  • Binder机制的作用和原理?
  • Binder框架中ServiceManager的作用?
  • Bundle传递对象为什么需要序列化?Serialzable和Parcelable的区别?
  • 讲讲AIDL?原理是什么?如何优化多模块都使用AIDL的情况?
  • 5、Android 系统启动流程是什么?
  • 6、启动一个程序,可以主界面点击图标进入,也可以从一个程序中跳转过去,二者有什么区别?
  • 7、AMS 家族重要术语解释。
  • 8、App 启动流程(Activity 的冷启动流程)。
  • 9、ActivityThread 工作原理。
  • 10、说下四大组件的启动过程,四大组件的启动与销毁的方式。
  • 广播发送和接收的原理了解吗?
  • 11、AMS 是如何管理 Activity 的?
  • 12、理解 Window 和 WindowManager。
  • 13、WMS 是如何管理 Window 的?
  • 14、大体说清一个应用程序安装到手机上时发生了什么?
  • 15、Android 的打包流程?apk 里有哪些东西?签名算法的原理?
  • apk 打包流程
  • apk 组成
  • 签名算法的原理
  • 为什么要签名?
  • 什么是签名?
  • 数字摘要
  • 签名和校验的主要过程
  • 签名过程:
  • 校验过程:
  • 数字证书
  • keystore 和证书格式
  • jarsigner 和 apksigner 的区别
  • 在签名时,除了要指定 keystore 文件和密码外,也要指定 alias 和 key 的密码,这是为什么呢?
  • Android Apk V1 签名原理
  • 16、说下安卓虚拟机和 java 虚拟机的原理和不同点?(JVM、 Davilk、ART 三者的原理和区别)
  • JVM 和 Dalvik 虚拟机的区别
  • Android2 个虚拟机的区别(一个 5.0 之前,一个 5.0 之后)
  • 17、安卓采用自动垃圾回收机制,请说下安卓内存管理的原理?
  • 18、Android 中 App 是如何沙箱化的,为何要这么做?
  • 19、一个图片在 app 中调用 R.id 后是如何找到的?
  • 20、JNI
  • Java 调用 C++
  • C++调用 Java
  • 21、请介绍一下 NDK?


1、Android 系统架构

Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建。下图所示为
Android 平台的五大组件:

1.应用程序

Android 随附一套用于电子邮件、短信、日历、互联网浏览和联系人等的核心应用。平台随附的应
用与用户可以选择安装的应用一样,没有特殊状态。因此第三方应用可成为用户的默认网络浏览器、短信Messenger 甚至默认键盘(有一些例外,例如系统的“设置”应用)。
系统应用可用作用户的应用,以及提供开发者可从其自己的应用访问的主要功能。例如,如果您的应
用要发短信,您无需自己构建该功能,可以改为调用已安装的短信应用向您指定的接收者发送消息。

2、Java API 框架

您可通过以 Java 语言编写的 API 使用 Android OS 的整个功能集。这些API 形成创建 Android 应用所
需的构建块,它们可简化核心模块化系统组件和服务的重复使用,包括以下组件和服务:

  • 丰富、可扩展的视图系统,可用以构建应用的 UI,包括列表、网格、文本框、按钮甚至可嵌入的网络浏览器
  • 资源管理器,用于访问非代码资源,例如本地化的字符串、图形和布局文件
  • 通知管理器,可让所有应用在状态栏中显示自定义提醒
  • Activity 管理器,用于管理应用的生命周期,提供常见的导航返回栈
  • 内容提供程序,可让应用访问其他应用(例如“联系人”应用)中的数据或者共享其自己的数据开发者可以完全访问 Android 系统应用使用的框架 API。

3、系统运行库

1原生 C/C++ 库

许多核心 Android 系统组件和服务(例如 ART 和 HAL)构建自原生代码,需要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向应用显示其中部分原生库的功能。例如,您可以通过Android 框架的 Java OpenGL API 访问 OpenGL ES,以支持在应用中绘制和操作 2D 和 3D图形。如果开发的是需要 C 或 C++ 代码的应用,可以使用 Android NDK 直接从原生代码访问某些原生平台库。

2Android Runtime

对于运行 Android 5.0(API 级别 21)或更高版本的设备,每个应用都在其自己的进程中运行,并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。

ART 的部分主要功能包括:

  • 预先 (AOT) 和即时 (JIT) 编译
  • 优化的垃圾回收 (GC)
  • 更好的调试支持,包括专用采样分析器、详细的诊断异常和崩溃报告,并且能够设置监视点以监控特定字段
  • 在 Android 版本 5.0(API 级别 21)之前,Dalvik 是 Android Runtime。如果您的应用在ART 上运行效果很好,那么它应该也可在 Dalvik 上运行,但反过来不一定。

Android 还包含一套核心运行时库,可提供 Java API 框架使用的 Java 编程语言大部分功能,包括一些
Java 8 语言功能。

4、硬件抽象层 (HAL)

硬件抽象层 (HAL) 提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。HAL 包含多
个库模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如相机或蓝牙模块。当框架 API 要求访问设备硬件时,Android 系统将为该硬件组件加载库模块。

5、Linux 内核

Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和低层内存管理。使用 Linux 内核可让 Android 利用主要安全功能,并且允许设备制造商为著名的内核开发硬件驱动程序。

对于 Android 应用开发来说,最好能手绘下面的系统架构图:

android wms 面试题 android framework面试_android wms 面试题

2、View 的事件分发机制?滑动冲突怎么解决?

 

3、View 的绘制流程?

View的绘制流程

4、跨进程通信

Android 中进程和线程的关系?区别?

线程是 CPU 调度的最小单元,同时线程是一种有限的系统资源;而进程一般指一个执行单元,在
PC 和移动设备上指一个程序或者一个应用。

一般来说,一个 App 程序至少有一个进程,一个进程至少有一个线程(包含与被包含的关系),通
俗来讲就是,在 App 这个工厂里面有一个进程,线程就是里面的生产线,但主线程(即主生产线)
只有一条,而子线程(即副生产线)可以有多个。

进程有自己独立的地址空间,而进程中的线程共享此地址空间,都可以并发执行。

如何开启多进程?应用是否可以开启 N 个进程?

在 AndroidManifest 中给四大组件指定属性 android:process 开启多进程模式,在内存允许的条
件下可以开启 N 个进程。

为何需要 IPC?多进程通信可能会出现的问题?

所有运行在不同进程的四大组件(Activity、Service、Receiver、ContentProvider)共享数据都会失败,这是由于 Android 为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。比如常用例子(通过开启多进程获取更大内存空间、两个或者多个应用之间共享数据、微信全家桶)。
一般来说,使用多进程通信会造成如下几方面的问题:

  • 静态成员和单例模式完全失效:独立的虚拟机造成。
  • 线程同步机制完全失效:独立的虚拟机造成。
  • SharedPreferences 的可靠性下降:这是因为 Sp 不支持两个进程并发进行读写,有一定几率导致数
    据丢失。
  • Application 会多次创建:Android 系统在创建新的进程时会分配独立的虚拟机,所以这个过程其实
    就是启动一个应用的过程,自然也会创建新的 Application。

Android中IPC方式、各种方式优缺点,为什么选择Binder?

参考解答:

android wms 面试题 android framework面试_缓存_02

与Linux上传统的IPC机制,比如System V,Socket相比,Binder好在哪呢?

传输效率高、可操作性强:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝,如图:

android wms 面试题 android framework面试_面试_03

而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程,如图:

android wms 面试题 android framework面试_android wms 面试题_04

由于共享内存操作复杂,综合来看,Binder的传输效率是最好的。
实现C/S架构方便:Linux的众IPC方式除了Socket以外都不是基于C/S架构,而Socket主要用于网络间的通信且传输效率较低。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。
安全性高:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID且在Binder通信时会根据UID/PID进行有效性检测。

Binder机制的作用和原理?

Linux系统将一个进程分为用户空间和内核空间。对于进程之间来说,用户空间的数据不可共享,内核空间的数据可共享,为了保证安全性和独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的,这就需要跨进程之间的数据通信方式

android wms 面试题 android framework面试_缓存_05

一次完整的 Binder IPC 通信过程通常是这样:

  • 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
  • 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
  • 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

Binder框架中ServiceManager的作用?

  • Binder框架 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder驱动,其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间
  • android wms 面试题 android framework面试_android_06

  • Server&Client:服务器&客户端。在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。

  • ServiceManager(如同DNS域名服务器)服务的管理者,将Binder名字转换为Client中对该Binder的引用,使得Client可以通过Binder名字获得Server中Binder实体的引用。

  • Binder驱动(如同路由器):负责进程之间binder通信的建立,传递,计数管理以及数据的传递交互等底层支持。
  • android wms 面试题 android framework面试_缓存_07

图片出自Android跨进程通信:图文详解 Binder机制 原理

Bundle传递对象为什么需要序列化?Serialzable和Parcelable的区别?

  • 因为bundle传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、IPC(比如启动另一个进程的Activity、Service和Reciver)之间进行传输,也可以存储到本地。
  • 序列化实现的两种方式:实现Serializable/Parcelable接口。不同点如图:
  • android wms 面试题 android framework面试_缓存_08


讲讲AIDL?原理是什么?如何优化多模块都使用AIDL的情况?

AIDL(Android Interface Definition Language,Android接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。
AIDL的本质是系统提供了一套可快速实现Binder的工具。关键类和方法:

  • AIDL接口:继承IInterface。
  • Stub类:Binder的实现类,服务端通过这个类来提供服务。
  • Proxy类:服务器的本地代理,客户端通过这个类调用服务器的方法。
  • asInterface():客户端调用,将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象。如果客户端和服务端位于统一进程,则直接返回Stub对象本身,否则返回系统封装后的Stub.proxy对象
  • asBinder():根据当前调用情况返回代理Proxy的Binder对象。
  • onTransact():运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
  • transact():运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的onTransact()直到远程请求返回,当前线程才继续执行。

当有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。解决办法是建立Binder连接池,即将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。
工作原理:每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service,服务器提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对象,不同的业务模块拿到所需的Binder对象后就可进行远程方法的调用了

5、Android 系统启动流程是什么?

init 进程 -> Zygote 进程–> SystemServer 进程 –> 各种系统服务 –> 应用进程
Android 系统启动的核心流程如下:

  • 1、启动电源以及系统启动:当电源按下时引导芯片从预定义的地方(固化在 ROM)开始执行,加载引导程序 BootLoader 到 RAM,然后执行。
  • 2、引导程序 BootLoader:BootLoader 是在 Android 系统开始运行前的一个小程序,主要用于把系统 OS 拉起来并运行。
  • 3、Linux 内核启动:当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。当其完成系统设置时,会先在系统文件中寻找 init.rc 文件,并启动 init 进程。
  • 4、init 进程启动:初始化和启动属性服务,并且启动 Zygote 进程。
  • 5、Zygote 进程启动:创建 JVM 并为其注册 JNI 方法,创建服务器端 Socket,启动 SystemServer进程。
  • 6、SystemServer 进程启动:启动 Binder 线程池和 SystemServiceManager,并且启动各种系统服务。
  • 7、Launcher 启动:被 SystemServer 进程启动的 AMS 会启动 Launcher,Launcher 启动后会将已安装应用的快捷图标显示到系统桌面上。

需要更详细的分析请查看以下系列文章:
Android系统启动

6、启动一个程序,可以主界面点击图标进入,也可以从一个程序中跳转过去,二者有什么区别?

是因为启动程序(主界面也是一个 app),发现了在这个程序中存在一个设置为的 activity, 所以这个 auncher 会把 icon 提出来,放在主界面上。当用户点击 icon 的时候,发出一个 Intent.

public class ActivityManagerService extends ActivityManagerNative {
@Override
public int startActivity(Intent intent) throws RemoteException {
// 启动 activity
return 0;
}
}

跳过去可以跳到任意允许的页面,如一个程序可以下载,那么真正下载的页面可能不是首页(也有可能是首页),这时还是构造一个 Intent,startActivity。这个 intent 中的 action 可能有多种 view,download 都有可能。系统会根据第三方程序向系统注册的功能,为你的 Intent 选择可以打开的程序或者页面。所以唯一的一点不同的是从 icon 的点击启动的 intent 的 action 是相对单一的,从程序中跳转或者启动可能样式更多一些。本质是相同的。

7、AMS 家族重要术语解释。
  • 1.ActivityManagerServices,简称 AMS,服务端对象,负责系统中所有 Activity 的生命周期。

  • 2.ActivityThread,App 的真正入口。当开启 App 之后,调用 main()开始运行,开启消息循环队列,这就是传说的 UI 线程或者叫主线程。与 ActivityManagerService 一起完成 Activity 的管理工作。

  • 3.ApplicationThread,用来实现 ActivityManagerServie 与 ActivityThread 之间的交互。在ActivityManagerSevice 需要管理相关 Application 中的 Activity 的生命周期时,通过ApplicationThread
    的代理对象与 ActivityThread 通信。

  • 4.ApplicationThreadProxy,是 ApplicationThread 在服务器端的代理,负责和客户端的pplicationThread 通信。AMS 就是通过该代理与 ActivityThread 进行通信的。

  • 5.Instrumentation,每一个应用程序只有一个 Instrumetation 对象,每个 Activity 内都有一个对该对象的引用,Instrumentation 可以理解为应用进程的管家,ActivityThread 要创建或暂停某个Activity 时,都需要通过 Instrumentation 来进行具体的操作。

  • 6.ActivityStack,Activity 在 AMS 的栈管理,用来记录经启动的 Activity 的先后关系,状态信息等。通过ActivtyStack 决定是否需要启动新的进程。

  • 7.ActivityRecord,ActivityStack 的管理对象,每个 Acivity 在 AMS 对应一个 ActivityRecord,来记录Activity 状态以及其他的管理信息。其实就是服务器端的 Activit 对象的映像。

  • 8.TaskRecord,AMS 抽象出来的一个“任务”的概念,是记录 ActivityRecord 的栈,一个“Task” 包含若干ActivityRecord。AMS 用 TaskRecord 确保 Activity 启动和退出的顺序。如果你清楚 Activity的 4 种launchMode,那么对这概念应该不陌生

8、App 启动流程(Activity 的冷启动流程)。
Intent intent =
mActivity.getPackageManager().getLaunchIntentForPackage(packageName);
mActivity.startActivity(intent);

整个流程涉及的主要角色有:

  • Instrumentation: 监控应用与系统相关的交互行为。

  • AMS:组件管理调度中心,什么都不干,但是什么都管。

  • ActivityStarter:Activity 启动的控制器,处理 Intent 与 Flag 对 Activity 启动的影响,具体说来有:

    1寻找符合启动条件的 Activity,如果有多个,让用户选择;
    2 校验启动参数的合法性;
    3 返回 int参数,代表 Activity 是否启动成功。

  • ActivityStackSupervisior:这个类的作用你从它的名字就可以看出来,它用来管理任务栈。

  • ActivityStack:用来管理任务栈里的 Activity。

  • ActivityThread:最终干活的人,Activity、Service、BroadcastReceiver 的启动、切换、调度等各
    种操作都在这个类里完成。

注:这里单独提一下 ActivityStackSupervisior,这是高版本才有的类,它用来管理多个
ActivityStack,早期的版本只有一个 ActivityStack 对应着手机屏幕,后来高版本支持多屏以后,就有了多个ActivityStack,于是就引入了 ActivityStackSupervisior 用来管理多个 ActivityStack。

整个流程主要涉及四个进程:

  • 调用者进程,如果是在桌面启动应用就是 Launcher 应用进程。
  • ActivityManagerService 等待所在的 System Server 进程,该进程主要运行着系统服务组件。
  • Zygote 进程,该进程主要用来 fork 新进程。
  • 新启动的应用进程,该进程就是用来承载应用运行的进程了,它也是应用的主线程(新创建的进程就是主线程),处理组件生命周期、界面绘制等相关事情。

有了以上的理解,整个流程可以概括如下:

  • 1、点击桌面应用图标,Launcher 进程将启动 Activity(MainActivity)的请求以 Binder 的方式发送给了 AMS。
  • 2、AMS 接收到启动请求后,交付 ActivityStarter 处理 Intent 和 Flag 等信息,然后再交给ActivityStackSupervisior/ActivityStack 处理 Activity 进栈相关流程。同时以 Socket 方式请求 Zygote进程 fork 新进程。
  • 3、Zygote 接收到新进程创建请求后 fork 出新进程。
  • 4、在新进程里创建 ActivityThread 对象,新创建的进程就是应用的主线程,在主线程里开启Looper 消息循环,开始处理创建 Activity。5、ActivityThread 利用 ClassLoader 去加载 Activity、创建 Activity 实例,并回调 Activity 的onCreate()方法,这样便完成了 Activity 的启动。

最后,再看看另一幅启动流程图来加深理解:

android wms 面试题 android framework面试_面试_09

9、ActivityThread 工作原理。10、说下四大组件的启动过程,四大组件的启动与销毁的方式。

广播发送和接收的原理了解吗?

继承 BroadcastReceiver,重写 onReceive()方法。
通过 Binder 机制向 ActivityManagerService 注册广播。
通过 Binder 机制向 ActivityMangerService 发送广播。
ActivityManagerService 查找符合相应条件的广播(IntentFilter/Permission)的
BroadcastReceiver,将广播发送到 BroadcastReceiver 所在的消息队列中。
BroadcastReceiver 所在消息队列拿到此广播后,回调它的 onReceive()方法。

11、AMS 是如何管理 Activity 的?12、理解 Window 和 WindowManager。

1.Window 用于显示 View 和接收各种事件,Window 有三种型:应用 Window(每个 Activity 对应一个Window)、子 Widow(不能单独存在,附属于特定 Window)、系统 window(toast 和状态栏)

2.Window 分层级,应用 Window 在 1-99、子 Window 在 1000-1999、系统 Window 在2000- 2999.WindowManager 提供了增改 View 的三个功能。

3.Window 是个抽象概念:每一个 Window 对应着一个 ViewRootImpl,Window 通过ViewRootImpl 来和View 建立联系,View 是 Window 存在的实体,只能通过 WindowManager 来访问 Window。

4.WindowManager 的实现是 WindowManagerImpl,其再委托 WindowManagerGlobal 来对Window进行操作,其中有四种 List 分别储存对应的 View、ViewRootImpl、 WindowManger.LayoutParams 和正在被删除的 View。

5.Window 的实体是存在于远端的 WindowMangerService,所以增删改 Window 在本端是修改上面的几个 List 然后通过 ViewRootImpl 重绘 View,通过 WindowSession(每 Window 个对应一个)在远端修改Window。

6.Activity 创建 Window:Activity 会在 attach()中创建 Window 并设置其回调onAttachedToWindow()、dispatchTouchEvent()),Activity 的 Window 是由 Policy 类创建 honeWindow 实现的。然后通过Activity#setContentView()调用 PhoneWindow 的 setContentView。

13、WMS 是如何管理 Window 的?14、大体说清一个应用程序安装到手机上时发生了什么?

android wms 面试题 android framework面试_缓存_10


复制 APK 到/data/app 目录下,解压并扫描安装包。

资源管理器解析 APK 里的资源文件。

解析 AndroidManifest 文件,并在/data/data/目录下创建对应的应用数据目录。

然后对 dex 文件进行优化,并保存在 dalvik-cache 目录下。

将 AndroidManifest 文件解析出的四大组件信息注册到 PackageManagerService 中。

安装完成后,发送广播。

15、Android 的打包流程?apk 里有哪些东西?签名算法的原理?

apk 打包流程

Android 的包文件 APK 分为两个部分:代码和资源,所以打包方面也分为资源打包和代码打包两个方面,下面就来分析资源和代码的编译打包原理。

APK 整体的的打包流程如下图所示:

android wms 面试题 android framework面试_面试_11


具体说来:

  • 通过 AAPT 工具进行资源文件(包括 AndroidManifest.xml、布局文件、各种 xml 资源等)的打包,生成 R.java 文件。通过 AIDL 工具处理 AIDL 文件,生成相应的 Java 文件。

  • 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成.class 文件。

  • 通过 dex 命令,将.class 文件和第三方库中的.class 文件处理生成 classes.dex,该过程主要完成 Java- - 字节码转换成 Dalvik 字节码,压缩常量池以及清除冗余信息等工作。

  • 通过 ApkBuilder 工具将资源文件、DEX 文件打包生成 APK 文件。

  • 通过 Jarsigner 工具,利用 KeyStore 对生成的 APK 文件进行签名。

  • 如果是正式版的 APK,还会利用 ZipAlign 工具进行对齐处理,对齐的过程就是将 APK 文件中所有的资源文件距离文件的起始距位置都偏移 4 字节的整数倍,这样通过内存映射访问 APK 文件的速度会更快,并且会减少其在设备上运行时的内存占用。

apk 组成

  • dex:最终生成的 Dalvik 字节码。
  • res:存放资源文件的目录。
  • asserts:额外建立的资源文件夹。
  • lib:如果存在的话,存放的是 ndk 编出来的 so 库。
  • META-INF:存放签名信息
  • MANIFEST.MF(清单文件):其中每一个资源文件都有一个 SHA-256-Digest 签名,
  • MANIFEST.MF 文件的 SHA256(SHA1)并 base64 编码的结果即为 CERT.SF 中的 SHA256-Digest-Manifest 值。
  • CERT.SF(待签名文件):除了开头处定义的 SHA256(SHA1)-Digest-Manifest 值,后面几项的值是对 MANIFEST.MF 文件中的每项再次 SHA256 并 base64 编码后的值。
  • CERT.RSA(签名结果文件):其中包含了公钥、加密算法等信息。首先对前一步生成的MANIFEST.MF使用了 SHA256(SHA1)-RSA 算法,用开发者私钥签名,然后在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF 文件)进行对比,如果相符,则表明内容没有被修改。
  • androidManifest:程序的全局清单配置文件。
  • resources.arsc:编译后的二进制资源文件。

签名算法的原理

为什么要签名?

  • 确保 Apk 来源的真实性。
  • 确保 Apk 没有被第三方篡改。

什么是签名?

在 Apk 中写入一个“指纹”。指纹写入以后,Apk 中有任何修改,都会导致这个指纹无效,Android系统在
安装 Apk 进行签名校验时就会不通过,从而保证了安全性。

数字摘要

对一个任意长度的数据,通过一个 Hash 算法计算后,都可以得到一个固定长度的二进制数据,这个
数据就称为“摘要”。
补充:
散列算法的基础原理:将数据(如一段文字)运算变为另一固定长度值。

  • SHA-1:在密码学中,SHA-1(安全散列算法 1)是一种加密散列函数,它接受输入并产生一个160
    位(20 字节)散列值,称为消息摘要 。

  • MD5:MD5 消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个 128 位(16 字节)的散列值(hash value),用于确保信息传输完整一致。

  • SHA-2:名称来自于安全散列算法 2(英语:Secure Hash Algorithm 2)的缩写,一种密码散列函数算法标准,其下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
    特征:唯一性.
    固定长度:比较常用的 Hash 算法有 MD5 和 SHA1,MD5 的长度是 128 拉,SHA1 的长度是 160位。不可逆性

签名和校验的主要过程

签名就是在摘要的基础上再进行一次加密,对摘要加密后的数据就可以当作数字签名。

签名过程:

1、计算摘要:通过 Hash 算法提取出原始数据的摘要。
2、计算签名:再通过基于密钥(私钥)的非对称加密算法对提取出的摘要进行加密,加密后的数据就是签名信息。
3、写入签名:将签名信息写入原始数据的签名区块内。

校验过程:

1、首先用同样的 Hash 算法从接收到的数据中提取出摘要。
2、解密签名:使用发送方的公钥对数字签名进行解密,解密出原始摘要。
3、比较摘要:如果解密后的数据和提取的摘要一致,则校验通过;如果数据被第三方篡改过,解密后的数据和摘要将会不一致,则校验不通过。

数字证书

如何保证公钥的可靠性呢?答案是数字证书,数字证书是身份认证机构(Certifificate Authority)颁发
的,包含了以下信息:

  • 证书颁发机构
  • 证书颁发机构签名
  • 证书绑定的服务器域名
  • 证书版本、有效期
  • 签名使用的加密算法(非对称算法,如 RSA)
  • 公钥等

接收方收到消息后,先向 CA 验证证书的合法性,再进行签名校验。
注意:Apk 的证书通常是自签名的,也就是由开发者自己制作,没有向 CA 机构申请。Android 在安装
Apk 时并没有校验证书本身的合法性,只是从证书中提取公钥和加密算法,这也正是对第三方 Apk重新签名后,还能够继续在没有安装这个 Apk 的系统中继续安装的原因。

keystore 和证书格式

keystore 文件中包含了私钥、公钥和数字证书。根据编码不同,keystore 文件分为很多种,Android
使用的是 Java 标准 keystore 格式 JKS(Java Key Storage),所以通过 Android Studio 导出的keystore 文件是以.jks 结尾的。
keystore 使用的证书标准是 X.509,X.509 标准也有多种编码格式,常用的有两种:pem(Privacy Enhanced Mail)和 der(Distinguished Encoding Rules)。jks 使用的是 der 格式,Android也支持直接使用 pem 格式的证书进行签名。

两种证书编码格式的区别:

  • DER(Distinguished Encoding Rules)二进制格式,所有类型的证书和私钥都可以存储为 der 格式。
  • PEM(Privacy Enhanced Mail)base64 编码,内容以-----BEGIN xxx----- 开头,以-----END xxx----- 结尾。

jarsigner 和 apksigner 的区别

Android 提供了两种对 Apk 的签名方式,一种是基于 JAR 的签名方式,另一种是基于 Apk 的签名方式,它们的主要区别在于使用的签名文件不一样:jarsigner 使用 keystore 文件进行签名;apksigner 除
了支持使用 keystore 文件进行签名外,还支持直接指定 pem 证书文件和私钥进行签名。

在签名时,除了要指定 keystore 文件和密码外,也要指定 alias 和 key 的密码,这是为什么呢?

keystore 是一个密钥库,也就是说它可以存储多对密钥和证书,keystore 的密码是用于保护keystore 本
身的,一对密钥和证书是通过 alias 来区分的。所以 jarsigner 是支持使用多个证书对 Apk 进行签名的,apksigner 也同样支持。

Android Apk V1 签名原理

1、解析出 CERT.RSA 文件中的证书、公钥,解密 CERT.RSA中的加密数据。
2、解密结果和 CERT.SF 的指纹进行对比,保证 CERT.SF 没有被篡改。
3、而 CERT.SF 中的内容再和 MANIFEST.MF 指纹对比,保证MANIFEST.MF 文件没有被篡改。
4、MANIFEST.MF 中的内容和 APK 所有文件指纹逐一对比,保证 APK 没有被篡改。

16、说下安卓虚拟机和 java 虚拟机的原理和不同点?(JVM、 Davilk、ART 三者的原理和区别)

JVM 和 Dalvik 虚拟机的区别

JVM:.java -> javac -> .class -> jar -> .jar
架构: 堆和栈的架构.

DVM:.java -> javac -> .class -> dx.bat -> .dex 架构:
寄存器(cpu 上的一块高速缓存)

Android2 个虚拟机的区别(一个 5.0 之前,一个 5.0 之后)

什么是 Dalvik:Dalvik 是 Google 公司自己设计用于 Android 平台的 Java 虚拟机。Dalvik 虚拟机是 Google等厂商合作开发的 Android 移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即 DalvikExecutable)格式的 Java 应用程序的运行,.dex 格式是专为 Dalvik 应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚机的实例,并且每一个 Dalvik 应用作为独立的 Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
什么是 ART:Android 操作系统已经成熟,Google 的 Android 团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的 Dalvik 运行时。Google 开发者已经花了两年时间开发更快执行效率更高更省电的替代 ART 运行时。ART 代表 Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik 是依靠一个 Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART 则完全改变了这套做法,在应用安装的时候就预编译字节码为机器语言,这一机制叫 Ahead-Of-ime(AOT)
编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

ART 优点:
系统性能的显著提升。
应用启动更快、运行更快、体验更流畅、触感反馈更及时。
更长的电池续航能力。
支持更低的硬件。ART 缺点:
更大的存储空间占用,可能会增加 10%-20%。
更长的应用安装时间。

17、安卓采用自动垃圾回收机制,请说下安卓内存管理的原理?18、Android 中 App 是如何沙箱化的,为何要这么做?19、一个图片在 app 中调用 R.id 后是如何找到的?20、JNI

Java 调用 C++

在 Java 中声明 Native 方法(即需要调用的本地方法)编译上述 Java 源文件 javac(得到 .class 文件) 3。 通过 javah 命令导出 JNI 的头文件(.h 文件)使用 Java 需要交互的本地代码 实现在 Java 中声明的 Native 方法编译.so 库文件通过 Java 命令执行 Java 程序,最终实现 Java 调用本地代码

C++调用 Java

从 classpath 路径下搜索 ClassMethod 这个类,并返回该类的 Class 对象。
获取类的默认构造方法 ID。
查找实例方法的 ID。
创建该类的实例。
调用对象的实例方法

JNIEXPORT void JNICALL
Java_com_study_jnilearn_AccessMethod_callJavaInstaceMethod
(JNIEnv *env, jclass cls)
{
jclass clazz = NULL;
jobject jobj = NULL;
jmethodID mid_construct = NULL;
jmethodID mid_instance = NULL;
jstring str_arg = NULL;
// 1、从 classpath 路径下搜索 ClassMethod 这个类,并返回该类的 Class 对象
clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassMethod");
if (clazz == NULL) {
printf("找不到'com.study.jnilearn.ClassMethod'这个类");
return;
}
// 2、获取类的默认构造方法 ID
mid_construct = (*env)->GetMethodID(env,clazz, "<init>","()V");
if (mid_construct == NULL) {
printf("找不到默认的构造方法");
return;
}
// 3、查找实例方法的 ID
mid_instance = (*env)->GetMethodID(env, clazz, "callInstanceMethod", "
(Ljava/lang/String;I)V");
21、请介绍一下 NDK?

请介绍一下NDK