安卓 面试八股文整理(基础组件篇)

整理了一些安卓的面试题,大概是1-2年水平,本人菜鸡,如有错漏欢迎留言

1. Activity

1.1 生命周期

|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| onCreate(Bundle savedInstanceState)​​ | 进行一次性初始化操作,如加载布局(setContentView)、初始化变量、绑定数据到视图、设置点击监听器等。如果Activity之前被销毁并有状态保存,可以通过savedInstanceState参数恢复数据。执行后Activity已创建但不可见​。 |
| onStart()​​ | Activity由不可见变为可见时。处理Activity即将对用户可见时需要进行的准备工作。例如,注册广播接收器、绑定可以影响UI的数据(但此时Activity还未进入前台,无法与用户交互)。执行后Activity可见但不在前台 |
| onResume()​​ | Activity进入前台,获得焦点,开始可与用户交互时。启动或恢复需要与用户交互紧密相关的操作,例如开启动画、启动摄像头预览、注册传感器监听、恢复音频/视频播放等。执行后Activity可见且位于前台​​ |
| onPause()​​ | Activity正在失去焦点,但通常仍部分可见,此时不能交互。保存持久性数据:提交未保存的更改(如用户编辑的草稿);停止消耗CPU的操作:暂停动画、停止传感器、释放相机等独占资源。此方法必须迅速执行,等待此方法返回后才能继续下个生命周期(如onStop),耗时操作会影响用户体验和界面流畅度。 |
| onStop()​​ | Activity完全不可见时。释放或减少在Activity不可见时不需要的资源,以节省系统内存。例如,停止网络请求、注销广播接收器、暂停一些重量级操作等。系统内存紧张时,处于Stopped状态的Activity可能会被回收 |
| onRestart()​​ | Activity从Stopped状态(完全不可见)重新变为可见之前调用(在onStart之前)。处理Activity从停止状态恢复时需要特别更新的逻辑,许多简单的恢复操作通常在onStart或onResume中处理。 |
| onDestroy()​​ | Activity被销毁之前,进行最终的清理工作,释放所有资源,防止内存泄漏。例如,取消正在执行的任务、关闭数据库连接、解绑服务等。 |

额外的重要方法:onSaveInstanceState(Bundle outState)​​:并非生命周期循环中的常规方法,在Activity可能被系统销毁(如内存回收、配置变更)前调用(通常在onStop之前,但时机有版本差异)。用于将Activity的临时UI状态(如文本框内容、列表滚动位置)保存到Bundle中,以便在后续onCreate或onRestoreInstanceState中恢复

onRestoreInstanceState(Bundle savedInstanceState):在onStart之后、onResume之前被调用,当Activity确实是从之前保存的状态重建时,用于恢复UI状态。也可以在onCreate中通过判断savedInstanceState是否为null来进行恢复。

1.2 activity启动模式有几种

标准模式(standard):每次启动Activity时都会创建一个新的实例,即使已存在该Activity的实例。新实例将被放入启动它的Activity所在的任务栈中。

栈顶复用模式(singleTop):如果新启动的Activity已经位于任务栈的栈顶,则不会创建新的实例,而是调用现有实例的onNewIntent()方法。

栈内复用模式(singleTask):在任务栈中只允许一个Activity实例存在。如果栈中已有该Activity的实例,则将该实例移至栈顶,并调用onNewIntent()方法,同时清除该实例之上的所有Activity。

单例模式(singleInstance):该Activity将在一个新的任务栈中运行,确保只有一个实例存在。如果其他应用启动了这种模式的Activity,它将共享这个实例。(例如手机拨号的活动)

1.3 Activity的onNewIntent

onNewIntent的主要职责是处理新传递的Intent,并更新Activity的状态或UI,确保用户看到的是最新数据,常用于需要复用Activity实例且处理动态数据的场景.

onNewIntent的调用需满足Activity已存在且通过特定方式启动的条件,主要包括以下场景:

启动模式触发:当Activity的launchMode设置为singleTop(栈顶复用)或singleTask(栈内复用)时,若目标Activity已在任务栈中(singleTop要求在栈顶,singleTask无位置限制),再次启动该Activity不会创建新实例,而是调用现有实例的onNewIntent。

