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

Android面试指南(六)

目录

一、Android常用架构模式

1.1、MVC

1.2、MVP

1.3、MVVM

二、模块化和组件化

2.1、单一工程

2.2、模块化

2.3、组件化

三、插件化和容器化

3.1、插件化

3.2、容器化

四、热修复

五、进程保活

一、Android常用架构模式

1.1、MVC

  • MVC的起源:源自前端开发,用于分离数据和视图层,但在安卓中效果有限。
  • MVC的组成:
    • Controller:通常指Activity或Fragment。
    • Model:负责数据读取和操作。
    • View:由XML文件实例化或自定义的视图对象。
  • MVC的缺点:
    • 代码臃肿:逻辑复杂时,Activity代码量易超千行。
    • 维护困难:业务逻辑与视图层高度耦合,修改成本高。
  • 适用场景:简单页面(如设置页)仍可使用MVC模式。

简单代码示例如下:

interface CallBack<T>{void onSuccess(T t);
}
interface IHomeModel {public void getUserInfo(CallBack<User> callBack);
}
public class HomeModel implements IHomeModel{@Overridepublic void getUserInfo(CallBack<User> callBack) {Restful.create(User.class).getUserInfo().enqueue(callBack);}
}
public class MVCActivity extends AppCompatActivity{@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);HomeModel model = new HomeModel();model.getUserInfo(user -> {textview.setText(user.name);});}
}

1.2、MVP

  • 让宿主专注UI逻辑和用户交互的处理。把宿主中的业务逻辑全部分离出来, 所有跟Android API无关的业务逻辑由Presenter 层来完成,缺点就是增加了代码量。
  • Activity和Fragment 视为View层, 负责处理 UI和用户交互。
  • Presenter 为业务处理层, 负责处理业务逻辑, 发起数据请求。
  • Model 层中包含着具体的数据请求, 数据源。
  • 通信流程:View→Presenter→Model→Presenter→View的闭环数据流

层级

职责

交互方式

View

处理UI逻辑和用户交互

通过接口回调更新数据

Presenter

处理业务逻辑和数据请求

调用Model获取数据并回传至View

Model

数据的具体请求和存储

通过Presenter返回数据

  • MVP的优势:
    • 解耦视图与数据:View层与Model层完全隔离,通过Presenter桥接。
    • 逻辑清晰:业务逻辑集中于Presenter,便于维护和复用。
  • 实现要点:
    • BaseView:定义通用方法(如判断宿主存活)。
    • BasePresenter:通过泛型绑定具体View类型。
    • Contract类:统一管理View和Presenter的接口。

举例实现:

public class User {public String userName;public String address;
}
public interface BaseView {boolean isAlive();
}
public class BasePresenter<IView extends BaseView> {protected IView view;public void attach(IView view) {this.view = view;}public void detach() {view = null;}
}
public interface HomeContract {interface View extends BaseView{void onGetUserInfoResult(User user,String errorMsg);}abstract class Presenter extends BasePresenter<View>{abstract void getUserInfo();}
}
public class HomePresenter extends HomeContract.Presenter {@Overridevoid getUserInfo() {Restful.create(Home.class).getUserInfo(new Callback<User>() {void onSuccess(User user) {if (view != null && view.isAlive()) {view.onGetUserInfoResult(user, null);}}});}
}
public class MVPActivity extends AppCompatActivity implements HomeContract.View {private HomePresenter presenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);presenter = new HomePresenter();presenter.attach(this);presenter.getUserInfo();}@Overridepublic void onGetUserInfoResult(User user, String errorMsg) {}@Overridepublic boolean isAlive() {return !isDestroyed() && !isFinishing();}@Overrideprotected void onDestroy() {super.onDestroy();presenter.detach();}
}
  • BaseView实现:定义通用方法(如isAlive())。
  • BasePresenter实现:
    • 使用泛型绑定具体View类型。
    • 提供attachView和detachView方法管理生命周期。
  • Contract类设计:
    • View接口:定义数据回调方法(如onUserInfoResult)。
    • Presenter抽象类:声明业务逻辑方法(如getUserInfo)。
  • 代码优化:
    • BaseActivity封装:自动绑定Presenter和View,简化重复操作。
    • 适用场景:复杂页面推荐MVP,简单页面可沿用MVC。

