为什么 Android 要把数据、视图和适配器分开?RecyclerView解读

文章目录

    • 一、我们到底在开发什么?
    • [二、Android 里的三个世界](#二、Android 里的三个世界)
      • [1. 数据层(Data)](#1. 数据层(Data))
      • [2. 视图层(View)](#2. 视图层(View))
      • [3. 适配层(Adapter)](#3. 适配层(Adapter))
    • 三、为什么不直接揉在一起?
    • [四、RecyclerView 到底做了什么?](#四、RecyclerView 到底做了什么?)
    • [五、MVC 思想的清晰体现](#五、MVC 思想的清晰体现)
    • [六、为什么 View 要复用?------ 一个与解耦一脉相承的设计选择](#六、为什么 View 要复用?—— 一个与解耦一脉相承的设计选择)
    • [七、现代 UI 框架的演进:解耦思想的延续](#七、现代 UI 框架的演进:解耦思想的延续)
    • 结尾:一张图总结一切

我们每天写列表、调 Adapter、填 ViewHolder,有没有想过:

为什么数据不能直接塞进界面?

为什么 Android 要刻意把 数据、视图和适配器 拆成三个世界?

这篇文章,试着从 RecyclerView 这个"最熟悉的陌生人"开始,聊一聊 Android UI 背后真正重要的设计思想。


一、我们到底在开发什么?

先看一段代码:

java 复制代码
Fruit fruit = new Fruit(
    "Apple",
    R.drawable.apple_pic
);

这个 fruit 对象,能直接显示到屏幕上吗?

不能。

那换一个写法:

java 复制代码
TextView tv = new TextView(this);

这个 TextView,就一定能显示 "Apple" 吗?

也不能。

这说明一个非常简单、但很多人会下意识忽略的事实:

数据 ≠ 界面

数据是数据,界面是界面。

你创建了一个 Fruit,只意味着内存里有了一段描述水果的数据,而它和屏幕之间,还隔着一条巨大的鸿沟。

同样,你 new 出来的 TextView,只是一个"空壳",它不知道你的水果叫什么名字,也不知道该显示哪张图片。

所以,我们每天真正在做的开发,其实不是"写界面",也不是"处理数据"------

而是在维护"数据状态"与"界面表现"之间的映射关系。


二、Android 里的三个世界

为了系统地解决这个映射问题,Android 把 UI 架构拆成了三层:
#mermaid-svg-o2SMAzjeBugAOZz8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o2SMAzjeBugAOZz8 .error-icon{fill:#552222;}#mermaid-svg-o2SMAzjeBugAOZz8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o2SMAzjeBugAOZz8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o2SMAzjeBugAOZz8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o2SMAzjeBugAOZz8 .marker.cross{stroke:#333333;}#mermaid-svg-o2SMAzjeBugAOZz8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o2SMAzjeBugAOZz8 p{margin:0;}#mermaid-svg-o2SMAzjeBugAOZz8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster-label text{fill:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster-label span{color:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster-label span p{background-color:transparent;}#mermaid-svg-o2SMAzjeBugAOZz8 .label text,#mermaid-svg-o2SMAzjeBugAOZz8 span{fill:#333;color:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 .node rect,#mermaid-svg-o2SMAzjeBugAOZz8 .node circle,#mermaid-svg-o2SMAzjeBugAOZz8 .node ellipse,#mermaid-svg-o2SMAzjeBugAOZz8 .node polygon,#mermaid-svg-o2SMAzjeBugAOZz8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o2SMAzjeBugAOZz8 .rough-node .label text,#mermaid-svg-o2SMAzjeBugAOZz8 .node .label text,#mermaid-svg-o2SMAzjeBugAOZz8 .image-shape .label,#mermaid-svg-o2SMAzjeBugAOZz8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-o2SMAzjeBugAOZz8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-o2SMAzjeBugAOZz8 .rough-node .label,#mermaid-svg-o2SMAzjeBugAOZz8 .node .label,#mermaid-svg-o2SMAzjeBugAOZz8 .image-shape .label,#mermaid-svg-o2SMAzjeBugAOZz8 .icon-shape .label{text-align:center;}#mermaid-svg-o2SMAzjeBugAOZz8 .node.clickable{cursor:pointer;}#mermaid-svg-o2SMAzjeBugAOZz8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-o2SMAzjeBugAOZz8 .arrowheadPath{fill:#333333;}#mermaid-svg-o2SMAzjeBugAOZz8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o2SMAzjeBugAOZz8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o2SMAzjeBugAOZz8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o2SMAzjeBugAOZz8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-o2SMAzjeBugAOZz8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o2SMAzjeBugAOZz8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster text{fill:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 .cluster span{color:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-o2SMAzjeBugAOZz8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-o2SMAzjeBugAOZz8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-o2SMAzjeBugAOZz8 .icon-shape,#mermaid-svg-o2SMAzjeBugAOZz8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o2SMAzjeBugAOZz8 .icon-shape p,#mermaid-svg-o2SMAzjeBugAOZz8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-o2SMAzjeBugAOZz8 .icon-shape .label rect,#mermaid-svg-o2SMAzjeBugAOZz8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o2SMAzjeBugAOZz8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-o2SMAzjeBugAOZz8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-o2SMAzjeBugAOZz8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据
翻译
数据层 Data

Fruit / User / Message
适配层 Adapter

FruitAdapter
视图层 View

TextView / ImageView / RecyclerView

1. 数据层(Data)

这一层只负责一件事:保存状态

比如一个 Fruit 类:

java 复制代码
class Fruit {
    String name;
    int imageId;
}

它只知道自己的名字和图片资源 ID。

它不知道 TextView 是什么,不知道 RecyclerView 是什么,甚至不知道自己最终会被显示成方形还是圆形。

数据层是全宇宙最"纯粹"的地方------它不依赖 Android,不依赖 UI,只依赖业务。

2. 视图层(View)

这一层也只负责一件事:显示

TextView 负责显示文字,ImageView 负责显示图片,Button 负责让用户点击。

它们提供了 setText()setImageResource() 这样的接口,但从不关心传入的字符串是从 Fruit 来的,还是从网络 JSON 里解析出来的。

视图层不知道数据的存在,只知道"给我内容,我就渲染"。

3. 适配层(Adapter)

这一层是翻译官

它知道 Fruit 里有什么字段,也知道 TextView 需要什么字符串。

于是它把数据"翻译"成视图能懂的指令:

java 复制代码
holder.fruitName.setText(fruit.getName());
holder.fruitImage.setImageResource(fruit.getImageId());

适配层把数据世界和视图世界连接在了一起,同时又不让它们互相依赖。


三、为什么不直接揉在一起?

很多人初学时可能会想:何必这么麻烦,直接把 TextView 写进 Fruit 里不就行了?

java 复制代码
// ❌ 错误设计
class Fruit {
    String name;
    int imageId;
    TextView textView;  // 直接持有视图
}

这看起来省了一个 Adapter,实际上带来了灾难:

  • Fruit 强依赖 Android
    一旦脱离 Android 环境(比如在后台解析 JSON),TextView 就完全无用,甚至会导致代码无法运行。
  • 无法独立测试
    想对 Fruit 做单元测试,还得 mock 一个 TextView,荒谬。
  • 无法复用
    同一个 Fruit 对象既不能用于网络传输,也不能存数据库,因为它拖着一个沉重的视图包袱。
  • 数据无法脱离界面存在
    所有数据逻辑都和 UI 生命周期绑定,内存泄漏、状态混乱将是家常便饭。

而标准设计里:

java 复制代码
// ✅ 正确设计
class Fruit {
    String name;
    int imageId;
}

这个 Fruit 可以做什么?

  • 存到数据库
  • 转成 JSON 发送到服务器
  • 在单元测试里验证逻辑
  • 在 RecyclerView 里显示
  • 甚至在 Compose 或 Flutter 里复用

因为数据只包含状态,不包含任何 UI 细节。

这就是软件工程里最重要的两个词:

高内聚,低耦合

数据负责自己的状态(高内聚),适配器负责连接(低耦合),视图负责渲染(高内聚)。

三者各司其职,任何一个改变都不会引起雪崩式的连锁反应。


四、RecyclerView 到底做了什么?

RecyclerView 是这个三层架构的绝佳例证。

一个典型流程如下:
#mermaid-svg-gFTjLw2dLCLchWnm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gFTjLw2dLCLchWnm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gFTjLw2dLCLchWnm .error-icon{fill:#552222;}#mermaid-svg-gFTjLw2dLCLchWnm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gFTjLw2dLCLchWnm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gFTjLw2dLCLchWnm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gFTjLw2dLCLchWnm .marker.cross{stroke:#333333;}#mermaid-svg-gFTjLw2dLCLchWnm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gFTjLw2dLCLchWnm p{margin:0;}#mermaid-svg-gFTjLw2dLCLchWnm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gFTjLw2dLCLchWnm .cluster-label text{fill:#333;}#mermaid-svg-gFTjLw2dLCLchWnm .cluster-label span{color:#333;}#mermaid-svg-gFTjLw2dLCLchWnm .cluster-label span p{background-color:transparent;}#mermaid-svg-gFTjLw2dLCLchWnm .label text,#mermaid-svg-gFTjLw2dLCLchWnm span{fill:#333;color:#333;}#mermaid-svg-gFTjLw2dLCLchWnm .node rect,#mermaid-svg-gFTjLw2dLCLchWnm .node circle,#mermaid-svg-gFTjLw2dLCLchWnm .node ellipse,#mermaid-svg-gFTjLw2dLCLchWnm .node polygon,#mermaid-svg-gFTjLw2dLCLchWnm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gFTjLw2dLCLchWnm .rough-node .label text,#mermaid-svg-gFTjLw2dLCLchWnm .node .label text,#mermaid-svg-gFTjLw2dLCLchWnm .image-shape .label,#mermaid-svg-gFTjLw2dLCLchWnm .icon-shape .label{text-anchor:middle;}#mermaid-svg-gFTjLw2dLCLchWnm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gFTjLw2dLCLchWnm .rough-node .label,#mermaid-svg-gFTjLw2dLCLchWnm .node .label,#mermaid-svg-gFTjLw2dLCLchWnm .image-shape .label,#mermaid-svg-gFTjLw2dLCLchWnm .icon-shape .label{text-align:center;}#mermaid-svg-gFTjLw2dLCLchWnm .node.clickable{cursor:pointer;}#mermaid-svg-gFTjLw2dLCLchWnm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gFTjLw2dLCLchWnm .arrowheadPath{fill:#333333;}#mermaid-svg-gFTjLw2dLCLchWnm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gFTjLw2dLCLchWnm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gFTjLw2dLCLchWnm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gFTjLw2dLCLchWnm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gFTjLw2dLCLchWnm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gFTjLw2dLCLchWnm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gFTjLw2dLCLchWnm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gFTjLw2dLCLchWnm .cluster text{fill:#333;}#mermaid-svg-gFTjLw2dLCLchWnm .cluster span{color:#333;}#mermaid-svg-gFTjLw2dLCLchWnm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gFTjLw2dLCLchWnm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gFTjLw2dLCLchWnm rect.text{fill:none;stroke-width:0;}#mermaid-svg-gFTjLw2dLCLchWnm .icon-shape,#mermaid-svg-gFTjLw2dLCLchWnm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gFTjLw2dLCLchWnm .icon-shape p,#mermaid-svg-gFTjLw2dLCLchWnm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gFTjLw2dLCLchWnm .icon-shape .label rect,#mermaid-svg-gFTjLw2dLCLchWnm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gFTjLw2dLCLchWnm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gFTjLw2dLCLchWnm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gFTjLw2dLCLchWnm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Fruit 数据
Adapter
ViewHolder 缓存
View 界面对象
用户看到

  • Fruit:纯数据,只包含 name、imageId。
  • View :真正的界面对象,比如 TextViewImageView,占用内存大,创建成本高。
  • ViewHolder缓存 View 的引用 ,避免每次 findViewById,但它本身不是界面,只是一个普通 Java 类。
  • Adapter :建立映射,把 fruit.getName() 翻译成 holder.fruitName.setText(...)

因此,ViewHolder 不是界面,View 才是界面

这个区分非常重要,它让 RecyclerView 可以在性能上做极致的优化------复用。


五、MVC 思想的清晰体现

RecyclerView 的设计不是拍脑袋想出来的,它是 MVC 架构 在 Android 世界里的一次优雅落地。
#mermaid-svg-W1WxdY7WOLSHRKhi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W1WxdY7WOLSHRKhi .error-icon{fill:#552222;}#mermaid-svg-W1WxdY7WOLSHRKhi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W1WxdY7WOLSHRKhi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W1WxdY7WOLSHRKhi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W1WxdY7WOLSHRKhi .marker.cross{stroke:#333333;}#mermaid-svg-W1WxdY7WOLSHRKhi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W1WxdY7WOLSHRKhi p{margin:0;}#mermaid-svg-W1WxdY7WOLSHRKhi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster-label text{fill:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster-label span{color:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster-label span p{background-color:transparent;}#mermaid-svg-W1WxdY7WOLSHRKhi .label text,#mermaid-svg-W1WxdY7WOLSHRKhi span{fill:#333;color:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi .node rect,#mermaid-svg-W1WxdY7WOLSHRKhi .node circle,#mermaid-svg-W1WxdY7WOLSHRKhi .node ellipse,#mermaid-svg-W1WxdY7WOLSHRKhi .node polygon,#mermaid-svg-W1WxdY7WOLSHRKhi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W1WxdY7WOLSHRKhi .rough-node .label text,#mermaid-svg-W1WxdY7WOLSHRKhi .node .label text,#mermaid-svg-W1WxdY7WOLSHRKhi .image-shape .label,#mermaid-svg-W1WxdY7WOLSHRKhi .icon-shape .label{text-anchor:middle;}#mermaid-svg-W1WxdY7WOLSHRKhi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W1WxdY7WOLSHRKhi .rough-node .label,#mermaid-svg-W1WxdY7WOLSHRKhi .node .label,#mermaid-svg-W1WxdY7WOLSHRKhi .image-shape .label,#mermaid-svg-W1WxdY7WOLSHRKhi .icon-shape .label{text-align:center;}#mermaid-svg-W1WxdY7WOLSHRKhi .node.clickable{cursor:pointer;}#mermaid-svg-W1WxdY7WOLSHRKhi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W1WxdY7WOLSHRKhi .arrowheadPath{fill:#333333;}#mermaid-svg-W1WxdY7WOLSHRKhi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W1WxdY7WOLSHRKhi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W1WxdY7WOLSHRKhi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1WxdY7WOLSHRKhi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W1WxdY7WOLSHRKhi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1WxdY7WOLSHRKhi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster text{fill:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi .cluster span{color:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W1WxdY7WOLSHRKhi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W1WxdY7WOLSHRKhi rect.text{fill:none;stroke-width:0;}#mermaid-svg-W1WxdY7WOLSHRKhi .icon-shape,#mermaid-svg-W1WxdY7WOLSHRKhi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1WxdY7WOLSHRKhi .icon-shape p,#mermaid-svg-W1WxdY7WOLSHRKhi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W1WxdY7WOLSHRKhi .icon-shape .label rect,#mermaid-svg-W1WxdY7WOLSHRKhi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1WxdY7WOLSHRKhi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W1WxdY7WOLSHRKhi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W1WxdY7WOLSHRKhi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Model

Fruit 数据
View

TextView / ImageView
Controller

Adapter / Activity

  • Model(数据):Fruit 类,只包含状态。
  • View(视图) :item 布局里的 TextViewImageView,只负责渲染。
  • Controller(控制器):Adapter 承担核心控制逻辑------决定数据如何绑定到视图,以及何时更新;Activity/Fragment 负责更高层的页面控制和生命周期管理。

很多人以为 MVC 里,Controller 是 Activity,View 是 XML。

实际上在 RecyclerView 这一层,Adapter 才是真正的"C"

它接受数据(Model),操作视图(View),但自己不持有状态,也不负责显示。

理解这一点,就能理解为什么 Adapter 是 RecyclerView 的灵魂,也能理解为什么 解耦的本质是让每一层只做自己该做的事。


六、为什么 View 要复用?------ 一个与解耦一脉相承的设计选择

假设现在有 10000 条水果数据。

问:需要创建 10000 个 TextViewImageView 吗?

答案:完全不需要。

因为用户同时只能看到十几个 item。

如果真的创建 10000 个 View,内存会直接爆炸,滑动也会卡成 PPT。

所以,RecyclerView 名字里就藏了答案:

Recycle + View = 回收并复用 View
#mermaid-svg-y0w3kqxQQHd909ty{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y0w3kqxQQHd909ty .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y0w3kqxQQHd909ty .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y0w3kqxQQHd909ty .error-icon{fill:#552222;}#mermaid-svg-y0w3kqxQQHd909ty .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y0w3kqxQQHd909ty .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y0w3kqxQQHd909ty .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y0w3kqxQQHd909ty .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y0w3kqxQQHd909ty .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y0w3kqxQQHd909ty .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y0w3kqxQQHd909ty .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y0w3kqxQQHd909ty .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y0w3kqxQQHd909ty .marker.cross{stroke:#333333;}#mermaid-svg-y0w3kqxQQHd909ty svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y0w3kqxQQHd909ty p{margin:0;}#mermaid-svg-y0w3kqxQQHd909ty .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y0w3kqxQQHd909ty .cluster-label text{fill:#333;}#mermaid-svg-y0w3kqxQQHd909ty .cluster-label span{color:#333;}#mermaid-svg-y0w3kqxQQHd909ty .cluster-label span p{background-color:transparent;}#mermaid-svg-y0w3kqxQQHd909ty .label text,#mermaid-svg-y0w3kqxQQHd909ty span{fill:#333;color:#333;}#mermaid-svg-y0w3kqxQQHd909ty .node rect,#mermaid-svg-y0w3kqxQQHd909ty .node circle,#mermaid-svg-y0w3kqxQQHd909ty .node ellipse,#mermaid-svg-y0w3kqxQQHd909ty .node polygon,#mermaid-svg-y0w3kqxQQHd909ty .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y0w3kqxQQHd909ty .rough-node .label text,#mermaid-svg-y0w3kqxQQHd909ty .node .label text,#mermaid-svg-y0w3kqxQQHd909ty .image-shape .label,#mermaid-svg-y0w3kqxQQHd909ty .icon-shape .label{text-anchor:middle;}#mermaid-svg-y0w3kqxQQHd909ty .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y0w3kqxQQHd909ty .rough-node .label,#mermaid-svg-y0w3kqxQQHd909ty .node .label,#mermaid-svg-y0w3kqxQQHd909ty .image-shape .label,#mermaid-svg-y0w3kqxQQHd909ty .icon-shape .label{text-align:center;}#mermaid-svg-y0w3kqxQQHd909ty .node.clickable{cursor:pointer;}#mermaid-svg-y0w3kqxQQHd909ty .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y0w3kqxQQHd909ty .arrowheadPath{fill:#333333;}#mermaid-svg-y0w3kqxQQHd909ty .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y0w3kqxQQHd909ty .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y0w3kqxQQHd909ty .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y0w3kqxQQHd909ty .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y0w3kqxQQHd909ty .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y0w3kqxQQHd909ty .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y0w3kqxQQHd909ty .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y0w3kqxQQHd909ty .cluster text{fill:#333;}#mermaid-svg-y0w3kqxQQHd909ty .cluster span{color:#333;}#mermaid-svg-y0w3kqxQQHd909ty div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y0w3kqxQQHd909ty .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y0w3kqxQQHd909ty rect.text{fill:none;stroke-width:0;}#mermaid-svg-y0w3kqxQQHd909ty .icon-shape,#mermaid-svg-y0w3kqxQQHd909ty .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y0w3kqxQQHd909ty .icon-shape p,#mermaid-svg-y0w3kqxQQHd909ty .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y0w3kqxQQHd909ty .icon-shape .label rect,#mermaid-svg-y0w3kqxQQHd909ty .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y0w3kqxQQHd909ty .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y0w3kqxQQHd909ty .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y0w3kqxQQHd909ty :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 旧 View 滚出屏幕
回收至缓存池
重新绑定新数据
再次显示为新 item

这就是 ViewHolder 机制的底层逻辑:

ViewHolder 把 View 引用暂存起来,当新数据需要显示时,直接复用旧 View,只替换里面的文字、图片等数据。

这种"复用"思想之所以能成立,恰恰是因为数据和视图是分离的

如果数据里直接持有 View,那每个数据就不得不持有一份独立的 View,永远无法复用。

所以,解耦不仅是为了代码漂亮,更是为了性能上获得质的飞跃。


七、现代 UI 框架的演进:解耦思想的延续

RecyclerView 已经是 Android 里解耦思想的标志性实现。这里的关键词是 "手动"

Controller 必须明确知道:

  • 什么时候 数据变了(网络请求完成、用户输入......)
  • 怎么更新 界面(调哪个 Adapter 方法、刷新哪部分 View)

这就是 命令式编程:一步一步告诉框架"先去把数据换了,再去刷新列表"。

在小项目里这没问题。但一旦界面逻辑复杂起来:

  • 多个数据源(本地数据库、网络、用户操作)都可能改变同一个界面
  • 数据之间存在依赖(购物车总价 = 每个商品价格 × 数量)
  • 界面状态有加载中、空数据、错误等多种情况

手动维护这些 "何时更新、更新什么" 会迅速变成一场噩梦。忘记调用一次

notifyDataSetChanged(),界面就"卡"在了旧状态上。

但它不是终点。

现代 UI 架构开始走向 响应式
#mermaid-svg-7xWLZNYHRCr8hAPT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7xWLZNYHRCr8hAPT .error-icon{fill:#552222;}#mermaid-svg-7xWLZNYHRCr8hAPT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7xWLZNYHRCr8hAPT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7xWLZNYHRCr8hAPT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7xWLZNYHRCr8hAPT .marker.cross{stroke:#333333;}#mermaid-svg-7xWLZNYHRCr8hAPT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7xWLZNYHRCr8hAPT p{margin:0;}#mermaid-svg-7xWLZNYHRCr8hAPT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster-label text{fill:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster-label span{color:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster-label span p{background-color:transparent;}#mermaid-svg-7xWLZNYHRCr8hAPT .label text,#mermaid-svg-7xWLZNYHRCr8hAPT span{fill:#333;color:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT .node rect,#mermaid-svg-7xWLZNYHRCr8hAPT .node circle,#mermaid-svg-7xWLZNYHRCr8hAPT .node ellipse,#mermaid-svg-7xWLZNYHRCr8hAPT .node polygon,#mermaid-svg-7xWLZNYHRCr8hAPT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7xWLZNYHRCr8hAPT .rough-node .label text,#mermaid-svg-7xWLZNYHRCr8hAPT .node .label text,#mermaid-svg-7xWLZNYHRCr8hAPT .image-shape .label,#mermaid-svg-7xWLZNYHRCr8hAPT .icon-shape .label{text-anchor:middle;}#mermaid-svg-7xWLZNYHRCr8hAPT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7xWLZNYHRCr8hAPT .rough-node .label,#mermaid-svg-7xWLZNYHRCr8hAPT .node .label,#mermaid-svg-7xWLZNYHRCr8hAPT .image-shape .label,#mermaid-svg-7xWLZNYHRCr8hAPT .icon-shape .label{text-align:center;}#mermaid-svg-7xWLZNYHRCr8hAPT .node.clickable{cursor:pointer;}#mermaid-svg-7xWLZNYHRCr8hAPT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7xWLZNYHRCr8hAPT .arrowheadPath{fill:#333333;}#mermaid-svg-7xWLZNYHRCr8hAPT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7xWLZNYHRCr8hAPT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7xWLZNYHRCr8hAPT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7xWLZNYHRCr8hAPT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7xWLZNYHRCr8hAPT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7xWLZNYHRCr8hAPT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster text{fill:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT .cluster span{color:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7xWLZNYHRCr8hAPT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7xWLZNYHRCr8hAPT rect.text{fill:none;stroke-width:0;}#mermaid-svg-7xWLZNYHRCr8hAPT .icon-shape,#mermaid-svg-7xWLZNYHRCr8hAPT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7xWLZNYHRCr8hAPT .icon-shape p,#mermaid-svg-7xWLZNYHRCr8hAPT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7xWLZNYHRCr8hAPT .icon-shape .label rect,#mermaid-svg-7xWLZNYHRCr8hAPT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7xWLZNYHRCr8hAPT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7xWLZNYHRCr8hAPT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7xWLZNYHRCr8hAPT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据 / State
声明式 UI
自动刷新界面

比如:

  • LiveData / StateFlow:当数据变化时,自动通知 UI 更新。
  • Jetpack Compose:彻底用 Kotlin 代码写 UI,界面由状态驱动。

响应式的核心思想就一句话:

界面是状态的函数

UI = f(State)

用伪代码表示:

kotlin 复制代码
// 状态
val fruits: State<List<Fruit>>

// 界面自动由状态驱动
Text(fruits[0].name)
Image(fruits[0].image)

fruits 这个状态发生变化时,引用它的 UI 自动刷新。你不需要再写"数据变了,请刷新那个 TextView"的指令。

在 Android 里:

  • LiveData / StateFlow 负责持有可观察的状态
  • Jetpack Compose 直接让 UI 成为 Kotlin 代码,能响应状态重组

viewModel.fruits 的值发生变化(比如从网络加载完成),FruitList 会自动重组,无需手动通知任何 Adapter

  • 但本质上,这仍然是:

数据 → 映射 → 界面

只不过映射关系从"命令式"变成了"声明式",由框架自动处理。

这说明:当解耦做到底层,无论上层 API 怎么变,架构思想都能平滑过渡。


结尾:一张图总结一切

#mermaid-svg-8RKKhmvooTNIuCnE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8RKKhmvooTNIuCnE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8RKKhmvooTNIuCnE .error-icon{fill:#552222;}#mermaid-svg-8RKKhmvooTNIuCnE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8RKKhmvooTNIuCnE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8RKKhmvooTNIuCnE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8RKKhmvooTNIuCnE .marker.cross{stroke:#333333;}#mermaid-svg-8RKKhmvooTNIuCnE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8RKKhmvooTNIuCnE p{margin:0;}#mermaid-svg-8RKKhmvooTNIuCnE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8RKKhmvooTNIuCnE .cluster-label text{fill:#333;}#mermaid-svg-8RKKhmvooTNIuCnE .cluster-label span{color:#333;}#mermaid-svg-8RKKhmvooTNIuCnE .cluster-label span p{background-color:transparent;}#mermaid-svg-8RKKhmvooTNIuCnE .label text,#mermaid-svg-8RKKhmvooTNIuCnE span{fill:#333;color:#333;}#mermaid-svg-8RKKhmvooTNIuCnE .node rect,#mermaid-svg-8RKKhmvooTNIuCnE .node circle,#mermaid-svg-8RKKhmvooTNIuCnE .node ellipse,#mermaid-svg-8RKKhmvooTNIuCnE .node polygon,#mermaid-svg-8RKKhmvooTNIuCnE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8RKKhmvooTNIuCnE .rough-node .label text,#mermaid-svg-8RKKhmvooTNIuCnE .node .label text,#mermaid-svg-8RKKhmvooTNIuCnE .image-shape .label,#mermaid-svg-8RKKhmvooTNIuCnE .icon-shape .label{text-anchor:middle;}#mermaid-svg-8RKKhmvooTNIuCnE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8RKKhmvooTNIuCnE .rough-node .label,#mermaid-svg-8RKKhmvooTNIuCnE .node .label,#mermaid-svg-8RKKhmvooTNIuCnE .image-shape .label,#mermaid-svg-8RKKhmvooTNIuCnE .icon-shape .label{text-align:center;}#mermaid-svg-8RKKhmvooTNIuCnE .node.clickable{cursor:pointer;}#mermaid-svg-8RKKhmvooTNIuCnE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8RKKhmvooTNIuCnE .arrowheadPath{fill:#333333;}#mermaid-svg-8RKKhmvooTNIuCnE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8RKKhmvooTNIuCnE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8RKKhmvooTNIuCnE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8RKKhmvooTNIuCnE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8RKKhmvooTNIuCnE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8RKKhmvooTNIuCnE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8RKKhmvooTNIuCnE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8RKKhmvooTNIuCnE .cluster text{fill:#333;}#mermaid-svg-8RKKhmvooTNIuCnE .cluster span{color:#333;}#mermaid-svg-8RKKhmvooTNIuCnE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8RKKhmvooTNIuCnE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8RKKhmvooTNIuCnE rect.text{fill:none;stroke-width:0;}#mermaid-svg-8RKKhmvooTNIuCnE .icon-shape,#mermaid-svg-8RKKhmvooTNIuCnE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8RKKhmvooTNIuCnE .icon-shape p,#mermaid-svg-8RKKhmvooTNIuCnE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8RKKhmvooTNIuCnE .icon-shape .label rect,#mermaid-svg-8RKKhmvooTNIuCnE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8RKKhmvooTNIuCnE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8RKKhmvooTNIuCnE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8RKKhmvooTNIuCnE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据 Data

Fruit / User / Message
适配 Adapter

RecyclerView.Adapter
视图 View

TextView / ImageView / Button
RecyclerView

负责管理和复用
Activity

负责承载界面

Android 开发表面上是在写按钮、列表和页面,

实际上,你一直在维护"数据状态"与"界面表现"的映射关系。

RecyclerView 只是这个思想最经典、最容易观察到的例子。

一旦你真正理解这一点,无论是写自定义 View、设计 App 架构,还是迁移到 Compose 等新框架,

你会发现自己一直在一个稳固的根基上前进。


本文定位为设计思想分享,不深入源码细节,力求让每一位 Android 开发者都能透过现象看到架构的本质。

如果你也曾在一堆 Adapter 代码里感到迷茫,希望这篇文章能帮你看清方向。

相关推荐
码农coding2 小时前
android 12 中的VSYNC的请求
android
阿pin2 小时前
Android随笔-Activity
android·activity
YF02112 小时前
详解Android所有文件访问权限
android
时间的拾荒人4 小时前
MySQL C语言连接 - 从入门到实战
android·c语言·mysql
Meteors.4 小时前
Android性能优化:01. 指标体系 + 分析工具链
android
jike_20265 小时前
安卓平台免费录音转文字工具深度测评:5 款 APP 功能对比与选型指南
android·智能电视
Code Man5 小时前
Windows 下使用 Appium
android·windows·appium
码农coding6 小时前
android 12 中的VSYNC接收
android
GitLqr6 小时前
Flutter 3.44 性能飞跃:深度解析 Android Platform View 的 HCPP 新特性
android·flutter·性能优化
码农coding6 小时前
android 12 SurfaceFlinger中的CompositionEngine
android