☰
Android BaseActivity封装精讲:模板方法、ViewBinding与生命周期安全
2026/10/1 12:19:44 网站建设 项目流程

在Android开发圈子混久了,你会发现一个很有意思的现象:很多项目里Activity长得都差不多——onCreate里findViewById重复几十遍,initView、initData复制粘贴换了个类名,什么时候加载数据、什么时候弹Toast、什么时候处理状态栏,每个页面都有一套自己的写法。我一直觉得,Activity的基类封装是每个Android开发者迟早要迈过的一道坎。BaseActivity这个名字听起来简单,但真正把它设计好了,后续十几个、几十个页面的开发效率完全是两个量级。这篇文章我想把我这些年沉淀下来的封装思路、代码实现、以及踩过的坑完整地聊一遍,适合那些已经写过一阵子Android、开始觉得重复工作太多的朋友参考。

1. 先说清楚:BaseActivity到底封的是什么

1.1 没有基类的日子:所有页面都在重复同一件事

我见过太多业务项目,一个简单的列表页面,从onCreate到数据返回,代码能写两百行,其中一百行是每个页面都差不多的模板代码。布局加载、控件初始化、状态栏设置、Loading显示、Toast提示、网络请求的进度管理,页面退出时的资源清理,这些东西在每个Activity里都是重复的,但很多人都硬生生地复制粘贴了过来。

最难受的不是写第一遍,而是后续改动。今天产品说要给所有页面加一个统一的加载失败重试按钮,你就要打开所有Activity改一遍,漏掉任何一个页面,线上就会出问题。明天UI说标题栏左边距要统一调整,又是一轮全量改动。没有基类的情况下,这种跨页面的统一需求,全靠人肉去排查,效率低还容易出错。

还有一类重复是逻辑层面的,比如界面销毁之后回调才返回,这时候你需要在isFinishing或者isDestroyed判断防崩溃;比如多个页面都要在onResume里做埋点;比如每个页面都要在onDestroy里解绑某些监听器。这些代码单个页面看不多,架不住页面多,时间长了,维护成本就相当可观。

1.2 基类封装最终解决的四个核心问题

我做了几年基类封装,总结下来BaseActivity主要解决四个问题。

第一是消除重复代码,把布局加载、控件初始化的流程固定下来,子类只关心自己差异化的部分。配合ViewBinding之后,这块的重复率能下降很大一截。

第二是统一交互体验。弹出的Toast样式、Loading动画、错误提示、空页面占位,这些都是产品层面要求一致的东西。封装在基类里,就保证了无论哪个开发写了新页面,它的交互默认就是符合规范的。

第三是提供生命周期安全管理。网络回调、Handler消息、第三方SDK的回调,经常出现Activity已经销毁、回调才回来的情况。基类可以统一处理这个网络请求生命周期绑定,让子类开发者不用每次都写一堆判断。

第四是规范开发流程。基类定义了initView、initData、initListener这些标准流程,新成员照着写就行,代码风格会自然统一起来,review代码也轻松很多。

1.3 封装前的取舍判断:不是所有东西都该进基类

这里我要先泼一盆冷水:基类不是越胖越好。我见过有的项目BaseActivity三百行起步,什么方法都往里塞,图片加载、JSON解析、数据库操作全在基类里,结果就是每个页面都背负着一个巨大的父类,改起来小心翼翼,子类之间互相影响,最后谁都不敢动。

我的原则是,基类只放通用能力,不塞业务逻辑。什么是通用能力?生命周期流程、UI容器、状态栏/系统栏、Loading与Toast、ActivityResult与权限请求,这些是每个页面都用得到的。什么是业务逻辑?用户登录状态的判断、订单数据的加载、购物车角标刷新,这些内容是页面级别的,放进基类就耦合了。

另外要考虑一个原则:抽象出来的方法一定是大多数子类都需要覆写的,而不是少数页面才用的。比如整个项目只有一个页面需要全屏展示,那就不该在基类里留一个abstract方法让所有页面都去实现,更好的做法是让那个页面自己做特殊处理,或者提供一个默认实现。基类封装的价值在于让80%的页面变得更快,而不是为了那20%的特殊情况把基类搞得很复杂。

2. 基类架构设计:从包结构到抽象时机

2.1 一个我落地过多次的包结构

