门——Activity(一)
Activity是什么:
Activity作为Andorid的四大组件之一,为用户提供了一个界面,即我们能看到的界面。相当于一张画画用的纸,我们可以在上面画任何我们想要的内容。
Activity的创建:
就像我们画画需要拿一张画纸铺在画板上一样,想要看到界面我们也需要创建一个Activity。创建的方法很简单,分为2步:
-
继承Activity类
-
在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相关的其他内容。
欢迎大家指出错误的地方,我们共同学习。