«

安卓速通(其实是期末复习~)(第四章+第五章)

ZealSinger 发布于 阅读:128 期末复习


程序活动单元Activity⭐️

定义

Android的四大组件分别是Activity ; Service ; ContentProvider ; BroadcastReceiver,四大组件中在考试范围来讲,主要考试的就是Activity,其Activity是负责和用户交互的组件

个人感觉可能会考四大组件有哪些,每个组件的作用感觉不会怎么考(因为书上说的也说的少),感觉可以记一下有哪些组件

拓展:
其余三大组件的作用
(1)Service-服务-在后台执行长时间的运行操作,不依赖于用户界面
例如:音乐软件,放到后台一样的可以播放;游戏更新后台下载
特点:默认在主线程上执行,一般需要创建子线程处理耗时操作,通过startService()启动服务,bindService()绑定服务与组件交互

(2)ContentProvider-内容提供者-实现应用间数据的共享
例如:通讯录,短信等系统提供信息给别的应用
特点:通过contentResolver实现数据的增删改查;可以设置访问权限保证安全

(3)BroadcastReceiver-广播接收器-监听响应系统或者应用发起的全局广播事件
例如:开机完成 ; 电量低 ;网络变化
特点:可以静态注册也可以动态注册

Activity的生命周期⭐️

Activity的生命周期是指从Activity的创建到销毁的全过程,当我们在使用,,浏览,退出,返回应用程序的时候,应用程序中的Activity实例都会在不同的生命周期状态之间进行转换,存在启动状态 ; 运行状态 ;暂停状态 ;停滞状态;销毁状态五个状态

当Activity在生命周期的各个状态之间进行转化的时候,对应的提供了六个核心回调集,当Activity进行进入新状态的时候就会触发对应的回调方法

案例代码

class MainActivity : ComponentActivity() {

   private val TAG = "MainActivityLifecycle"

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       Log.d(TAG, "onCreate called")
       enableEdgeToEdge()
       setContent {
           KtAndroisTheme {
               Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                   Greeting(
                       name = "Android",
                       modifier = Modifier.padding(innerPadding)
                  )
              }
          }
      }
  }

   override fun onStart() {
       super.onStart()
       Log.d(TAG, "onStart called")
  }

   override fun onResume() {
       super.onResume()
       Log.d(TAG, "onResume called")
  }

   override fun onPause() {
       super.onPause()
       Log.d(TAG, "onPause called")
  }

   override fun onStop() {
       super.onStop()
       Log.d(TAG, "onStop called")
  }

   override fun onDestroy() {
       super.onDestroy()
       Log.d(TAG, "onDestroy called")
  }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
   Column(modifier = modifier) {
       // 新增 XML 布局
       AndroidView(
           factory = { context ->
               // 加载 XML 布局
               val view = View.inflate(context, R.layout.relativelayouttext, null)
               val activity = context as MainActivity
               // createRelativeLayout(context)
               view
          },
           modifier = Modifier.padding(20.dp)
      )
  }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
   KtAndroisTheme {
       Greeting("Android")
  }
}

从日志中和操作视频上可以看到,首先是onCreated方法 然后Start方法,之后就是Resume方法,执行玩resume方法后整个Activity就是在运行状态了,当我们退出之后回到主界面,依次触发Pause方法和stop方法,我们再次返回应用程序,又是start方法和resume方法,最后将activi从后台移除之后才是destory方法

横竖屏切换的时候的生命周期变化(留心小知识)

当横竖屏幕切换的时候,软件默认是会随着手机的变化而变化的,在变化的时候,如果对应的Activity的配置项android:configChanges没有设置,那么每次横竖屏切换,其实底层就是调用onDestory()方法然后依次执行onCreate , onStart , onResume方法创建一个新的Activity 这样子的好处是默认配置不需要任何修改 , 缺点是因为是重新创建了一个Activity,在原本Activity中做的一些交互无法被保存需要重新进行

如下就是没有设置任何属性配置的时候的样子

image-20250515145109350

反之,如果android:configChanges属性被设置为了对应的属性(android:configChanges="orientation|keyboardHidden"),横竖切换的时候就不会进行如上的过程,程序自适应的调整而不会触发生命周期的任何回调过程。

前后对比的操作视屏(注意有bgm 中间有一小部分属性填充错误了 所以重试了一下)

