Skip to content

Commit

Permalink
jetpack部分准备livedata
Browse files Browse the repository at this point in the history
  • Loading branch information
iOrchid committed Mar 23, 2024
1 parent 392d748 commit 91ab49b
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
android:orientation="vertical"
android:padding="8dp">

<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv_title_kotlin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="DataBinding學習"
android:textSize="22sp" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_base_db"
android:layout_width="wrap_content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.zhiwei.jetpack.components", appContext.packageName)
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
package org.zhiwei.jetpack.components

import android.os.Bundle
import androidx.activity.viewModels
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentContainerView
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import org.zhiwei.jetpack.components.ui.JetpackViewModel

/**
* jetpack相关演示代码的功能模块的主入口页面
* jetpack相关演示代码的功能模块的主入口页面,
* todo 如果要使用navigation,则外部activity必须是fragmentActivity或其子类。否则不会有fragmentManager,没法navController
* 1. Navigation的使用要点:a.需要FragmentContainerView作为容器(配置defaultNavHost,navGraph,name三大属性);
* b.需要res下navigation中有graph配置(注意startDestination,子节点fragment配置)
* c.使用处findNavController,可navController.navigateUp()/navigate()
*/
class JetpackActivity : FragmentActivity() {

private val fcv: FragmentContainerView by lazy { findViewById(R.id.fcv_jetpack) }
private val bnv: BottomNavigationView by lazy { findViewById(R.id.bnv_jetpack) }

//activity-ktx库提供的扩展函数,
private val vm by viewModels<JetpackViewModel>()


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//此界面在AndroidManifest中使用了theme,状态栏和导航栏的透明
Expand All @@ -31,5 +41,8 @@ class JetpackActivity : FragmentActivity() {
val navController = fcv.findNavController()
// val navController = findNavController(R.id.fcv_jetpack)//也可以这么写
bnv.setupWithNavController(navController)
//去掉bottom navigation view的color tint,则就变成了原始的imageView的效果,
//navView.menu.getItem(2).icon 就是ImageView,可以加载gif,webp等icon效果
bnv.itemIconTintList = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.zhiwei.jetpack.components.ui

import android.icu.math.BigDecimal
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import org.zhiwei.jetpack.components.R

/**
* 这部分主要演示除dataBinding外的,主要的几个jetpack组件的用法;
* 1. liveData、lifecycle、viewModel,room,paging,work,navigation
* 2. 其实在JetpackActivity中就已经使用了navigation结合bottomNavigationView的简单用法
*/
class JetpackFragment : Fragment() {
//fragment-ktx提供的扩展函数,便于获取viewModel的实例;该方式获取的vm,不同的fragment对象获取后的vm,不是同一个实例。
private val vm: JetpackViewModel by viewModels()

//fragment-ktx提供的扩展函数,便于获取该fragment依附的activity的viewModel的实例。
// 该方式获取的vm,如果是同一个activity下不同的fragment获取vm,对象是同一个。
// private val vm2: JetpackViewModel by activityViewModels()

private val tvLive: TextView by lazy { requireView().findViewById(R.id.tv_live_ret_jetpack) }

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
//这里注意,第三个参数一定要设置false,否则报错
return inflater.inflate(R.layout.fragment_jetpack, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
testLiveData()
}

private val TAG = "JetpackFragment"
private fun testLiveData() {
//模拟生成数据
vm.startSendScore()
//观察live的数据变化,不要关联生命周期的lifecycleOwner,observeForever不需要。
//viewLifecycleOwner是fragment的,activity就是自身。
vm.liveScore.observe(viewLifecycleOwner) {
//保留小数点2位
val num = BigDecimal(it).setScale(2, BigDecimal.ROUND_UP)
Log.d(TAG, "testLiveData:👀到$it ,保留2位:$num")
tvLive.text = "文本$num"
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.zhiwei.jetpack.components.ui

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.math.nextDown
import kotlin.random.Random

/**
* 作用于JetpackActivity和JetpackFragment的viewModel;
* viewModel作为数据管理层,可以给多个fragment共用。
*
*/
class JetpackViewModel : ViewModel() {
//一般使用ViewModel作为基类即可,另有AndroidViewModel,内含application参数,根据业务场景来选择使用与否。

//region liveData
private val _liveStudentScore =
MutableLiveData<Double>()//一般mutable的都是可修改的,开发习惯,可修改额私有,对外提供不可变的。
val liveScore: LiveData<Double> = _liveStudentScore

//endregion

internal fun startSendScore() {
viewModelScope.launch {
repeat(20) {
delay(500)
_liveStudentScore.postValue(Random.nextDouble(20.0, 100.0).nextDown())
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#### Jetpack笔记速览

##### 一、前言

> [Jetpack](https://developer.android.google.cn/jetpack?hl=zh-cn)
> 是Android官方推出的一些列组件库,最早`databinding`
> 也属于官方推荐的工具写法,但是由于其影响编译性能,且`xml`
> 中编写出错的时候IDE不易准确检测和报错,逐步不再使用。且`compose`
> 更为高效的UI编写方式,所以该项目内`dataBinding`有独立module作为代码示例,并有详细注释说明,便于学习。
>
> 如果不是接手老项目需要,可以跳过,直接学习compose;如果是老项目有databinding,则可以学习了解即可。
Jetpack的其他组件则都会共用在同一个项目module内,同时也是使用MVVM的架构设计模式,来简单实现一个组合使用各个库的示例。

<font color=orange>
以下笔记仅用于初步了解库的使用和简单构成,不太深入基本原理。先知其然,然后自然而然你会探索其所以然。</font>

可以在`IDE`的project模式下,`External Libraries`找到想看的库的编译源码。

##### 二、Lifecycle

> 简言之,Lifecycle就是观察者模式,用以感知UI元素的生命周期的。Android中Activity/Fragment级别的实现。
简要理解`Lifecycle`库就核心三个类文件:`Lifecycle``LifecycleOwner``LifecycleObserver`

1. `Lifecycle`就是表述生命周期的,抽象类,提供观察者的注册/注销。内有生命周期的不同状态`State`
`Event`
2. `LifecycleOwner`就是上面`lifecycle`的持有者,Android中是`componentActivity/Fragment`
类实现的。两个`componentActivity`类,一般使用`andriodx.activity`包下的,因为这个实现了`viewModel`
的结合。开发中多用`AppcompatActivity来作为基类`,直接使用`Activity`作为基类就没有这个实现。
3. `LifecycleObserver`和子类`LifecycleEventObsever`就可以添加到上面的`lifecycle`
注册观察,就能感知到生命周期变化的回调。具体你不实现,可参照源码,`LifecycleController`等分析。

##### 三、LiveData

> 又一个观察者模式的实现,数据变化时候可以得到及时的通知。两个核心库`lifecycle-livedata-core`
> `lifecycle-livedata`
1. `core`库就三个文件:`Observer`接口,`LiveData`抽象类和`MutableLiveData`
的类。LiveData内有观察者的注册/注销,及数据变化的管理和通知等逻辑。
2. `livedata`库有`MediatorLiveData``Transformations`和一些**协程**相关的实现类,扩展函数等。

```kotlin
```

Worker
任务的执行者,是一个抽象类,需要继承它实现要执行的任务。

WorkRequest
指定让哪个 Worker 执行任务,指定执行的环境,执行的顺序等。
要使用它的子类 OneTimeWorkRequest 或 PeriodicWorkRequest。

WorkManager
管理任务请求和任务队列,发起的 WorkRequest 会进入它的任务队列。

WorkStatus
包含有任务的状态和任务的信息,以 LiveData 的形式提供给观察者。

WorkManager适用于那些即使应用程序退出,系统也能够保证这个任务正常运行的场景,比如将应用程序数据上传到服务器。
它不适用于应用进程内的后台工作,如果应用进程消失,就可以安全地终止,对于这种情况,推荐你使用线程池
15 changes: 0 additions & 15 deletions jetpack/src/main/java/org/zhiwei/jetpack/components/ui/work.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.zhiwei.jetpack.components.ui;
package org.zhiwei.jetpack.components.ui.work;

import android.content.Context;
import android.util.Log;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.zhiwei.jetpack.components.ui
package org.zhiwei.jetpack.components.ui.work

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.Data
Expand Down Expand Up @@ -31,21 +34,21 @@ import java.util.concurrent.TimeUnit
* ----------------------------------------------------------------
* workmanager的演示界面
*/
class WorkActivity : AppCompatActivity() {
class WorkFragment : Fragment() {
//todo 这里workmanager的request有个高级用法,就是添加环境约束 ,比如网络、电量等
var constraints: Constraints = Constraints.Builder()
private var constraints: Constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) //联网状态
.setRequiresBatteryNotLow(true) //低电量不操作
// .setRequiresCharging(true) // TODO 充电时候才开始,这个条件开启后,测试机不充电则无演示效果
// .setRequiresDeviceIdle(true)//待机状态下才执行,api 23 以上,此处开启的话,手机熄屏才会执行了
.setRequiresStorageNotLow(true) //存储空间不能太小
.build()

private val name = "tmc"
private val name = "Kotlin小娜娜"
private val age = 18

//类似于intent的bundle
var data = Data.Builder()
private var data = Data.Builder()
.putString("name", name)
.putInt("age", age)
.build()
Expand All @@ -62,22 +65,29 @@ class WorkActivity : AppCompatActivity() {
.setBackoffCriteria(BackoffPolicy.LINEAR, 20, TimeUnit.MINUTES)
.build()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_work)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
return inflater.inflate(R.layout.fragment_work, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//3、加入任务管理,但不是执行,执行的代码稍后
WorkManager.getInstance(this).enqueue(workRequest)
WorkManager.getInstance(requireContext()).enqueue(workRequest)
//4、通过workRequest的唯一标记id,来操作request,并获取返回数据
//todo 这里因为在oncreate中执行,会先与work执行,toast会弹出未执行work的空结果,work变化后,还会显示出成功后的结果。这是因为observe监测worker的status变化 enqueued、RUNNING、successed、retry、failure等
WorkManager.getInstance(this).getWorkInfoByIdLiveData(workRequest.id)
.observe(this) { workStatus: WorkInfo ->
//todo 这里toast会弹出未执行work的空结果,work变化后,还会显示出成功后的结果。这是因为observe监测worker的status变化 enqueued、RUNNING、successed、retry、failure等
WorkManager.getInstance(requireContext()).getWorkInfoByIdLiveData(workRequest.id)
.observe(viewLifecycleOwner) { workStatus: WorkInfo ->
//接收从worker中返回的任务结果,最好在这里判断status为success再做具体操作
if (workStatus.state == WorkInfo.State.SUCCEEDED) {
val data = workStatus.outputData
val result = data.getString("result")
val status = data.getInt("status", 0)
Toast.makeText(
this@WorkActivity,
requireContext(),
"result: $result status: $status",
Toast.LENGTH_SHORT
).show()
Expand Down Expand Up @@ -114,6 +124,6 @@ class WorkActivity : AppCompatActivity() {
//</editor-folder>
override fun onDestroy() {
super.onDestroy()
WorkManager.getInstance(this).cancelWorkById(workRequest.id)
WorkManager.getInstance(requireContext()).cancelWorkById(workRequest.id)
}
}
28 changes: 28 additions & 0 deletions jetpack/src/main/res/layout/fragment_jetpack.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv_title_kotlin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:fontFamily="@font/hongkong"
android:text="Jetpack學習"
android:textSize="22sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tv_live_ret_jetpack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="此处显式liveData的变化值"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title_kotlin" />

</androidx.constraintlayout.widget.ConstraintLayout>
7 changes: 6 additions & 1 deletion jetpack/src/main/res/navigation/graph_main_jetpack.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/graph_main"
app:startDestination="@+id/databinding_item_menu">

<!--这里id保持和bnv_menu_jetpack中一致,才能有效处理点击切换-->
<fragment
android:id="@+id/databinding_item_menu"
android:name="org.zhiwei.jetpack.databinding.DataBindingFragment"
android:label="DataBindingFragment"
tools:layout="@layout/fragment_databinding" />

<fragment
android:id="@+id/kotlin_item_menu"
android:name="org.zhiwei.jetpack.components.ui.JetpackFragment"
app:route="route_nav_jetpack"
tools:layout="@layout/fragment_jetpack" />
</navigation>
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}

}

0 comments on commit 91ab49b

Please sign in to comment.