• Android学习笔记5 - 初学Activity(二)多个、生命周期、启动模式


    Activity的内容比较多,这是剩下的第二部分。

    多个Activity

    一般应用会有多个Activity,下面来进行多个跳转。
    我们还是和上一章一样的方法创建一个新的Activity,我们取名SeconeActivity。如图:
    在这里插入图片描述
    点击Finish。

    显式Intent调用

    我们发现Mainfest里已经存在了SecondActivity,我们直接在FirstActivity里去通过Intent来显示我们的SecondActivity,代码如下:

    binding.button1.setOnClickListener(){
    
                val intent = Intent(this,SecondActivity::class.java)
                startActivity(intent)
                Toast.makeText(this,"点击了按钮!",Toast.LENGTH_SHORT).show()
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后我们修改SeconeActivity.kt
    让它的按钮点击返回First界面。

    class SecondActivity : AppCompatActivity() {
        protected lateinit var binding : ActivitySecondBinding //定义全局的,方便别的函数使用
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            binding = ActivitySecondBinding.inflate(layoutInflater)
            val view = binding.root
            setContentView(view.rootView)
    
            binding.button2.setOnClickListener(){
    
                finish()
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    点击后,我们还是调用finish,上一章讲过了,相当于Back了。

    隐式Intent调用

    我们在项目Manifest中添加intent-filter

    		<activity
                android:name=".SecondActivity"
                android:exported="false">
                <intent-filter>
                    <action android:name="android.intent.action.SEC" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后调用方式改为

    binding.button1.setOnClickListener(){
    
                //val intent = Intent(this,SecondActivity::class.java)
                val intent = Intent("android.intent.action.SEC")
                startActivity(intent)
                Toast.makeText(this,"点击了隐式Intent!",Toast.LENGTH_SHORT).show()
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    多个category

    每个Intent中只能指定一个Action,可以多个category.

    binding.button1.setOnClickListener(){
    
                //val intent = Intent(this,SecondActivity::class.java)
                val intent = Intent("android.intent.action.SEC")
                intent.addCategory("android.intent.category.My")
                startActivity(intent)
                Toast.makeText(this,"点击了隐式Intent!",Toast.LENGTH_SHORT).show()
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们通过addCategory函数添加了android.intent.category.My,相应在manifest里也要添加。

    添加多个的意义是什么呢 ?书中没有明示,可能后面才有详细讲解。

    Intent调用别的应用

    例如你可以需要跳转浏览器,你可以

    binding.button1.setOnClickListener(){
    
                val intent = Intent(Intent.ACTION_VIEW)
                intent.data = Uri.parse("https://csdn.net")
                startActivity(intent)
                Toast.makeText(this,"点击了隐式Intent!",Toast.LENGTH_SHORT).show()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    让Activity能够响应http请求

    上面的例子跳转了浏览器,这里我们自己做一个接受网页的Activity。
    我们创建一个新的ThirdActivity。
    在这里插入图片描述
    manifest里

    		<activity
                android:name=".ThirdActivity"
                android:exported="false"
                tools:ignore="AppLinkUrlError">
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:scheme="https" />
                </intent-filter>
            </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意这里的intent.action.VIEW和android:scheme=“https”

    然后运行后点击button1,应该会弹起浏览器选择界面,里面就有我们的NewActivity应用。

    我的又没成功,为什么拉不起来呢。

    这里虽然可以选择我们的应用,但是没有实质的显示网页的功能。

    电话调用

    我们继续修改firstActivity。

    binding.button1.setOnClickListener(){
    
                val intent = Intent(Intent.ACTION_DIAL)
                intent.data = Uri.parse("tel:10086")
                startActivity(intent)
                Toast.makeText(this,"点击了隐式Intent!",Toast.LENGTH_SHORT).show()
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    点击后会弹起电话拨号,并输入10086。

    传递数据

    假设从First传递数据到Second,可以这样,在First中

    			val intent = Intent(this,SecondActivity::class.java)
                val data = "send data !"
                intent.putExtra("extra_data",data)
    
                startActivity(intent)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在Second中

    		val extraData = intent.getStringExtra("extra_data")
            Log.d("test" , "extraData : $extraData")
    
    • 1
    • 2

    运行后点击按钮,就切换到了SecondActivity,并且log输出了 “send data !”

    返回数据

    在First里

    import androidx.activity.result.contract.ActivityResultContracts
    	private val getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
        {
            if(it.resultCode == Activity.RESULT_OK)
            {
                val value = it.data?.getStringExtra("inputs")
                Log.d("test",value.toString())
            }
        }
    
    	binding.button1.setOnClickListener(){
    			binding.button1.setOnClickListener(){
                val intent = Intent(this,SecondActivity::class.java)
                getResult.launch(intent)
                }
                
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    再Sec里

    			binding.button2.setOnClickListener()
    			{
                	onBackPressed()
            	}
            	
    	override fun onBackPressed() {
            val intent = Intent()
            intent.putExtra("inputs","xxxxxx223")
            setResult(RESULT_OK,intent)
            Log.d("test","button 2 click - onBackPressed")
    
            finish()
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    书上的StartActivityForResult方法过时了。
    在这里插入图片描述
    上面的代码是替换方案,使用registerForActivityResult。
    Sec里返回用的是覆写了返回事件。

    Activity生命周期

    返回栈

    每个Activity都放入栈里,最新的在最上面,每退出一个就会返回下面一个。

    Activity状态

    1,运行状态,位于栈顶部。
    2,暂停状态,不是顶部,但是仍然可见。
    3,停止状态,不是顶部,也不可见。
    4,销毁状态,从栈中移除。

    生存期

    OnCreate , 第一次被创建
    OnStart , 不可见变可见
    OnResume,准备号和用户交互,一定位于栈顶,且是运行状态
    OnPause,准备去启动或者回复一个Activity的时候调用
    OnStop,完全不可见的时候调用。
    OnDestroy,销毁之前调用
    OnRestart,由停止状态变为运行状态前调用。

    被回收了的处理

    假如在Activity A的基础上启动了B,系统内存不足把A回收了,那么返回的时候还是能回到A的,这时候A会执行onCreate方法。要注意你的临时数据是全部都丢失了。因此提供了一个onSaveInstanceState的回调方法来解决问题。

    	override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //setContentView(R.layout.first_layout)
    
            binding = FirstLayoutBinding.inflate(layoutInflater)
            val view = binding.root
            setContentView(view.rootView)
    
            if(savedInstanceState != null)
            {
                val d = savedInstanceState.getString("data_recover")
                Log.d("test","data_recover$d")
            }
            。。。。。
            。。。。。
        }
    	override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
            super.onSaveInstanceState(outState, outPersistentState)
            val tempData = "xxxxxxxxx"
            outState.putString("data_recover",tempData)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    覆写onSaveInstanceState函数保存数据,然后再Oncreate函数里进行恢复。

    启动模式

    启动模式可以通过manifest修改launchMode来修改:

    	<activity
                android:name=".FirstActivity"
                android:launchMode="singleTop"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    下面是4种启动模式特点

    standard

    单个Activity可以启动多次,也会back多次的模式。

    singleTop

    只会启动一个,如果不在栈顶的话,启动的时候还是会进入onCreate,它会创建新的,把老的关闭掉,如果back,它最下面的还是会进入一次。

    singleTask

    如果不在顶部,调用的时候会把其他Activity全部请出栈。

    singleInstance

    会单独一个返回栈,解决共享Activity实例的问题。

    要熟练掌握上面的模式,需要在工作中大量的使用,下面介绍几个常用的实践。

    实践

    当前是哪一个Activity

    当调试别人代码的时候,可能需要只是简单的增加一个组件,但是无法从一堆Activity里找到对应的。
    我们可以创建一个基类BaseActivity,让其他Activity都继承它。

    package com.example.newactivity
    
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    
    open class BaseActivity : AppCompatActivity() {
        override fun onCreate(saveInstanceState: Bundle?)
        {
            super.onCreate(saveInstanceState)
            Log.d("BaseActivity",javaClass.simpleName)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    退出程序

    如果你打开了多层的Activity,一直Back好几次才能退出,如果只是按Home,那么只能返回。
    我们可以制作一个单例类ActivityCollector,通过ArrayList来存储Activity,关闭的时候在基类里的onDestroy函数里通过ActivityCollector.removeActivity(this)移除。在单例类中可以写一个removeAll,遍历List,让他们都finish()。
    最后还可以加上杀进程操作。
    android.os.Process.killProcess(android.os.Process.myPid())

    启动Activity最佳写法

    前面的文章中提到需要通过Intent,调用putExtra传递参数。
    还有一种方法,假如要启动SecondActivity,你可以直接在SecondActivity里添加

    		companion object{
            fun MyStart(context: Context, data1:String, data2:String)
            {
                val intent = Intent(context,SecondActivity::class.java)
                intent.putExtra("parm1",data1)
                intent.putExtra("parm2",data2)
                context.startActivity(intent)
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在启动页面里直接

    SecondActivity.MyStart(this,"mydata1","mydata2")
    
    • 1

    companion是一个新的语法结构,里面的方法都可以使用类似静态方法的方式调用。

    标准函数

    Kotilin标准函数指的是Standard.kt中定义的函数,下面讲解几个常用的。

    with

    和c#的with类似。
    例如

    			val builder = StringBuilder()
                builder.append("xxx1")
                val result = builder.toString()
                println(result)
    
                val b = StringBuilder()
                val result2 = with(b){
                    append("yyyyy1")
                    toString()
                }
                println(result2)
                println("over")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2022-08-03 14:40:56.865 8116-8116/com.example.newactivity I/System.out: xxx1
    2022-08-03 14:40:56.866 8116-8116/com.example.newactivity I/System.out: yyyyy1
    2022-08-03 14:40:56.866 8116-8116/com.example.newactivity I/System.out: over

    run

    run函数和with很类似,必须是一个对象调用,其他没区别。

    			val c = StringBuilder()
                val result3 = c.run{
                    append("cccc1")
                    toString()
                }
                println(result3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    apply

    apply函数和run类似,不过它只能改变对象自己的数据,像上面的toString是不能有的(书上是这么说的),但是我测试下来和run没有什么区别,为什么呢?

    			val d = StringBuilder()
                val result4 = d.apply{
                    append("ddd1")
                    toString()
                }
                println(result4)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    调试发现apply返回的只是对象本身,并不是字符串。所以那个toString应该没有任何意义,但是又不会报错。

    静态方法

    和C#不一样,在函数上加上static关键字就可以了。Kotilin有其他的方法,那就是单例。

    object单例

    package com.example.newactivity
    
    object Comm {
        fun comm1()
        {
            println("fun - comm1")
        }
    }
    
    Comm.comm1()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    com.example.newactivity I/System.out: fun - comm1

    是不是挺简单,但是这样会导致Comm这个单例类里面的所有方法都可以直接使用。

    我们可以继续使用上面的伴生类(companion object)。把类的object改为class,如下:

    伴生类

    package com.example.newactivity
    
    class Comm {
        fun comm1()
        {
            println("fun - comm1")
        }
    
        companion object
        {
            fun comm2()
            {
                println("fun - comm2")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    我们发现comm1已经无法调用了。

    上面的方法都是模仿了静态的方法,他们并不是真正意义的静态方法,下面介绍真正的。

    注解静态方法

    package com.example.newactivity
    
    class Comm {
        fun comm1()
        {
            println("fun - comm1")
        }
    
        companion object
        {
            @JvmStatic
            fun comm2()
            {
                println("fun - comm2")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    只要加上@JvmStatic注解就可以了。它只能加在单例或者伴生方法中。那么现在不管是Kotilin还是Java都可以使用Comm.comm2()来调用了。

    顶层方法

    还有一种,就是在kt文件中的直接书写的函数名,在整个项目中都是静态的,可以直接调用。

    package com.example.newactivity
    
    fun comm0()
    {
        println("fun - comm0")
    }
    
    class Comm {
        fun comm1()
        {
            println("fun - comm1")
        }
    
        companion object
        {
            @JvmStatic
            fun comm2()
            {
                println("fun - comm2")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    调用:

    comm0()
    Comm.comm2()
    
    • 1
    • 2

    输出:
    I/System.out: fun - comm0
    I/System.out: fun - comm2
    但是这种Java代码中是找不到这个方法的。这个类是Comm.kt,其实被编译成了Commkt.class的Java类。
    在这里插入图片描述
    我Java中我们可以使用CommKt.comm0()的写法来调用。

    最后Activity的基础就到这里了。文中有几处红色字体标注的不太明白,后面有机会再回头看看。

    参考
    《第一行代码》Android第三版
    Kotilin Function

  • 相关阅读:
    NK-RTU980烧写裸机程序
    c++编程实例
    史上最强HashMap源码深度解析(3w字图文并茂)
    Zabbix 5.0 使用自带Redis模版监控
    【JS笔记】JS中的BOM对象及其常见的BOM相关操作
    [护网杯 2018]easy_tornado 解析
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第12章 Vue3.X新特性解析 12.1 Vue3.0 新特性
    5+非肿瘤+WGCNA+PPI+实验,简单WGCNA分析思路,干湿结合
    facebook审核流程
    计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序
  • 原文地址:https://blog.csdn.net/thinbug/article/details/125842912