Jetpack是开发组件工具集,主要目的是帮助我们编写出更加简洁的代码,简化开发过程。许多开发组件为MVVM架构量身打造。
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
通常要为每个活动和碎片创建一个ViewModel
计数器
class MainViewModel : ViewModel(){
var counter = 0
}
class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//不能直接创建实例,因为ViewModel的生命周期长于活动,如果活动创建的时候创建实例,那就无法保存数据
//不推荐使用
//ViewModelProviders.of().get(
让退出程序后也能保存数据
class MainViewModel(countReserved: Int): ViewModel(){
var counter = countReserved
}
class MainViewModelFactory(private val countReserved: Int) :ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
return MainViewModel(countReserved) as T
}
}
class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sp = getPreferences(Context.MODE_PRIVATE)
val countReserved = sp.getInt("count_reserved",0)
clearBtn.setOnClickListener {
viewModel.counter = 0
refreshCounter()
}
//不能直接创建实例,因为ViewModel的生命周期长于活动,如果活动创建的时候创建实例,那就无法保存数据
viewModel = ViewModelProviders.of(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
plusOneBtn.setOnClickListener {
viewModel.counter++
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter() {
infoText.text = viewModel.counter.toString()
}
override fun onPause() {
super.onPause()
sp.edit{
putInt("count_reserved",viewModel.counter)
}
}
}
时刻感知活动的生命周期,以便在恰当时候进行相应的逻辑控制。如:界面中发情网络请求,得到响应的时候界面已经关闭,这时候就不应该对相应继续处理。
如何在非活动的类中去感知活动的生命周期。1.活动中嵌入隐蔽的碎片2.手写监听器的方式感知
手写监听器的方式感知,缺点:要在活动中编写大量代码,不够优雅
lateinit var observer: MyObserver
observer = MyObserver()
override fun onStart() {
super.onStart()
observer.activityStart()
}
override fun onStop() {
super.onStop()
observer.activityStop()
}
class MyObserver : LifecycleObserver {
private val TAG = "MyObserver"
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun activityStart() {
Log.d(TAG, "activityStart: ")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun activityStop() {
Log.d(TAG, "activityStop: ")
}
}
在活动中调用
lifecycle.addObserver(MyObserver())
class MyObserver(val lifecycle:Lifecycle) : LifecycleObserver {
private val TAG = "MyObserver"
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun activityStart() {
Log.d(TAG, "activityStart: ")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun activityStop() {
Log.d(TAG, "activityStop: ")
}
}
有了对象后可以在任何地方调用
Lifecycle.currentState获知当前的生命周期状态
响应式编程组件,包含任何类型的数据,数据发生变化的时候通知给观察者。一般和ViewModel一起使用
将计数器用 LiveData 包装,然后 活动中去观察它,就能主动把变化通知给活动
class MainViewModel(countReserved: Int): ViewModel(){
//泛型指定为Int ML表示的是可变的LiveData。
//getValue 获取LD中的数据
//setValue 设置数据,只能在主线程
//postValue 非主线程设置数据
val counter = MutableLiveData()
init {
counter.value = countReserved
}
fun plusOne() {
val count = counter.value ?: 0
counter.value = count + 1
}
fun clear() {
counter.value = 0
}
}
class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycle.addObserver(MyObserver(lifecycle))
sp = getPreferences(Context.MODE_PRIVATE)
val countReserved = sp.getInt("count_reserved",0)
clearBtn.setOnClickListener {
viewModel.clear()
}
//不能直接创建实例,因为ViewModel的生命周期长于活动,如果活动创建的时候创建实例,那就无法保存数据
viewModel = ViewModelProviders.of(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
plusOneBtn.setOnClickListener {
viewModel.plusOne()
}
//任何livedata对象都有observe方法
//第一个参数是lifecycleowner,第二个参数是Observer接口,当数据发生变化的时候就会回调到这里
viewModel.counter.observe(this, Observer { count ->
infoText.text = count.toString()
})
}
override fun onPause() {
super.onPause()
sp.edit{
putInt("count_reserved",viewModel.counter.value ?: 0)
}
}
}
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
//优化写法
viewModel.counter.observe(this){ count ->
infoText.text = count.toString()
}
问题:暴露数据counter给外部,破坏了livedata的封装性
解决:暴露不可变的LiveData给外部,这样非ViewModel中就只能观察Livedata的数据变化
class MainViewModel(countReserved: Int): ViewModel(){
val counter:LiveData
get() = _counter
private val _counter = MutableLiveData()
init {
_counter.value = countReserved
}
fun plusOne() {
val count = counter.value ?: 0
_counter.value = count + 1
}
fun clear() {
_counter.value = 0
}
}
看书的逻辑
data class User(var firstName:String,var lastName:String,var age: Int) {
}
val userLiveData = MutableLiveData()
//当userLD数据发生变化后,map会监听到,然后转换函数后将数据通知给username的监听者
//保证数据的封装性
private val userLiveData = MutableLiveData()
//map接收原始livedata,第一个参数是一个转换函数
val username: LiveData = Transformations.map(userLiveData){ user ->
"${user.firstName} ${user.lastName}"
}
object Repository {
fun getUser(userId: String) : LiveData {
val liveData = MutableLiveData()
liveData.value = User(userId, userId, 0)
return liveData
}
}
fun getUser(userId: String): LiveData {
return Repository.getUser(userId)
}
//当用户调用这个函数的时候会给userIdLiveData设值
fun getUser(userId: String) {
userIdLiveData.value = userId
}
//userIdLiveData值发生变化后会通知,switchMap
private val userIdLiveData = MutableLiveData()
//switchMap函数会将不可观察的它转换为一个新的LiveData,我们观察这个新的就可以了
val user: LiveData = Transformations.switchMap(userIdLiveData){ userId ->
Repository.getUser(userId)
}
getUserBtn.setOnClickListener {
val userId = (0..10000).random().toString()
viewModel.getUser(userId)
}
viewModel.user.observe(this, Observer { user ->
infoText.text = user.firstName
})
问题 ViewModel 获取数据的方法可能是没有参数的,需要搭建一个空的LiveData
class MyViewModel : ViewModel() {
private val refreshLiveData = MutableLiveData()
val refreshResult = Transformations.switchMap(refreshLiveData){
Repository.refresh()
}
fun refresh() {
refreshLiveData.value = refreshLiveData.value
}
}
https://developer.android.google.cn/jetpack/androidx/releases/room?hl=zh_cn ROOM官网地址
//kapt只能在kotlin项目中使用,如果是java要使用,annotationProcessor
id 'kotlin-kapt'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
kapt "androidx.room:room-compiler:$room_version"
//注解Entity:声明为实体类
@Entity
data class User(var firstName:String,var lastName:String,var age: Int) {
//添加字段id,设置为主键,并且自动生成
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}
//Room识别成Dao
@Dao
interface UserDao {
//插入完把生成的主键返回
//对插入的时候已经有相应id进行处理
//解决插入冲突 它是取代就数据同时继续事务
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUser(user: User):Long
@Update
fun updateUser(newUser: User)
@Query("select * from User")
fun loadAllUsers(): List
@Query("select * from User where age > :age")
fun loadUserOlderThan(age: Int): List
@Delete
fun deleteUser(user: User)
@Query("delete from User where lastName = :lastName")
fun deleteUserByLastName(lastName: String):Int
}
val userDao = AppDatabase.getDatabase(this).userDao()
val user1 = User("Tom","Brady",40)
val user2 = User("Tom","Hanks",63)
addDataBtn.setOnClickListener {
thread {
user1.id = userDao.insertUser(user1)
user2.id = userDao.insertUser(user2)
}
}
updateDataBtn.setOnClickListener {
thread {
user1.age = 42
userDao.updateUser(user1)
}
}
deleteDataBtn.setOnClickListener {
thread {
userDao.deleteUserByLastName("Hanks")
}
}
queryDataBtn.setOnClickListener {
thread {
for (user in userDao.loadAllUsers()) {
Log.d(TAG, user.toString())
}
}
}
注:Room不允许在主线程中使用,添加一个方法就允许在主线程中使用,建议只在测试环境中用
AppDatabase::class.java,"app_database")
.allowMainThreadQueries()
.build().apply {
instance = this
}
AppDatabase::class.java,"app_database")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()//能够在数据库升级的时候删除旧表,创建新表
.build().apply {
instance = this
}
@Entity
data class Book(var name:String,var pages:Int) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}
@Dao
interface BookDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertBook(book:Book):Long
@Query("select * from Book")
fun loadAllBooks():List
}
//版本号和数据库包含的实体类,如果实体类不止一个用逗号分隔
//必须继承,指定抽象类,提供抽象方法,获取之前的Dao实例
@Database(version = 2, entities = [User::class,Book::class]) 1
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao():UserDao
abstract fun bookDao(): BookDao 2
companion object{
//数据库版本从1升级到2时候执行匿名类中的升级逻辑
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) { 3
database.execSQL("create table Book (id integer primary" +
" key autoincrement not null,name text not null," +
"pages integer not null)")
}
}
//单例模式
private var instance: AppDatabase? = null
@Synchronized
fun getDatabase(context: Context): AppDatabase {
instance?.let {
return it
}
//第一个参数只能使用这个,不能用context
//第二个参数是AppDatabase的class类型
return Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java,"app_database")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2) 4
.build().apply {
instance = this
}
}
}
}
如何给某张表加一个列
@Entity
data class Book(var name:String,var pages:Int,var author: String) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}
package com.android.jetpacktest
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
//版本号和数据库包含的实体类,如果实体类不止一个用逗号分隔
//必须继承,指定抽象类,提供抽象方法,获取之前的Dao实例
@Database(version = 3, entities = [User::class,Book::class]) 1
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao():UserDao
abstract fun bookDao(): BookDao
companion object{
val MIGRATION_2_3 = object : Migration(2, 3) { 2
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("alter table Book add column author text not null default 'unknown'")
}
}
//数据库版本从1升级到2时候执行匿名类中的升级逻辑
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("create table Book (id integer primary" +
" key autoincrement not null,name text not null," +
"pages integer not null)")
}
}
//单例模式
private var instance: AppDatabase? = null
@Synchronized
fun getDatabase(context: Context): AppDatabase {
instance?.let {
return it
}
//第一个参数只能使用这个,不能用context
//第二个参数是AppDatabase的class类型
return Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java,"app_database")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2, MIGRATION_2_3) 3
.build().apply {
instance = this
}
}
}
}
作用:处理一些定时执行的任务,可以根据操作系统的版本自动选择底层使用的是AlarmManager还是JobScheduler。支持周期性任务,链式任务处理。和服务没联系,在应用退出,手机重启依然会执行。适用于执行定期和服务器交互的任务:周期性的同步数据。不一定准时,因为为了减少耗电,会把几个任务放在一起执行。
implementation "androidx.work:work-runtime:2.7.1"
class SimpleWorker(context: Context, params:WorkerParameters):Worker(context,params) {
override fun doWork(): Result {
//后台任务逻辑,不会运行在主线程,可以编写耗时逻辑
Log.d("SimpleWorker","do work in SimpleWorker")
return Result.success()
//Result.failure()表示任务失败
//Result.retry()表示失败并且结合后面学的内容可以重新执行任务
}
}
添加按钮,如果按钮超出LL的范围可以用ScrollView滚动查看屏幕外的内容
doWorkBtn.setOnClickListener {
//构建单次运行的后台任务请求
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java).build()
//PeriodWorkRequest.Builder周期性运行后台任务,不过传入的周期间隔不能少于15分钟
WorkManager.getInstance(this).enqueue(request)
}
doWorkBtn.setOnClickListener {
//构建单次运行的后台任务请求
//PeriodWorkRequest.Builder周期性运行后台任务,不过传入的周期间隔不能少于15分钟
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInitialDelay(5,TimeUnit.MINUTES)//让任务在指定的延迟时间后执行。时间单位可以自由的指定:ms,s,m,h,d
.addTag("simple")//给后台任务请求添加标签,好处:同一组标签可以同时一下取消
//如果后台任务doWork返回了Result.retry()
//可以结合下面方法来重新执行任务
.setBackoffCriteria(BackoffPolicy.LINEAR,10,TimeUnit.SECONDS)
//第一个参数指定任务失败,下次重新执行以什么样的形式延迟。这个是线性
//EXPONENTIAL这个是指数
//第二个和第三个指定多久后执行,不能少于10分钟
.build()
WorkManager.getInstance(this).enqueue(request)
}
WorkManager.getInstance(this).cancelAllWorkByTag("simple")//通过标签来取消后台任务
WorkManager.getInstance(this).cancelWorkById(request.id)//通过id来关闭任务,坏处:只能关闭单个
WorkManager.getInstance(this).cancelAllWork()//关闭所有后台任务
//doWork返回值的利用
WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id).observe(this){workInfo ->
if ((workInfo.state == WorkInfo.State.SUCCEEDED)) {
Log.d(TAG, "do work success")
}else if(workInfo.state == WorkInfo.State.FAILED){
Log.d(TAG, "do work failed")
}
}
//链式任务
//假设有三个独立的后台任务
val sync = ...
val compress = ...
val upload = ...
/**
*前一个后台任务完成后,后一个任务才能执行。前面的如果被取消,后面的也就不会执行。
*WorkManager在国产手机上不稳定,不要用它写核心功能
*/
WorkManager.getInstance(this)
.beginWith(sync)//开启链式任务
.then(compress)//需要接上的链式任务
.then(upload)
.enqueue(request)
DSL:领域特定语言,别写出一些看似脱离原始语法结构的代码。
利用高阶函数实现DSL。实现类似依赖库中的内容
class Dependency{
val libraries = ArrayList()
fun implementation(lib: String) {
libraries.add(lib)
}
}
fun dependencies(block: Dependency.() -> Unit): List {
val dependency = Dependency()
dependency.block()
return dependency.libraries
}
fun main() {
//调用
dependencies {
implementation("com.squareup.retrofit2:retrofit2:2.6.1")
implementation("com.squareup.retrofit2:converter-gson2.6.1")
}
//获取所有添加到依赖库中的数据
val libraries = dependencies {
implementation("com.squareup.retrofit2:retrofit2:2.6.1")
implementation("com.squareup.retrofit2:converter-gson2.6.1")
}
for (lib in libraries) {
println(lib)
}
}
package com.android.jetpacktest
import java.lang.StringBuilder
class Td{
var content = ""
fun html() = "\n\t\t$content "
}
class Tr{
private val children = ArrayList()
fun td(block: Td.() -> String) {
val td = Td()
td.content = td.block()
children.add(td)
}
fun html():String{
val builder = StringBuilder()
builder.append("\n\t ")
for (childTag in children) {
builder.append(childTag.html())
}
return builder.toString()
}
}
class Table{
private val children = ArrayList ()
fun tr(block: Tr.() -> Unit) {
val tr = Tr()
tr.block()
children.add(tr)
}
fun html(): String {
val builder = StringBuilder()
builder.append("")
for (childTag in children) {
builder.append(childTag.html())
}
builder.append("\n
")
return builder.toString()
}
}
fun table(block: Table.() -> Unit): String {
val table = Table()
table.block()
return table.html()
}
fun main() {
val html = table {
tr{
td { "Apple" }
td { "Grape" }
td { "Orange" }
}
tr {
td { "Pear" }
td { "Banana" }
td { "Watermelon" }
}
}
println(html)
}
fun main() {
val html = table {
repeat(2) {
tr {
val fruits = listOf("Apple","Grape","Orange")
for (fruit in fruits) {
td { fruit }
}
}
}
}
println(html)
}
你可能感兴趣的:(安卓,android,kotlin,开发语言)