1.3、MVVM

MVP模式因接口定义繁琐,后续演化出MVVM模式。MVVM通过数据和视图的双向绑定解决接口定义问题,通常配合Data Binding实现。Data Binding的特性包括:

  • 数据变更自动刷新UI
  • UI变化自动同步数据

MVVM中Data Binding是实现双向绑定的方式之一,但非必须。双向绑定具体表现为:

  • 数据驱动UI:对象字段数据变化时无需手动刷新UI
  • UI同步数据:CheckBox等状态视图变化时,关联数据字段自动更新

①、传统MVVM

传统MVVM架构中:

  • View层:包含Activity、Fragment或XML实例化的View对象
  • ViewModel:普通类(非Jetpack组件),负责从Model获取数据
  • 数据更新机制:通过ObserverField观察者实现UI更新

实现步骤:

  • 定义普通类获取数据
  • 使用ObserverField持有数据
  • 布局文件使用Layout标签,包含Data标签声明绑定字段

关键特性:

  • 单向绑定:TextView使用@{}语法
  • 双向绑定:EditText使用@={}语法
  • Activity绑定:通过DataBindingUtil.setContentView实现

简单示例:

public class User {public String userName;public String address;
}
public class HomeViewModel {public ObservableField<User> userField = new ObservableField<>();public void getUserInfo(){User user = new User();user.userName = "jarchie";user.address = "南京";userField.set(user);}
}
public class MVVMActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);HomeViewModel model = new HomeViewModel();binding.setViewModel(model);model.getUserInfo();binding.editText.addTextChangedListener(new TextWatcher() {@Overridepublic void afterTextChanged(Editable s) {Log.i("MVVMActivity", "onTextChanged: " +    model.userField.get().address);}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}});}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="viewModel"type="com.jarchie.kotlindemo.structure.mvvm.HomeViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{viewModel.userField.userName}"/><EditTextandroid:id="@+id/editText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@={viewModel.userField.address}"/></LinearLayout>
</layout>

②、Jetpack模式下的MVVM

  • 核心组件:ViewModel+LiveData组合使用
  • 优势特点:
    • 数据持久化:保证数据不会无缘无故丢失
    • 生命周期感知:自动关联宿主的生命周期,避免空指针问题
    • 职责分离:Activity/Fragment只需处理UI逻辑和用户交互控制
  • 数据绑定:推荐使用DataBinding完成数据绑定工作
  • 数据流向:
    • 宿主调用ViewModel方法
    • ViewModel通过Repository获取数据(可能包含Room/Retrofit等数据源)
    • 获取数据后通过LiveData发送回UI层

简单示例:

public class User {public String userName;public String address;
}
public class HomeViewModel extends ViewModel {public LiveData<User> getUserInfo(){MutableLiveData<User> liveData = new MutableLiveData<>();User user = new User();user.userName = "jarchie";user.address = "NanJing";liveData.postValue(user);return liveData;}
}
public class JetpackActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityJetpackBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_jetpack);ViewModelProvider provider = new ViewModelProvider(this);HomeViewModel model = provider.get(HomeViewModel.class);model.getUserInfo().observe(this, new Observer<User>() {@Overridepublic void onChanged(User user) {binding.setUser(user);}});}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="user"type="com.jarchie.kotlindemo.structure.jetpack.User" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.userName}"/><EditTextandroid:id="@+id/editText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@={user.address}"/></LinearLayout>
</layout>

二、模块化和组件化

