Jetpack架构组件之Android Architecture

Android Jetpack 组件是库的集合,这些库是为协同工作而构建的,不过也可以单独采用,同时利用 Kotlin提高工作效率。可全部使用,也可混合搭配,这些库包括基础、架构、行为、界面四个模块,其中主要学习的是架构这个模块,也称为Android
Architecture,如图所示:
在这里插入图片描述

Data Binding Library

功能介绍:
Data Binding Library 可以在布局中通过表达式的语言绑定UI组件,可以在数据变化时更新Ui组件,减少很多样板代码,且提供更好的灵活性以及更强的兼容性,在Android 4.0以上设备运行。

环境配置,app的gralde中打开dataBinding的开关:

android {
    ...
    dataBinding {
        enabled = true
    }
}

基本使用,XML文件定义,data binding的XML文件以layout作为根tag, 节点中包含该XML绑定的对象,接下来才是页面的布局文件,例如activity_main.xml:

  <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
  
    <!--定义数据源-->
    <data>
        <variable name="user" type="com.djm.jetpackdemo.model.User"/>
    </data>

    <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <android.support.v7.widget.AppCompatTextView
                android:id="@+id/tv_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user.id}"  <!--直接引用数据源的值-->
        />

        <android.support.v7.widget.AppCompatTextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toBottomOf="@id/tv_id"
                android:text="@{user.name}"
        />
    </android.support.constraint.ConstraintLayout>

</layout>

如何进行数据绑定?当我们在layout文件中使用了data binding之后,ide会生成相应的binding class,默认的名字也是根据layout文件名生成,例如xml文件名为xxx_yyy.xml,那么生成的class名为XxxYyyBinding,例如demo_activity.xml,生成DemoActivityBinding:

class DataBindingAcy : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        /**
         * 获取dataBinding class 的两种方法
         */
        val binding:DemoActivityBinding = DataBindingUtil.setContentView(this,R.layout.demo_activity)
        val bindingOther:DemoActivityBinding = DemoActivityBinding.inflate(layoutInflater)
        binding.user = User(100,"Demo")

    }
}

表达式语言可以使用以下的一些操作符或者关键词:

  • 数学操作符:+ - * / %
  • 字符串拼接: +
  • 逻辑操作符:&& ||
  • 二元操作符: & | ^
  • 一元操作符:+ - ! ~
  • 移位操作符:>> >>> <<
  • 比较操作符:== > < >= <=
  • instanceof
  • Grouping()
  • character、String、numeric、null
  • Cast
  • 调用方法
  • 属性
  • 数组元素
  • 三元操作符

例如:

  <android.support.v7.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{String.valueOf(index + 1)"
                android:visibility="@{id < 10 ? View.GONE : View.VISIBLE}"
                android:hint='@{"hint_"+id}'
        />
        

三元操作符判断非空也可以用??来表述,例如:

android:text="@{user.firstName ?? user.secondName"}
//等同于
android:text="@{user.firstName != null ? user.firstName:user.lastName}"

以下的关键字需要不能在layout中使用:

  • this
  • super
  • new

data binding class可以避免空指针异常,例如当“@{user.name}"的user为空时,那么自动给这个值赋值为空,或者“@{user.id}"的user为空时,这个值默认设置为0.

在xml中使用集合:

 <data>
        <import type="java.util.List"/>
        <import type="java.util.Map"/>
        <variable name="list" type="List<String>"/>
        <variable name="map" type="Map<String,String>"/>
        <variable name="index" type="int"/>
        <variable name="key" type="String"/>
  </data>
    
   <android.support.v7.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{map[key]}"
     />

      <android.support.v7.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="list[index]"
        />

更多的用法可以参考官方文档…

如何在layout中进行事件处理?

Data binding允许我们通过表达式去处理view相关的事件,事件属性名由listener决定,例如View.OnClickListener拥有一个onClick()方法,那么这个事件的属性名就是android:onClick.

方法引用:

事件可以直接绑定给方法,例如:

class MyHandlers{
  fun onClickFriend(view:View){...}

xml中应用:

<data>
   <variable name="handlers" type="com.djm.MyHanlders"/>
 </data>
 
 <TextView 
 	android:layout_width="wrap_content"
 	android:layout_height="wrap_content"
 	android:onClick="@{handlers::onClickFriend}"/>
</TextView>

Listener bindings

例如:

class Presenter{
   fun onSaveClick(task:Task){}
    <android.support.v7.widget.AppCompatTextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="list[index]"
               android:onClick="@{() -> presenter.onSaveClick(task)}"
        />

处理可观察的数据对象

Data Binding Library可以使得对象、变量、集合成为可观察的。当这些可观察数据对象绑定了UI,observable UI就能自动更新。

基本使用,定义observable类型的model类:

class ObservableUser {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}

xml中:

  <variable name="observableUser" type="com.djm.jetpackdemo.model.ObservableUser"/>
 