Intent Flag触发:启动Activity时设置FLAG_ACTIVITY_SINGLE_TOP标志(等同于singleTop模式),若Activity在栈顶,会触发onNewIntent

2. Fragment

2.1 Fragment是什么?和Activity的联系?

可以理解为"Activity的片段"或"子模块"。它代表Activity中界面的一部分行为或用户界面;Activity作为容器,可以包含一个或多个Fragment;Fragment作为内容,必须依附于Activity存在;Fragment的生命周期受宿主Activity的生命周期直接影响。当Activity暂停时,其中的所有Fragment也会暂停;当Activity被销毁时,其中的Fragment也会被销毁;

主要优势:

|----------------------------------|
| 模块化:将代码分散到不同Fragment中,提高代码可维护性。 |
| 可重用性:同一个Fragment可以被多个Activity复用。 |
| 可适配性:根据屏幕尺寸和方向灵活调整布局。 |
| 轻量切换:相比Activity切换更流畅。 |

数据交互:

|--------------------------------------------------|
| Activity向Fragment传递数据:通过Bundle和setArguments()方法。 |
| Fragment向Activity传递数据:通过定义回调接口,让Activity实现该接口。 |
| Fragment间通信:通过共享的Activity作为中介。 |

2.2 Fragment生命周期

创建阶段

​​onAttach()​​:当Fragment与Activity建立关联时调用。

​​onCreate()​​:Fragment被创建时调用,进行初始化操作。

​​onCreateView()​​:创建并返回与Fragment关联的视图层次结构。这是必须实现的方法,通常在这里加载布局文件。

​​onActivityCreated()​​:当宿主Activity的onCreate()方法完成时调用,表明Activity已完全创建,可以安全执行与Activity相关的操作。

可见阶段

​​onStart()​​:Fragment变为可见状态时调用,但还不可交互。

​​onResume()​​:Fragment变为可见且可交互状态,可以获取焦点。

不可见阶段

​​onPause()​​:Fragment开始离开前台,失去交互能力但可能仍部分可见。

​​onStop()​​:Fragment完全不可见。

销毁阶段

​​onDestroyView()​​:与Fragment关联的视图被移除时调用。

​​onDestroy()​​:Fragment被销毁前调用。

​​onDetach()​​:Fragment与Activity解除关联时调用,生命周期中最后一个方法。

2.3 Fragment 管理

Fragment的所有动态操作(添加、移除、替换)都必须通过FragmentTransaction进行事务提交**。**常用事务方法:

|----------------------------------------|
| add():添加Fragment到容器 |
| remove():移除Fragment |
| hide()/show():隐藏/显示Fragment(不销毁实例) |
| detach():分离Fragment(销毁视图但保留实例) |
| attach():重新关联Fragment |
| replace():替换容器中的Fragment(先remove后add) |

FragmentManager内部维护回退栈,通过addToBackStack()将事务加入栈中。用户点击返回按钮时,会回退到上一个Fragment状态。回退栈记录每次add或replace的Fragment,点击返回时自动执行退栈操作。

2.4 IllegalStateException异常

Can not perform this action after onSaveInstanceState:commit()在onSaveInstanceState()之后调用导致。避免方案:不要在异步任务回调中执行Fragment事务,或使用commitAllowingStateLoss()。

2.5. getActivity()返回null

应该在Fragment的onAttach()中获取Activity引用,避免在异步任务中直接调用getActivity()。

3. Context

Context(上下文)是 Android 系统的核心组件,代表应用程序的运行环境信息,提供了访问应用资源、启动组件、获取系统服务等能力。Context 是一个抽象类,其具体实现由 Android 系统提供

3.1 主要功能

|------------------------------------------------------------|
| 访问应用资源(Resources、Assets、字符串、图片等) |
| 启动四大组件(Activity、Service、BroadcastReceiver、ContentProvider) |
| 获取系统服务(LayoutInflater、NotificationManager 等) |
| 文件操作和数据库访问 |
| 权限检查和系统配置访问 |

3.2 ApplicationContext

生命周期与整个应用进程一致,应用启动时创建,进程结束时销毁,无内存泄漏风险;无主题支持,不能做ui操作。无任务栈,启动activity时必须在 Intent 中添加 FLAG_ACTIVITY_NEW_TASK标志。

3.3 ActivityContext

