当前位置: 首页 > ai >正文

Android第六次面试总结之Java设计模式(二)

一、适配器模式(Adapter Pattern)

1. ListView vs RecyclerView 的 Adapter 核心区别?为什么 RecyclerView 需要 ViewHolder?

解答

  • 核心区别

    特性ListView.Adapter(如 ArrayAdapter)RecyclerView.Adapter
    ViewHolder 机制无,直接通过 getView 重复创建 View(性能差)强制使用 ViewHolder 缓存 View,避免重复 inflate
    数据更新只能全局刷新(notifyDataSetChanged支持局部刷新(notifyItemChanged 等细粒度方法)
    布局管理器内置固定布局(垂直 / 水平)可自定义(LinearLayoutManager/GridLayoutManager)
  • ViewHolder 必要性
    RecyclerView 通过 ViewHolder 缓存列表项的视图组件,避免每次滑动都调用 LayoutInflater.inflate,将列表滑动性能从 O(n) 提升至接近 O(1)

    // RecyclerView 适配器必须实现 ViewHolder  
    class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {  public static class ViewHolder extends RecyclerView.ViewHolder {  TextView textView;  ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv); }  }  
    }  
    
2. RecyclerView 适配器如何实现多类型布局?对比 ListView 有何优势?

解答

  • 多类型布局实现
    通过重写getItemType返回不同类型标识,在onCreateViewHolder中创建对应 ViewHolder。
    @Override  
    public int getItemType(int position) {  return dataList.get(position).getType(); // 根据数据类型返回不同标识  
    }  @NonNull  
    @Override  
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {  if (viewType == TYPE_ITEM1) {  return new Item1ViewHolder(inflate(R.layout.item1, parent));  } else {  return new Item2ViewHolder(inflate(R.layout.item2, parent));  }  
    }  
    
  • 对比 ListView 优势
    • ViewHolder 复用机制:避免重复创建 View,性能提升显著。
    • 局部刷新:支持notifyItemChanged等细粒度更新,减少 UI 线程负担。
    • 布局灵活性:通过LayoutManager实现线性、网格、瀑布流等多种布局。

真题陷阱
getItemType返回固定值,RecyclerView 会如何处理?

  • 答案:所有 Item 使用同一 ViewHolder,无法实现多类型布局。
3. 如何优化 RecyclerView 适配器的滑动性能?

解答

  • ViewHolder 复用:避免在onCreateViewHolder频繁创建 View,RecyclerView 已内置此机制。
  • 减少布局层级:使用merge标签或约束布局,避免过度嵌套。
  • 预加载与缓存
    • 设置setItemViewCacheSize增加缓存空间。
    • 开启setHasFixedSize(true)(若 Item 高度固定)。
  • 异步加载:在onBindViewHolder中避免耗时操作,如网络请求或复杂计算。

真题陷阱
为什么setHasFixedSize(true)能提升性能?

  • 答案:告知 RecyclerView 无需重新测量 Item 尺寸,减少requestLayout调用。

二、观察者模式(Observer Pattern)

1. LiveData 如何实现生命周期感知?粘性事件的原理是什么?

