温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 3 探究Activity

第三章:先从看的到的入手,探究Activity

目录

  • 第三章:先从看的到的入手,探究Activity
    • Activity是什么?
    • Activity的用法
      • 配置主Activity(APP运行后首先启动的Activity)
      • Toast、findViewById方法和Menu
        • Toast
        • findViewById方法
        • Menu
      • Intent
        • 显式Intent
        • 隐式Intent
        • deep link功能
        • 向下一个Activity传递数据
        • 返回数据给上一个Activity
    • Activity的基础知识
      • Activity的生命周期
        • 返回栈
        • Activity状态
        • Activity生命周期
          • 从Activity A跳转到Activity B(均不是对话框Activity),请简述A B所经历的状态:
          • 又在Activity B按下返回键,请简述A B所经历的状态:
          • 从Activity A跳转到Activity B(B是对话框Activity),请简述A B所经历的状态:
          • 又在Activity B按下返回键,请简述A B所经历的状态:
          • Activity被回收了怎么办?
          • Activity被回收了,怎么保存/恢复数据?
      • Activity启动模式
        • standard
        • singleTop
        • singleTask
        • singleInstance
    • Activity实用技巧
      • 知晓当前是在个Activity
      • 随时随地退出程序
      • 启动Activity的最佳写法
    • Kotlin课堂

Activity是什么?

Activity是可以包含用户界面的组件,主要用于和用户进行交互。一个APP中可以包含0个或多个Activity。

Activity的用法

  • 每个Activity都要重写onCreate方法,用setContentView方法绑定布局。
    项目中添加的任何资源都会在R文件中生成一个相应的资源id。
  • 如果想销毁一个Activity,调用finish方法
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.first_layout)
	}

配置主Activity(APP运行后首先启动的Activity)

在AndroidManifest.xml中找到要设置的活动,在< activity >内部加入标签,设置好action和category。
android:label可指定Activity的标题栏。

<activity
    android:name=".FirstActivity"
    android:label="This is FirstActivity">
    <intent-filter>
      	 <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

如果一个APP没有主Activity,这个APP仍然可以正常安装,只是无法在启动器中看到或者打开这个程序。这种程序一般是作为第三方服务供其他应用在内部进行调用的。

Toast、findViewById方法和Menu

Toast

Toast.makeText(this,"内容",Toast.LENGTH_SHORT).show

findViewById方法

findViewById方法常用来获取布局文件中控件的实例。不过Kotlin编写的Android项目在app/build.gradle文件头部默认引入了一个kotlin-android-extensions插件,这个插件会根据布局文件中定义的控件id自动生成一个具有相同名称的变量。因此,该方法在Kotlin中几乎不再被使用。

Menu

Menu即菜单栏,新建menu文件后按照需要编码。有两种标签,item和group,前者即菜单项,后者是不可见容器,用来对菜单项进行分组,使一组菜单项共享可用性、可见性等属性。
以下是将Menu和Activity绑定的代码:

//将Menu和Activity绑定
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    //自动调用了父类的getMenuInflater方法
    menuInflater.inflate(R.menu.main, menu)
    return true
}
//为菜单项绑定回调动作
override fun onOptionsItemSelected(item: MenuItem): Boolean {
   //自动调用了getItemId方法
   when(item.itemId){
       R.id.add_item -> Toast.makeText(this,"Add",Toast.LENGTH_SHORT).show()
       R.id.remove_item -> Toast.makeText(this,"Remove",Toast.LENGTH_SHORT).show()
       R.id.update_item -> Toast.makeText(this,"Update",Toast.LENGTH_SHORT).show()
       R.id.baidu -> {
           val intent = Intent(Intent.ACTION_VIEW)
           //自动调用了intent.setData方法
           intent.data = Uri.parse("https://www.baidu.com")
           startActivity(intent)
        }
    }
    return true
}

这其中含有一个Kotlin语法糖,即:我们可以直接获取对象的字段,并进行修改或读取,Kotlin会为我们自动调用访问器方法和修改器方法。

Intent

  • Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。
  • Intent一般可用于启动Activity、Service以及发送广播等。
  • Intent分两种,显式Intent和隐式Intent。

显式Intent

Intent的一个构造函数是:

> Intent(Context packageContext, Class cls)

第一个参数是上下文,第二个参数用于指定想要启动的目标Activity。
用显式Intent启动Activity的代码如下:

val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
//这里的SecondActivity::class.java 就相当于Java中的SecondActivity.class

隐式Intent

