Activity详解

2020-09-22  本文已影响0人  code希必地

1、Intent

Intent是各个组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,而且还能在各组件之间传递数据。Intent一般可用于启动Activity、启动Service、发送广播等场景。
Intent大致可分为2中:

1.1、显示Intent打开Activity

 fun openActivity(){
        val intent = Intent(this, KotlinFirstActivity::class.java)
        intent.putExtra("param", "testParams")
        startActivity(intent)
    }

注意:KotlinFirstActivity::class.java就相当于Java中的KotlinFirstActivity.class

1.2、隐式Intent打开程序内的Activity

相比于显示Intent,隐式Intent并不指明启动那个Activity而是指定了一系列的action和category,然后交由系统去分析找到合适的Activity并打开。
什么是合适的Activity,其实就是和隐式Intent中指定的action和category完全匹配的Activity,而action和category我们可以在AdnroidManifest中指定。

<activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.abu.alertdialogdemo.ACTION_START" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

在标签<intent-filter>中配置了action和category,只有和隐式Intent中的action和category完全匹配才能正常的打开该页面。

  val intent = Intent("com.example.abu.alertdialogdemo.ACTION_START")
  startActivity(intent)

不是说action和category要完全匹配才能打开页面吗?这是因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()时会自动将这个category添加到Intent中。所以在Manifest中一定不要忘记配置这个默认的category:android.intent.category.DEFAULT,否则会报错。
还有一点需要注意:Intent中只能添加一个action,但是可以添加多个category。

1.3、隐式Intent打开程序外的Activity

比如我们要打开系统的浏览器

fun openWeb(view: View) {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.data = Uri.parse("https://www.baidu.com")
        startActivity(intent)
    }

Intent.ACTION_VIEW是系统内置的动作,然后将https://www.baidu.com通过Uri.parse()转换成Uri对象,传递给intent.setData(Uri uri)函数。Kotlin中intent.data=Uri.parse("https://www.baidu.com")就相当于Java中的intent.setData(Uri.parse("https://www.baidu.com")),这是Kotlin中的语法糖。
与此对应在标签<intent-filter>中配置一个<data>标签,用于更精确的指定当前Activity能够相应的数据。<data>标签中主要可以配置一下内容:

      <activity android:name=".ThirdActivity">
            <intent-filter tools:ignore="AppLinkUrlError">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="https"/>
            </intent-filter>
        </activity>

我们在ThirdActivity的<intent-filter>中配置当前Activity能够响应的action是Intent.ACTION_VIEW的常量值,而category指定了默认的category值,另外在标签data中我们通过android:scheme="https"指定了数据的协议必须是https。另外由于AndroidStudio认为能够响应ACTION_VIEW的Activity都应该加上BROWSABLE的category,否则会报出警告。加上BROWSABLE的category是为了实现deep link 功能和目前学习无关,所以我们在intent-filter标签上添加tools:ignore="AppLinkUrlError"忽略警告。
然后我们就能通过隐式Intent的方法打开ThirdActivity了,代码如下:

 val intent= Intent(Intent.ACTION_VIEW)
        intent.data=Uri.parse("https:")
        startActivity(intent)

除了指定android:schemehttps我们也能随意指定它的值,只需要保证AndroidManifest中设置的值和Intent中设置的data相对应即可。

2、Activity的生命周期

Activity类中定义了7个回调方法,覆盖了Activity声明周期的每一个环节。

3、体验Activity的生命周期

下面我们通过实例更直观的看下Activity的生命周期过程。

FirstActivity  onCreate
FirstActivity  onStart
FirstActivity  onResume
FirstActivity onPause
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
FirstActivity onStop

可以看到在打开SecondActivity时FirstActivity的onPause会执行,所以在onPause中是不能做耗时操作的,否则会影响SecondActivity的打开。

SecondActivity onPause
FirstActivity onRestart
FirstActivity onStart
FirstActivity onResume
SecondActivity onStop
SecondActivity onDestroy

可以看到在返回FirstActivity时会调用SecondActivity的onPause,如果SecondActivity的onPause中做了耗时操作的话,那么也会影响Activity的返回。而且当FirstActivity再次回到栈顶时会调用其onRestart,此时并不会执行onCreate因为FirstActivity并没有销毁。

4、Activity被回收了时的生命周期

现在描述一种场景:打开ActivityA,然后在ActivityA的页面中打开ActivityB,此时ActivityA不在栈顶了如果内存不足可能会被回收,此时从ActivityB再回到ActivityA,下面描述下整个过程的生命周期。

ActivityA  onCreate
ActivityA  onStart
ActivityA  onResume
ActivityA  onPause
ActivityB  onCreate
ActivityB  onStart
ActivityB  onResume
ActivityA  onStop
ActivityA  onDestory
ActivityB  onPause
ActivityA  onCreate
ActivityA  onStart
ActivityA  onResume
ActivityB  onStop

