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

本文详细介绍了Android中Activity的多个方面,包括如何创建和跳转多个Activity,显式和隐式Intent的使用,Activity的生命周期,启动模式的应用,以及数据的传递和返回。此外,还探讨了静态方法和标准函数在Kotlin中的实现。通过实例代码,展示了如何在不同场景下使用这些概念和技术。
摘要由CSDN通过智能技术生成

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()

        }

然后我们修改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()
        }
    }
}

点击后,我们还是调用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>

然后调用方式改为

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()

        }

多个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()

        }

我们通过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()
        }

让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>

注意这里的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()

        }

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

传递数据

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

			val intent = Intent(this,SecondActivity::class.java)
            val data = "send data !"
            intent.putExtra("extra_data",data)

            startActivity(intent)

在Second中

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

运行后点击按钮,就切换到了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)
            }
            

再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()
    }

书上的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)
    }

覆写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>

下面是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)
    }
}

退出程序

如果你打开了多层的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)
        }
    }

在启动页面里直接

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

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")

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)

apply

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

			val d = StringBuilder()
            val result4 = d.apply{
                append("ddd1")
                toString()
            }
            println(result4)

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

静态方法

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

object单例

package com.example.newactivity

object Comm {
    fun comm1()
    {
        println("fun - comm1")
    }
}

Comm.comm1()

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")
        }
    }
}

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

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

注解静态方法

package com.example.newactivity

class Comm {
    fun comm1()
    {
        println("fun - comm1")
    }

    companion object
    {
        @JvmStatic
        fun comm2()
        {
            println("fun - comm2")
        }
    }
}

只要加上@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")
        }
    }
}

调用:

comm0()
Comm.comm2()

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值