隐式Intent并不直接写出要启动哪一个Activity,而是指定一系列action、category等信息,交由系统去分析,如果有完全匹配的活动则启动,没有程序则崩溃。
每个Intent只能指定一个action,但能指定多个category。
用隐式Intent启动Activity的代码如下:

val intent = Intent("com.example.activitytest.ACTION_START")
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)

此外,隐式Intent还可以启动其他程序的Activity,这就使多APP之间的功能共享成为了可能。如调用系统浏览器打开网页:

val intent = Intent(Intent.ACTION_VIEW)
//自动调用了intent.setData方法
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)

与此对应,我们还可以在Activity中的标签中配置< data >标签,用于更精确的指定当前Activity能够响应的数据。不过,只有在data标签和Intent携带的Data完全一致时,Activity才会响应Intent 。

deep link功能

DeepLink,深度链接技术,主要应用场景是通过Web页面直接调用Android原生app,并且把需要的参数通过Uri的形式,直接传递给app,节省用户的注册成本。简单讲,就是你在手机上点击一个链接之后,可以直接链接到app内部的某个页面,而不是app正常打开时显示的首页。
比如:各种APP的购物分享链接。

向下一个Activity传递数据

用putExtra()将我们要传递的数据暂存在Intent中,在启动另一个Activity后从Intent中取出数据。注意,Intent中的数据以键值对的形式传入传出。
如:

//FirstActivity中传入数据
val intent = Intent(this,SecondActivity::class.java)
intent.putExtra("extra_data",data)//第一个参数是键,第二个参数才是值
startActivity(intent)
//SecondActivity中取出数据
text.text = intent.getStringExtra("extra_data")

返回数据给上一个Activity

如果一个Activity希望在启动的另一个Activity销毁之后传递一个数据,则应用startActivityForResult方法。代码如下:

//在FirstActivity中启动SecondActivity
val intent = Intent(this,SecondActivity::class.java)
startActivityForResult(intent,1) 
//这里的1是请求码,用于在之后的回调中判断数据的来源
//在SecondActivity中向FirstActivity传递数据
val intent = Intent()
intent.putExtra("data_return","Hello FirstActivity")
setResult(Activity.RESULT_OK,intent)

回调代码如下:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when(requestCode){
        1 -> if (resultCode == Activity.RESULT_OK){
            val returnedData = data?.getStringExtra("data_return")
            Toast.makeText(this,returnedData,Toast.LENGTH_SHORT).show()
        }
    }
}

Activity的基础知识

Activity的生命周期

返回栈

  • Android是使用任务来管理Activity的,一个任务就是一组存放在栈里的Activity的集合。这个栈又称返回栈。
  • 每当我们启动了一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。
  • 系统总是会显示处于栈顶的Activity给用户。

Activity状态

Activity在其生命周期中最多有四种状态:

  1. 运行状态:当一个Activity处于栈顶时处于的状态,这种状态的Activity也是系统最不愿回收的Activity
  2. 暂停状态:当一个Activity不再处于栈顶,但仍然可见时处于的状态(比如当前栈顶为对话框时,栈顶Activity之下的Activity就处于该状态)。只有在内存极低的情况下,系统才会考虑回收这种Activity
  3. 停止状态:当一个Activity不处于栈顶且完全不可见时处于的状态。系统仍然会为这种Activity保存响应的状态和成员变量,但这并不是完全可靠的,当其他地方需要内存时,处于停止状态的Activity就有可能会被系统回收
  4. 销毁状态,当一个Activity出栈后就进入了销毁状态,系统最倾向与回收这种状态下的Activity,以保证手机内存充足。

Activity生命周期

Activity类定义了7种回调方法,覆盖了Activity生命周期的每一个环节。

  1. onCreate:Activity被第一次创建时调用,在该方法中完成加载布局、绑定时间等初始化操作
  2. onStart:Activity由不可见变为可见时调用
  3. onResume:Activity准备好与用户交互的时候调用,这时Activity一定处于栈顶
  4. onPause:Activity在系统准备启动或恢复另一个Activity的时候调用,在该方法中将一些消耗CPU的资源释放掉并保存一些关键数据
  5. onStop:Activity完全不可见的时候调用
  6. onDestroy:Activity被销毁之前调用,之后Activity变为销毁状态
  7. onRestart:Activity由停止状态变为运行状态之前调用,即Activity被重新启动