2.1、单一工程

  • 原始结构:安卓开发最基础的工程形式,按文件类型(如activity包、fragment包)或业务类型分包,所有代码存放于同一目录。
  • 适用场景:仅适合1-3人开发的小型APP,编译速度与维护成本可控。
  • 缺陷:
    • 强耦合性:业务间相互调用形成复杂引用链,删除或复用模块需逐个操作文件。
    • 扩展性问题:多人协作或多业务线开发时易出现合并冲突、版本管理困难,代码复杂度指数上升。
    • 编译效率低:修改代码需全量编译,增加开发耗时。

2.2、模块化

1) 模块化及组件化的区别

  • 粒度差异:模块以业务为导向,包含多个组件;组件以功能为导向(如UI库中的按钮组件、线程池组件)。
  • 本质共性:均通过化整为零实现代码重用与解耦,区别仅在于描述粒度。
  • 工程形态:组件化基于模块化演进,支持业务模块在集成模式(library)与独立调试模式(application)间切换。

2) 模块化的特点

  • 低耦合性:业务模块间隔离,代码边界清晰,降低协作开发复杂度。
  • 高效编译:模块独立管理,减少全量编译次数,提升开发效率。

3) 模块化的狭义

从IDE视角看,模块化需完成以下操作:

  • 基础库抽离:封装功能控件、基础类及三方库。
  • 业务拆分:各业务模块独立管理,仅依赖基础库,禁止模块间直接依赖。

4) 模块化的广义

广义模块化指将复杂业务按功能或页面维度拆分为独立模块,通过编程思想实现解耦。

5) 壳工程

  • 核心职责:负责打包配置(签名、混淆规则)、业务模块集成、APP基础配置(主题、LOGO、启动初始化)。
  • 依赖关系:
    • 业务模块(如home、detail)以aar形式集成至壳工程,禁止相互调用。
    • 公共能力(网络、缓存)下沉至基础模块(com),供业务模块统一依赖。
  • 优势:模块下架仅需移除依赖,复用可通过发布maven实现。

推荐阅读:微信 Android 模块化架构重构实践

2.3、组件化

组件化改造需解决以下问题:

  • 跨模块调用:如首页模块调用详情页方法。
  • 通信机制:模块间页面跳转与数据共享。
  • 资源冲突:公共资源复用与版本管理。
  • 独立调试:业务模块打包为独立APP。
  • 依赖冲突:各模块引用的三方库版本一致性。

①、模块之间如何进行通信?

  • 通信方式:使用路由组件实现页面跳转
  • 传统方式问题:模块间类不可直接访问,无法使用显式Intent
  • 解决方案:通过路由解耦,各模块注册路由表统一管理跳转逻辑

②、不同模块之间如何实现方法调用?

  • SPI机制:Service Provider Interface,通过接口解耦服务使用者和提供者
  • 实现原理:
    • 服务接口打包成独立jar
    • 实现类注册到META-INF/services目录
    • 通过ServiceLoader动态加载实现类
  • ARouter方案:已内置服务发现机制,可简化SPI实现流程

③、模块之间的JavaBean,公共资源如何共享?

  • 共享方案:提取公共部分形成独立aar或jar包
  • pub_mod作用:存放多个模块共用的资源类、JavaBean和业务逻辑
  • 注意事项:避免将所有公共内容下沉到common模块导致业务耦合

④、如何防止资源名称冲突?

  • Gradle配置:通过resourcePrefix强制模块资源使用前缀
  • android {resourcePrefix 'home_'
    }
  • 批量配置:在根build.gradle中统一设置所有模块前缀
  • subprojects {afterEvaluate {android {resourcePrefix "${project.name}_"}}
    }

⑤、如何解决重复依赖以及第三方版本号控制的问题?

  • 版本强制:使用resolutionStrategy统一版本
  • configurations.all {resolutionStrategy.force 'com.alibaba:arouter:api:1.1'
    }
  • 远程管理:将依赖配置发布到仓库,通过URL动态引用
  • apply from: 'https://api.devio.org/as/project/dependencies.gradle'