生命周期与 Activity 绑定,Activity 销毁时随之销毁;ui操作相关的context只能用ActivityContext;有主题支持;被生命周期长的对象(如静态变量、单例)持有,容易导致内存泄漏。

3.4 ServiceContext

生命周期与 Servcie绑定,Service 销毁则其 Context 销毁;不支持直接进行 UI 操作(如显示 Dialog),但可通过 WindowManager添加视图到系统窗口(需权限);无主题支持;无任务栈,启动activity时必须在 Intent 中添加 FLAG_ACTIVITY_NEW_TASK标志。

3.5 getApplication() 与 getApplicationContext() 的区别

getApplication():返回 Application 实例,仅 Activity 和 Service 可用

getApplicationContext():返回 Application Context,所有 Context 实现类都可用

两者本质上返回的是同一个 Application 实例,但 getApplication() 是 Activity/Service 的方法,而 getApplicationContext() 是 Context 接口的方法。applicationcontext需要转换类型才能调appliaction的方法

4. Intent

4.1 Intent是什么

Intent是一种组件之间的通信机制,可以将其理解为一个含有指令和参数的包裹,告诉系统或应用你想执行什么操作,操作的对象是谁,以及附带什么数据。

4.2 显式 Intent 与隐式 Intent

显式 Intent直接指定目标组件的完整类名(ComponentName),明确告诉系统要启动哪个组件。通过 setClass()setComponent()明确指定目标组件,目标组件唯一,系统无需解析,通常用于应用内部组件跳转。

java 复制代码
// 方式一:构造函数指定
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);

// 方式二:setClass 方法
Intent intent = new Intent();
intent.setClass(this, TargetActivity.class);
startActivity(intent);

隐式Intent不指定具体组件类名,而是通过 Action、Category、Data 等属性描述要执行的操作,由系统根据这些属性找到合适的组件来处理。通过 IntentFilter 匹配规则找到目标组件,通常用于启动其他应用中的组件或执行系统级别操作。

java 复制代码
// 启动系统拨号器
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:123456"));
startActivity(intent);

// 打开网页
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
startActivity(intent);

4.3 intent如何工作

工作机制大概是由系统解析intent,如果是显式的,获取目标组件的Class对象。随后检查目标组件是否在清单文件中声明(未声明则抛出异常),若存在则启动组件;如果是隐式intent,系统向PackageManager查询所有已安装应用的AndroidManifest.xml,收集所有声明了<intent-filter>的组件(Activity/Service/BroadcastReceiver)。对每个组件,依次匹配Action、Data、Category(若任一条件不满足,则排除该组件)。

如果涉及跨进程就走binder进行通信,一般都是通知AMS或者ATMS进行启动

4.4 数据传递

通过putExtra,指定key和value进行设置,获取的时候不同类型有对应函数,指定key进行获取;如果传递的是对象,需要实现序列化接口。

如果当需要传递的数据量较大、类型较多,或者这些数据需要作为一个整体在多个Activity间连续传递时,使用 Bundle是更优选择,它能让代码更清晰、更易于维护。使用方法是创建Bundle对象,然后对于普通数据有对应的put函数,复杂数据调用putExtra,并且要求数据是序列化的。

总结:Intent 可以传递基本数据类型、String、数组、实现了 Serializable 或 Parcelable 接口的对象,以及 Bundle 对象。

注:Intent 传递数据时,数据会存放在 Binder 的事务缓冲区中,该缓冲区大小通常为 1MB。如果传递的数据量过大,会抛出 TransactionTooLargeException异常,所以避免传输大数据。

4.5 IntentFilter 匹配规则

