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

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 仅在数据变化时调用)。
    • 自定义观察者可添加去重逻辑(如比较新旧数据)。

相关文章:

  • LeetCode 1. 两数之和(Java)
  • flex布局打印对联
  • 如何对 Redis 进行水平扩展和垂直扩展以应对微服务流量的增长?
  • C++ 重载
  • Matplotlib 饼图
  • 【C++】C++中的命名/名字/名称空间 namespace
  • 爱普生VG7050EFN压控晶振在小基站的应用优势
  • 【T2I】DreamBench++: A Human-Aligned Benchmark for Personalized Image Generation
  • Kali:利用rockyou文本字典hash破解zip压缩包密码
  • NetApp SAS 连接线:铜缆与光缆的全面介绍
  • 开源智能体MetaGPT记忆模块解读
  • 【5G通信】天线调整
  • 【AI】用AI将文档、文字一键生成PPT的方法(百度的自由画布版)
  • STL详解 - map和set
  • 性能分析硬核特训 · Perf 全面指南:内核实例 + 原理实战 + 面试答疑
  • JAVA SE(9)——多态
  • OpenCV进阶操作:角点检测
  • n8n系列(1)初识n8n:工作流自动化平台概述
  • 基于 GO 语言的 Ebyte 勒索软件——简要分析
  • 【Vue】全局事件总线 TodoList 事件总线
  • 央行:全力推进一揽子金融政策加快落地
  • 马上评|不再提“智驾”,新能源车企回归理性
  • 现场|万米云端,遇见上博
  • 8小时《大师与玛格丽特》:长度可以是特点,但不是价值标准
  • 丁薛祥在学习《习近平经济文选》第一卷专题研讨班上强调:深入学习贯彻习近平经济思想,加强党中央对经济工作的集中统一领导
  • 上海车展侧记|中国汽车产业的韧性从何而来