解答

  • 生命周期感知原理
    LiveData 通过LifecycleOwner获取生命周期状态,仅在STARTEDRESUMED时通知观察者。关键源码:
    public void observe(LifecycleOwner owner, Observer<? super T> observer) {  owner.getLifecycle().addObserver(wrapper); // 注册生命周期观察者  
    }  class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {  @Override  public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {  if (event == Lifecycle.Event.ON_DESTROY) {  removeObserver(observer); // 自动移除观察者  }  }  
    }  
    
  • 粘性事件原理
    LiveData 缓存最新数据,新注册的观察者会立即收到当前数据。可通过MediatorLiveData或自定义LiveData去除粘性。

真题陷阱
若在onStop后数据更新,Activity 恢复后是否会收到通知?

  • 答案:会。LiveData 在 Activity 重新回到STARTED状态时重新通知最新数据。
2. LiveData 的setValuepostValue有何区别?为何前者必须在主线程?(字节跳动 / 腾讯 2024 面试真题)

解答

方法线程安全调用时机原理
setValue非线程安全必须在主线程直接调用dispatchingValue触发通知,未通过队列保证顺序。
postValue线程安全任意线程将数据封装为Runnable通过Handler发送到主线程,保证顺序性。
  • 主线程限制原因
    setValue未通过队列调度,若在子线程调用可能导致数据竞争或通知顺序混乱。

真题陷阱
在子线程中调用setValue会发生什么?

  • 答案:抛出IllegalStateException,提示必须在主线程调用。
3. 对比 LiveData 与 EventBus,各自的优缺点?

解答

特性LiveDataEventBus
生命周期感知支持(自动移除观察者)不支持(需手动移除,否则内存泄漏)
线程调度仅主线程更新(setValue)/ 支持子线程(postValue)支持自定义线程(@Subscribe (threadMode = ...))
数据类型强类型(泛型约束)弱类型(任意对象)
适用场景页面内数据共享、组件间通信(同作用域)跨组件复杂通信(如跨 Activity/Fragment)
  • 最佳实践
    页面内数据绑定优先用 LiveData,跨组件通信可用 MutableLiveData + ViewModel,复杂场景(如事件总线)再考虑 EventBus。

三、责任链模式(Chain of Responsibility Pattern)

1. OkHttp 拦截器链如何实现责任链模式?自定义拦截器要注意什么?

解答

  • 核心原理
    OkHttp 的拦截器(Interceptor)按顺序组成链条,每个拦截器可处理请求 / 响应,或调用 chain.proceed(request) 将请求传递给下一个拦截器。

    // 自定义日志拦截器  
    public class LoggingInterceptor implements Interceptor {  @Override  public Response intercept(Chain chain) throws IOException {  Request request = chain.request();  Log.d("OkHttp", "Request: " + request.url());  Response response = chain.proceed(request); // 传递给下一个拦截器  Log.d("OkHttp", "Response: " + response.code());  return response;  }  
    }  
    
  • 拦截器顺序影响

    • 应用拦截器addInterceptor):先于网络拦截器执行,且不关心重定向(适合添加公共 Header)。
    • 网络拦截器addNetworkInterceptor):在重试 / 重定向之后执行,可获取真实网络请求结果(适合处理 Gzip 响应)。
  • 面试陷阱
    若拦截器未调用 chain.proceed(request),会导致链条中断,后续拦截器无法执行(类似 Android 事件分发的 onTouchEvent 返回 true)。

2. Android 事件分发是否用到责任链模式?举例说明。

解答

  • 事件分发链条
    触摸事件从 Activity.dispatchTouchEvent 开始,依次经过 ViewGroup(如 LinearLayout)的 dispatchTouchEvent → onInterceptTouchEvent,最后到 View 的 dispatchTouchEvent → onTouchEvent

    • 每个节点(Activity/ViewGroup/View)可决定自己处理事件或传递给子节点(责任链核心:节点处理或转发请求)。
  • 示例代码(ViewGroup 拦截事件):

    public class CustomViewGroup extends ViewGroup {  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {  if (ev.getAction() == MotionEvent.ACTION_DOWN) {  return true; // 拦截事件,不再传递给子 View  }  return super.onInterceptTouchEvent(ev);  }  
    }  
    
3. 责任链模式的优缺点?适用场景有哪些?

解答

  • 优点
    • 解耦请求发送者与处理者,处理逻辑可动态添加 / 删除(如 OkHttp 灵活配置拦截器)。
    • 符合开闭原则,新增处理逻辑无需修改原有代码(只需添加新拦截器)。
  • 缺点
    • 调试困难:事件传递路径不明确,需逐层打印日志定位问题。
    • 可能导致处理链过长,影响性能(需控制拦截器数量)。
  • Android 适用场景
    • 网络请求处理(OkHttp 拦截器)。
    • 事件分发(触摸事件、按键事件处理)。
    • 复杂业务流程(如订单状态校验链:库存检查→价格校验→支付权限校验)。

四、综合面试题:设计模式对比与选型

1. 适配器模式与装饰器模式的区别?(阿里 / 华为 2024 面试真题)

解答

特性适配器模式装饰器模式
目的转换接口,使不兼容的类协同工作动态扩展对象功能,不改变原有接口
类关系适配器持有目标类或继承目标类装饰器持有被装饰对象,实现相同接口
Android 案例RecyclerView.Adapter 适配数据到视图TextAppearance为 TextView 添加样式

真题陷阱
如何区分两者?

  • 答案:适配器改变接口,装饰器增强功能。例如,将 List 适配为 ListView 是适配器,给 Button 添加点击动画是装饰器。
2. 观察者模式中,如何避免 “内存泄漏” 和 “重复通知”?

解答

  • 内存泄漏
    • LiveData 依赖 LifecycleOwner,自动移除观察者;传统观察者需在宿主销毁时调用 subject.removeObserver(observer)
    • 真题陷阱:若观察者被 Activity 持有,而被观察者是单例,会导致内存泄漏吗?

               答案:会。单例持有观察者的强引用,Activity 无法被回收。

  • 重复通知
    • LiveData 通过 mVersion 版本号控制,相同版本数据不会重复通知(onChanged 仅在数据变化时调用)。
    • 自定义观察者可添加去重逻辑(如比较新旧数据)。
http://www.xdnf.cn/news/4117.html

相关文章:

  • vue3+ts+自定义指令,生产页面水印
  • 云计算训练营笔记day02(Linux、计算机网络、进制)
  • 论文速读《Embodied-R: 基于强化学习激活预训练模型具身空间推理能力》
  • STL之stackqueue
  • 【Vue.js】 插槽通信——具名插槽通信
  • 大模型在宫颈癌诊疗全流程预测与应用研究报告
  • 免费视频压缩软件
  • Paramiko 性能优化详解
  • 神经网络之互动练习详解:从基础到拟合非线性数据
  • 【回眸】QAC使用指南——导出 Dashboard Report个性化定制Report
  • NLP中词嵌入的几个方法介绍快速入门视频推荐
  • Adobe卸载清理工具Creative Cloud Cleaner Tool下载
  • C# Winforms 本地化 多语言支持 字符串资源
  • 【商城系统中的多商户和单商户模式差异】
  • 第22节:深度学习基础-损失函数介绍
  • 神经网络:节点、隐藏层与非线性学习
  • 【WPS】怎么解决“word的复制表格”粘贴到“excel的单元格”变多行单元格的问题
  • Suno v4.5:AI 音乐创作的新突破
  • PHP分页显示数据,在phpMyadmin中添加数据
  • window 显示驱动开发-线程同步和 TDR
  • 【Docker系列】使用格式化输出与排序技巧
  • 如何对 Redis 进行水平扩展和垂直扩展以应对微服务流量的增长?
  • 【ARM】DS-试用授权离线激活
  • 动手学深度学习12.1. 编译器和解释器-笔记练习(PyTorch)
  • Kubernetes环境部署OwnCloud网盘服务
  • 学苑教育杂志学苑教育杂志社学苑教育编辑部2025年第9期目录
  • 前端知识-hook
  • 荣耀A8互动娱乐组件部署实录(第4部分:房间服务端逻辑与玩家交互处理)
  • spring cloud gateway(网关)简介
  • C++ 类