必须同时匹配 action、category 和 data(如果定义了)。

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Intent Filter可以声明多个Action。只要Intent的Action与其中任何一个匹配即可, 如果Intent Filter未声明任何Action,则只有不包含Action的Intent才能与之匹配 |
| Data匹配是两部分:MIME类型和URI。 1. MIME类型:必须完全匹配,或Intent Filter使用通配符(如image/*匹配所有图片类型)。 2. URI:被拆分为Scheme、Host、Port、Path等部分进行匹配,遵循线性依赖规则: - 若未指定Scheme,则Host被忽略。 - 若未指定Host,则Port和Path被忽略。 - Path支持通配符匹配。 3. 特殊规则:如果Intent Filter只指定了MIME类型而未指定URI,则默认支持content:file:这两种Scheme的URI |
| Intent Filter声明的Category可以比Intent中的多,但只要包含了Intent的所有Category,就算通过。 特殊情况:使用startActivity()startActivityForResult()发起的隐式Intent,系统会自动为其添加android.intent.category.DEFAULT类别。因此,希望接收隐式Intent的Activity必须在Intent Filter中包含此类别。 如果Intent Filter未声明任何Category,则只有不包含任何Category的Intent才能与之匹配 |

5. 序列化

序列化是将对象转换为可存储或传输的字节序列的过程,以便在不同场景(如内存、磁盘、网络或进程间)中重建对象。其核心目的是解决对象在非运行时状态下的持久化与传输问题。

5.1 常见序列化

安卓主要两种序列化方式, Serializable(Java原生接口)实现简单,仅需声明接口(如public class User implements Serializable)。依赖反射机制,性能较低,可能触发频繁GC。支持版本控制(通过serialVersionUID),但需手动维护以避免反序列化失败;

Parcelable(Android专属接口)通过手动实现writeToParcel()和CREATOR完成序列化,无反射,性能高效。代码较复杂,需处理Parcel对象的读写逻辑。仅适用于内存中数据传输(如Intent传参),不支持直接存储到磁盘。适合高频内存操作(如Activity间传递大数据),AIDL跨进程通信。

另外常见还有使用Gson进行序列化变成Json。

5.2 为什么parcelable不能持久存储

设计初衷是为了在内存中高效地进行对象传输,特别是在进程间通信(IPC)和组件(如 Activity、Service)间传递数据。Parcelable是使用共享内存完成用户空间和内核空间的数据交换,没有io操作,也没有版本控制,速度快,但是由于不同版本实现可能不一样,无法保证反序列化成功(没有版本控制),所以不支持持久存储。

6. 广播

6.1 Android里广播的分类

普通广播和有序广播;普通广播异步、同时到达、无法中断、效率高;有序广播同步、按优先级顺序传递、可中断、可修改数据。

按注册方式还可以分静态注册和动态注册, 静态注册方式在应用未启动也能接收,常驻系统,但 Android 8.0+ 大部分广播受限,主要是系统广播才可以用;普通应用需要动态注册广播,按需注册和销毁。

6.2 广播是否可以跨应用

使用隐式intent发送广播,只要指定应用配置的intent过滤器匹配就可以收到

7. ContentProvider

Android app进程间有隔离,contentProvider可以从一个app暴露出指定接口,控制app的部分数据允许外部访问,实现应用程序之间的数据共享。底层用的是binder机制。

7.1 ContentProvider组成

7.1.1 URI

|----------------------------------------------------------------------------|
| URI 是 ContentProvider 中数据的唯一标识符,格式固定为:content://authority/path/[id]。 |
| content://:标准前缀,表示该 URI 由 ContentProvider 管理。 |
| authority:ContentProvider 的唯一标识,通常在 AndroidManifest.xml 中注册,一般使用包名来避免冲突。 |
| path:路径,用于区分不同类型的数据(如 users, books)。 |
| id:可选部分,指定某一条具体记录(如 users/1) |

7.1.2 ContentResolver

这是数据访问方(客户端)使用的核心类。应用通过 getContentResolver()获取 ContentResolver 实例,然后调用其 query(), insert(), update(), delete()等方法。ContentResolver 并不直接处理数据,而是作为代理,根据 URI 将请求转发给正确的 ContentProvider。

7.1.3 ContentObserver

这是一个回调类,用于监听指定 URI 对应的数据变化。当 ContentProvider 中的数据发生变更(插入、更新、删除)后,可以调用 getContext().getContentResolver().notifyChange(uri, null)来通知所有注册了该 URI 的 ContentObserver,其 onChange()方法会被回调,从而实现数据更新的实时通知。

7.2 为什么要用ContentResolver来访问

访问contentProvider需要使用ContentResolver,主要有几个原因:

|----------------------------------------------------------------------------------|
| ContentResolver作为系统级服务,为所有ContentProvider提供了标准化的接口。客户端无需关心底层ContentProvider的具体实现 |
| 解耦 |
| 屏蔽binder跨进程的复杂性,缓存contentProvider的Binder代理对象,减少重复IPC连接的开销 |
| 集中管理权限检查 |
| 异步查询避免主线程anr |
| 查询cursor自动释放资源避免内存泄露 |

7.3 线程问题

ContentProvider 的 CRUD 方法(query, insert等)运行在 Binder 线程池中,而非主线程。这意味着这些方法可能被多个线程同时调用,必须确保其实现是线程安全的,特别是在操作数据库时。

8. Service

8.1 Service启动方式

startService启动方式,生命周期onCreate→ onStartCommand→ 运行中→ onDestroy;Service启动后与调用者(如Activity)无关,即使调用者退出或销毁,Service仍可在后台长期运行;调用者无法直接调用Service中的方法,只能通过Intent传递简单数据;必须显式调用stopService()或Service内部调用stopSelf()才能停止。(后台音乐播放,上传下载,日志记录等。)

bindService启动方式,生命周期:onCreate→ onBind→ 运行中 → onUnbind→ onDestroy;Service与调用者绑定,调用者退出时如果没有解绑,Service也会随之销毁;调用者可以通过Binder接口调用Service中的方法,实现双向通信。(需要与Activity频繁交互的后台服务,跨进程通信,提供数据查询或计算服务的场景。)

同时startservice启动和bindservice绑定, Service既能独立运行,又能与组件交互;​​必须同时调用stopService()和unbindService()才会销毁Service;生命周期顺序:无论启动和绑定的顺序如何,onCreate()只会调用一次。如果先绑定后启动,启动时会直接调用onStartCommand();如果先启动后绑定,绑定时会直接调用onBind()

8.2 onStartCommand返回值 (用于被意外杀死后的恢复逻辑)

返回值 含义 适用场景
START_STICKY 粘性重启。服务被杀死后,系统会尝试重新创建服务并再次调用 onStartCommand()。但之前传入的 Intent 不会保留,如果重启时没有新的启动命令,Intent 参数将为 null 适用于需要长期运行但不依赖于特定 Intent 数据的服务,如背景音乐播放。
START_NOT_STICKY 非粘性。服务被杀死后,系统不会自动重启该服务。除非后续有新的 startService调用。 适用于执行一次性任务或可以安全中断的任务,例如定时轮询服务器数据。
START_REDELIVER_INTENT 重传 Intent。服务被杀死后,系统会重新创建服务,并将最后一次传入的 Intent 重新传递给 onStartCommand(),保证任务数据不丢失。 适用于必须完成的关键任务,例如文件下载,确保中断后能继续执行。

8.3 后台服务与前台服务

Android服务默认在后台运行,但可以通过设置为前台服务来提升优先级。系统在内存不足时可能会将后台服务回收。而前台服务通过调用startForeground()方法,会在状态栏显示一个不可移除的通知,具有更高的优先级,系统不易回收,适合执行用户可感知的持续任务(如音乐播放、文件下载等)。从Android 8.0(API 26)开始,系统对后台服务有严格限制,长时间运行的后台任务推荐使用前台服务或JobScheduler/WorkManager等替代方案。

8.4 是否可以在Service做耗时操作

一般服务是在后台运行,可以做一些耗时操作,但是有几个点要注意可能导致anr;

Service的生命周期方法(onCreate(), onStartCommand(), onBind())都运行在主线程,如果服务在前台,这些生命周期方法执行超过20s会anr,后台200s,所以在这些生命周期方法里面做耗时操作要考虑到这一点。

耗时操作可以用线程完成;IntentService继承Service的抽象类,内置工作线程处理任务,自动停止Service

8.5 IntentService

IntentService是 Android 中用于处理异步后台任务的特殊 Service子类,其核心设计结合了 Service的稳定性和 HandlerThread的异步处理能力。内部通过 HandlerThread创建一个独立的工作线程,所有耗时操作在 onHandleIntent()方法中执行,避免阻塞主线程(UI 线程)。开发者无需手动创建或管理线程,简化了代码逻辑。仅支持 startService()启动方式,适用于无需绑定的场景。

多个请求通过 startService(Intent)发送时,IntentService会按顺序逐个处理,形成串行队列,确保任务执行的顺序性。当所有 Intent处理完成后,IntentService会自动调用 stopSelf()停止服务,无需开发者干预。

8.6 通信

Intent通信(简单数据传递),activity通过intent携带数据传给service;

binder通信适用于bindService启动方式,同进程内的方法调用,需要Service创建继承Binder的内部类并暴露公共方法,通过ServiceConnection获取Binder实例并调用方法;

Messenger通信(跨进程消息传递),Service创建Handler和Messenger处理消息,Activity通过Messenger发送Message;

使用广播,Service发送广播Intent,Activity注册广播接收器

9.如何判断当前线程是否在主线程

|--------------------------------------------------------------------------------------------|
| 1.判断当前线程消息循环是否是主线程消息循环 Looper.myLooper() == Looper.getMainLooper() |
| 2.通过当前线程对象比较主线程关联的 Thread对象 Thread.currentThread() == Looper.getMainLooper().getThread() |
| 3.app启动时记录主线程id,后面需要比较时拿线程id比较 |

10. View

10.1 自定义View的流程

先选择继承哪个类,可以继承现有控件扩展,继承ViewGroup创建组合控件,继承view完全自定义;然后定义一些需要传入的自定义属性,实现构造函数,提取自定义属性,测量,布局,绘制,事件处理等方法

10.2 自定义view如何处理padding

1.测量阶段:在onMeasure()中计算包含padding的总尺寸。确保子view的范围已经是加上padding的作用后的结果

2.绘制阶段:在onDraw()中根据padding调整内容位置。防止显示不全

3.ViewGroup:需额外处理子View的布局偏移。设置子view要测量的尺寸

10.3 自定义View需要重写的方法

首先构造函数要重写,包括不带属性集的构造函数,带属性集的,带默认属性的,还有api21以后带默认样式的构造函数。建议在构造函数中完成对象的创建,而不是在绘制相关方法中创建。

OnMeasure处理view大小,要处理好三种测量模式;

onDraw()方法用于绘制View的内容;

onLayout()(仅继承ViewGroup需要),用来定位子View

10.4 自定义View的完整生命周期方法调用顺序

​​ 构造函数:View初始化时调用

​​ onAttachedToWindow():View被附加到窗口时调用

​​ onMeasure():测量View尺寸

​​ onSizeChanged():View尺寸变化时调用(首次也会调用)

​​ onLayout():ViewGroup定位子View(普通View不需要)

​​ onDraw():绘制View内容

​​ onDetachedFromWindow():View从窗口分离时调用

10.5 自定义view绘制流程的典型顺序:

背景绘制(drawBackground())

主体内容绘制(onDraw())

子View绘制(dispatchDraw(),仅ViewGroup)

前景绘制(onDrawForeground())

10.6 MeasureSpec

32位int,头两位表示测量模式,后面30位是测量值,测量模式三种

精确模式,对应match_parent或者写死数值,特点是父视图已经确定了子视图的确切尺寸,子视图必须使用这个尺寸;

最大模式,对应wrap_content,特点是父视图给出最大尺寸,子视图只能小于等于它;

未指定模式,在scrollview、listview等使用,父视图不做限制。

10.7 在ondraw、onmeasure、onlayout创建对象会怎么样

应该避免在onMeasure()和onLayout()中创建对象,onMeasure()可能被调用多次以确定合适尺寸,onLayout()在布局变化时会被调用;

Ondraw不建议创建对象,onDraw()会被频繁调用(如动画、滚动时),频繁创建对象会导致内存抖动,可能引发GC停顿和界面卡顿,影响绘制性能,降低帧率。

10.8 自定义view和xml布局优劣

|---------------------------------------------------------------------|---------------------------------------------------------|
| 解析开销:XML需通过LayoutInflater解析为View对象,涉及反射和属性解析,嵌套过深时(如10层)解析耗时可能增加3倍。 | 直接控制绘制:避免XML解析,复杂UI(如游戏、图表)通过onDraw()直接操作Canvas,性能更优。 |
| 内存占用:XML属性需存储冗余数据,复杂布局可能占用更多内存(如60MB vs Compose的30MB)。 | 动态布局灵活:高频增删子View或调整参数时,代码动态创建(如addView())比XML更高效。 |
| 动态更新效率低:频繁调用findViewById()更新视图会导致性能损耗。 | 硬件加速优化:自定义View可针对性启用GPU加速(如LAYER_TYPE_HARDWARE),减少绘制卡顿。 |

10.9 视图刷新

Android视图刷新过程主要通过测量(Measure)、布局(Layout)、绘制(Draw)​​三大流程实现,并受VSYNC信号和消息队列调度控制。触发重绘有三种方法

​​invalidate():标记视图为"脏区",仅触发重绘(onDraw()),不重新测量或布局。适用于内容变化但尺寸不变的场景(如动画、文本更新)。非线程安全。

​​ requestLayout():触发完整的三大流程(Measure→Layout→Draw),用于视图尺寸或位置变化(如动态调整布局参数)。

​​ postInvalidate():通过ViewRootImpl中的mHandler将invalidate重绘请求发送到主线程队列。

Vsync信号每隔一定时间触发一次,同步cpu、gpu、display的工作节奏避免画面撕裂;Choreographer接收VSYNC信号后,通过Handler将performTraversals()(三大流程的入口)加入主线程工作队列,确保刷新在下一帧完成;

10.10 事件分发

Activity接收系统事件,一层一层往下分发到子view,首先到ViewGroup,ViewGroup的子View可以是ViewGroup或者view。在通过dispatchTouchEvent往子view传递前,viewGroup可以调用onInterceptTouchEvent()拦截,通过自身onTouchEvent处理。如果不拦截就由子view处理,如果子view的onTouchEvent()返回true代表事件消费了,就此结束;如果返回false代表没有处理或者需要父ViewGroup处理,此时会把事件分发到父ViewGroup的onTouchEvent,甚至可以回传到activity、系统层。如果子view没有消费onEventDown,那后续的move、up都不会分发给它(down、move、up需要由同个view处理)

特殊的事件:

ACTION_CANCEL:事件被上层拦截时触发(如父 View 调用 onInterceptTouchEvent)。

ACTION_OUTSIDE:触摸点超出视图边界时触发

当一个子View消费了ACTION_DOWN事件后,系统默认后续的ACTION_MOVE和ACTION_UP事件应由该子View处理。若父容器(ViewGroup)中途拦截事件,需通过ACTION_CANCEL通知子View事件序列被强制终止,避免子View因未收到ACTION_UP而残留错误状态(如按钮保持按下效果)。

11 ViewPager2 ViewPager​

|--------------------------------|------------------------|
| ViewPager2 | ViewPager |
| 基于RecyclerView | 自定义 ViewGroup​ |
| 支持水平和垂直滑动 | 仅支持水平 |
| Adapter统一为RecyclerView.Adapter | 分FragmentPagerAdapter等 |
| 性能更高(视图回收优化) | 较低 |
| 原生支持RTL(右到左) | 需手动实现 |
| 支持 notifyDatasetChanged() | 不支持 |
| 懒加载默认支持 | 需手动实现 |

​12. 动画

属性动画可作用于任何对象的属性(如 View 的透明度、位置、缩放等),不限于View,修改对象的实际属性值,动画结束后状态保留,支持组合动画、自定义插值器和估值器。

补间动画仅作用于View,通过图像变换(平移、旋转、缩放、透明度)实现动画。仅改变 View 的绘制效果,不改变实际属性(如点击事件仍在原位置)。

帧动画逐帧播放静态图片序列(类似 GIF)。需预加载所有帧图片,内存占用较高。

插值器控制动画的变化速率(如加速、减速、弹跳等)

相关推荐
爱笑的眼睛114 小时前
SQLAlchemy 核心 API 深度解析:超越 ORM 的数据库工具包
java·人工智能·python·ai
爱学习的小可爱卢4 小时前
JavaEE进阶——SpringBoot拦截器详解:从入门到实战
java·spring boot·后端
ooolmf4 小时前
matlab2024读取温度01
java·前端·javascript
曹牧5 小时前
Java:Foreach语法糖
java·开发语言·python
编程火箭车5 小时前
【Java SE 基础学习打卡】24 循环结构 - while
java·编程基础·循环结构·while循环·java se·do-while循环·避免死循环
Haooog5 小时前
微服务保护学习
java·学习·微服务·sentinel
程序员云帆哥5 小时前
告别Swagger!Spring Boot集成Smart-Doc自动生成API文档
java·接口文档·api文档
222you5 小时前
SpringIOC的注解开发
java·开发语言
hgz07105 小时前
Spring Boot、Spring MVC、Spring 三者核心区别
java