本次的学习指南系列将分为三个模块
- 第一 Java知识点汇总
- 第二 Android基础知识点汇总(一)(二)
- 第三 Android进阶知识点汇总
后续的NDK、跨平台、底层源码等技术,也会抽空给大家整理一下知识点,如果喜欢的话,希望大家给个关注,点个赞呗,个人主页简介有联系方式,可找我拿PDF版本的学习指南。
- Activity A 启动另一个Activity B,回调如下:
Activity A 的onPause() → Activity B的onCreate() → onStart() → onResume() → Activity A的onStop();如果B是透明主题又或则是个DialogActivity,则不会回调A的onStop; - 使用onSaveInstanceState()保存简单,轻量级的UI状态
讯享网

讯享网
- Fragment 解决 Activity 间的切换不流畅,轻量切换
- 可以从 startActivityForResult 中接收到返回结果,但是View不能
- 只能在 Activity 保存其状态(用户离开 Activity)之前使用 commit() 提交事务。如果您试图在该时间点后提交,则会引发异常。 这是因为如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交无关紧要的情况,请使用 commitAllowingStateLoss()。


执行此操作的一个好方法是,在片段内定义一个回调接口,并要求宿主 Activity 实现它。
Service 分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和 Service 的交互。

讯享网


讯享网
target 26 之后,无法在 AndroidManifest 显示声明大部分广播,除了一部分必要的广播,如:
- ACTION_BOOT_COMPLETED
- ACTION_TIME_SET
- ACTION_LOCALE_CHANGED
讯享网
ContentProvider 管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。
ContentProvider 无法被用户感知,对于一个 ContentProvider 组件来说,它的内部需要实现增删该查这四种操作,它的内部维持着一份数据集合,这个数据集合既可以是数据库实现,也可以是其他任何类型,如 List 和 Map,内部的 insert、delete、update、query 方法需要处理好线程同步,因为这几个方法是在 Binder 线程池中被调用的。
ContentProvider 通过 Binder 向其他组件乃至其他应用提供数据。当 ContentProvider 所在的进程启动时,ContentProvider 会同时启动并发布到 AMS 中,需要注意的是,这个时候 ContentProvider 的 onCreate 要先于 Application 的 onCreate 而执行。
讯享网
ContentProvider 和 sql 在实现上有什么区别?
- ContentProvider 屏蔽了数据存储的细节,内部实现透明化,用户只需关心 uri 即可(是否匹配)
- ContentProvider 能实现不同 app 的数据共享,sql 只能是自己程序才能访问
- Contentprovider 还能增删本地的文件,xml等信息
ViewRoot 对应于 ViewRootImpl 类,它是连接 WindowManager 和 DecorView 的纽带,View 的三大流程均是通过 ViewRoot 来完成的。在 ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 添加到 Window 中,同时会创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联
View 的整个绘制流程可以分为以下三个阶段:
- measure: 判断是否需要重新计算 View 的大小,需要的话则计算
- layout: 判断是否需要重新计算 View 的位置,需要的话则计算
- draw: 判断是否需要重新绘制 View,需要的话则重绘制