从上面可以看出在ActivityA被回收后再次回到ActivityA时不再调用ActivityA的onRestart了,而是调用了ActivityA的onCreate,因为ActivityA已经被销毁了。

5、Activity被销毁,其中的临时数据怎么办

ActivityA被销毁了,这也就说明其中的临时数据也会丢失了,比如ActivityA中有一个EditText,我们在其中输入了一段文字,然后启动ActivityB,此时由于内存紧张处于停止状态的ActivityA被回收了,返回到ActivityA你会发现EditText中输入的文字不见了,因为ActivityA重新创建了。
为此Activity提供了onSaveInstanceState方法,该方法能确保在被回收之前被调用,我们就能在onSaveInstanceState方法中进行临时数据的保存,然后我们可以在onCreate(savedInstanceState: Bundle?)中利用savedInstanceState进行数据的恢复。Activity被回收重新创建时onCreate(savedInstanceState: Bundle?)中的savedInstanceState不再为null否则为null,其中带有在savedInstanceState中保存的所有数据。
具体代码如下:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.e("tag", "$tag onCreate")
        //恢复数据
        val tempData = savedInstanceState?.getString("data") ?: ""
        et.setText(tempData)
    }

    //保存数据
    override fun onSaveInstanceState(outState: Bundle?) {
        super.onSaveInstanceState(outState)
        Log.e("tag", "$tag onSaveInstanceState")
        val tempData = et.text.toString().trim()
        outState?.putString("data", tempData)
    }

6、onSaveInstanceState()调用时机

android:screenOrieritation="portrait"

禁止屏幕旋转时Activity的重建

android:configChanges="orientation丨keyboardHidden丨screenSize"

7、Activity的启动模式

启动模式分为4种:

7.1、standard模式

如果不显示指定启动模式,那么Activity的启动模式就是standard,在该模式下不管Activity栈中有无该Activity,均会创建一个新的Activity并入栈,并处于栈顶的位置。

7.2、singleTop模式

ActivityA onPause
ActivityA onNewIntent
ActivityA onResume

可以看到调用了onNewIntent(intent: Intent?),我们可以在onNewIntent(intent: Intent?)中通过intent来获取新传递过来的数据,因为此时数据可能已经发生了变化。

7.3、singleTask模式

如果准备启动的ActivityA的启动模式为singleTask的话,那么会先从栈中查找是否存在ActivityA的实例:

ActivityB onPause
ActivityA onRestart
ActivityA onStart
ActivityA onNewIntent
ActivityA onResume
ActivityB onStop
ActivityB onDestroy

此时ActivityA不在栈顶,ActivityA之上有ActivityB,所以在启动ActivityA时ActivityA之上的ActivityB会出栈,ActivityA将置于栈顶,所以ActivityA的onRestart和ActivityB的onDestory会执行。
场景二:ActivityA启动ActivityA,此时生命周期过程:

ActivityA onPause
ActivityA onNewIntent
ActivityA onResume

此时ActivityA位于栈顶,此时singleTask和singleTop作用一样,都是直接使用ActivityA,并且会调用ActivityA的onPause、onNewIntent、onResume
场景三:ActivityA启动ActivityB,此时生命周期过程:

ActivityA onCreate
ActivityA onStart
ActivityA onResume

7.4、singleInstance模式

不同于另外3个模式,指定singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈),这么做的意义是什么?
想象一个场景:如果我们程序内的一个Activity是允许其他程序访问的,如果想实现其他程序和我们程序能共享这个Activity实例,该怎么实现呢?使用前面3中模式是无法做到的,因为每个应用程序都有自己的返回栈,同一个Activity在不同返回栈中肯定都创建了新的实例,而使用singleInstance就可以解决这个问题。在这种模式下,会有一个单独的返回栈来管理这个Activity,无论哪个应用程序来访问这个Activity,都在同一个返回栈中,也就解决了共享Activity实例的问题。
为了更好的理解下面我们实战一下:

 <activity
            android:name=".ActivityB"
            android:launchMode="singleInstance" />
 ##ActivityA
 val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.e("TAG", "$tag taskId=$taskId")
    }
    
    ##ActivityB
     val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(activity_second)
        Log.e("TAG", "$tag taskId=$taskId")
    }
    
    ##ActivityC
     val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(activity_c)
        Log.e("TAG", "$tag taskId=$taskId")
    }
ActivityA taskId=4163
ActivityB taskId=4164
ActivityC taskId=4163

可以看到ActivityB的taskId是不同于ActivityA 和ActivityC的,这也说明了ActivityB 是在一个单独的栈中的,并且返回栈中只有这一个Activity。

上一篇 下一篇

猜你喜欢

热点阅读