- 博客(74)
- 收藏
- 关注
原创 Java 线程池
Java 线程池核心是,靠 corePoolSize、maximumPoolSize、workQueue、拒绝策略这几个参数控制资源上限。任务提交流程是核心线程 → 队列 → 最大线程 → 拒绝策略。阿里规范不推荐用Executors的几个快捷方法,因为它们背后用的是无界队列或无限线程数,容易 OOM,建议手动 new ThreadPoolExecutor 显式指定参数。线程数设置要区分 CPU 密集型和 IO 密集型,没有万能公式,要结合压测。
2026-09-11 16:34:23
158
原创 Java 线程状态
操作系统层面线程有五态:New、Ready、Running、Blocked、Terminated,核心是就绪、运行、等待三态循环。Java 在此基础上做了 JVM 视角的重新划分,共 6 种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。区别主要在于两点:一是 Java 把 OS 的 Ready 和 Running 合并成了 RUNNABLE,因为 JVM 不感知 CPU 调度;
2026-09-11 16:24:04
272
原创 volatile 关键字 作用
volatile 是 Java 提供的轻量级同步关键字,只能修饰成员变量。保证可见性:一个线程修改了 volatile 变量,其他线程能立即看到最新值。禁止指令重排:通过插入内存屏障,保证有序性。但它不保证原子性,像 i++ 这种复合操作仍然线程不安全。典型应用是状态标志位和双重检查锁单例。
2026-09-10 18:06:23
139
原创 Java Synchronized 关键字
轻量级锁状态下,代码进入同步块时,如果同步对象锁状态为无锁状态, 虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝 ,接着虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针。这个操作如果成功则代表获取到了锁,但是如果失败,则会检查对象Mark Word是不是指向当前线程栈帧中的锁记录,如果是,则说明本身当前线程就拥有此对象的锁,就可以直接执行同步代码。退出代码块后释放锁。
2026-09-10 16:20:11
163
原创 Java 静态内部类、非静态内部类、匿名内部类的区别?
写 new Outer().new Inner() 时,底层字节码其实是 new Outer$Inner(outerInstance)。不管你用不用 外部类的成员变量,这个 this$0 引用都会指向 Outer。只要是 new 在非静态上下文(普通方法里),匿名内部类的构造器第一个参数永远藏着外部类 this。例如在Activity里写一个非静态Handler,如果消息队列还没处理完,Activity就泄露了。非静态内部类持有外部类引用,会导致。是解决内存泄漏的标准套路。编译后 会 生成两个。
2026-09-09 15:48:00
72
原创 Android Handler 内存泄漏分析
10000),延迟10秒才执行),然后用户按返回键退出了Activity。主线程 的 ThreadLocal 强引用了 Looper(主线程初始化时初始化Looper时被引用),而主线程生命周期跟APP进程一样长,属于GC Root。因为你的Handler是非静态内部类,编译器会偷偷给它加一个成员变量,叫 this$0,专门指向外部的MainActivity实例。➡️ msg.target(指向Handler,具体发送消息的那个 Handler)➡️ Message(延迟10秒的那个消息对象)
2026-09-09 15:47:06
218
原创 Java String 解析
现代 JVM 中,String 对象本身仍然位于堆中。所谓字符串常量池,可以理解为 JVM 维护的一张用于复用字符串的引用表,而不是“String 对象存放在堆外的另一个区域”。是不可变对象,两个 String 都不会修改这份底层字符数据,因此可以安全共享。如果池中已经存在内容为 “abc” 的 String 对象,则直接复用。StringTable 中保存对该 String 对象的引用。保存了对这个池化 String 对象的引用。它们是两个不同的 String 对象。不一定意味着重新创建一份。
2026-09-07 19:25:56
260
原创 include、merge、ViewStub与 性能优化
则用于延迟加载低频布局,只有真正需要显示时才进行 inflate,从而减少初始化阶段的 View 创建和内存开销。使用,去掉多余的根 ViewGroup,从而降低 View 树层级;自己不会生成 View,而是把内部 View 直接添加到父布局。例如多个页面都有相同的标题栏,可以将标题栏单独写成。用于把一个公共 XML 布局引入当前布局。主要用于公共布局复用;适合错误页、空数据页等。
2026-08-27 16:05:38
204
原创 KSP 和 ASM 字节码框架 差异
需要生成代码时,选 KSP:如果你的目标是自动化创建重复、模板化的代码(如定义路由表、依赖图),KSP是更现代、高效的选择。需要修改已有代码行为时,选 ASM:如果你的目标是在不修改源码的情况下,给所有方法加上日志、监控性能或实现热修复,那么ASM是唯一的选择。KSP和ASM并非替代关系,而是解决不同问题的工具。在实际项目中,它们甚至可以协同工作,例如用KSP在编译时生成代码,再用ASM对生成的字节码进行进一步优化或插桩。
2026-08-25 14:35:25
339
原创 OkHttp 原理
一句话概括:OkHttp 基于责任链模式,通过拦截器链处理请求的各个阶段,配合Dispatcher调度请求,连接池复用 TCP 连接。流程简述Request→RealCall→execute()enqueue()→Dispatcher调度 →→ 拦截器链依次处理 → 返回Response。亮点展开拦截器链:7 个拦截器的职责和顺序连接池Address决定连接复用,减少三次握手开销Dispatcher:并发控制(64/5)和线程池调度。
2026-08-25 14:26:55
323
原创 触摸事件 从 屏幕 到 Activity
所以 InputDispatcher 收到 Motion 事件以后,可以结合触摸坐标、Window 层级、Touchable Region、焦点等信息找到目标 Window。Window 的位置、层级、Touchable Region 等信息主要由 WMS 管理,并同步给 InputDispatcher,所以它可以根据触摸坐标找到目标窗口。DecorView 收到事件以后,会优先调用 Window.Callback,而 Activity 实现了 Window.Callback,因此事件会先进入。
2026-08-21 13:26:41
148
原创 Android: 显示过程
为了解决“渲染+合成”耗时长的问题,Offset偏移量设计让APP和SF接收信号的时机错开,理论上可在一个硬件周期内完成两步,降低触控延迟。面试核心点:HWC有图层合成数量上限,超出部分会回退给GPU处理(标记为CLIENT合成),GPU处理不完则丢帧。GPU/DPU/CPU访问GraphicBuffer前需检查Fence信号,防止数据读写冲突(如GPU还在写,DPU就来读,导致花屏)。将多个渲染好的图层(如状态栏、壁纸、应用界面)按Z轴顺序合并,计算重叠区域的最终颜色,并驱动屏幕显示。
2026-08-12 17:14:41
388
原创 Android: setContentView
但是 viewRootImpl 没有创建时(onResume()后,由 WindowManager.addView() 创建的), 在首次启动 Activity 在 onCreate() 可以在子线程 尝试进行UI更新,在viewRootImpl 创建之前,某些 View 属性修改可能只是改变了内存中的字段,没有经过 ViewRootImpl 的线程检查,因此偶尔不会报错。在每次绘制之前,ViewRootImpl 会判断当前线程是否是 主线程,如果不是会抛出异常,因此,更新 UI 只能在主线程。
2026-08-06 18:54:58
371
原创 Activity 的状态改变、状态保存
是系统的“序列化仓库”(只存小东西,且必须由 Activity 亲自管理)。ViewModel是内存中的“大型数据仓库”(配置变更时不怕,但进程死亡就完蛋)。是“ViewModel 随身携带的钥匙”,让 ViewModel 可以随时把关键的小数据锁进系统的“序列化仓库”里,让 ViewModel 同时具备了抵抗进程死亡的能力,且代码高度内聚,无需界面层参与。
2026-08-04 15:24:12
371
原创 Android:Handler
最后,由于Looper 中的loop是在不断轮询MessageQueue的,一旦发现MessageQueue里面有可执行的消息,那么就会将消息取出来,然后通过消息所携带的handler (msg.target.dispathMessage(msg) msg.target 就是 发送信息的 handler )去执行。然后,系统再调用Looper.loop()函数,这个函数会开启一个死循环,在循环中不断的轮询MessageQueue消息队列,从消息队列中取出可以执行的Message消息,然后进行执行。
2026-08-04 01:43:49
256
原创 Android: Intent 理解
Intent 实现了 Parcelable,跨进程时会被序列化到 Parcel 中,因此目标进程获得的是重新构造的 Intent,而不是原对象引用。是的,因为这里涉及应用进程和 AMS 所在的 system_server 进程的 Binder IPC 通信息,所以 Intent 传递数据会受到 Binder 事务的大小限制。使用全局变量或单例模式:将数据存储在全局变量(静态变量或自定义Application中变量,主要满足生命周期与进程一样)或单例对象中,这样在不同的 Activity 之间可以直接访问。
2026-08-01 02:59:24
204
原创 Android 中 Pid 和 Uid 的区别和联系
在实际开发中,我们应默认使用单进程,只有当确实需要隔离内存或提升稳定性时才引入多 PID;而 UID 的共享则要慎用,因为涉及签名绑定,且可能破坏沙箱安全模型。面试中能理清这两个概念的区别与应用边界,就能体现出对 Android 底层安全模型和进程管理的深入理解。
2026-07-31 14:57:45
290
原创 Android MVC, MVP, MVVM, MVI 架构
MVC 架构主要分为以下几个部分:请求的流程:存在问题:MVP的构成:流程:相较于MVC:仍然存在的缺点: 模式将 改名为 ,基本上与 模式完全一致。唯一的区别是,它采用双向数据绑定():的变动,自动反映在 ,反之亦然同时将代码划分为三个部分:流程: 与 很相似,其借鉴了前端框架的思想,更加强调数据的单向流动和唯一数据源。![[Pasted image 20260424011110.png]]分为三个部分:MVI强调数据的单向流动,主要分为以下几步:用户操作以Intent的形式通知ModelMode
2026-07-30 13:18:38
296
原创 Android Context 理解
Activity、Service 等继承 ContextWrapper,可以在不修改 ContextImpl 的情况下增加主题、生命周期或组件特有的逻辑,这体现了装饰器模式中“包装原对象并扩展功能”的思想。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的。因为 Context 一共有 Application、Activity和Service三种类型,因此,一个应用程序中 Context 的数量为。
2026-07-30 11:12:45
213
原创 Java 并发编程
Lock提供了公平锁和非公平锁的机制,公平锁是指线程竞争锁资源时,如果已经有其他线程正在排队等待锁释放,那么当前竞争锁资源的线程无法插队。synchronized在性能方面和lock相差不大,在实现上会有一个区别synchronized引入了偏向锁,轻量级锁,重量级锁,以及锁升级的机制去实现锁的优化,而lock则用到了自旋锁的方式实现性能优化。,来控制锁的作用范围,比如锁对象是静态对象,或者类对象,那么这个锁就是属于全局锁,如果锁对象是实例对象,那么这个锁的范围取决于这个对象的生命周期。
2026-07-29 21:10:19
155
原创 Java Synchronized 基本使用
实际锁的是对象对应的 Monitor,而不是直接锁住某个变量或某段代码。只有多个线程使用同一个锁对象时,才会产生互斥效果。锁的不是代码本身,而是某个对象对应的 Monitor 监视器锁。只有多个线程竞争同一个锁对象时,才会产生互斥效果。同一个实例上的同步方法互斥,不同实例上的同步方法不互斥。两个线程使用的是不同实例对应的锁,因此可以同时执行。不同线程获得的是不同锁对象,因此同步基本失效。修饰代码块时,锁的是括号中指定的对象。对应的锁,因此不能同时执行静态同步方法。读写使用不同的锁对象,因此不能相互约束。
2026-07-29 21:08:45
215
原创 计算机:操作系统
死锁是系统中多个进程因争夺资源而造成的一种僵局。当每个进程都持有一定的资源并等待其他进程释放它们所需的资源时,如果这些资源都被其他进程占有且不释放,就导致了死锁。互斥条件:同一时刻一个进程占用了某个资源时,其他进程无法同时占用该资源。请求保持条件:一个线程因为请求资源而阻塞的时候,不会释放自己的资源。不可剥夺条件:资源不能被强制性地从一个进程中剥夺,只能由持有者自愿释放。循环等待条件:多个进程之间形成一个循环等待资源的链,每个进程都在等待下一个进程释放所占有的资源。
2026-07-29 20:43:23
207
原创 Java 类加载器
当一个类加载器收到类加载请求时,首先检查自己是否已经加载过这个类。如果缓存中已经存在,就直接返回;如果不存在,就把加载请求交给父加载器。父加载器也会进行同样的检查,并继续。,直到最顶层的引导(根)类加载器。只有当父加载器无法完成加载时,子加载器才会尝试自己加载。双亲委派保证: 核心类永远由 Bootstrap 加载, 加载后的类,不会被重复加载。整体的层级关系,这个层级关系不是 继承关系。重写 loadClass()
2026-07-29 20:41:25
232
原创 Java 泛型
泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即编译器会在编译期间擦除代码中的所有泛型语法并相应的做出一些类型转换动作。换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。看一个例子,假如我们给 ArrayList 集合传入两种不同的数据类型,并比较它们的类信息。// true。
2026-07-28 18:41:32
170
原创 Android 事件分发 常见案例
Android 事件分发的核心是先通过 ACTION_DOWN 确定当前事件序列的 TouchTarget。父 ViewGroup 的 dispatchTouchEvent 会先通过 onInterceptTouchEvent 判断是否拦截;不拦截就把事件分发给命中的子 View。子 View 如果消费了 DOWN,后续 MOVE 和 UP 默认都会继续交给它。
2026-07-28 16:43:17
195
原创 JVM vs. DVM vs. ART
解释器执行每条字节码时,都需要经过取指、解码、分派和执行。取指是读取下一条字节码,分派是根据操作码跳转到对应的处理逻辑,解释执行则是由解释器完成该指令表示的操作。寄存器型字节码通常用更少的指令完成相同任务,因此可以减少解释器循环和指令分派次数,这在以解释执行为主的早期移动设备上有一定性能优势。JIT 出现之前或尚未触发时,JVM 通过解释器逐条读取和分派字节码,执行解释器内部已有的本地处理逻辑;当方法或循环成为热点后,JIT 会把整段字节码编译成本地机器码,后续直接执行机器码,不再逐条解释。
2026-07-27 16:16:39
202
原创 Git 基本使用
git initgit addgit commitgit 仓库的状态变化:工作区 --git add–> 暂存区 --git commit–> 本地仓库。
2026-07-25 21:34:55
203
原创 RecyclerView 缓存机制
RecyclerView 的缓存机制是其高性能的核心。它通过来高效地复用,从而 减少的开销 以及 inflate 的次数,实现流畅的滑动体验。
2026-07-20 01:30:14
331
原创 JVM 基础
垃圾回收(Garbage Collection, GC)是自动管理内存的一种机制,它负责自动释放不再被程序引用的对象所占用的内存,这种机制减少了内存泄漏和内存管理错误的可能性。内存不足时:当JVM检测到堆内存不足,无法为新的对象分配内存时,会自动触发垃圾回收。手动请求:虽然垃圾回收是自动的,开发者可以通过调用或建议 JVM 进行垃圾回收。不过这只是一个建议,并不能保证立即执行。JVM参数-Xmx(最大堆大小)、-Xms(初始堆大小)等。对象数量或内存使用达到阈值。
2026-07-16 15:52:51
198
原创 Android 跨进程通信方式
发送进程,将数据发送到内核缓存区(数据拷贝一次),由于 内核缓存区 与 接收进程的用户空间地址存在映射,因此,相当于发送到了接收进程的用户空间地址。Binder 驱动,是 连接 Service 进程、Client 进程 和 ServiceManager 的桥梁, 通过内存映射 传递进程间的数据。传统的跨进程通信需要数据拷贝两次,也就是 从 用户空间 复制到内核空间的内核缓存区,然后再将数据复制到另一个进程的用户空间。Client 进程 表示使用服务 的进程。Server 进程 是 提供服务的进程。
2026-07-16 14:39:33
185
原创 Android Navigation 架构
在 setGraph() 的开头,源码会主动调用 popBackStackInternal(null, true),把所有旧的回退栈条目全部清除。这意味着新设置的 startDestination 一定是新栈底的唯一页面,用户按返回键不会回到旧图里的页面。即 先清空栈才会调用。的初始化发生在 NavHostController(NavController 的子类)的构造方法中。这也是为什么我们在 navigate() 时传入的是。返回 FragmentDestination。返回完整的 NavGraph。
2026-07-06 23:28:48
360
原创 Kotlin Flow 使用
flow {}创建的通常是冷流。只有调用collect时,上游代码才会开始执行;每个收集者都会独立执行一次上游逻辑,因此不同收集者之间默认不共享数据生产过程。StateFlow是用于保存状态的热流,必须提供初始值,并始终保存一个当前最新值。新收集者开始收集时,会立即获得当前状态。StateFlow 会通过equals比较新旧值,如果新旧状态相等,就不会重复通知收集者,因此非常适合表示 UI 状态。SharedFlow是可以向多个收集者广播数据的热流。它不要求初始值,并且可以通过replay。
2026-06-24 23:58:05
323
原创 Kotlin Flow
但真正的“确保不丢失”事件建议建模为状态或使用 Channel(点对点)。SharedFlow 在无订阅者时事件会被丢弃。A: 最后一个订阅者离开后延迟一定时间停止上游,既节省资源,又防止配置变更(如旋转)造成的立即重启。A: StateFlow/SharedFlow 是热流,不会随生命周期自动停止。collect 触发 → 重新 emit(1),emit(2)比较新旧值,相同则不重新发射,所以不适合连续相同的“事件”。保证只在界面可见时收集,后台自动取消。A: 因为它表示当前状态,任何时候用。
2026-06-24 23:17:57
411
原创 Android Kotlin 协程
协程是 Kotlin 提供的一种轻量级并发任务抽象。它运行在线程之上,由协程调度器将协程任务分发到一个或多个线程中执行。协程本身不直接对应某一个固定线程,一个协程在挂起和恢复前后,甚至可能运行在不同线程上。挂起恢复非阻塞式等待结构化并发要求协程必须运行在明确的作用域中,任务之间形成清晰的父子关系,使协程的生命周期可追踪、可取消、可等待。父协程取消,所有子协程都会被取消普通子协程异常,通常会取消父协程和兄弟协程父协程会等待所有子协程完成后再结束子协程的生命周期不会随意超出父作用域。
2026-06-24 21:08:10
437
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