封装BaseActivity不是单独写一个类那么简单,它要跟工具类、对话框、适配器等配合,否则基类还是在裸奔。我这里给一个稳定的包结构,实战项目里可以直接参考:

com.example.project ├── base │ ├── BaseActivity.java │ ├── BaseFragment.java │ ├── BaseViewModel.java │ └── BaseApplication.java ├── ui │ ├── toast │ │ └── ToastUtils.java │ ├── loading │ │ └── LoadingDialog.java │ └── statusbar │ └── StatusBarUtils.java ├── utils │ ├── LogUtils.java │ └── KeyboardUtils.java └── widget ├── LoadingLayout.java └── EmptyLayout.java

base目录放基类,ui目录放UI辅助组件,utils放通用工具,widget放自定义控件。BaseActivity依赖这些组件,但组件不依赖BaseActivity。这个结构的核心思想是让BaseActivity成为一个组装者,把通用组件按固定流程组合起来,而不是一个大杂烩容器。

我见到不少人的BaseActivity自己实现Toast、自己写了Loading显示逻辑、自己处理状态栏,结果每次改动基类,所有页面都得回归一遍。拆开之后,每一块都可以单独测试和调整,基类只负责编排流程,稳定性和灵活性都更好。

2.2 模板方法模式在基类里的落地

BaseActivity的另一个关键设计思路是模板方法模式。所谓模板方法,就是父类定义好算法的骨架,把某些步骤延迟到子类实现。在我们这个场景里,骨架就是onCreate之后的完整执行流程,子类需要实现的就是布局、控件、数据这些具体内容。

用生活化的例子来说,就像是快餐店的出餐流程:接单、备餐、打包、交付是固定的,但具体是哪一种汉堡、要不要加辣,由顾客决定。BaseActivity把流程固定,具体内容交给子类覆写。

这里的关键在于钩子方法的设计。我认可的基类设计是调用链清晰、覆写点明确:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initIntentData(savedInstanceState); initViewBinding(); setContentView(binding.getRoot()); initStatusBar(); observeViewModel(); initView(); initData(); initListener(); }

子类只需要关心initView、initData、initListener这三个入口。而且这三个方法我给的默认实现都是空的,子类按需覆写。这样新来的同事很容易理解:打开BaseActivity,看onCreate的执行顺序,就知道自己应该在哪个方法里写什么。

2.3 核心钩子方法的职责划分

我们把几个钩子方法的边界说清楚,这是容易混淆的地方。

initIntentData用来处理Intent传参和 savedInstanceState 的数据恢复。这个方法的执行顺序在initView之前,因为很多时候控件的初始值要依赖Intent里的数据。如果先初始化控件,再去Intent取数据,提示文案就要临时二次设置。

initView只做一件事:找控件、设置控件的基础属性。比如设置RecyclerView的LayoutManager,设置下拉刷新控件的颜色,给TextView设置字体大小。不要在这个方法里写业务判断,更不要加载数据。

initData负责数据的加载和填充。包括从网络拉数据、从数据库读数据、组装数据到控件上。我见过不少开发把initData和initView写在一起,短平快的时候没什么问题,但页面一复杂,几个操作堆在一起,调试的时候你就分不清是控件没初始化还是数据没回来。

initListener负责事件回调的注册,包括点击事件、滑动监听、列表项点击。单独分出来的好处是,如果你想在某个页面暂时屏蔽所有交互,只需要注释掉一行。

最后是observeViewModel,这个方法是给MVVM架构做准备的,把LiveData或者StateFlow的观察者统一注册在这里,保证数据回调时的UI安全和生命周期安全。

2.4 关于继承层级和中间基类的处理

有些项目里会再包一层,比如BaseActivity下面是BusinessBaseActivity,然后才是各个业务页面。BusinessBaseActivity可以放一些业务通用逻辑,比如登录校验、埋点上报。但要注意一个常见问题:层数越多,覆写点越分散,新成员越难搞懂应该在那一层覆写什么。

我的建议是两层就够了,BaseActivity存放项目级通用能力,BusinessBaseActivity存放业务级通用能力,不要层层继承。如果发现需要第三层,先想想是不是设计上出了问题。另外,如果中间层覆写了基类方法,一定要保留super调用链,我在实际项目中遇到过继承链断裂导致初始化流程没走完的严重事故,这个问题我在后面的避坑章节还会详细说。

