安卓学习-第三章-Acticity

239 阅读6分钟

关于安卓学习的博客将是根据第一行代码Android的学习总结,内容将不会面面俱到,而是挑选重要的知识进行梳理。

3. Activity

3.1 Activity的基本用法

在创建Activity时的两个选项:

  1. Generate Layour File:自动为当前Activity创建一个布局文件(Layout)。
  2. Launcher Activity:自动将当前Activity设置为当前项目的主Activity。

布局文件XML中命名语法:

  1. 引用id:@id/id_name
  2. 定义id:@+id/id_name

给当前Activity加载布局的方式:

  1. 在重写父类onCreate函数时调用setContentView(layout id)。值得注意的是项目中添加的任何资源都会在R文件中根据名称的路径生成一个相应的资源id。
  2. 使用binding.root的方式调用layout。setContentView(binding.root)

在AndroidManifest文件中注册的说明:我们可以在AndroidManifest中对各个Activity进行配置,包括加载方式、主题等,还可以制定MainActivity等。

  1. 通过android:name来指定Activity。
  2. 指定MainActivity的方式为在<activity>标签内部添加<intent-filter>标签并在里面设置<action><category>。我认为可以理解为将当前Activity与触发MainActivity的Intent绑定,即设置当前Activity为MainActivity。

使用Toast进行消息提醒:

  1. 弹出Toast的函数Toast.makeText(context, text, length).show()。一般Toast设置在Button等控件的触发函数内。makeText函数需要三个参数。第一个是Toast要求的上下文,可以设置为当前Activity,即this。第二个参数是Toast显示的内容,第三个参数是显示的时常。

使用binding获取布局控件的步骤:

  1. 在项目gradle文件中加入代码
  buildFeatures {
      viewBinding true
  }
  1. 获取对应布局类实例:

    val binding = FirstLayoutBinding.inflate(layoutInflater)

    或者

    val binding = FirstLayoutBinding.inflate(LayoutInflater.from(this))

  2. 直接从binding中获取控件。binding.button1.setOnClickListener{...}

在Acticity中使用Menu:

  1. 在res目录下创建menu文件夹
  2. 在menu文件夹下创建Menu的XML文件例如‘main’
  3. 在main.XML中通过增加<item>设置下拉菜单
  4. 重写Activity的onCreateOptionMenu()方法。其中menuInflater用到了Kotlin简化get/set的语法糖
  5. 重写Activity的onOptionItemSelect方法,使用when语句设置点击Menu中Item的响应

3.2 Intent与Activity

Intent是Android程序中各组件进行交互的一种重要方式,不仅可以指明当前组件想要执行的操作,还可以在不同组件之间传递数据。

显式Intent

  1. 使用Intent的构造函数Intent(context, Class<?> cls)
  2. 在控件的Listener中调用函数startActivity(intent)来显式启动目标Activity

隐式Intent

通过指定<action><category>的方式交由系统决定启动对应的Activity。

  1. 对于配置Activity的<action><category>,可以在AndroidManifest.XML文件中对应<activity><intent-filter>条目内进行指定。
  2. 使用Intent的构造函数Intent(action :String)来创建Intent,并使用函数Intent.addCategory(category :String)来增加category。
  3. 注意事项:一个Activity可以设置一个action和多个category。当没有Activity符合当前Intent的要求时,系统会被报错。
  4. 隐式Intent的扩展:Intent的action有很多玩法,例如可以启动其他的程序。eg.使用Intent.ACTION_VIEW + intent.data = uri的方法打开浏览器。注意此时要在对应Activity中设置<data>标签并进行一些scheme,host,port等的规定。
binding.button1.setOnClickListener {
    val intent = Intent(Intent.ACTION_VIEW)
    intent.data = Uri.parse("https://www.baidu.com")
    startActivity(intent)
}

使用Intent向下传递数据

上层Activity在Intent中保存数据,下层Activity从启动自身的Intent中获取数据。

  1. 在创建Intent后使用函数Intent.putExtra(key, value)来保存数据。
  2. 下层Activity根据Kotlin的语法糖直接获取intent并选择调用其函数intent.getStringExtra(key)intent.getIntExtra(key)intent.getBooleanExtra(key)来获取数据。

