Activity全解(一)

本文详细介绍了Android中Activity的生命周期,包括典型情况下的生命周期流程,如onCreate、onStart、onResume等方法的作用;以及异常情况下的生命周期管理,例如资源相关的系统配置变化导致Activity重建的过程,和内存不足时Activity如何被系统回收。

Activity是android的四大组件之一,它的使用频率是最高的,因此想要了解android开发,那么必须要了解Activity。

Activity的生命周期全面解析

一、典型情况下的生命周期

正常情况下,Activity会经历如下声明周期。
1. onCreate():Activity的第一个生命周期,主要是通过setContentView()加载布局。
2. onStart():表示Activity已经被启动了,但是还没有到前台,无法与用户进行交互。
3. onResume():表示Activity已经到了前台,开始活动了,可以与用户交互
4. onPause():表示Activity正在停止。此时可以做一些保存数据、停止动画等操作,但是不能太耗时。
5. onStop():表示Activity即将停止,可以做一些稍微重量级的回收工作,但是不能太耗时
6. onDestroy():Activity的最后一个生命周期,主要做一些回收和释放工作。
7. onRestart():这是一个特殊的生命周期,当用户按下home键或者被覆盖之后,重新将Activity显示出来的时候,执行onRestart()->onStart()->onResume()

这里写图片描述

二、异常情况下的生命周期

异常情况下是指Activity被系统回收或者是当前设备的Configuration发生改变从而导致Activity被销毁重建。

情况一:资源相关的系统配置发生改变造成的Activity的销毁与重建

这里写图片描述
图2 异常 情况下的Activity的重建过程
当系统配置发生改变后,Activity会被销毁,onPause、onStop、onDestroy都会被调用,同时会调用onSaveInstanceState来保存当前Activity状态,这个方法执行在onStop()之前,它和onPause()没有既定的时序关系。当Activity被重现的时候调用onRestoreInstanceState()恢复原先状态。它执行在onStart()之后。
例如:
布局:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="cn.centran.zx_mylock.MainActivity">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/et"/>
</RelativeLayout>

MainActivity:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = (EditText) findViewById(R.id.et);

    }

在Editext中输入文字后,切换到横屏,那么,Activity重建之后,输入的文字也恢复了,大家可以试试。
原因:
查看Editext源码中的onSaveInstanceState()方法:

@Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

        // Save state if we are forced to
        final boolean freezesText = getFreezesText();
        boolean hasSelection = false;
        int start = -1;
        int end = -1;

        if (mText != null) {
            start = getSelectionStart();
            end = getSelectionEnd();
            if (start >= 0 || end >= 0) {
                // Or save state if there is a selection
                hasSelection = true;
            }
        }

        if (freezesText || hasSelection) {
            SavedState ss = new SavedState(superState);

            if (freezesText) {
                if (mText instanceof Spanned) {
                    final Spannable sp = new SpannableStringBuilder(mText);

                    if (mEditor != null) {
                        removeMisspelledSpans(sp);
                        sp.removeSpan(mEditor.mSuggestionRangeSpan);
                    }

                    ss.text = sp;
                } else {
                    ss.text = mText.toString();
                }
            }

            if (hasSelection) {
                // XXX Should also save the current scroll position!
                ss.selStart = start;
                ss.selEnd = end;
            }

            if (isFocused() && start >= 0 && end >= 0) {
                ss.frozenWithFocus = true;
            }

            ss.error = getError();

            if (mEditor != null) {
                ss.editorState = mEditor.saveInstanceState();
            }
            return ss;
        }

        return superState;
    }

onRestoreInstanceState():

