1. 为什么我们需要BaseActivity
在Android开发中,Activity作为四大组件之一,承载着用户界面的展示和交互功能。每个项目都会创建大量的Activity,而这些Activity往往需要处理许多相同的逻辑和配置。比如状态栏设置、权限申请、生命周期管理、数据统计等。如果每个Activity都重复实现这些功能,不仅代码冗余,维护起来也相当困难。
我经历过一个电商项目,高峰期有超过200个Activity,每个Activity都要处理相同的网络状态监听、用户登录校验和页面跳转逻辑。后来我们花了整整两周时间重构,才把这些公共逻辑抽离到BaseActivity中。从那以后,我深刻认识到一个好的BaseActivity设计能极大提升开发效率和代码质量。
2. BaseActivity的核心功能设计
2.1 基础功能封装
一个完善的BaseActivity应该包含以下基础功能:
- 统一的生命周期管理:封装onCreate、onDestroy等生命周期方法,处理公共逻辑
- 权限请求封装:简化权限申请流程,提供回调接口
- 状态栏设置:统一应用主题和状态栏样式
- ButterKnife绑定:简化View绑定和资源释放
- 日志和统计:自动打点页面访问和停留时长
java复制public abstract class BaseActivity extends AppCompatActivity {
// ButterKnife绑定
private Unbinder unbinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
unbinder = ButterKnife.bind(this);
initStatusBar();
initData();
initView();
initListener();
}
protected abstract int getLayoutId();
protected abstract void initData();
protected abstract void initView();
protected abstract void initListener();
// 统一的状态栏设置
protected void initStatusBar() {
StatusBarUtil.setColor(this, ContextCompat.getColor(this, R.color.colorPrimary), 0);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (unbinder != null) {
unbinder.unbind();
}
}
}
2.2 高级功能扩展
除了基础功能,我们还可以在BaseActivity中加入一些高级特性:
- 网络状态监听:自动检测网络变化并提示用户
- 页面跳转封装:统一管理Intent跳转和参数传递
- MVP/MVVM支持:提供基础Presenter或ViewModel绑定
- 多语言切换:监听语言变化自动刷新界面
- 内存泄漏检测:在onDestroy时检查潜在的内存泄漏
提示:不是所有功能都需要一次性实现,可以根据项目需求逐步添加。过度设计反而会增加维护成本。
3. 具体实现与代码解析
3.1 权限请求封装实现
Android 6.0引入的动态权限机制让权限处理变得复杂。我们可以封装一个简单的权限请求工具:
java复制public class PermissionHelper {
private Activity mActivity;
private PermissionListener mListener;
public PermissionHelper(Activity activity) {
this.mActivity = activity;
}
public void requestPermissions(String[] permissions, PermissionListener listener) {
this.mListener = listener;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
List<String> deniedPermissions = new ArrayList<>();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(mActivity, permission)
!= PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permission);
}
}
if (!deniedPermissions.isEmpty()) {
ActivityCompat.requestPermissions(mActivity,
deniedPermissions.toArray(new String[0]), REQUEST_CODE);
} else {
listener.onGranted();
}
} else {
listener.onGranted();
}
}
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_CODE) {
List<String> deniedPermissions = new ArrayList<>();
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permissions[i]);
}
}
if (deniedPermissions.isEmpty()) {
mListener.onGranted();
} else {
mListener.onDenied(deniedPermissions);
}
}
}
public interface PermissionListener {
void onGranted();
void onDenied(List<String> deniedPermissions);
}
}
在BaseActivity中使用:
java复制protected void requestPermissions(String[] permissions, PermissionListener listener) {
new PermissionHelper(this).requestPermissions(permissions, listener);
}
3.2 MVP模式集成
对于采用MVP架构的项目,可以在BaseActivity中加入Presenter管理:
java复制public abstract class BaseMvpActivity<P extends BasePresenter> extends BaseActivity {
protected P mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachView(this);
}
super.onCreate(savedInstanceState);
}
protected abstract P createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
mPresenter = null;
}
}
}
4. 常见问题与优化建议
4.1 内存泄漏预防
Activity是最容易发生内存泄漏的地方之一。以下是几个常见的内存泄漏场景和解决方案:
-
Handler内存泄漏:
java复制// 错误写法 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // 处理消息 } }; // 正确写法 private static class SafeHandler extends Handler { private WeakReference<Activity> mActivityRef; public SafeHandler(Activity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = mActivityRef.get(); if (activity != null && !activity.isFinishing()) { // 处理消息 } } } -
单例持有Context:避免让单例对象持有Activity的引用,应该使用Application Context。
-
匿名内部类:匿名内部类会隐式持有外部类引用,可以考虑使用静态内部类+弱引用的方式。
4.2 性能优化建议
-
延迟加载:在BaseActivity中可以加入延迟加载机制,将非必要的初始化操作放到onCreate之后执行。
java复制private Handler mHandler = new Handler(); protected void postDelay(Runnable runnable) { mHandler.postDelayed(runnable, 100); } -
布局优化:可以在BaseActivity中加入布局加载时间的统计,帮助发现性能瓶颈。
-
资源释放:统一管理广播、服务等资源的注册和反注册。
5. 实际项目中的应用技巧
5.1 多模块项目中的BaseActivity设计
在大型多模块项目中,可以考虑分层设计BaseActivity:
- 核心BaseActivity:放在基础模块中,包含最基础的功能
- 业务BaseActivity:各业务模块可以继承核心BaseActivity,添加业务相关功能
- 特性BaseActivity:针对特定功能(如地图、支付)的BaseActivity
这种分层设计既能保持核心功能的统一,又能满足不同模块的特殊需求。
5.2 主题和样式的统一管理
通过BaseActivity可以统一管理所有Activity的主题和样式:
java复制@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 必须在super.onCreate之前设置主题
setTheme(getCustomTheme());
super.onCreate(savedInstanceState);
// ...
}
protected int getCustomTheme() {
return isNightMode() ? R.style.AppTheme_Night : R.style.AppTheme_Day;
}
5.3 页面跳转的封装
封装一个统一的页面跳转方法可以避免Intent参数传递的混乱:
java复制public void startActivity(Class<?> clazz, Bundle bundle) {
Intent intent = new Intent(this, clazz);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivity(intent);
// 可以在这里统一添加页面跳转动画
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
}
6. 测试与维护建议
6.1 单元测试策略
虽然Activity测试比较困难,但BaseActivity中的一些工具方法还是可以测试的:
- 权限请求逻辑的测试
- 生命周期回调的测试
- 页面跳转参数的测试
可以使用AndroidJUnitRunner配合Mockito进行测试。
6.2 版本兼容处理
在BaseActivity中统一处理版本兼容问题:
java复制protected void setFullScreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
} else {
// 老版本处理逻辑
}
}
6.3 迭代优化建议
- 定期review BaseActivity,移除不再使用的功能
- 收集团队反馈,持续优化公共逻辑
- 关注新版本Android特性,及时更新封装
我在实际项目中发现,BaseActivity的优化是一个持续的过程。每次项目迭代后,都应该看看哪些新功能可以抽象到BaseActivity中,哪些旧功能已经不再需要。保持BaseActivity的简洁和高效,才能让它真正成为开发中的利器而不是负担。