Android从入门到放弃—— 一、Activity详解1

门——Activity(一)

Activity是什么:

Activity作为Andorid的四大组件之一,为用户提供了一个界面,即我们能看到的界面。相当于一张画画用的纸,我们可以在上面画任何我们想要的内容。

Activity的创建:

就像我们画画需要拿一张画纸铺在画板上一样,想要看到界面我们也需要创建一个Activity。创建的方法很简单,分为2步:

  1. 继承Activity类

  2. 在AndroidManifest.xml文件中配置

这里是使用Android Studio创建项目默认创建的MainActivity代码

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidteach">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

代码分析:

我们上面说第一步是要新建一个类继承自Activity,但是我们的MainActivity是继承的AppCompatActivity类。大家看名字就能知道这个是Activity的子类。当然你也可以手动修改为Activity类。之后的setContentView(R.layout.activity_main)是为当前界面设置一个布局文件,布局文件中就可以放下我们需要的各种控件,这里activity_main布局文件中有一个TextView,显示的文本内容是Hello Word。

我们再看AndroidManifest的内容:

在AndroidManifest中我们只需要关注activity标签中的内容。android:name属性指定当前配置的activity名称,这里配置的是” .MainActivity”,注意,前面有一个点,是相对路径,即当java代码包的根路径下的MainActivity文件。里面的intent-filter内容:<action android:name="android.intent.action.MAIN" />表示启动当前app的默认界面。<category android:name="android.intent.category.LAUNCHER" /> 表示当前的activity会在桌面(launcher)显示一个图标。

运行程序可以在手机或者模拟器看到当前界面显示hello word字样。

启动Activity:

首先我们需要自己创建一个Activity,操作如下图:

点击Finish完成即可,这样就创建好了一个Activity,同时AS也帮我们创建好了一个布局文件activity_second。

进入布局文件,我们同样的添加一个TextView:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SecondActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SecondActivity"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

这里完成了第二个Activity的创建。回到activity_main.xml布局中,给TextView添加一个Id,同时改变显示的文字。

记住必须要在Manifest文件中配置:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SecondActivity"></activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

这里添加了一个activity标签,android:name属性值为".SecondActivity",同样的前面有一个点,因为我们的SecondActivity也是在当前项目的根目录,所以这里的路径就直接写。如果还有子包,则这里的路径需要加上。

在MainActivity的代码中添加如下代码:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        acMainTvJump.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
    }

运行程序,然后点击MainActivity文本,界面就会跳转到我们刚刚建立的SecondActivity中。

代码分析:

创建SecondActivity的代码比较简单就不做介绍了,主要的代码就在MainActivity中添加的4行代码。

acMainTvJump为textView的ID,这里使用了kotlin语言,kotlin相关的内容这里就不做介绍了。通过设置textview的点击事件,我们在点击时进行activity的跳转。其中关键的一个类:Intent。Intent可以理解为我们想要完成某一件事情的意图,这里我们的意图是跳转到SecondActivity,其构造方法接受2个参数(Intent的构造方法有很多):

可以看到这里第一个参数为packageContext,这里可以直接传入this。第二参数为class,即我们需要跳转的activity。

Activity的生死历程:

要了解Activity的生命周期,这里就不得不祭出官方的祖传图了:

Activity 类提供六个核心回调:onCreate()、onStart()、onResume()、onPause()、onStop() 和 onDestroy()

onCreate:这个方法是我们必须要重写的,activity刚刚创建的是系统会调用这个方法,我们通过setContentView来为Activity设置布局,可以是布局ID,也可以是直接用代码写的一个View。这个时候Activity还是不可见的。

onStart:这个时候Activity已经可见了,相当于我们的界面经过onCreate已经准备好了。但是Activity并不是一直处于这个状态,系统调用完这个方法后会进入下一阶段。

onResume:这个状态Google定义为“已恢复”状态,这个时候Activity进入前台,将长期处于当前状态,并且能和用户进行交互。

onPause:当发生中断事件时,Activity 进入已暂停状态,系统调用 onPause() 回调。这里的中端事件有点模糊,待会我们可以用代码来模拟。如果 Activity 从onPause状态返回“已恢复”状态,系统将再次调用 onResume() 方法。此方法表示 Activity 不再位于前台(尽管如果用户处于多窗口模式,Activity 仍然可见)

onStop:这个时候Activity已经不可见,比如我们新启动了一个Activity覆盖了当前的Activity。

onDestroy:调用此方法时Activity被销毁。如用户按下返回键、代码中调用finish。

通过图片我们看到还有一个方法:onRestart,这个回调发生在重新进入Activity的时候。下面我们通过代码来实际看看各个回调的调用顺序。

