新的Android体系结构指南定义了一个好的Android应用程序应该遵循的一些关键原则,并为开发人员提供了一个安全的途径来创建一个好的应用程序。但是,该指南明确指出所提出的路线不是强制性的,最终决定是个人的; 开发人员应该决定采用哪种类型的架构。
根据该指南,一个好的Android应用程序应该提供一个牢固 的关注点分离, 并从一个模型驱动UI。任何不处理UI或操作系统交互的代码都不应该在Activity或Fragment中,因为尽可能保持它们干净将允许您避免许多与生命周期相关的问题。毕竟,系统可以随时销毁活动或碎片。此外,数据应由与UI隔离的模型处理,从而处理生命周期问题。
新推荐的架构
Android推荐的架构无法在我们所知的标准模式中轻松标注。它看起来像一个模型视图控制器模式,但它与系统的架构密切相关,很难使用已知的约定标记每个元素。但这并不重要,因为重要的是它依赖于新的架构组件来创建关注点分离,具有出色的可测试性和可维护性。而且更好的是,它很容易实现。
要了解Android团队提出的建议,我们必须了解架构组件的所有元素,因为它们将为我们带来繁重的工作。有四个组成部分,每一个特定的角色: Room
,ViewModel
,****LiveData
,和**Lifecycle
**。所有这些部分都有自己的责任,他们共同努力创建一个坚实的架构。让我们看一下拟议架构的简化图,以便更好地理解它。
如您所见,我们有三个主要元素,每个元素都有其责任。
-
在**
Activity
** 和**Fragment
代表的View
**层,不与业务逻辑和复杂的操作处理。它只配置视图,处理用户交互,最重要的是,观察和展示 **LiveData
**从中获取的元素ViewModel
。 -
的**
ViewModel
** 自动观察**Lifecycle
的视图的状态下,维持期间配置更改和其他Android生命周期事件的一致性。视图还要求从中获取数据Repository
,这是作为可观察的提供的LiveData
。重要的是要理解,ViewModel
从不View
直接引用,并且数据的更新总是由LiveData
**实体完成。 -
这**
Repository
**不是一个特殊的Android组件。它是一个简单的类,没有任何特定的实现,它负责从所有可用的源,从数据库到Web服务获取数据。它处理所有这些数据,通常将它们转换为可观察数据LiveData
并使它们可用于ViewModel
。 -
该**
Room
数据库是一个SQLite映射库,它有助于处理数据库。它会自动编写大量样板文件,在编译时检查错误,最重要的是,它可以直接返回带有observable的查询LiveData
**。
我相信你已经注意到我们已经谈了很多关于可观测量的东西。该Observer模式是的基地之一**LiveData
元素和Lifecycle
**感知组件。此模式允许对象通知观察者列表其状态或数据的任何更改。因此,当一个Activity观察一个LiveData
实体时,它会在该数据经过任何修改时收到更新。
另一个Android建议是使用依赖注入系统来巩固其架构,例如Google的Dagger 2 或使用服务定位器模式(这比DI更简单,但没有很多优点)。我们不会在本教程中介绍DI或服务定位器,但Envato Tuts +有一些关于这些主题的优秀教程。但是,请注意,使用Dagger 2和Android组件有一些特殊性,将在本系列的第二部分进行说明。
我们必须深入研究新组件的各个方面,以便能够真正理解和采用这种架构模型。但是,我们不会深入了解本教程中的所有细节。由于每个元素的复杂性,在本教程中,我们只讨论每个元素背后的一般概念,并查看一些简化的代码片段。我们将尝试覆盖足够的空间来展示组件并让您入门。但不要害怕,因为本系列中的未来文章将深入挖掘并涵盖架构组件的所有特性。
生命周期感知组件
大多数Android应用程序组件都附加了生命周期,这些生命周期由系统本身直接管理。直到最近,开发人员才能监控组件的状态并采取相应的措施,在适当的时候初始化和结束任务。然而,很容易混淆并犯这种类型的操作相关的错误。但是这个[android.arch.lifecycle]( )
包改变了这一切。
现在,活动和片段有一个[Lifecycle]( )
附加到它们的对象,可以通过[LifecycleObserver]( )
类来观察,比如[ViewModel]( )
实现此接口的任何对象。这意味着观察者将收到有关其正在观察的对象的状态更改的更新,例如暂停活动或何时启动。它还可以检查被观察对象的当前状态。因此,现在处理必须考虑框架生命周期的操作要容易得多。
现在,创建一个Activity
或Fragment
符合这一新标准,你要扩展LifecycleActivity
或LifecycleFragment
。但是,这可能并不总是必要的,因为Android团队的目标是将这些新工具与其框架完全集成。
class MainActivity : LifecycleActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
的LifecycleObserver接收Lifecycle事件,并且可以通过注解反应。不需要方法覆盖。
class MainActivityObserver : LifecycleObserver, AnkoLogger {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
info("onResume")
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
info("onPause")
}
该LiveData
组件
该LiveData
组件是一个数据持有者,包含可以观察到的值。鉴于观察者Lifecycle
在LiveData
实例化期间提供了一个,LiveData
将根据Lifecycle
状态行事。如果观察者的 Lifecycle
状态是 STARTED
或者 RESUMED
,观察者是active
; 否则就是inactive
。
LiveData
知道数据何时被更改,以及观察者是否active
应该接收更新。另一个有趣的特性LiveData
是它能够在观察者处于某种Lifecycle.State.DESTROYED
状态时将其移除,从而避免在活动和碎片观察时发生内存泄漏。
一个LiveData
必须实现 onActive
和 onInactive
方法。
class LocationLiveData(context: Context)
: LiveData<Location>(), AnkoLogger, LocationListener {
private val locationManager: LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
override fun onActive() {
info("onActive")
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this)
}
override fun onInactive() {
info("onInactive")
locationManager.removeUpdates(this)
}
// ....
}
要观察LiveData 组件,您必须致电observer(LifecycleOwner, Observer)。
class MainActivity : LifecycleActivity(), AnkoLogger {
fun observeLocation() {
val location = LocationLiveData(this)
location.observe(this,
Observer { location ->
info("location: $location")
})
}
}
该ViewModel
组件
新架构组件中最重要的一类是ViewModel
,它用于保存与UI相关的数据,在屏幕轮换等配置更改期间保持其完整性。在ViewModel
能够与交谈Repository
,渐渐LiveData
从它并使它又可以通过视图来观察。ViewModel
也不需要对Repository
after配置更改进行新调用,这会对代码进行大量优化。
要创建视图模型,请扩展ViewModel
该类。
class MainActivityViewModel : ViewModel() {
private var notes: MutableLiveData<List<String>>? = null
fun getNotes(): LiveData<List<String>> {
if (notes == null) {
notes = MutableLiveData<List<String>>()
loadNotes()
}
return notes!!
}
private fun loadNotes() {
// do async operation to fetch notes
}
}
要从视图访问,您可以致电ViewProviders.of(Activity|Fragment).get(ViewModel::class)。此工厂方法将根据需要返回该实例ViewModel或获取保留的实例。
class MainActivity : LifecycleActivity(), AnkoLogger {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProviders.of(this)
.get(MainActivityViewModel::class.java)
viewModel.getNotes().observe(
this, Observer {
notes -> info("notes: $notes")
}
)
}
}
该Room
组件
Android从一开始就支持SQLite; 然而,为了使它工作,总是需要写很多样板。此外,SQLite没有保存POJO(普通旧Java对象),也没有在编译时检查查询。一路来 Room
解决这些问题!它是一个SQLite映射库,能够持久化Java POJO,直接将查询转换为对象,在编译时检查错误,并LiveData
从查询结果中生成可观察对象 。Room
是一个对象关系映射 库,带有一些很酷的Android附加功能。
到目前为止,您可以完成大部分Room
能够使用其他ORM Android库的功能。但是,它们都没有得到官方支持,最重要的是,它们无法产生LifeData
结果。该Room
库非常适合作为拟议Android架构上的持久层。
要创建Room
数据库,您需要一个 @Entity
持久化,可以是任何Java POJO,一个@Dao
用于进行查询和输入/输出操作的接口,以及一个@Database
必须扩展的抽象类RoomDatabase
。
@Entity
class Note {
@PrimaryKey
var id: Long? = null
var text: String? = null
var date: Long? = null
}
@Dao
interface NoteDAO {
@Insert( onConflict = OnConflictStrategy.REPLACE )
fun insertNote(note: Note): Long
@Update( onConflict = OnConflictStrategy.REPLACE )
fun updateNote(note: Note): Int
@Delete
fun deleteNote(note: Note): Int
@Query("SELECT * FROM note")
fun findAllNotes(): LiveData<Note>
// on Kotlin the query arguments are renamed
// to arg[N], being N the argument number.
// on Java the arguments assume its original name
@Query("SELECT * FROM note WHERE id = :arg0")
fun findNoteById(id: Long): LiveData<Note>
}
@Database( entities = arrayOf(Note::class), version = 1)
abstract class Databse : RoomDatabase() {
abstract fun noteDAO(): NoteDAO
}
将架构组件添加到项目中
目前,要使用新的架构组件,您需要先将Google存储库添加到您的build.gradle
文件中。有关更多详细信息,请参阅官方指南。
`allprojects {`
`repositories {`
`jcenter()`
`// Add Google repository`
`maven { url '[https://maven.google.com]( )' }`
@Database( entities = arrayOf(Note::class), version = 1)
abstract class Databse : RoomDatabase() {
abstract fun noteDAO(): NoteDAO
}
将架构组件添加到项目中
目前,要使用新的架构组件,您需要先将Google存储库添加到您的build.gradle
文件中。有关更多详细信息,请参阅官方指南。
`allprojects {`
`repositories {`
`jcenter()`
`// Add Google repository`
`maven { url '[https://maven.google.com]( )' }`