@Override
    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());

        // XXX restore buffer type too, as well as lots of other stuff
        if (ss.text != null) {
            setText(ss.text);
        }

        if (ss.selStart >= 0 && ss.selEnd >= 0) {
            if (mText instanceof Spannable) {
                int len = mText.length();

                if (ss.selStart > len || ss.selEnd > len) {
                    String restored = "";

                    if (ss.text != null) {
                        restored = "(restored) ";
                    }

                    Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
                          "/" + ss.selEnd + " out of range for " + restored +
                          "text " + mText);
                } else {
                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);

                    if (ss.frozenWithFocus) {
                        createEditorIfNeeded();
                        mEditor.mFrozenWithFocus = true;
                    }
                }
            }
        }

        if (ss.error != null) {
            final CharSequence error = ss.error;
            // Display the error later, after the first layout pass
            post(new Runnable() {
                public void run() {
                    if (mEditor == null || !mEditor.mErrorWasChanged) {
                        setError(error);
                    }
                }
            });
        }

        if (ss.editorState != null) {
            createEditorIfNeeded();
            mEditor.restoreInstanceState(ss.editorState);
        }
    }

不需要细看,大致知道当输入数据之后,切换屏幕,那么因为系统配置发生变化,导致该Activity被杀死,但是在onStop()方法之前调用了Editext的onSaveInstanceState()方法,保存了数据,之后重建的时候有调用onRestoreInstanceState()恢复了。

关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外结束的时候,Activity会嗲用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着window在委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说,它可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。这是一种典型的委托思想。

情况二:内存不足,造成低优先级的Activity被杀死

Activity按照优先级从高到低,可以分为如下三种:

  1. 前台Activity——正在和用户交互的Activity,它的优先级最高
  2. 可见但非前台Activity——比如Activity中弹出一个对话框,或者打开一个透明的Activity,导致Activity可见,但是位于后台无法和用户直接交互。
  3. 后台Activity——已经被暂停的Activity,比如执行了onStop(),优先级最低。

    当系统内存不足的时候,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstance和onRestoreInstanceState来存储和恢复数据。

    如果一个进程中没有四大组件在执行,那么这个进程将会很快被系统杀死,因此后台工作不适合脱离四大组件单独存在,比较好的方法就是将这些工作放在service中从而保证进程具有一定的优先级,这样不容易被杀死。

如果当系统配置发生改变后,Activity不想被销毁重建,那么应该就在资源配置文件中给Activity制定configChanges属性。

android:configChanges="orientation"

如果想要指定多个值,可以用“|”链接起来,比如:

android:configChanges="orientation|keyboardHidden"

当Activity如果没有被销毁重建,就不会执行onSaveInstanceState和onRestoreInstanceState方法,但是会执行onConfigurationChanged方法。

configChanges的属性含义:
locale:设备的位置发生改变,一般指切换系统语言。
orientation:屏幕方向发生改变,这个是最常用的,比如旋转手机屏幕
keyboardHidden:键盘的可访问性发生了改变,比如用户调出了键盘
screenSize:当屏幕的尺寸信息发生了改变,当旋转设备屏幕时,屏幕尺寸会发生改变,这个选项比较特殊,他和编译选项有关,当编译选项中的minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则就会。
smallestScreenSize:与screenSize类似,只是当minSdkVersion和targetSdkVersion有小于13时,Activity会重启,否则不会。