以上7个方法除OnRestart外两两相对,又可分为三种生存期:即。完整生存期、可见生存期和前台生存期。
onCreate和onDestroy方法之间是完整生存期,一般情况下:在onCreate进行初始化操作,在onDestroy方法中释放内存。
onStart和onStop方法之间是可见生存期,一般情况下:在onStart方法中加载资源,在onStop方法中释放资源。
onResume和onPause方法之间是前台生存期,此时的Activity总是处于运行状态,可与用户交互。

从Activity A跳转到Activity B(均不是对话框Activity),请简述A B所经历的状态:

A.onPause, B.onCreate, B.onStart,B.onRusume,A.onStop

又在Activity B按下返回键,请简述A B所经历的状态:

B.onPause, A,onRestart, A.onStart, A.onResume, B.onStop, B.onDestroy

从Activity A跳转到Activity B(B是对话框Activity),请简述A B所经历的状态:

A.onPause, B.onCreate, B.onStart,B.onRusume

又在Activity B按下返回键,请简述A B所经历的状态:

B.onPause, A.onResume, B.onStop, B.onDestroy

Activity被回收了怎么办?

如果一个返回栈中的Activity被系统回收了,当返回到该Activity界面的时候会重新创建一个Activity。

Activity被回收了,怎么保存/恢复数据?

如果需要保存数据,可以通过onSaveInstanceState()回调方法。这个方法可以保证Activity被回收之前一定会被调用。该方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,如putString()方法保存字符串,putInt()方法保存整型数据,以此类推。每个保存方法需要传递两个参数,第一个参数是键,第二个参数是值。代码如下:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    val tempData = "Something you just typed"
    outState.putString("data_key",tempData)
}

然后,再修改onCreate方法,恢复数据:

if (savedInstanceState!=null){
    val tempData = savedInstanceState.getString("data_key")
    Log.d(tag,tempData)
}

此外,Intent还可以结合Bundle一起用于传递数据。首先吧需要传递的数据都保存早Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle中一一取出数据。

Activity启动模式

Activity启动模式一共有四种:standard、singleTop、singleTask 和singleInstance。

standard

standard是Activity的默认启动模式,在该模式下,每当启动一共新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。

singleTop

当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用,不会再创建新的Activity实例。

singleTask

当Activity的启动模式指定为singleTask,每次启动Activity时,系统首先会在返回栈中检查是否存在该Activity,如果发现已经存在则直接使用该实例吗,并把该Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。

singleInstance

不同于以上几种模式,指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity。在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个APP来访问这个Activity,都共用一个返回栈,也就解决了共享Activity实例的问题。

Activity实用技巧

知晓当前是在个Activity

新建一个类继承自AppCompatActivity,在onCreate()中加入以下代码,

Log.d("BaseActivity",javaClass.simpleName)
//这里,我们先获取当前实例的Class对象,然后调用simpleName获取当前实例的类名

注意,Kotlin中的JavaClass表示获取当前实例的Class对象,相当于在Java中调用getClass()方法;
而Kotlin中的ActivityName::class.java表示获取ActivityName类的Class对象,相当于在Java中调用ActivityName.class。

然后再将其他Activity的父类改为该类,重启项目运行程序时观察Logcat即可。

随时随地退出程序

新建一个单例类ActivityCollector作为Activity的集合,每当Activity的onCreate方法调用时就将自己添加到集合中,而当onDestroy方法调用时就将自己从集合中删除。如果我们需要退出程序,就对集合中所有Activity调用finish方法即可。最后还可以杀掉进程。
ActivityCollector代码如下:

object ActivityCollector {
    private val activities = ArrayList<Activity>()

    fun addActivity(activity: Activity){
        activities.add(activity)
    }

    fun removeActivity(activity: Activity){
        activities.remove(activity)
    }

    fun finishAll(){
        for (activity in activities){
            if (!activity.isFinishing)
                activity.finish()
        }
        activities.clear()
        android.os.Process.killProcess(android.os.Process.myPid())
    }
}

启动Activity的最佳写法

如果对一个Activity需要传递哪些数据感动迷惑,可采用下列代码的方法启动该Activity。

//在SecondActivity中加入
companion object {//该关键字用来模拟Java中静态方法的调用
    fun actionStart(context : Context, data1 : String, data2: String){
        val intent = Intent(context,SecondActivity::class.java)
        intent.putExtra("param1",data1)
        intent.putExtra("param2",data2)
        context.startActivity(intent)
    }
}

这样的写法最大的好处就是一目了然,该活动需要哪些数据一眼就看出来了。

Kotlin课堂

被我并入Chapter2中。

你可能感兴趣的:(安卓基础知识)