返回数据给上层Activity

  1. 上层Activity使用startActivityForResult(intent, requestCode)启动下层Activity。
  2. 下层Activity调用intent.putExtra(key, value)保存数据
  3. 下层在调用销毁函数finish()之前调用函数setResult(resultCode, intent)来配置结果码,一般是RESULT_OK/RESULT_CANCELED。
  4. 上层Activity重写函数onActivityResult(requestCode, resultCode, intent)来根据requestCode确定下层Activity的intent,根据resultCode确定下层的处理结果,进而处理intent中保存的数据。
  5. 额外知识:使用back按键返回时Activity会调用onBackPressed()函数,可以重写该函数并在此之前设置serResult(),从而使back键返回也能附带intent数据。

Activity的生存周期

Activity状态

Android使用task来管理Activity,一个task就是一组存放在返回栈里的Activity。每个Activity在其生存周期中最多会有四种状态,分类的根据是Activity在返回栈中的位置和Activity的可见行,并且系统在回首内存是会对不同状态的Activity采用不同的优先级。

Activity状态描述
运行状态Activity处于站顶,即运行状态(交互状态)
暂停状态Activity不再处于栈顶,但仍可见
停止状态Activity不再处于栈顶,也不可见
销毁状态Activity从返回栈中移除

Activity的生存期

Activity类中定义了七个方法用以改变运行状态。状态转移图

stateDiagram-v2
[启动Activity] --> onCreate()
onCreate() --> onStart()
onStart() --> onResume()
onResume() --> [Activity运行中]
[Activity运行中] --> onPause():另一个Activity来到站顶
onPause() --> onStop():Activity不再可见
onStop() --> [杀掉进程]:优先级更高的应用需要内存
[杀掉进程] --> onCreate():返回应用
onStop() --> onDestory()
onDestory() --> [关闭Activity]
onPause() --> onResume()
onStop() --> onRestart():返回该Activity
onRestart() --> onStart()

markdown怎么才能让状态图不这么乱...

Activity的三种生存期

  1. 完整生存期。onCreate() -> onDestory()
  2. 可见生存期。onStart() -> onStop()
  3. 前台生存期。onResume() -> onPause()

应对Activity被回收

为防止未手动销毁的Activity因内存不足被系统回收引起的数据丢失问题,可通过重写回调函数onSaveInstance(Bundle),并将数据通过函数Bundle.putString(key, val)及其类似函数存储在Bundle类对象中,在onCreate()阶段重新赋值即可。

Activity的启动模式

  1. standard模式:每次启动都会创建一个该Activity的新实例。
  2. singleTop模式:在启动Activity时如果发现返回栈已经是该Activity,则直接使用当前栈顶的Acticity。
  3. singleTask模式:每次启动Activity时若返回栈中存有该Activity,则将该Activity之上的所有其他Activity出栈。
  4. singleInstance:启用单独的返回栈(task)来管理该Activity,任何应用程序以singleInstance的方式启动该Activity时都会访问这个返回栈,从而解决共享Activity实例的问题。

Activity的最佳实践

创建Activity的基类BaseActivity并继承AppCompatActivity()。我们可以在BaseActivity中新增一些功能,从而更好地处理实际的Activity。

  1. 确定当前的界面对应的Activity:在BaseActivity的onCreate()函数中加入Log.d("BaseActivity", javaClass.simpleName)。根据日志信息确定当前Activity。
  2. 一键退出程序:创建单例类object ActivityCollector,使用类中list管理所有的Activity,内部实现finishAll()函数销毁所有不处于isFinishing的Activity。BaseActivity在onCreate()onDestroy()时在list中增删Activity实例。这样若想一键退出程序时,直接调用ActivityCollector.finishAll() + killProcess(pid)即可。 启动Activity的最佳写法:为了明确创建Activity需要的参数,我们可以在Acticity中添加静态函数actionStart(context, param1, param2)实现(在Kotlin中可以通过companion object结构体实现)。在actionStart()函数内部可以创建Intent并传入参数,随后使用context.startActivity(intent)启动自身Activity,再配合onCreate()即可完成Activity的创建和初始化。在上层Activity调用时,即可通过TargetActivity.actionStart(this, param1, param2)启动该Activity。即函数actionStart()作为启动Activity的接口函数显式地声明需要的参数。