当然,而也可以通过设置android:screenOrientation="portrait" 锁定应用竖屏或者android:screenOrientation="landscape"缩影横屏

Activity的创建,配置,启动和关闭⭐️

这一部分很需要代码支持,纯粹的背诵就是背API了

创建和配置

创建就是纯电脑操作就行,可以直接创建Activiy,甚至还帮我们配备了常用的Activity模板

image-20250515151057775

我们这里选择LoginActivity,他会对应的帮我们创建好一个普通的登录逻辑所需要的View视图对象,整个Login登录揭密胺的layout布局,以及创建对应的LoginActivity,全自动化很舒服

image-20250515151942378

这里如果硬要说要考试的话,感觉应该是如下几个点

启动,切换和关闭

启动和切换一块讲

我们可以拿上面那个checkBox的代码,现在有一个需求,点击篮球之后需要跳转到上面新键的这个LoginActivity登录页面,这个里面其实就设置到了checkBox的点击监听和Activity的启动和切换了

首先是Activity的启动,我们知道activity的生命周期的开始其实就是onCreate方法,在Android中,我们可以通过startActicity(Intent intent)方法求启动一个Activity

public void startActivity(Intent intent)
Intent

这里的Intent对象,是一个Activti的桥梁对象,表示一种意图,为何这么说,因为你启动了一个Activiv其实就是想要运行和交互他,因为同一时间只有一个Activity能获得焦点,所以必定会出现Activity之间的焦点争夺问题,我们创建一个Activity的时候就是其实就是想从当前的Activity跳转到了一个Actvivity中,这个就是所谓的意图,所以Intent的构造函数也就是如下

// 创建一个从A-Activity到B-Activity的意图对象
Intent intent = new Intent(Activity-A,Activity-B)
可以立即为,当你点击“开始游戏”其实就是想匹配开一把游戏,此时就是有一个意图:从正常的游戏首页面到匹配界面,跳转到匹配界面也就是
startActivity( 跳转到匹配界面的意图 ) = startActivity(new Intent(当前界面到匹配界面))

所以对应的,我们就可以完成上述需求

// 这里是直接在checkBox那个代码的handler方法进行修改的 其余的地方和checkBox中一样。LoginActivity是直接使用的系统创建的自带的模板,不需要单独写代码和修改
private fun handler(buttonView: View,isChecked: Boolean){
       var checkBox = buttonView as CheckBox
       var boxContent = checkBox.text.toString()
       var hobbyText = this.findViewById<TextView>(R.id.hobbyText)
       if(isChecked){  // 为true则说明被选中 加入到likeList中
           likeList.add(boxContent)
           hobbyText.setText("你选择的是:${likeList}")
           Log.d(TAG,"准备跳转")
           val intent = Intent(this,LoginActivity::class.java)
           startActivity(intent)
      }else{
           likeList.remove(boxContent)
           hobbyText.setText("你选择的是:${likeList}")
      }
  }

image-20250515162108807

点击“篮球”选项之后,就可以发现Activity进行了切换

image-20250515162233724

而关闭Activity就需要在对应的Activity中调用自己的finish()方法即可

这个是属于显示Intent,除此之外还有隐式Intent

IntentFile

隐式Intent不需要明确指明需要激活的目标组件,只需要对应的编写<IntentFile>标签并且针对action属性,data属性,category属性进行匹配,当三个属性全部匹配完毕,就可以进行唤起对应的目标组件

Activity之间的数据传输

假设我们在MainActivity中传数据给SecondActivity:

Activity之间可以借助Intent传输数据,主要利用putExtra(key,value)方法

val intent = Intent(this,LoginActivity::class.java)
intent.putExtra("name","zealsinger")
intent.putExtra("age",21)
intent.putExtra("isPeople",true)

而在被接收方,主要通过getIntExtra()/getStringExtra()/getBooleanExtra()通过key获取

val intent  = intent
var name = intent.getStringExtra("name")
// 第二个参数表示当没有这个数值的时候设置的默认值
var age = intent.getIntExtra("age", 0)
var isPeople = intent.getBooleanExtra("isPeople", true)

当然也可以批量发送和获取,将有关的数据放入到一起进行传输。利用Bundle对象,Bundle对象类似于Map


// 实例化一个Bundle  
Bundle bundle = new Bundle();
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
//设置数据
String name="admin";String num="123";
//把数据保存到Bundle里  
bundle.putString("name", name);
bundle.putString("num",num);
//把bundle放入intent里  
intent.putExtra("Message",bundle);
startActivity(intent);