<think>嗯,用户想了解Android DataBinding的详细用法和实战技巧,包括教程、示例代码和最佳实践。首先,我需要回忆下DataBinding的核心概念。记得DataBinding可以帮助将UI组件直接绑定到数据源,减少模板代码,比如findViewById和手动更新UI。 用户可能需要从基础开始,比如如何启用DataBinding,配置Gradle文件。然后,布局文件的结构变化,使用<layout>标签包裹,定义数据变量。接下来是绑定类的生成,如何在Activity或Fragment中初始化绑定对象,替代setContentView。 然后,可能需要讲解单向绑定的例子,比如在XML中使用@{}语法绑定数据到TextView。还要提到响应式更新,使用ObservableField或者BaseObservable类,这样数据变化时UI自动刷新。这里可能需要引用现有的资料,比如Android官方文档中的最佳实践[^1]。 用户可能对事件处理感兴趣,比如如何绑定点击事件,可以使用方法引用或监听器绑定。这里可能需要示例代码,比如在XML中设置android:onClick="@{viewModel::onButtonClick}",或者在ViewModel中定义方法。这部分可能有官方示例或者站内引用提到的示例应用[^1]。 另外,用户提到实战技巧,可能涉及性能优化。比如,使用BindingAdapter自定义属性,处理复杂逻辑,或者结合LiveData和ViewModel实现更清晰的架构。这里需要注意DataBinding和LiveData的结合,需要在布局中设置lifecycleOwner,这样数据变化才会自动更新UI。可能引用到改善性能的版本,比如使用DataBinding减少findViewById的开销[^2]。 最佳实践部分,可能需要强调避免在XML中编写复杂逻辑,保持简洁,将业务逻辑移到ViewModel中。还有如何正确处理内存泄漏,比如在Fragment中清除绑定引用。可能引用到示例代码中的处理方式,比如在onDestroyView中置空binding对象[^3]。 用户可能还遇到常见问题,比如绑定表达式错误、编译问题,或者性能问题。需要提供排查方法,比如检查日志、确保变量正确声明、使用正确的作用域。可能参考官方文档中的 troubleshooting 部分。 最后,生成相关问题时,要考虑用户可能进步的需求,比如性能优化、与Compose的比较、高级功能如双向绑定,或者与RecyclerView的集成。这些问题可以帮助用户深入理解DataBinding的不同方面。</think>### Android DataBinding 实战详解 #### 、基础配置与数据绑定 1. **启用DataBinding** 在模块级`build.gradle`中添加配置: ```groovy android { buildFeatures { dataBinding true } } ``` 2. **布局文件改造** 使用`<layout>`标签包裹根布局,并定义数据变量: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <TextView android:text="@{user.name}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </layout> ``` 3. **绑定类初始化** ```kotlin val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.user = User("张三") ``` #### 二、响应式数据更新 1. **Observable字段** ```kotlin class User { val name = ObservableField<String>("初始值") } // 更新数据 user.name.set("李四") ``` 2. **BaseObservable对象** ```kotlin class User : BaseObservable() { @get:Bindable var name: String = "" set(value) { field = value notifyPropertyChanged(BR.name) } } ``` #### 三、事件绑定实践 1. **方法引用式绑定** ```xml <Button android:onClick="@{viewModel::onButtonClick}" .../> ``` 2. **监听器绑定(带参数)** ```xml <Button android:onClick="@{() -> viewModel.onButtonClick(user.id)}" .../> ``` #### 四、高级技巧与最佳实践 1. **BindingAdapter自定义属性** ```kotlin @BindingAdapter("imageUrl") fun loadImage(view: ImageView, url: String?) { Glide.with(view.context).load(url).into(view) } ``` XML使用: ```xml <ImageView app:imageUrl="@{user.avatarUrl}"/> ``` 2. **结合LiveData使用** ```kotlin binding.lifecycleOwner = this // 在Fragment/Activity中设置 binding.viewModel = viewModel ``` 布局中直接绑定LiveData: ```xml <TextView android:text="@{viewModel.liveDataText}"/> ``` 3. **RecyclerView中的使用** ```kotlin class UserAdapter(val items: List<User>) : RecyclerView.Adapter<UserAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemUserBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding) } } ``` #### 五、性能优化建议 1. 避免在绑定表达式中进行复杂计算 2. 使用`@={}`实现双向绑定时注意内存泄漏 3. 在Fragment的`onDestroyView`中及时清除绑定: ```kotlin override fun onDestroyView() { super.onDestroyView() binding = null } ``` #### 六、调试技巧 1. 开启编译日志检查绑定错误: ```groovy android { dataBinding { addDefaultAdapters = true enabled = true } } ``` 2. 查看生成的绑定类代码(位于`app/build/generated`目录) : Android 官方文档中详细说明了 DataBinding 的最佳实践和架构整合方案 [^2]: 使用 DataBinding 可减少 30% 的视图操作代码量,有效提升界面渲染性能 [^3]: 事件绑定语法与传统 View.OnClickListener 实现方式存在兼容性差异
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值