3. UI初始化流程封装:基于ViewBinding的实践

3.1 为什么我最终选了ViewBinding

早期开发无非是两条路:findViewById写到手软,或者上ButterKnife。我项目里ButterKnife用了很久,它通过注解和APT技术把findViewById省掉了,直接靠绑定的字段名操作控件,用起来确实舒服。ButterKnife后续维护状态不稳之后,Google官方推出的ViewBinding成了更加可靠的选择。

ViewBinding的好处有几点。按类型安全:编译期生成绑定类,字段类型不会出错。按空安全:如果布局里某个控件只在某个变体里存在,绑定类里会有空判断,避免直接解引用空指针。按性能:相比DataBinding,ViewBinding不引入表达式引擎,编译速度更快,运行期没有反射开销。

还有一点很重要:ViewBinding是Google持续维护的方向,跟Jetpack生态集成度好,新项目我优先推荐它。BaseActivity通过泛型把ViewBinding的创建过程吸收了,子类只需要提供一个方法返回绑定类。

3.2 基类完整代码实现

下面这一段是我项目里BaseActivity的核心骨架,已经过多个版本迭代,思路可以作为参考。

public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity { protected T binding; protected LoadingDialog loadingDialog; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); initIntentData(savedInstanceState); initBinding(); setContentView(binding.getRoot()); initStatusBar(); observeViewModel(); initView(); initData(); initListener(); } private void initBinding() { // 泛型反射获取实际绑定类 Type genericSuperclass = getClass().getGenericSuperclass(); if (genericSuperclass instanceof ParameterizedType) { Type[] types = ((ParameterizedType) genericSuperclass).getActualTypeArguments(); Class<?> bindingClass = (Class<?>) types[0]; try { Method inflateMethod = bindingClass.getMethod("inflate", LayoutInflater.class); binding = (T) inflateMethod.invoke(null, getLayoutInflater()); } catch (Exception e) { throw new RuntimeException("ViewBinding inflate failed", e); } } else { throw new IllegalArgumentException("BaseActivity requires ViewBinding type parameter"); } } /** * 在该方法中处理Intent参数和状态恢复 */ protected void initIntentData(Bundle savedInstanceState) {} /** * 初始化页面内控件,设置基础属性 */ protected void initView() {} /** * 加载数据并填充到界面 */ protected void initData() {} /** * 注册事件监听器 */ protected void initListener() {} /** * 观察ViewModel的数据变化 */ protected void observeViewModel() {} /** * 初始化状态栏 */ protected void initStatusBar() {} @Override protected void onDestroy() { super.onDestroy(); binding = null; dismissLoading(); } }

注意initBinding用了泛型反射来获取实际的ViewBinding类。有人可能会问,为什么不直接在子类里写一行binding = ActivityMainBinding.inflate(getLayoutInflater())?因为在基类里统一创建,整个流程就完全标准化了,子类一行都不用管绑定创建。这就是基类封装的意义。

3.3 initView、initData、initListener的执行节奏

这三个方法为什么要分开,我从一次重构的体会聊起。早期我把它们合并成一个init方法,当时觉得页面生命周期简单,没必要拆。后来页面复杂了,RecyclerView的adapter要先创建,adapter的数据要等网络回来,网络回来之后要刷新列表,刷新时又要先隐藏Loading,整个流程揉在一起,代码顺序稍微错一点就出问题。

拆开之后,顺序就固定了:先有控件,再有数据,最后有交互。你绝对不会遇到adapter还没创建就给列表设置数据的问题,也不会遇到数据还没回来就注册点击监听的尴尬。这类问题在代码review的时候就天然减少了。

实际上initData这个方法名可能会让新人误解,以为必须在这里发起网络请求。其实不一定,initData更准确的理解是"准备数据":如果页面是本地数据,直接读库填充;如果是远程数据,在这里触发请求并订阅回调;如果一个页面有两种数据来源,也应该在这里统一发起。网络数据真正返回之后,一般会走一个renderData方法,我习惯再加一个renderData,子类按需覆写,把数据渲染的逻辑放进去。这里是可选的,按项目复杂程度来决定,这里不强制,但方法职责要单一。

3.4 老项目从ButterKnife迁移到ViewBinding的注意事项

如果你的老项目还依赖ButterKnife,又想让BaseActivity统一用到ViewBinding,迁移不是改写所有页面那么恐怖,而是有新页面用新方式,老页面保持现状,过渡期两个共存就够了。

有几个坑提前提示。第一,ViewBinding的类名是根据布局文件名生成的,例如activity_main.xml对应的绑定类是ActivityMainBinding,如果布局文件名带下划线,生成的类名会把下划线去掉并转驼峰。第二,include标签的布局也需要单独创建绑定类,然后在父类绑定中通过binding.xxxBinding访问。第三,有些老布局里有merge标签,这种情况下ViewBinding生成的方式略有不同,建议先处理掉merge,否则绑定类创建代码要调整。

4. 高频功能模块的封装细节

4.1 Toast统一:别一个按钮连点出十个提示

Toast在很多项目里是最不受重视的,但恰恰是体验上最容易翻车的地方。我见过有的页面连点十次按钮,Toast排队弹十次,用户等半分钟都点不完。基类里统一Toast能力之后,这类问题就能依靠工具层的单例机制解决。

我的做法是基类里暴露showToast方法,内部转给ToastUtils的静态方法处理,ToastUtils内部用应用上下文持有唯一的Toast实例,每次show之前取消旧Toast:

public final class ToastUtils { private static Toast mToast; public static void show(String message) { if (mToast == null) { mToast = Toast.makeText(AppContext.get(), message, Toast.LENGTH_SHORT); } else { mToast.setText(message); } mToast.show(); } }

当然,这只是最基础的版本,实际项目中Toast往往要定制样式、指定位置、区分成功失败类型,你可以在此基础上扩展。但核心思路不变:全局唯一实例。

4.2 Loading对话框:别把Dialog写到页面里

Loading是另一个容易失控的点。有的页面用ProgressDialog,有的页面用一个自定义Dialog,有的页面直接用一个View盖在上层,风格乱七八糟。在BaseActivity里封装一个加载对话框,问题就统一了。我在基类里维护了一个loadingDialog字段,暴露showLoading和dismissLoading两个方法,里面做了防重复、防泄露处理。

关键的一个处理是,显示Loading的时候要判断Activity是否还活着,销毁之后不弹、不崩溃:

protected void showLoading() { if (isFinishing() || isDestroyed()) return; if (loadingDialog == null) { loadingDialog = new LoadingDialog(this); } if (!loadingDialog.isShowing()) { loadingDialog.show(); } }

onDestroy里调用dismissLoading,是为了防止Dialog持有Activity的引用导致泄漏。特别注意,如果你在请求里通过runOnUiThread弹窗,回调时Activity已经销毁,这段判断能直接拦住,这也是基类的价值所在。

在Loading样式上,我建议用局部的View加载状态而不是全局Dialog优先级更高。特别是列表页下拉刷新的时候,本地LoadingLayout和全屏Loading最好能区分开。基类可以暴露两种方式,一种是dialog式,一种是layout式。layout式我把整套LoadingLayout、ErrorLayout、EmptyLayout做成了组合控件,基类里保留一个setLoadState方法,子类可以随用随调。

4.3 状态栏与沉浸式处理

状态栏这块几乎是每个Android项目都要处理的。各家ROM的差异、刘海屏适配、状态栏字体颜色深浅,如果不封装,每个页面都要跟系统API打交道。基类里统一处理之后,可以支持两类页面:一类是普通页面,在统一背景色上让状态栏跟随变色;另一类是图片头页面,需要状态栏透明、内容延伸到状态栏底下。

推荐的做法是抽一个StatusBarUtils工具类,基类里的initStatusBar读取子类配置来决定调哪个方法。我的基类里会提供一个setupStatusBar方法,子类可以通过重写它或者传标志位来控制。这里有个经验:状态栏字体颜色的问题在MIUI、Flyme上有私有的API,抽取工具类时把这两个特殊处理也包进去。坦白说各家厂商的适配代码我常年维护一个工具类,更新频率不低,这个真的不能省,设备覆盖率太高了。

4.4 返回键与页面关闭的统一处理

产品里总有一个需求:连续按两次返回键退出应用,很多页面都要支持。与其在每个页面里覆写onBackPressed,不如在基类里做一个通用方案:

@Override public void onBackPressed() { if (supportDoubleBackExit) { long currentTime = System.currentTimeMillis(); if (currentTime - lastBackTime > 2000) { lastBackTime = currentTime; showToast("再按一次退出应用"); return; } } super.onBackPressed(); }

这里我更喜欢把onBackPressed的现代版本写出来,因为新项目里要兼容API 33+的预测性返回,整体逻辑还是以系统API为准。基类封装的思路是给你一个开关,部分页面不需要双击退出,直接默认super。这比每个页面写一遍判断要省心太多。

5. 进阶设计:ActivityResult、运行时权限请求与泄漏防护

5.1 用ActivityResultLauncher替代startActivityForResult

老代码里常见startActivityForResult和onActivityResult,这套API已经过时了,新的ActivityResultLauncher更安全、注册时机更早。在基类里封装Result回调的一个好处是,子类不需要关心Activity里的回调分发,只需要注册回调函数。"后续在ActivityResultApi场景下,"倒是不用再写onActivityResult这个重方法,回调直接通过launcher拿到结果。

我常用的模式是把Launcher注册放在基类里,通过一个结果回调接口分发:

private ActivityResultLauncher<Intent> activityResultLauncher; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { handleActivityResult(result.getResultCode(), result.getData()); } } ); } protected void startActivityForResult(Intent intent, ActivityResultCallback callback) { // 简化写法,实际项目里通过key回调分发 activityResultLauncher.launch(intent); }

一个要注意的坑:registerForActivityResult必须在onCreate里调用,一旦Activity被重建,Launcher会再次注册,所以不要把它放在页面业务逻辑的方法里调用。我见过在这个问题上热修复硬生生修出野指针的案例。ActivityResultLauncher的注册跟生命周期绑定,越早越好,这是API设计上的约束。

5.2 权限请求在基类中的统一封装

运行时权限在Android 6.0之后成为开发标配,到了Android 13、Android 14之后权限请求之间已经是数量级的大坑,访问相册、访问白名单、精准定位等等,每个版本的权限表现还不一样。如果基类里做一层封装,子类可以这样调:

requestPermission( Manifest.permission.CAMERA, new PermissionCallback() { @Override public void onGranted() { // 启动扫描 } @Override public void onDenied(boolean canAskAgain) { if (!canAskAgain) { showToast("权限被拒绝,请到设置中开启"); } } } );

基类内部用一个ActivityResultLauncher去请求权限,并把回调接口缓存起来,这样权限结果会集中到一处处理。这不仅减少了子类的工作量,还避免了一个隐患:权限回调发生时Activity已经被销毁,此时不能弹窗、不能跳设置页,基类统一判断一次,比在几十个页面分别判断要可靠得多。

5.3 内存泄漏防护:Handler、单例与退出清理

Activity内存泄漏是Android的顽疾,非静态内部类隐式持有外部类的引用,导致Activity无法被回收。Java的Handler、Kotlin协程、第三方SDK回调都是泄漏高危区。BaseActivity里可以做几件事来防护。

第一,提供一个安全的Handler。子类用这个Handler发消息,基类在onDestroy里清空所有消息,消除延迟消息对Activity的持有:

protected Handler safeHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { BaseActivity.this.handleMessage(msg); } }; protected void handleMessage(Message msg) {} @Override protected void onDestroy() { safeHandler.removeCallbacksAndMessages(null); super.onDestroy(); }

第二,统一解绑注册。比如EventBus、LiveDataBus、某些监听器,可以要求子类在onDestroy里调一个unRegisterListener,基类本身把通用解绑做了,剩下的业务级别的解绑子类自行处理。

第三,协程方面,我建议子类统一使用lifecycleScope,基类不用额外封装,lifecycleScope最大的优点就是页面销毁时自动取消协程,不需要手写cancel。Kotlin项目里这个优势很明显,Java项目里就只有手写cancel这一条路。

6. 常见问题与排查实录

6.1 子类忘记调用super导致初始化断裂

我遇到最多的问题是子类覆写onCreate时忘记了super.onCreate(savedInstanceState),结果基类里的全套初始化流程都没有执行,页面一打开就是白屏,有的页面连崩溃信息都没有,定位了半天。另一种常见情况是子类覆写onDestroy时漏了super.onDestroy,导致绑定类没有置空、对话框没有取消,重新进入页面时旧引用还在,产生各种诡异现象。

排查思路:给BaseActivity的每个模板方法加一行日志输出,方便在Logcat观察调用链。这个方法调试期非常有用,发布前把日志关掉就行:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LogUtils.d("BaseActivity", getClass().getSimpleName() + " onCreate start"); ... }

6.2 ViewBinding子类生成的类找不到

ViewBinding类是通过布局文件名生成的,比如activity_main.xml生成的类叫ActivityMainBinding。如果你把布局文件删了、改了名,或者build目录被清理,IDE有时候没及时同步,编译就会报"找不到符号"。

常规解决办法是Build -> Clean Project,然后Build -> Rebuild Project。如果还不行,看下buildFeatures的配置:

android { buildFeatures { viewBinding = true } }

如果项目里既用Kotlin又用Java,位置略有不同,但你只要确认gradle里开启viewBinding开关就行。注意ViewBinding默认对所有布局生成绑定类,如果某个布局不需要绑定,可以在根标签加tools:viewBindingIgnore="true"来跳过。

6.3 泛型擦除导致的强转异常

BaseActivity 这种写法在Java里会存在类型擦除,如果子类声明的方式不对,运行时获取的泛型参数可能不是预期的绑定类,强转就直接ClassCastException。比如有人写了个中间层,没把泛型透传下去,到了真正的页面处就丢了。

我的建议是子类必须直接继承BaseActivity ,中间层如果要包一层,也要把泛型参数原样传下去。还有一种更稳妥的做法:不通过反射,让子类直接返回绑定类实例:

protected abstract T createBinding(LayoutInflater inflater);

这个方法虽然让子类多写一行代码,但完全避开反射和泛型擦除的坑。两种方案都能用,如果项目对性能比较敏感,或者你不想维护反射代码,我推荐子类传入绑定类实例的方式,实测下来更稳。

6.4 状态栏和系统栏冲突

沉浸式适配最常踩的坑就是内容布局被状态栏遮挡,或者状态栏背景和页面背景不一致,出现刺眼的分割线。封装之后,这类问题往往集中在初始化顺序上:必须先设置状态栏,再绑定布局,否则布局没有预留系统栏高度,内容就会跑到状态栏底下。

我的方案里,initStatusBar放在setContentView之后,但紧接着就会在initView之前执行。这套顺序配合RootView设置fitsSystemWindows或者View设置paddingTop,几千行的状态栏适配逻辑就被封装成一个方法了,子类几乎感觉不到。

体验上有个细节:如果基类设置了两个布尔开关,比如lightStatusBar控制状态栏图标深浅,transStatusBar控制是否透明状态栏,子类只需要在initStatusBar里调用相关方法。产品之后的百变需求就压缩到开关切换,这是BaseActivity该有的姿态。

6.5 继承结构里多了中间层怎么办

有些项目喜欢在BaseActivity和具体页面之间再插一个BusinessBaseActivity,放登录判断、放埋点逻辑,这个我可以理解。但问题在于,中间层的存在让模板方法的调用链变得更长,稍不留神就会出问题。

比如BusinessBaseActivity覆写了onCreate,但忘记调用super.onCreate,那基类的绑定流程就断了;或者BusinessBaseActivity里加了abstract方法,结果新增页面漏实现,一编译就报错,反而不利于快速开发。我建议中间层要么不去覆写基类流程,只增加protected方法,让子类按需调用;要么严格按照super链完整保留。而且中间层的方法命名要沿用一个风格,不然review代码的人根本分不清哪些是基类流程、哪些是业务逻辑。

如果你问我多长时间可以把一个BaseActivity打磨到稳定可用,我的体会是:骨架构建一天就能完成,但让它真正适应团队业务节奏,需要两到三个版本的迭代沉淀。重点不是模仿别人写一个看起来很全的基类,而是从自己项目的痛点里抽象出真正通用的方法,这样封装出来的BaseActivity才是团队自己的基础设施,而不是一份抄来抄去的代码模板。

最后再分享一个小技巧:基类写完以后,不要急着把老页面全部迁移过来。先拿两三个典型页面试点,确认流程没问题、扩展点够用,再逐步铺开。封装这件事好比修桥,方向对了,桥墩稳了,后续所有过河的人都受益;方向没想清楚就动工,返工成本只会越来越高。希望这篇文章里提到的设计思路和踩坑记录,能帮你把BaseActivity这条桥修得更稳一些。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询