⑥、如何将各个模块打包成独立的APP进行调试?

  • 实现方式:通过build.gradle开关控制模块类型
  • if (isRunAlone) {apply plugin: 'com.android.application'
    } else {apply plugin: 'com.android.library'
    }
  • 伪需求:模块间必然存在服务调用依赖,因此模块独立运行具有局限性:
    • 需维护多套manifest配置
    • 无法解决模块间服务依赖问题

三、插件化和容器化

3.1、插件化

1) 插件化的概念

  • 核心价值:解决业务模块动态发布问题,支持按需下载(如淘宝限时秒杀插件)。
  • 技术对比:
    • 与热修复差异:插件化侧重新功能动态加载,需处理四大组件生命周期(如Activity与AMS交互)。
    • 与组件化差异:组件化解决开发阶段工程结构,插件化解决运行阶段动态更新。
  • 实现难点:插件中四大组件需绕过Manifest预注册限制(参考Tinker动态加载方案)。

2) 常见插件化方案

主流框架包括:

  • 阿里Atlas、360 RePlugin、腾讯Shadow。
  • 入门推荐:Small轻量级框架。

3)插件化现状

安卓插件化技术曾作为重要开发方向被广泛应用,但这类技术属于特定时代产物,最终随安卓系统升级逐渐退出主流视野,当前开发环境已不再依赖插件化框架。

当前技术环境发生显著变化:

  • 应用市场支持差分更新:100M安装包实际仅需下载20M差异内容
  • 静默安装机制:实现后台自动更新无需用户干预

Google Play动态交付:通过App Bundle技术实现模块化分发(国内不可用)

  • 主流技术趋势已转向跨平台开发(RN、Flutter等)和DSL动态化方案

新型动态化方案实现路径:

  • 组件模板化:将布局文件转换为可解析的XML描述

  • 云端下发:模板URL与业务数据同步推送
  • 本地缓存:优先加载已缓存模板文件

动态解析:实时解析XML并完成数据绑定

  • 该方案通过DSL描述语言实现组件级动态更新,既保持原生性能又具备跨平台一致性,成为替代传统插件化的有效技术路径。

3.2、容器化

1) VLayout

  • 功能定位:实现混合布局能力(网格、瀑布流、吸顶悬浮等),提升页面组件形态灵活性。
  • 应用场景:服务端配置动态调整页面布局,无需发版。

2) VirtualView

  • 动态化方案:通过XML模板描述组件布局与逻辑,编译为二进制后由框架解析渲染。
  • 优势:支持业务组件动态下发更新(如新增通告栏目),避免强依赖原生代码发布。
  • 同类框架:滴滴开源的Chameleon(小程序方向)。

四、热修复

1)、热修复流程

  • 线上崩溃检测:通过集成Crash SDK实时监控APP崩溃率,若超过阈值(如1‰)则触发修复流程。
  • 问题定位与分支创建:根据崩溃日志定位代码问题,从开发分支(develop)创建专用修复分支(bug fix)。
  • 代码修复与测试:在修复分支上修改代码,经开发自测和测试团队回归验证。
  • 补丁生成与分发:通过Jenkins自动化构建生成补丁文件,APP通过推送或拉取机制获取补丁。
  • 代码合并:将修复分支同步至master和develop分支,确保后续版本不受影响。

2)、热修复原理

①、安卓类加载机制

安卓类加载涉及两类加载器:

  • PathClassLoader:加载系统及应用类。
  • DexClassLoader:专用于加载APK、JAR及Dex文件。

②、热修复机制核心流程

  • DexElement数组:ClassLoader通过遍历DexElement数组加载Dex文件。
  • 补丁优先级:将修复后的Dex文件插入数组首位,确保优先加载正确类定义。
  • 问题规避:原始问题类因位于数组后方未被加载,实现无感修复。

五、进程保活

1)、Android的进程优先级

进程类型

特征

回收条件

前台进程

用户当前交互的进程(如Activity、调用startForeground的Service、广播接收者的onReceive回调)

仅当内存不足时回收

可见进程

无前台组件但影响用户界面内容(如非全屏Activity)

为维持前台进程运行时可能回收

服务进程

执行用户关注的后台操作(如网络请求、音乐播放)

