嘿,朋友!看到你这标题我就知道,你肯定是个正在或者准备深入Android开发的朋友。这行水挺深,但风景也好。今天咱们不整那些虚头巴脑的教科书式定义,我就把你从“Hello World”那个懵懂的少年,一步步带到MVVM架构的大佬境界,顺便把你脑子里那团关于“Activity生命周期”和“内存泄漏”的乱麻给你理顺了。
咱们得像剥洋葱一样,一层一层来。别急,慢慢看。
一、 那个让我们既爱又恨的“Hello World”
回想一下,你第一次在Android Studio里点击那个绿色的“Run”按钮,看到手机屏幕上跳出“Hello World”时的心情吧?那时候你觉得,哇,编程好简单,两行代码就搞定了。
没错,那是真的简单。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 这里有个TextView,我们动态改一下它的文字
val tvHello = findViewById<TextView>(R.id.tv_hello)
tvHello.text = "你好,Android!"
}
}
但这只是冰山一角。你以为这只是显示文字?不,setContentView背后是View系统的初始化,是Window的管理,是系统Activity栈的调用。这时候你还不懂什么是“上下文”,不懂什么是“资源ID”,但你已经迈出了第一步。
关键理解:Hello World的意义不在于代码本身,而在于让你明白:Android应用是由组件构成的,而Activity是其中最核心的组件之一。 它是用户与应用交互的窗口。
二、 Activity生命周期:不是背出来的,是“活”出来的
很多初学者,包括我当年,都是把Activity的生命周期图背下来的:onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy。背得滚瓜烂熟,一上机全废。
为什么?因为你没“经历”过。
咱们换个说法。把Activity想象成你的一天。
onCreate():早上起床,洗漱,换衣服。你初始化了你的状态(变量)。比如,你要加载布局,初始化数据库连接,或者从Intent里获取参数。记住:只在这里做一次性初始化操作。 别在这儿搞耗时操作,否则手机会卡死,ANR(无响应)就来了。onStart():你走出家门,准备出门。这时候用户可以看到你了,但还没法跟你互动。onResume():你到了公司,开始工作。这是前台状态,用户跟你能进行交互。onPause():老板突然找你谈话,屏幕可能变暗了。这时候你的Activity部分可见,或者失去了焦点。如果你正在播放音乐,这时候应该暂停;如果你在录音,这时候应该停止。注意:这里不能做耗时操作!onStop():你下班回家了,或者去了另一个完全不相关的页面,你的Activity完全不可见。这时候你可以释放一些重型资源,比如网络连接、传感器监听。onDestroy():你彻底下班了,公司把你除名了。这是最终状态。
实战坑点:Configuration Change
你有没有遇到过这种情况:App写得好好的,突然切到横屏,或者改了系统字体大小,结果数据全没了,页面重新加载了?
这就是Activity被重建了!
系统在Configuration Change时,默认会销毁并重新创建Activity。这是一个常见的“坑”。
怎么办?
方法一:在AndroidManifest.xml中声明configChanges,告诉系统你自个儿处理。
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden">
</activity>
然后在代码里重写onConfigurationChanged:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// 处理横竖屏切换逻辑,比如调整布局
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 横屏逻辑
} else {
// 竖屏逻辑
}
}
方法二(更推荐):使用ViewModel。这个咱们后面讲MVVM的时候细说。
三、 从“面条代码”到架构革命:为什么要MVVM?
回想一下,你早期的Activity是不是这样的:
class OldSchoolActivity : AppCompatActivity() {
private var mDataList = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_old)
// 1. UI操作
val button = findViewById<Button>(R.id.btn_load)
button.setOnClickListener {
// 2. 网络请求
loadFromNetwork(object : Callback {
override fun onSuccess(data: List<String>) {
mDataList.clear()
mDataList.addAll(data)
// 3. 刷新UI
adapter.notifyDataSetChanged()
}
})
}
}
}
看起来没问题?但随着业务复杂,这个Activity会变成几千行代码的“巨无霸”。UI逻辑、网络逻辑、数据逻辑全揉在一起。维护起来痛苦不堪,测试也测不了。
这时候,MVVM(Model-View-ViewModel)就应运而生。
MVVM的核心思想:解耦
- Model:你的数据。可以是数据库、网络请求结果,或者本地文件。
- View:你的UI。在Android里,主要是XML布局和Activity/Fragment。
- ViewModel:中间的桥梁。它持有UI需要展示的数据,并且处理业务逻辑。
关键点:View和Model不直接通信。View观察ViewModel,ViewModel提供数据给View。
为什么用LiveData或StateFlow?
以前我们用Observable,现在我们有更现代的LiveData(Google官方推荐,生命周期安全)和StateFlow(Kotlin协程时代的首选)。
咱们用LiveData举个例子:
1. 定义数据类(Model)
data class User(val name: String, val age: Int)
2. 创建ViewModel
class UserViewModel : ViewModel() {
// 用 MutableLiveData 暴露数据,外部可以观察
private val _users = MutableLiveData<MutableList<User>>()
val users: LiveData<MutableList<User>> = _users
private val repository = UserRepository()
fun loadUsers() {
// 模拟网络请求
viewModelScope.launch {
val result = repository.getUsers()
_users.postValue(result)
}
}
}
3. 在Activity中绑定
class ModernActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_modern)
// 获取ViewModel,它和Activity的生命周期绑定
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
val recyclerView = findViewById<RecyclerView>(R.id.recycler)
val adapter = UserAdapter()
recyclerView.adapter = adapter
// 观察数据变化,自动更新UI
viewModel.users.observe(this) { userList ->
adapter.submitList(userList)
}
// 点击按钮触发加载
findViewById<Button>(R.id.btn_load).setOnClickListener {
viewModel.loadUsers()
}
}
}
看到好处了吗?
- Activity里几乎没有业务逻辑,只有UI绑定和生命周期感知。
- 当Activity被销毁再重建(比如横竖屏切换),ViewModel不会被重建,数据还在!这就是为什么我说它是解决Configuration Change的好方法。
- 测试容易了,你可以单独测试ViewModel,不需要启动Activity。
四、 内存泄漏:Android开发的“隐形杀手”
做Android开发,最怕的就是OOM(内存溢出)和泄漏。一旦泄漏,App越来越卡,最终崩溃。
什么是内存泄漏?
简单说,就是某些对象不再被使用了,但因为还被某个长生命周期的对象引用着,导致GC(垃圾回收器)无法回收它们。这些对象占着茅坑不拉屎,内存越来越大,直到爆炸。
常见的内存泄漏场景及解法
场景1:非静态内部类持有Activity引用
这是最常见的!你看这个反例:
class MainActivity : AppCompatActivity() {
// 错误示例!这是一个非静态内部类,隐含持有外部Activity的引用
private val myTask = object : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String {
Thread.sleep(10000) // 模拟耗时操作
return "Done"
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
// 这里如果延时操作,Activity已经销毁,但这个Task还在,就泄漏了!
textView.text = result
}
}
}
解法:
- 把内部类改成静态内部类(Static Inner Class)。
- 使用弱引用(WeakReference)来持有Activity。
class MainActivity : AppCompatActivity() {
private var myTask: MyAsyncTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 传入弱引用
myTask = MyAsyncTask(this)
myTask?.execute()
}
override fun onDestroy() {
super.onDestroy()
myTask?.cancel(true)
myTask = null
}
// 静态内部类,不隐含持有外部类引用
private class MyAsyncTask(activity: MainActivity) : AsyncTask<Void, Void, String>() {
private val weakActivity = WeakReference(activity)
override fun doInBackground(vararg params: Void?): String {
Thread.sleep(10000)
return "Done"
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
val activity = weakActivity.get()
if (activity != null) {
activity.textView.text = result
}
}
}
}
场景2:单例持有Context
object SingletonHelper {
private var context: Context? = null
fun init(context: Context) {
this.context = context // 危险!如果传入的是Activity,它会一直持有直到进程结束
}
}
解法:单例里尽量用Application Context。
fun init(context: Context) {
this.context = context.applicationContext // 安全
}
场景3:Handler没有移除回调
class MyActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handler.postDelayed({
// 如果Activity销毁了,这个Runnable还在等,就泄漏了Activity
doSomething()
}, 10000)
}
}
解法:在onDestroy时移除回调。
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null) // 移除所有回调
}
场景4:注册了监听器但未注销
比如注册了BroadcastReceiver、LocationListener、EventBus等。
解法:在onPause或onDestroy中注销。
override fun onDestroy() {
super.onDestroy()
if (broadCastReceiver != null) {
unregisterReceiver(broadCastReceiver)
}
}
如何检测内存泄漏?
Google提供了一个强大的工具:LeakCanary。
只需在build.gradle中添加依赖:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
然后,当App出现内存泄漏时,它会自动弹出通知告诉你哪里泄漏了,连堆dump都给你分析好了。简直是神器!强烈推荐所有Android开发者都加上。
五、 总结:一条清晰的学习路径
好了,咱们从头到尾捋一遍:
- Hello World:让你入门,理解Activity是窗口。
- 生命周期:让你懂得尊重系统,知道什么时候该初始化,什么时候该释放资源。别在
onPause里干重活,别在onCreate里搞耗时操作。 - MVVM架构:让你写出可维护、可测试、健壮代码。把UI和业务逻辑分开,用ViewModel作为中间层,用LiveData/StateFlow驱动UI。
- 内存泄漏防范:让你写出高效的代码,避免App卡顿崩溃。记住:静态内部类+弱引用、单例用ApplicationContext、及时注销监听器、善用LeakCanary。
这四点,构成了Android开发的基础地基。地基打牢了,上面盖什么楼(Jetpack Compose、多模块架构等)都稳当。
我知道,刚开始学的时候,这些东西听起来挺抽象。但别怕,动手写。每次遇到崩溃、遇到卡顿,就去查一查、想一想,是不是生命周期没管好,是不是有内存泄漏。慢慢地,你就成了“老手”。
Android开发是一个不断迭代的过程,就像我们人类成长一样。保持好奇心,多动手,多反思。希望这篇文章能帮你少走弯路,在这个领域里走得更远、更稳。
加油,未来的Android专家!