//获取数据  
Intent intent = getIntent();
//从intent取出bundle  
Bundle bundle = intent.getBundleExtra("Message");
//获取数据  
String name = bundle.getString("name");
String num = bundle.getString("num");
//显示数据  
text_show.setText(name + "\n" + num);

如果需要回传,即secondActivity传数据给MainActivity

回传主要用如下三个方法

// 开启一个Activity且当创建的Activity销毁的时候就会接收其返回值
// 第二个参数代表一个请求COde 用于区分不同的子Activity
startActivityForResult(Intent intent , int requestCode)

// 子Activity中设置要返回的内容 返回的内容也可以让intent携带即可
setResult(int resultCode,Intent intent)

// 接收回传数据
onActivityResult(int requestCode,int resultCode,Intent data)

该方法需要重写 参考逻辑
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   if (requestCode == REQUEST_CODE) {
       if (resultCode == RESULT_OK && data != null) {
           String receivedData = data.getStringExtra("dataFromReceive");
           // 处理回传的数据
           System.out.println(receivedData);
      }
  }
}

任务栈与启动模式⭐️

Android任务栈其实也就是Task,也就是一个栈结构,先进后出的容器,用于管理Activity组件

如下,每次创建一个Activity都会往栈顶放入,类似于方法进入栈帧,先进后出

在我们实际的使用和开发中,会需要在多个Activity之间反复横跳,如上,我在Activity3的时候有需要跳到Actvity1,那么此时我应该再次创建一个Activity1还是让原来的Activity1放到栈顶上?对于不同操作我们就需要用启动模式进行标识

Android中存在四种启动模式

Fragment

Fragment(碎片) 是一种可以嵌入到Acitivity中的UI片段,用于描述Acitivty中的部分布局,主要是因为如果Activity中布局控件太多了一个Activity管理起来就很麻烦,通过Fragment进行模块化的管理更加高效

Fragment不能独立存在,必须嵌入到Activity中,一个Activity中可以有多个Fragment且Fragment的生命周期和Activity强绑定

Fragment的生命周期

Fragement的生命周期和Acitivty的生命周期是一样的,也是五个状态:启动状态;运行状态;暂停状态;停滞状态;销毁状态

Fragment的生命周期和Acitvity的生命周期相关,具体联系如下:

  1. 因为Fragment是Actvity中的一部分,所以Fragment至少需要等待Activity的创建才会启动整个生命,所以当Activity创建Fragment的时候,Fragment进入启动阶段

  2. 当Activity对内部的Fragment进行操作的时候,如果进行添加操作,Fragment处于启动状态 ; 反之如果进行删除则进入销毁状态

  3. 当Activity进入暂停状态,内部所有的Fragment进入暂停状态

  4. 当Activity进入销毁状态,内部素有的Fragment也会进入销毁状态

同样,每个Fragment的状态的切换都对应的存在回调方法/钩子函数,Fragment的相关方法如下

onAttach():用于Fragment和Acitivity建立关联
onCreateView():Fragment创建视图(加载布局的时候)调用
onActivityCreated():Fragment关联的Activity已经完成创建的时候调用
onDestoryView():Fragment的视图被移除删除的时候调用
onDetach():Fragment和Activity解除关联的时候调用

Fragment生命周期和钩子方法和视图生命周期之间的对应关系

可能的考点:Frgament和Activity的区别

image-20250515185746410

Fragment的创建和使用(偏向代码性质,先略过,理论考点应该很少)

数据存储⭐️

Android中数据存储的方式有五种,分别为文件存储 ; SharedPreferences存储 ;SQLite数据库存储 ; ContentProvider ; 网络存储 本节内容主要针对前三种存储

文件存储

文件存储主要就是利用本地文件进行存储,存入即写入文件,读取即从文件读取,其实就是I/O流的相关知识,因为Android的主要开发语言就是Java/Kotlin,所以本质上就是对Java/Kotlin中的IO文件相关知识的运用

对于文件存储,分为了内部存储和外部存储

内部存储

内部存储就是写入到应用程序下的data/data/<packagename>目录下,这个目录是应用程序专有的,其他程序如果需要访问需要权限申请,并且如果该应用程序被删除对应文件中的内容也会被删除

内部存储主要运用的就是Context提供的openFileInput() 和 openFileOutput()