内存不足且需维持前两类进程时回收

后台进程

对用户体验无直接影响(如已停止的Activity)

优先回收,采用LRU算法保留最近使用的进程

空进程

无活跃组件,仅作缓存加速启动

系统资源紧张时优先终止

2)、Android进程的回收策略

Low Memory Killer机制基于Linux的OOM(Out of Memory)改进,特点如下:

  • 定时检查进程优先级(通过oom_adj阈值判定),而OOM仅在内存不足时触发
  • 回收逻辑:oom_adj值越小优先级越高,越不易被回收;反之则优先终止
  • 与OOM差异:OOM将高分进程标记为"bad"并终止,而Low Memory Killer依赖动态阈值管理

3)、进程保活方案

①、利用系统广播拉活

原理:通过静态注册广播监听系统事件(如开机、网络变化)触发进程重启。

缺陷:

  • 被管理软件禁用自启动后失效
  • 事件不可控:无法实时响应进程终止,拉活存在延迟

②、利用系统Service拉活

实现方式:Service的onStartCommand返回START_STICKY,系统在内存充足时自动重启被杀的Service。

局限性:

  • 次数限制:连续三次被杀后不再重启(间隔时间递增:5秒→10秒→20秒)

③、利用native进程拉活

原理:通过Linux的fork创建native进程监控主进程,死亡时调用AMS拉活。

关键点:

  • 监控方式:轮询检查或文件锁阻塞(主进程持有锁,拉活进程获取锁失败即判定死亡)
  • 失效场景:Android5.0后系统限制native进程权限

④、利用JobScheduler机制拉活

适用版本:Android5.0+替代native方案的接口,通过JobScheduler监听进程状态并触发拉活。

⑤、利用账号同步机制拉活

原理:利用系统定期同步账号的特性激活进程。注意:高版本Android已限制此机制有效性。

OK,今天的内容就这么多啦,下期再会!

http://www.dtcms.com/a/361206.html

相关文章:

  • 大模型落地全流程实践:从技术选型到企业级部署
  • 音视频开发入门:FFmpeg vs GStreamer,新手该如何选择?
  • 松灵斯坦福Mobile ALOHA同款 | 通过低成本全身远程操作实现双手机器人移动操控学习
  • 01数据结构-红黑树
  • 永磁同步电机无速度算法--高频脉振方波注入法(测量轴系转子位置误差信号解耦处理)
  • Spark引擎中RDD的性质
  • 【牛客JZ31】—栈的压入弹出序列判断算法详解
  • 【73页PPT】MES应用介绍(附下载方式)
  • SpringBoot @RefreshScope 注解的极致玩法
  • SpringCloud-服务注册-服务发现
  • AI瘦身狂魔!微软推出原生1-bit大模型,性能不减,内存仅需同行零头!
  • 博0进化版
  • 9月校招难题怎么解?AI面试精准匹配人才
  • 系统架构设计师备考第12天——计算机语言-建模形式化语言
  • Windows 命令行:cd 命令1,cd 命令的简单使用
  • 数据结构:单链表的应用(力扣算法题)第二章
  • APP性能测试,你需要关注哪些指标?
  • React 学习笔记3 生命周期 受控/非受控组件
  • 阿里云代理商:轻量应用服务是什么?怎么用轻量应用服务器搭建个人博客?
  • 大模型落地:从微调到部署的全景式实战指南
  • MFC应用防止多开
  • Prometheus Alertmanager 告警组件学习
  • Linux 正则表达式与grep命令
  • 车载卫星通信:让自动驾驶“永不掉线”?
  • Kafka面试精讲 Day 4:Consumer消费者模型与消费组
  • 指针数组与数组指针的区别
  • 【第十一章】Python 队列全方位解析:从基础到实战
  • 鸿蒙NEXT表单选择组件详解:Radio与Checkbox的使用指南
  • 绝了!极空间搭配视频智语,生产力拉满,多平台视频摘要一键搞定
  • browsermobproxy + selenium 获取接口json