  <TextView
       android:id="@+id/tv_id"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@{observableUser. firstName}"
   />
 

java类/kotlin中:

获取生成的DemoActivityBinding对象,然后就可以获取到observableUser对象,给它赋值,当这个对象变化时,Ui就会自动更新,不用手动去重新设置。

Handing Lifecycles

通常管理生命周期时,都是直接实现各个生命周期函数,然后再相应的进行各种处理。但是这样会导致代码臃肿且难以维护,Lifecycle-Aware组件根据当前activity、fragment的生命周期状态自动的调整它们的行为。

这个功能所需的类和接口在这个包下“andorid.arch.leftcyle"。

大部分的在Android Framework中定义的组件都有附加的生命周期。生命周期由操作系统或者framework代码进行管理,由于这些是内核代码所以应用程序只能够遵从,尽量避免不合适的过多工作引起内存泄漏或者应用程序crash。

设想如果我们有一个需要展示当前设备位置的activity,一般的实现如下:

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        Util.checkUserStatus{
           result ->
           if(result){
               myLocationListener.start(). //可能存在activity已经stop了,这里才回调,那么这个listener可能就一直都存活,一直无法stop。
            }
        }    
           
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}


实际应用场景上,可能需要在生命周期中管理很多这种类似的代码,也有可能造成生命周期的泄漏。
Lifecycle就是解决上面问题的,Lifecycle用两个枚举去追踪生命周期状态与其关联的组件,

Event

生命周期events事件由framework和Lifecycle进行派发,这些事件映射到activity和fragment的回调事件。

State

表示被Lifecycle追踪的组件的当前状态。

State状态图

Lifecycle可以通过添加方法注解来监控组件的生命周期,然后可以通过addObserver()来添加一个观察者,例如:

class MyObserver : LifecycleObserver {

   @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)  //绑定到Lifecycle的onResume()
   fun connectListener() {
       ...
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE).  //绑定到Lifecycle的onPause()
   fun disconnectListener() {
       ...
   }
}

那么当组件执行onResume()或者onPause()时,就会调用connectListener()或者disconnectListener().如何解决上面的生命周期泄漏的问题呢,可以添加一个开关,如下:


class MyActivity : AppCompatActivity() {
   private lateinit var myLocationListener: MyLocationListener

   override fun onCreate(...) {
       myLocationListener = MyLocationListener(this, lifecycle) { location ->
           // update UI
       }
       Util.checkUserStatus { result ->
           if (result) {
               myLocationListener.enable() //执行了回调才打开开关
           }
       }
   }
}

enable()中根据条件打开开关:

internal class MyLocationListener(
       private val context: Context,
       private val lifecycle: Lifecycle,
       private val callback: (Location) -> Unit
) {

   private var enabled = false

   @OnLifecycleEvent(Lifecycle.Event.ON_START)
   fun start() {
       if (enabled) {
           // connect
       }
   }

   fun enable() {
       enabled = true
       if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {  //根据是否已经started才打开开关
           // connect if not connected
       }
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
   fun stop() {
       // disconnect if connected
   }
}

activity中注册:


class MyActivity : Activity(), LifecycleOwner {

   private lateinit var mLifecycleRegistry: LifecycleRegistry

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       mLifecycleRegistry = LifecycleRegistry(this)
       mLifecycleRegistry.markState(Lifecycle.State.CREATED)
   }

   public override fun onStart() {
       super.onStart()
       mLifecycleRegistry.markState(Lifecycle.State.STARTED)
   }

   override fun getLifecycle(): Lifecycle {
       return mLifecycleRegistry
   }
}

LiveData

LiveData是一种可观察型的数据对象,并且是有生命周期意识的,确保在数据更新时,去更新处于active状态的组件。LiveData型对象知道观察者的生命周期,相比起Observable fields更加便利,所以在Android studio3.1之后的版本,都可以用LiveData去替换Observable fields.

ViewModel

ViewModel是用于存储和管理Ui相关数据的。ViewModel允许数据在配置发生改变时能够保留下来。ViewModel用LiveData来保存相关数据。如上面使用一样,实现ViewModel然后创建LiveData型数据对象。

创建LiveData数据对象:

 class NameLiveDataModel :ViewModel() {

    //create a LiveData with a String
    val currentName:MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}
 

LiveData如何绑定UI控件呢?

class NameLiveDataAcy : AppCompatActivity() {
    private lateinit var mModel:NameLiveDataModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: DemoActivityBinding = DataBindingUtil.setContentView(this, R.layout.demo_activity)
        binding.setLifecycleOwner(this)

        mModel = ViewModelProviders.of(this).get(NameLiveDataModel::class.java)

        //create the observer which updates the UI
        val nameObserver = Observer<String>{
            newName -> tv_firstName.text = newName
        }
        //通过observe()将LiveData型数据和UI控件绑定起来
        mModel.currentName.observe(this,nameObserver)
        
        //更新LiveData对象
        mBtn.setOnClickListener{
        	 val anotherName = "John Doe"
        	 mModel.currentName.setValue(anotherName)  //会调用nameObserver的onChanged(),然后更新控件UI
        }	 
    }
}

猜你喜欢

转载自blog.csdn.net/qq_26984087/article/details/89413989