FileOutputStream fos = openFileOutput(String name,int mode)
FileInputStream fis = openFileInput(String name)

 

外部存储

而所谓外部存储就是以文件的形式写入到一些外部设备,常见的就是SD卡或者设备内嵌的存储卡上,属于永久性的存储方式

因为外部存储器可以换到别的设备上也可以被任何程序读取使用和修改,所以是不安全的

因为手机可能没有外部存储设备,所以在使用外部存储的时候,一定需要先检查外部设备是否可用

String status = Environment.getExternalStorageState; //获取可用状态
if(status.equal(MODE_MEDIA_MOUNTED)){ // 判断可用
// 获取外部存储设备的存储路径,不同的设备存储路径可能不一样
File sdPath = Environment.getExternalStorageDirectory()
}

SharedPreferences存储

定义

SharedPreferences是Android平台上一个轻量级的存储类,可以进行一些少量数据的持久化

存入数据到SharedPreferences

SharedPreferences本身是一个只能从中获取数据的类,其数据的存储和修改对外是不开放的,但是因为其底层是Editor对象,所以我们一般都是利用Editor对象进行数据的存储和修改

实现逻辑过程是:

Editor以Key-Value键值对的形式保存数据且支持多种类型,但需要注意的是,末尾一定需要用commit提交保存否则操作不生效

// 获取SP对象  第一个参数data表示文件名  第二个参数表示文件操作模式
SharedPreferences sp = getSharedPreferences("data",MODE_PRIVATE)
// 通过SP对象获取可添加的Editor对象
SharedPreferences。Editor ed = sp.edit()
// Editor允许存储String Int Float Long Boolean Set<String>
ed.putString("name","张三")
ed.putInt("age",22)
ed.commit() // 提交存储数据

读取与删除SharedPreferences中的数据

读取

SharedPreferences中读取数据非常简单,获取到SharedPreferences对象之后,利用getxxx()方法直接获取即可,如果存在则返回数据,如果不存在就返回空字符串

SharedPreferences sp = getSharedPreferences("data",MODE_PRIVATE)
String data = sp.getString("name")
删除

删除对象是需要调用Edit对象的remove()方法即可

ed.remove("name") // 删除某一条数据
ed.clear() // 删除所有数据

SQLite数据库存储

前面两种方式适合存储简单数据,当需要存储大量数据的时候就不合适,为此Android提供了SQLite数据库用于存储大量数据进行管理维护

SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎,支持SQL语句,是世界上部署最广泛的数据库引擎

SQLite的创建

在Android中创建SQLIte很简单,因为它本质上是一个库类即SQLiteOpenHelper类,所以我们只需要自定义类继承这个类,重写其中的onCreate()方法和onUpgrade()方法即可

// Java的写法中可能会需要super调用父类的构造
// 需要四个参数即super(Context context , String dbName ,CursorFactory factory, int version )
// 第一个参数为上下文环境 一般传入当前Activity的环境即可 第二个参数为数据库的名字
// 第三个参数为游标工厂,不知道的情况下可以使用null   ; 第四个参数为SQLite版本号,主流的为2,3目前
class MySQLite(val context:Context,val dbName:String):SQLiteOpenHelper(context,dbName,null,2){
 //数据库第一次被创建的时候回调用这个方法 这里面就是execSQL就是执行SQL语句 传入的参数就是需要执行的SQL语句
   override fun onCreate(db: SQLiteDatabase?) {
       db?.execSQL("""
           CREATE TABLE information(_id INTEGER PRIMARY KEY AUTOINCREMENT,
               name VARVHAR(20),PRICE INTEGER)
       """.trimIndent())
  }
   //当数据库版本号增加的时候就会调用
   override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {

  }

}

如下,我们创建对应的MySQLite对象并且调用了writableDatabase获取创建了数据库的SQLiteDatabase对象(切记 一定需要获取到这个对象才算创建数据库,如果只是创建MySQLite对象可能不会真正创建),如下,创建的数据库的db文件默认在路径:/data/data/<包名>/databases/,应该能看到两个文件:

image-20250515225614199

SQLite数据库的CRUD操作

SQLIte事务

事务老生常谈了,和MySQL的事务性类似,也是保持ACID的特性(原子性,一致性,隔离性,持久性),含义也不多做解释,网上都查得到也是很基础的东西了

image-20250515235414390

Android Kotlin 期末复习 编程