MeasureSpec表示的是一个32位的整形值,它的高2位表示测量模式SpecMode,低30位表示某种测量模式下的规格大小SpecSize。MeasureSpec 是 View 类的一个静态内部类,用来说明应该如何测量这个 View
对于 DecorView 而言,它的MeasureSpec 由窗口尺寸和其自身的 LayoutParams 共同决定;对于普通的 View,它的 MeasureSpec 由父视图的 MeasureSpec 和其自身的 LayoutParams 共同决定
直接继承 View 的控件需要重写 onMeasure 方法并设置 wrap_content 时的自身大小,因为 View 在布局中使用 wrap_content,那么它的 specMode 是 AT_MOST 模式,在这种模式下,它的宽/高等于父容器当前剩余的空间大小,就相当于使用 match_parent。这解决方式如下:
点击屏幕后松开,事件序列为 DOWN -> UP,点击屏幕滑动松开,事件序列为 DOWN -> MOVE -> ...> MOVE -> UP。
返回相对于当前View左上角的坐标, 返回相对于屏幕左上角的坐标
TouchSlop是系统所能识别出的被认为滑动的最小距离,不同设备值可能不相同,可通过 获取。
VelocityTracker 可用于追踪手指在滑动中的速度:
讯享网
GestureDetector 辅助检测用户的单击、滑动、长按、双击等行为:
如果是监听滑动相关,建议在 中实现,如果要监听双击,那么就使用 。
弹性滑动对象,用于实现 View 的弹性滑动,Scroller 本身无法让 View 弹性滑动,需要和 View 的 方法配合使用。方法是无法让 View 滑动的, 会导致 View 重绘,重回后会在 方法中又会去调用 方法, 方法又会去向 Scroller 获取当前的 scrollX 和 scrollY,然后通过 方法实现滑动,接着又调用 方法如此反复。
讯享网
适合对 View 内容的滑动。 实际上也是调用了 方法:
mScrollX的值等于 View 的左边缘和 View 内容左边缘在水平方向的距离,mScrollY的值等于 View 上边缘和 View 内容上边缘在竖直方向的距离。 和 只能改变 View 内容的位置而不能改变 View 在布局中的位置。
- 使用动画
操作简单,主要适用于没有交互的 View 和实现复杂的动画效果。 - 改变布局参数 操作稍微复杂,适用于有交互的 View.
讯享网
点击事件达到顶级 View(一般是一个 ViewGroup),会调用 ViewGroup 的 dispatchTouchEvent 方法,如果顶级 ViewGroup 拦截事件即 onInterceptTouchEvent 返回 true,则事件由 ViewGroup 处理,这时如果 ViewGroup 的 mOnTouchListener 被设置,则 onTouch 会被调用,否则 onTouchEvent 会被调用。也就是说如果都提供的话,onTouch 会屏蔽掉 onTouchEvent。在 onTouchEvent 中,如果设置了 mOnClickListenser,则 onClick 会被调用。如果顶级 ViewGroup 不拦截事件,则事件会传递给它所在的点击事件链上的子 View,这时子 View 的 dispatchTouchEvent 会被调用。如此循环。
- ViewGroup 默认不拦截任何事件。ViewGroup 的 onInterceptTouchEvent 方法默认返回 false。
- View 没有 onInterceptTouchEvent 方法,一旦有点击事件传递给它,onTouchEvent 方法就会被调用。
- View 在可点击状态下,onTouchEvent 默认会消耗事件。
- ACTION_DOWN 被拦截了,onInterceptTouchEvent 方法执行一次后,就会留下记号(mFirstTouchTarget == null)那么往后的 ACTION_MOVE 和 ACTION_UP 都会拦截。`
- Activity/View#onWindowFocusChanged
- view.post(runnable)
讯享网
- ViewTreeObserver
讯享网
- 继承 View 重写 方法
主要用于实现一些不规则的效果,静态或者动态地显示一些不规则的图形,即重写 方法。采用这种方式需要自己支持 wrap_content,并且 padding 也需要自己处理。
- 继承 ViewGroup 派生特殊的 Layout
主要用于实现自定义布局,采用这种方式需要合适地处理 ViewGroup 的测量、布局两个过程,并同时处理子元素的测量和布局过程。
- 继承特定的 View
用于扩张某种已有的View的功能

- 继承特定的 ViewGroup
用于扩张某种已有的ViewGroup的功能
进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件在相同的进程和线程(称为“主”线程)中运行。
各类组件元素的清单文件条目、、 和 —均支持 android:process 属性,此属性可以指定该组件应在哪个进程运行。
1、前台进程
- 托管用户正在交互的 Activity(已调用 Activity 的 方法)
- 托管某个 Service,后者绑定到用户正在交互的 Activity
- 托管正在“前台”运行的 Service(服务已调用 )
- 托管正执行一个生命周期回调的 Service(、 或 )
- 托管正执行其 方法的 BroadcastReceiver
2、可见进程
- 托管不在前台、但仍对用户可见的 Activity(已调用其 方法)。例如,如果 re前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
- 托管绑定到可见(或前台)Activity 的 Service
3、服务进程
- 正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。
4、后台进程
- 包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 方法)。通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。
5、空进程
- 不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
如果注册的四大组件中的任意一个组件时用到了多进程,运行该组件时,都会创建一个新的 Application 对象。对于多进程重复创建 Application 这种情况,只需要在该类中对当前进程加以判断即可。
一般来说,使用多进程会造成以下几个方面的问题:
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences 的可靠性下降
- Application 会多次创建

- 开启一个像素的 Activity
- 使用前台服务
- 多进程相互唤醒
- JobSheduler 唤醒
- 粘*** & 与系统服务捆绑
只要实现了 Parcelable 接口,一个类的对象就可以实现序列化并可以通过 Intent 和 Binder 传递。
讯享网
Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。序列化功能由 方法完成,最终是通过 Parcel 中的一系列 write 方法完成。反序列化功能由 CREATOR 来完成,通过 Parcel 的一系列 read 方法来完成反序列化过程。
- Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接在内存中读写
- Serializable 会使用反射,序列化和反序列化过程需要大量 I/O 操作, Parcelable 自已实现封送和解封(marshalled &unmarshalled)操作不需要用反射,数据也存放在 Native 内存中,效率要快很多
IPC 即 Inter-Process Communication (进程间通信)。Android 基于 Linux,而 Linux 出于安全考虑,不同进程间不能之间操作对方的数据,这叫做“进程隔离”。
在 Linux 系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间,进而不能操作其他进程的内存空间,只有操作系统才有权限操作物理内存空间。 进程隔离保证了每个进程的内存安全。
Binder 是 Android 中的一个类,实现了 IBinder 接口。从 IPC 角度来说,Binder 是 Android 中的一种扩进程通信方方式。从 Android 应用层来说,Binder 是客户端和服务器端进行通信的媒介,当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象。
Binder 相较于传统 IPC 来说更适合于Android系统,具体原因的包括如下三点:
- Binder 本身是 C/S 架构的,这一点更符合 Android 系统的架构
- 性能上更有优势:管道,消息队列,Socket 的通讯都需要两次数据拷贝,而 Binder 只需要一次。要知道,对于系统底层的 IPC 形式,少一次数据拷贝,对整体性能的影响是非常之大的
- 安全性更好:传统 IPC 形式,无法得到对方的身份标识(UID/GID),而在使用 Binder IPC 时,这些身份标示是跟随调用过程而自动传递的。Server 端很容易就可以知道 Client 端的身份,非常便于做安全检查
示例:
- 新建AIDL接口文件
系统会自动生成 :
讯享网

Android Interface Definition Language
使用示例:
- 新建AIDL接口文件
- 创建远程服务
讯享网
- 声明远程服务
- 绑定远程服务
讯享网
Messenger可以在不同进程中传递 Message 对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种轻量级的 IPC 方案,底层实现是 AIDL。
Window 是一个抽象类,它的具体实现是 PhoneWindow。WindowManager 是外界访问 Window 的入口,Window 的具体实现位于 WindowManagerService 中,WindowManager 和 WindowManagerService 的交互是一个 IPC 过程。Android 中所有的视图都是通过 Window 来呈现,因此 Window 实际是 View 的直接管理者。
Window 是一个抽象的概念,每一个 Window 对应着一个 View 和一个 ViewRootImpl。Window 实际是不存在的,它是以 View 的形式存在。对 Window 的访问必须通过 WindowManager,WindowManager 的实现类是 WindowManagerImpl:
WindowManagerImpl 没有直接实现 Window 的三大操作,而是全部交给 WindowManagerGlobal 处理,WindowManagerGlobal 以工厂的形式向外提供自己的实例:
讯享网
在 ViewRootImpl 中最终会通过 WindowSession 来完成 Window 的添加、更新、删除工作,mWindowSession 的类型是 IWindowSession,是一个 Binder 对象,真正地实现类是 Session,是一个 IPC 过程。
在 Activity 的创建过程中,最终会由 ActivityThread 的 performLaunchActivity() 来完成整个启动过程,该方法内部会通过类加载器创建 Activity 的实例对象,并调用 attach 方法关联一系列上下文环境变量。在 Activity 的 attach 方法里,系统会创建所属的 Window 对象并设置回调接口,然后在 Activity 的 setContentView 方法中将视图附属在 Window 上:
讯享网
这个时候 DecorView 还没有被 WindowManager 正式添加。在 ActivityThread 的 handleResumeActivity 方法中,首先会调用 Activity 的 onResume 方法,接着调用 Activity 的 makeVisible(),完成 DecorView 的添加和显示过程:

Dialog 的 Window 的创建过程和 Activity 类似,创建同样是通过 PolicyManager 的 makeNewWindow 方法完成的,创建后的对象实际就是 PhoneWindow。当 Dialog 被关闭时,会通过 WindowManager 来移除 DecorView:mWindowManager.removeViewImmediate(mDecor)。
讯享网
普通 Dialog 必须采用 Activity 的 Context,采用 Application 的 Context 就会报错,是因为应用 token 所导致,应用 token 一般只有 Activity 拥有。系统 Window 比较特殊,不需要 token。
Toast 属于系统 Window ,由于其具有定时取消功能,所以系统采用了 Handler。Toast 的内部有两类 IPC 过程,第一类是 Toast 访问 NotificationManagerService,第二类是 NotificationManagerService 回调 Toast 里的 TN 接口。
Toast 内部的视图由两种方式,一种是系统默认的样式,另一种是 setView 指定一个自定义 View,它们都对应 Toast 的一个内部成员 mNextView。
讯享网

Bitmap 中有两个内部枚举类:
- Config 是用来设置颜色配置信息
- CompressFormat 是用来设置压缩方式
通常我们优化 Bitmap 时,当需要做性能优化或者防止 OOM,我们通常会使用 Bitmap.Config.RGB_565 这个配置,因为 Bitmap.Config.ALPHA_8 只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444 显示图片不清楚, Bitmap.Config.ARGB_8888 占用内存最多。
虽然Matrix还可以调用postSkew方法进行倾斜操作,但是却不可以在此时创建Bitmap时使用。
讯享网
讯享网

讯享网
Bitmap 类的构造方法都是私有的,所以开发者不能直接 new 出一个 Bitmap 对象,只能通过 BitmapFactory 类的各种静态方法来实例化一个 Bitmap。仔细查看 BitmapFactory 的源代码可以看到,生成 Bitmap 对象最终都是通过 JNI 调用方式实现的。所以,加载 Bitmap 到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java 部分的,一部分是 C 部分的。这个 Bitmap 对象是由 Java 部分分配的,不用的时候系统就会自动回收了,但是那个对应的 C 可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用 recycle() 方法来释放 C 部分的内存。从 Bitmap 类的源代码也可以看到,recycle() 方法里也的确是调用了 JNI 方法了的。
- dpi 每英寸像素数(dot per inch)
- dp
密度无关像素 - 一种基于屏幕物理密度的抽象单元。 这些单位相对于 160 dpi 的屏幕,因此一个 dp 是 160 dpi 屏幕上的一个 px。 dp 与像素的比率将随着屏幕密度而变化,但不一定成正比。为不同设备的 UI 元素的实际大小提供了一致性。 - sp
与比例无关的像素 - 这与 dp 单位类似,但它也可以通过用户的字体大小首选项进行缩放。建议在指定字体大小时使用此单位,以便根据屏幕密度和用户偏好调整它们。
讯享网
- Android P 刘海屏适配方案
Android P 支持最新的全面屏以及为摄像头和扬声器预留空间的凹口屏幕。通过全新的 DisplayCutout 类,可以确定非功能区域的位置和形状,这些区域不应显示内容。要确定这些凹口屏幕区域是否存在及其位置,使用 getDisplayCutout() 函数。
Android P 中 WindowManager.LayoutParams 新增了一个布局参数属性 layoutInDisplayCutoutMode:
- Android P 之前的刘海屏适配
不同厂商的刘海屏适配方案不尽相同,需分别查阅各自的开发者文档。
Context 本身是一个抽象类,是对一系列系统服务接口的封装,包括:内部资源、包、类加载、I/O操作、权限、主线程、IPC 和组件启动等操作的管理。ContextImpl, Activity, Service, Application 这些都是 Context 的直接或间接子类, 关系如下:

ContextWrapper是代理Context的实现,简单地将其所有调用委托给另一个Context(mBase)。
Application、Activity、Service通过调用父类ContextWrapper的, 从而设置父类成员变量 mBase 为 ContextImpl 对象, ContextWrapper 的核心工作都是交给 mBase(ContextImpl) 来完成,这样可以子类化 Context 以修改行为而无需更改原始 Context。
SharedPreferences 采用key-value(键值对)形式, 主要用于轻量级的数据存储, 尤其适合保存应用的配置参数, 但不建议使用 SharedPreferences 来存储大规模的数据, 可能会降低性能.
SharedPreferences采用xml文件格式来保存数据, 该文件所在目录位于 ,如:
从Android N开始, 创建的 SP 文件模式, 不允许 和 模块, 否则会直接抛出异常 SecurityException。 这种多进程的方式也是 Google 不推荐的方式, 后续同样会不再支持。
当设置 MODE_MULTI_PROCESS 模式, 则每次 getSharedPreferences 过程, 会检查 SP 文件上次修改时间和文件大小, 一旦所有修改则会重新从磁盘加载文件。
Activity.getPreferences(mode): 以当前 Activity 的类名作为 SP 的文件名. 即 xxxActivity.xml
讯享网
PreferenceManager.getDefaultSharedPreferences(Context): 以包名加上 _preferences 作为文件名, 以 MODE_PRIVATE 模式创建 SP 文件. 即 packgeName_preferences.xml.
直接调用 Context.getSharedPreferences(name, mode),所有的方法最终都是调用到如下方法:
讯享网

SharedPreferences 与 Editor 只是两个接口. SharedPreferencesImpl 和 EditorImpl 分别实现了对应接口。另外, ContextImpl 记录着 SharedPreferences 的重要数据。
操作把数据写入到EditorImpl.mModified;
操作先调用 commitToMemory(), 将数据同步到 SharedPreferencesImpl 的 mMap, 并保存到 MemoryCommitResult 的 mapToWriteToDisk,再调用 enqueueDiskWrite(), 写入到磁盘文件; 先之前把原有数据保存到 .bak 为后缀的文件,用于在写磁盘的过程出现任何异常可恢复数据;
操作从 SharedPreferencesImpl.mMap 读取数据.
- apply 没有返回值, commit 有返回值能知道修改是否提交成功
- apply 是将修改提交到内存,再异步提交到磁盘文件,而 commit 是同步的提交到磁盘文件
- 多并发的提交 commit 时,需等待正在处理的 commit 数据更新到磁盘文件后才会继续往下执行,从而降低效率; 而 apply 只是原子更新到内存,后调用 apply 函数会直接覆盖前面内存数据,从一定程度上提高很多效率。
- 强烈建议不要在 sp 里面存储特别大的 key/value,有助于减少卡顿 / anr
- 不要高频地使用 apply,尽可能地批量提交
- 不要使用 MODE_MULTI_PROCESS
- 高频写操作的 key 与高频读操作的 key 可以适当地拆分文件,由于减少同步锁竞争
- 不要连续多次 edit(),应该获取一次获取 edit(),然后多次执行 putxxx(),减少内存波动
Handler 有两个主要用途:(1)安排 Message 和 runnables 在将来的某个时刻执行; (2)将要在不同于自己的线程上执行的操作排入队列。(在多个线程并发更新UI的同时保证线程安全。)
Android 规定访问 UI 只能在主线程中进行,因为 Android 的 UI 控件不是线程安全的,多线程并发访问会导致 UI 控件处于不可预期的状态。为什么系统不对 UI 控件的访问加上锁机制?缺点有两个:加锁会让 UI 访问的逻辑变得复杂;其次锁机制会降低 UI 访问的效率。如果子线程访问 UI,那么程序就会抛出异常。ViewRootImpl 对UI操作做了验证,这个验证工作是由 ViewRootImpl的 方法完成:
- Message:Handler 接收和处理的消息对象
- MessageQueue:Message 的队列,先进先出,每一个线程最多可以拥有一个
- Looper:消息泵,是 MessageQueue 的管理者,会不断从 MessageQueue 中取出消息,并将消息分给对应的 Handler 处理,每个线程只有一个 Looper。
Handler 创建的时候会采用当前线程的 Looper 来构造消息循环系统,需要注意的是,线程默认是没有 Looper 的,直接使用 Handler 会报错,如果需要使用 Handler 就必须为线程创建 Looper,因为默认的 UI 主线程,也就是 ActivityThread,ActivityThread 被创建的时候就会初始化 Looper,这也是在主线程中默认可以使用 Handler 的原因。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/194552.html