在MainActivity和SecondActivity中都重写这几个回调方法并且打印log

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("MainActivity", "ZLog onCreate :")
        setContentView(R.layout.activity_main)
        acMainTvJump.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
    }

    override fun onStart() {
        super.onStart()
        Log.d("MainActivity", "ZLog onStart :")
    }

    override fun onResume() {
        super.onResume()
        Log.d("MainActivity", "ZLog onResume :")
    }

    override fun onRestart() {
        super.onRestart()
        Log.d("MainActivity", "ZLog onRestart :")
    }

    override fun onPause() {
        super.onPause()
        Log.d("MainActivity", "ZLog onPause :")
    }

    override fun onStop() {
        super.onStop()
        Log.d("MainActivity", "ZLog onStop :")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("MainActivity", "ZLog onDestroy :")
    }
}

SecondActivity类似,就不贴代码了。同样我们运行程序查看log

看到如图的log,我们启动MainActivity后对应的3个回调被调用,最后停留在onResume这里。这个时候我们点击文本跳转到SecondActivity

我们看到首先被调用的是MainActivity的onPause,为什么?因为这个时候系统是先将当前的Activity设置为暂停状态,放到后台,而后开始准备要显示在前台的SecondActivity,于是SecondActivity开始创建和显示到前台。这个时候MainActivity完全不可见,设置为stop状态。

这个时候我们再点击返回按钮

点击返回按钮发生的事件流程:SecondActivity首先退出前台进入后台(调用onPause),MainActivity准备再次显示进入前台,所以调用了onRestart-onstart-onResume进入前台可见。这个时候SecondActivity不可见(onStop),并且进行销毁(onDestroy)。

Activity启动模式

之前说Activity就像画画的画布,这里的画布我们理解为透明的,我们是可以一层一层的叠加的,类似Photoshop的图层。这个时候添加就有讲究了。Android提供了4种模式:standard、singleTop、singleTask、singleInstance。4种模式的设置同样在Manifest种activity标签配置,android:launchMode="standard"。

standard:这个是默认的模式,也是最简单的模式。即每次启动任何activity都直接在之前的activity上添加。

我们正常启动A,在A种启动B,再在B中启动A。按照图中所示,这个时候栈中应该有3个Activity,我们需要按下3次返回键才能推出APP。上代码看看:

我们在SecondActivity中添加了点击事件,启动MainActivity。注意,2个Activity的onCreate方法中打印了当前的对象的地址

通过打印信息我们可以发现MainActivity出现2次,并且不是同一个实例。我们按下返回键查看,需要按下3次才能退出:

singleTop:这种模式主要是为了应对另一种情况,如果刚刚在SecondActivity中再次启动SecondActivity会怎么样?如果是standard则直接新加一个。但如果我们把SecondActivity的模式设置为singleTop呢?

再来查看log信息

可以看到我们点击事件触发后并没有新的SecondActivity被创建。

当我们设置Activity为singleTop时,会启动一个栈顶复用机制。即如果Activity栈顶的Activity跟我们要启动的Activity是同一个,系统并不会重新创建新的,而是直接使用当前的Activity。可以理解为不做任何操作。

singleTask:有没有感觉singleTop模式并不是那么的完美?只能实现栈顶的复用,那要是返回栈中有,但是不是在栈顶的想要复用呢?对,那就设置singleTask模式吧。为什么验证,我们代码需要再改改。

首先我们把MainActivity设置为singleTask,目的就是重复的启动MainActivity。

在SecondActivity中的点击事件我们就启动MainActivity。即 A——B——A这样的顺序,下面我们看log

可以看到SecondActivity中点击事件触发之后MainActivity并没有重新初始化。那是怎么复用的呢?

我们按下返回键看看

what? 为什么只按了一下返回就退出了?因为它很聪明,直接把MainActivity之上的所有Activity都移除了,直接让自己处于栈顶。

来看一下完整的log

重点是在SecondActivity中点击之后,SecondActivity被销毁了。

singleInstance:这个模式是最复杂的一种。之前的3种模式我们所说的返回栈都是同一个返回栈,而这个会启用新的返回栈。

这里我们先把文件改一下

BActivity设置为singleInstance,A、C为默认,按照A——B——C的顺序启动。

我们在onCreate中打印当前taskId

通过log我们可以看到BActivity的taskId跟其他2个不同,说明B是处在一个新的返回栈中。我们在B中启动了C,可能有的朋友会想,C应该跟B在同一个栈中才对。事实是C还是和A在同一个栈中。这里要特别注意,因为singleInstance是独占栈的模式,这个栈里面只能有B。

这个时候我们再来按下返回键看看

我们看到在C界面按下返回,显示的MainActivity(A),刚刚我们说了 C和A是在一个返回栈,所以C返回之后A就显示出来了,A再返回,当前返回栈就全部退出了,这个时候才会再显示B所在的返回栈,因为B里面只有它一个,所以显示了B,再返回所有的就都返回了,退出APP。

好了,先暂时写到这里,后续还将继续介绍Activity相关的其他内容。

欢迎大家指出错误的地方,我们共同学习。

猜你喜欢

转载自blog.csdn.net/zgy441008825/article/details/105961701