SlideShare uma empresa Scribd logo
1 de 90
Baixar para ler offline
Android Architecture Component

In Real Life
@akexorcist

Lovely Android Developer @ Nextzy
● Just Work ● MVC
● Maintainable
● Testable
● MVP
● MVVM
● VIPER
● MVI
● Awesomeable
● ...
Android

Behavior
• Con;guration Changes

• Android Lifecycle

• State Management
hCps://www.youtube.com/watch?v=BlkJzgjzL0c
Android Architecture Component
AAC
Architecture

Component
• Lifecycles

• LiveData

• ViewModel

• Room
Do the 

Proof of Concept 

every time when you learn
something new
Lifecycles & LiveData
Use case
Timer
TimerLiveData
TimerListener
ViewModel
Fragment/Activity
Timer
class TimerListener(private val duration: Long,
private val preferences: TimerPreferences,
private val onTickCallback: OnTickListener? = null)
: LifecycleObserver {
...
private fun updateCountDownTimer() {
var lastDuration: Long = preferences.get()
if (lastDuration == 0L || lastDuration > duration) {
lastDuration = duration
}
countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
onTickCallback?.onTickUpdate(millisUntilFinished)
preferences.put(millisUntilFinished)
}
override fun onFinish() {
onTickCallback?.onTickUpdate(0)
isStarted = false
clearTimer()
}
TimerListener.kt
class TimerListener(private val duration: Long,
private val preferences: TimerPreferences,
private val onTickCallback: OnTickListener? = null)
: LifecycleObserver {
...
private fun updateCountDownTimer() {
var lastDuration: Long = preferences.get()
if (lastDuration == 0L || lastDuration > duration) {
lastDuration = duration
}
countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
onTickCallback?.onTickUpdate(millisUntilFinished)
preferences.put(millisUntilFinished)
}
override fun onFinish() {
onTickCallback?.onTickUpdate(0)
isStarted = false
clearTimer()
}
TimerListener.kt
class TimerListener(private val duration: Long,
private val preferences: TimerPreferences,
private val onTickCallback: OnTickListener? = null)
: LifecycleObserver {
...
private fun updateCountDownTimer() {
var lastDuration: Long = preferences.get()
if (lastDuration == 0L || lastDuration > duration) {
lastDuration = duration
}
countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
onTickCallback?.onTickUpdate(millisUntilFinished)
preferences.put(millisUntilFinished)
}
override fun onFinish() {
onTickCallback?.onTickUpdate(0)
isStarted = false
clearTimer()
}
TimerListener.kt
class TimerListener(private val duration: Long,
private val preferences: TimerPreferences,
private val onTickCallback: OnTickListener? = null)
: LifecycleObserver {
...
private fun updateCountDownTimer() {
var lastDuration: Long = preferences.get()
if (lastDuration == 0L || lastDuration > duration) {
lastDuration = duration
}
countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
onTickCallback?.onTickUpdate(millisUntilFinished)
preferences.put(millisUntilFinished)
}
override fun onFinish() {
onTickCallback?.onTickUpdate(0)
isStarted = false
clearTimer()
}
TimerListener.kt
...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private fun onStart() {
updateCountDownTimer()
if (isStarted) {
countdownTimer?.start()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
if (isStarted) {
countdownTimer?.cancel()
}
}
...
TimerListener.kt
...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private fun onStart() {
updateCountDownTimer()
if (isStarted) {
countdownTimer?.start()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
if (isStarted) {
countdownTimer?.cancel()
}
}
...
TimerListener.kt
...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private fun onStart() {
updateCountDownTimer()
if (isStarted) {
countdownTimer?.start()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
if (isStarted) {
countdownTimer?.cancel()
}
}
...
TimerListener.kt
...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private fun onStart() {
updateCountDownTimer()
if (isStarted) {
countdownTimer?.start()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
if (isStarted) {
countdownTimer?.cancel()
}
}
...
TimerListener.kt
...
fun start() {
if (!isStarted) {
isStarted = true
countdownTimer?.start()
}
}
fun clear() {
if (isStarted) {
isStarted = false
countdownTimer?.cancel()
}
clearTimer()
}
...
TimerListener.kt
...
fun start() {
if (!isStarted) {
isStarted = true
countdownTimer?.start()
}
}
fun clear() {
if (isStarted) {
isStarted = false
countdownTimer?.cancel()
}
clearTimer()
}
...
TimerListener.kt
...
fun start() {
if (!isStarted) {
isStarted = true
countdownTimer?.start()
}
}
fun clear() {
if (isStarted) {
isStarted = false
countdownTimer?.cancel()
}
clearTimer()
}
...
TimerListener.kt
class TimerLiveData(private val duration: Long,
private val preferences: TimerPreferences) : LiveData<Long>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: TimerListener
override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = TimerListener(
duration = duration,
preferences = preferences,
onTickCallback = object : TimerListener.OnTickListener {
override fun onTickUpdate(remainingMillis: Long) {
value = remainingMillis
}
})
}
...
TimerLiveData.kt
class TimerLiveData(private val duration: Long,
private val preferences: TimerPreferences) : LiveData<Long>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: TimerListener
override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = TimerListener(
duration = duration,
preferences = preferences,
onTickCallback = object : TimerListener.OnTickListener {
override fun onTickUpdate(remainingMillis: Long) {
value = remainingMillis
}
})
}
...
TimerLiveData.kt
class TimerLiveData(private val duration: Long,
private val preferences: TimerPreferences) : LiveData<Long>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: TimerListener
override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = TimerListener(
duration = duration,
preferences = preferences,
onTickCallback = object : TimerListener.OnTickListener {
override fun onTickUpdate(remainingMillis: Long) {
value = remainingMillis
}
})
}
...
TimerLiveData.kt
class TimerLiveData(private val duration: Long,
private val preferences: TimerPreferences) : LiveData<Long>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: TimerListener
override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = TimerListener(
duration = duration,
preferences = preferences,
onTickCallback = object : TimerListener.OnTickListener {
override fun onTickUpdate(remainingMillis: Long) {
value = remainingMillis
}
})
}
...
TimerLiveData.kt
class TimerLiveData(private val duration: Long,
private val preferences: TimerPreferences) : LiveData<Long>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: TimerListener
override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = TimerListener(
duration = duration,
preferences = preferences,
onTickCallback = object : TimerListener.OnTickListener {
override fun onTickUpdate(remainingMillis: Long) {
value = remainingMillis
}
})
}
...
TimerLiveData.kt
...
fun start() {
listener.start()
}
fun clear() {
listener.clear()
value = 0L
}
override fun onActive() {
lifecycle.addObserver(listener)
}
override fun onInactive() {
lifecycle.removeObserver(listener)
}
...
TimerLiveData.kt
...
fun start() {
listener.start()
}
fun clear() {
listener.clear()
value = 0L
}
override fun onActive() {
lifecycle.addObserver(listener)
}
override fun onInactive() {
lifecycle.removeObserver(listener)
}
...
TimerLiveData.kt
...
fun start() {
listener.start()
}
fun clear() {
listener.clear()
value = 0L
}
override fun onActive() {
lifecycle.addObserver(listener)
}
override fun onInactive() {
lifecycle.removeObserver(listener)
}
...
TimerLiveData.kt
class TimerViewModel @Inject constructor(timerPreferences: TimerPreferences)
: ViewModel() {
val timerLiveData = TimerLiveData(
duration = TimeUnit.SECONDS.toMillis(30),
preferences = timerPreferences
)
fun startTimer() {
timerLiveData.start()
}
fun clearTimer() {
timerLiveData.clear()
}
}
TimerViewModel.kt
class TimerViewModel @Inject constructor(timerPreferences: TimerPreferences)
: ViewModel() {
val timerLiveData = TimerLiveData(
duration = TimeUnit.SECONDS.toMillis(30),
preferences = timerPreferences
)
fun startTimer() {
timerLiveData.start()
}
fun clearTimer() {
timerLiveData.clear()
}
}
TimerViewModel.kt
class TimerActivity : AppCompatActivity() {
private lateinit var viewModel: TimerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java)
viewModel.timerLiveData.observe(this, timerObserver)
viewModel.startTimer()
}
private val timerObserver = Observer<Long> { remainingMillis ->
if (remainingMillis > 0) {
onTimerUpdate(remainingMillis)
} else {
onTimerEnd()
}
}
...
TimerActivity.kt
class TimerActivity : AppCompatActivity() {
private lateinit var viewModel: TimerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java)
viewModel.timerLiveData.observe(this, timerObserver)
viewModel.startTimer()
}
private val timerObserver = Observer<Long> { remainingMillis ->
if (remainingMillis > 0) {
onTimerUpdate(remainingMillis)
} else {
onTimerEnd()
}
}
...
TimerActivity.kt
class TimerActivity : AppCompatActivity() {
private lateinit var viewModel: TimerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java)
viewModel.timerLiveData.observe(this, timerObserver)
viewModel.startTimer()
}
private val timerObserver = Observer<Long> { remainingMillis ->
if (remainingMillis > 0) {
onTimerUpdate(remainingMillis)
} else {
onTimerEnd()
}
}
...
TimerActivity.kt
class TimerActivity : AppCompatActivity() {
private lateinit var viewModel: TimerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java)
viewModel.timerLiveData.observe(this, timerObserver)
viewModel.startTimer()
}
private val timerObserver = Observer<Long> { remainingMillis ->
if (remainingMillis > 0) {
onTimerUpdate(remainingMillis)
} else {
onTimerEnd()
}
}
...
TimerActivity.kt
Google API Client for Android
class GoogleApiClientListener(private val builder: GoogleApiClient.Builder,
private val api: Api<out Api.ApiOptions.NotRequiredOptions>,
private val callback: Callback? = null) : LifecycleObserver {
private lateinit var googleApiClient: GoogleApiClient
init {
googleApiClient = builder.apply {
addOnConnectionFailedListener { result: ConnectionResult ->
callback?.onFailure(result)
}
addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
override fun onConnected(connectionHint: Bundle?) {
callback?.onConnected(googleApiClient)
}
override fun onConnectionSuspended(cause: Int) {
callback?.onSuspended(cause)
}
})
addApi(api)
}.build()
}
GoogleApiClientListener.kt
...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
if (!googleApiClient.isConnected || !googleApiClient.isConnecting) {
googleApiClient.connect()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
if (googleApiClient.isConnected) {
googleApiClient.disconnect()
}
}
fun getGoogleApiClient(): GoogleApiClient = googleApiClient
interface Callback {
fun onConnected(client: GoogleApiClient)
fun onFailure(result: ConnectionResult)
fun onSuspended(cause: Int)
}
GoogleApiClientListener.kt
class GoogleApiClientLiveData(private val builder: GoogleApiClient.Builder,
private val api: Api<out Api.ApiOptions.NotRequiredOptions>)
: LiveData<GoogleApiResult>() {
private lateinit var lifecycle: Lifecycle
private lateinit var listener: GoogleApiClientListener
override fun observe(owner: LifecycleOwner, observer: Observer<in GoogleApiResult>) {
super.observe(owner, observer)
lifecycle = owner.lifecycle
listener = GoogleApiClientListener(builder, api, callback)
}
override fun onActive() {
lifecycle.addObserver(listener)
}
override fun onInactive() {
lifecycle.removeObserver(listener)
}
...
GoogleApiClientLiveData.kt
private val callback = object : GoogleApiClientListener.Callback {
override fun onConnected(client: GoogleApiClient) {
value = GoogleApiResult(
status = GoogleApiResult.STATUS_SUCCESS,
errorCode = 0,
errorMessage = null,
client = listener.getGoogleApiClient())
}
override fun onFailure(result: ConnectionResult) {
value = GoogleApiResult(
status = GoogleApiResult.STATUS_FAILURE,
errorCode = result.errorCode,
errorMessage = result.errorMessage,
client = null)
}
override fun onSuspended(cause: Int) {
value = GoogleApiResult(
status = GoogleApiResult.STATUS_SUCCESS,
errorCode = cause,
errorMessage = null,
client = null)
}
}
GoogleApiClientLiveData.kt
data class GoogleApiResult(var status: String,
var errorCode: Int,
var errorMessage: String?,
var client: GoogleApiClient?) {
companion object {
const val STATUS_SUCCESS = "success"
const val STATUS_FAILURE = "failure"
const val STATUS_SUSPENDED = "suspended"
}
fun isSuccess(): Boolean = status == STATUS_SUCCESS
fun isFailure(): Boolean = status == STATUS_FAILURE
fun isSuspended(): Boolean = status == STATUS_SUSPENDED
}
GoogleApiResult.kt
class GoogleApiViewModel @Inject constructor(private var builder: GoogleApiClient.Builder)
: ViewModel() {
val googleApiClientLiveData = GoogleApiClientLiveData(builder, LocationServices.API)
}
GoogleApiViewModel.kt
class GoogleApiActivity : AppCompatActivity() {
private lateinit var viewModel: GoogleApiViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(GoogleApiViewModel::class.java)
viewModel.googleApiClientLiveData.observe(this, googleApiClientObserver)
}
private val googleApiClientObserver = Observer<GoogleApiResult> { result ->
when (result.status) {
GoogleApiResult.STATUS_SUCCESS -> onGoogleApiClientConnected(result.client)
GoogleApiResult.STATUS_FAILURE -> onGoogleApiClientFailure(result.errorMessage)
GoogleApiResult.STATUS_SUSPENDED -> onGoogleApiClientSuspend(result.errorCode)
}
}
...
GoogleApiActivity.kt
Firebase Realtime Database
class RealtimeDatabaseLiveData(userId: String,
firebaseDatabase: FirebaseDatabase) : LiveData<List<Post>>() {
private val databaseReference = firebaseDatabase.getReference(userId).child("posts")
override fun onActive() {
databaseReference.addValueEventListener(valueEventListener)
}
override fun onInactive() {
databaseReference.removeEventListener(valueEventListener)
}
private val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
value = dataSnapshot.children.map { snapshot ->
snapshot.getValue(Post::class.java)!!
}
}
override fun onCancelled(databaseError: DatabaseError) {}
}
}
RealtimeDatabaseLiveData.kt
Activity
Fragment Fragment
Fragment
Activity
Fragment Fragment
Fragment
ShareViewModel=SVM
SVM
SVM
SVM
SVM
class SingleEventLiveData<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, Observer { t: T? ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
override fun setValue(@Nullable t: T?) {
mPending.set(true)
super.setValue(t)
}
override fun postValue(value: T?) {
mPending.set(true)
super.postValue(value)
}
} SingleEventLiveData.kt
class HomeEventViewModel : ViewModel() {
val updateFeedEventLiveData = SingleEventLiveData<String>()
fun send(message: String) {
updateFeedEventLiveData.value = message
}
}
HomeEventViewModel.kt
1 Event : 1 SingleEventLiveData
Don't reuse
Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
}
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
} Fragment/Activity
ViewModel
Repository
Database Network
NetworkBoundResource
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
}.asLiveData()
}
fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> {
return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserInfo) { /*...*/ }
override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserInfo> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
override fun convertToResultType(response: UserInfo): UserInfo { /*...*/ }
override fun callFailed(errorMessage: String) { /*...*/ }
}.asLiveData()
}
fun getUserInfo(userId: String): LiveData<Resource<UserAccount>> {
return object : NetworkBoundResource<UserAccount, UserInfo>(appExecutors) {
override fun saveCallResult(item: UserAccount) { /*...*/ }
override fun shouldFetch(data: UserAccount?): Boolean { /*...*/ }
override fun loadFromDb(): LiveData<UserAccount> { /*...*/ }
override fun createCall(): LiveData<UserInfo> { /*...*/ }
override fun convertToResultType(response: UserInfo): UserAccount { /*...*/ }
override fun callFailed(errorMessage: String) { /*...*/ }
}.asLiveData()
}
fun getUserInfo(userId: String): LiveData<Resource<UserAccount>> {
return object : DirectNetworkBoundResource<UserAccount, UserInfo>(appExecutors) {
override fun createCall(): LiveData<UserInfo> { /*...*/ }
override fun convertToResultType(response: UserInfo): UserAccount { /*...*/ }
override fun callFailed(errorMessage: String) { /*...*/ }
}.asLiveData()
}
Fragment/Activity
ViewModel
Repository
Database Network
Fragment/Activity
ViewModel
Repository
Database Network
Repository
Database Network
Fragment/Activity
ViewModel
Repository
Database Network
Repository
Database Network
Repository
Database Network
ViewModel
Repository
ViewModel
Network
Retrofit
Call
Repository
ViewModel
Network
Retrofit + LiveDataCallAdapter
LiveData
LiveData
LiveData
Log In Homeapi/v1/login
What people think about log in flow
Log In Homeapi/v1/login api/v1/:userId/conDgs
api/v1/:userId/info
api/v1/:userId/banners
What log in flow actually be in real life
api/v1/:userId/caGs
Repository
ViewModel
Network
Retrofit +
LiveData
LiveData
LiveData
LiveDataCallAdapter
Repository
ViewModel
Network
Retrofit +
Single/Observable

RxJava/RxKotlin
LiveData
LiveData
RxJavaCallAdapter
+ Rx2LiveDataConverter
object Single2LiveDataConverter {
fun <T> convert(single: Single<T>): LiveData<T> {
return object : LiveData<T>() {
private var disposable: Disposable? = null
override fun onActive() {
super.onActive()
disposable = single
.subscribeOn(Schedulers.io())
.subscribe(Consumer<T> { this.postValue(it) })
}
override fun onInactive() {
super.onInactive()
if (disposable != null) disposable!!.dispose()
}
}
}
}
Single2LiveDataConveWer.kt
fun getNecessaryInfo(): Single<Response<LogInResult>> {
return Single.zip(
getUserInfo(),
getBannerList(),
BiFunction<Response<UserInfoResult>,
Response<BannerListResult>,
Response<LogInResult>> { userInfoResponse, bannerListResponse ->
Response.success(
LogInResult(
userInfoResponse.body(),
bannerListResponse.body()
)
)
})
}
override
fun createCall(): LiveData<Response<LogInResult>> {
return Single2LiveDataConverter.convert(getNecessaryInfo())
}
Deferred2LiveData.kt
Repository
ViewModel
Network
Retrofit +
Single/Observable

RxJava/RxKotlin
LiveData
LiveData
RxJavaCallAdapter
+ Rx2LiveDataConverter
Repository
ViewModel
Network
Retrofit
Call
LiveData
LiveData
+ Coroutine
Deferred2LiveDataConverter
class Deferred2LiveData<T>(var deferred: Deferred<Response<T>>)
: LiveData<ApiResponse<T>>() {
fun execute() {
launch(CommonPool) {
try {
val result: Response<T> = deferred.await()
postValue(ApiResponse.create(result))
} catch (e: Exception) {
postValue(ApiResponse.create(e))
}
}
}
}
Deferred2LiveData.kt
override
fun createCall(): LiveData<Response<LogInResult>> {
return Deferred2LiveData(async {

LogInResult(getUserInfo().execute(), getBannerList().execute())
})
}
Deferred2LiveData.kt
Fragment/Activity
ViewModel
Repository
Database Network
Fragment/Activity
ViewModel
UseCase
Database Network
Repository
Fragment/Activity
ViewModel
UseCase
Database Network
Repository
Data Layer
Presentation Layer
Domain Layer
Fragment/Activity
ViewModel
UseCase
Database Network
Repository
UseCaseUseCase
Fragment/Activity
ViewModel
UseCase
Database Network
Repository
UseCaseUseCase
LiveDataLiveDataLiveData
LiveData LiveDataLiveData
class SessionDetailViewModel @Inject constructor(
private val signInViewModelDelegate: SignInViewModelDelegate,
private val loadUserSessionUseCase: LoadUserSessionUseCase,
private val loadRelatedSessionUseCase: LoadUserSessionsUseCase,
private val starEventUseCase: StarEventUseCase,
private val reservationActionUseCase: ReservationActionUseCase,
private val getTimeZoneUseCase: GetTimeZoneUseCase,
private val snackbarMessageManager: SnackbarMessageManager,
timeProvider: TimeProvider,
private val networkUtils: NetworkUtils,
private val analyticsHelper: AnalyticsHelper
) : ViewModel(), SessionDetailEventListener, EventActions,
SignInViewModelDelegate by signInViewModelDelegate {
private val loadUserSessionResult:
MediatorLiveData<Result<LoadUserSessionUseCaseResult>>
private val loadRelatedUserSessions: LiveData<Result<LoadUserSessionsUseCaseResult
private val sessionTimeRelativeState: LiveData<TimeUtils.SessionRelativeTimeState>
class SessionDetailFragment : DaggerFragment() {
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
...
val starMenu = menu.findItem(R.id.menu_item_star)
sessionDetailViewModel.shouldShowStarInBottomNav.observe(this, Observer {
it?.let {
if (it) {
starMenu.setVisible(true)
} else {
starMenu.setVisible(false)
}
}
})
sessionDetailViewModel.userEvent.observe(this, Observer {
it?.let {
if (it.isStarred) {
starMenu.setIcon(R.drawable.ic_star)
} else {
starMenu.setIcon(R.drawable.ic_star_border)
}
sessionDetailViewModel.session.observe(this, Observer {
room = it?.room
shareString = if (it == null) {
""
} else {
getString(R.string.share_text_session_detail, it.title, it.sessionUrl)
}
})
Google I/O 2018 Source Code
hCps://github.com/google/iosched
hCps://github.com/googlesamples/android-architecture-components
Samples for AAC
hCps://developer.android.com/topic/libraries/architecture/
AAC Documentation
Thank you!

@akexorcist

Mais conteúdo relacionado

Mais procurados

Programação assíncrona utilizando Coroutines
Programação assíncrona utilizando CoroutinesProgramação assíncrona utilizando Coroutines
Programação assíncrona utilizando CoroutinesDiego Gonçalves Santos
 
Aplicações Assíncronas no Android com Coroutines e Jetpack
Aplicações Assíncronas no Android com Coroutines e JetpackAplicações Assíncronas no Android com Coroutines e Jetpack
Aplicações Assíncronas no Android com Coroutines e JetpackNelson Glauber Leal
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard LibraryNelson Glauber Leal
 
The Ring programming language version 1.10 book - Part 59 of 212
The Ring programming language version 1.10 book - Part 59 of 212The Ring programming language version 1.10 book - Part 59 of 212
The Ring programming language version 1.10 book - Part 59 of 212Mahmoud Samir Fayed
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using RoomNelson Glauber Leal
 
Creating Ext GWT Extensions and Components
Creating Ext GWT Extensions and ComponentsCreating Ext GWT Extensions and Components
Creating Ext GWT Extensions and ComponentsSencha
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackNelson Glauber Leal
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackNelson Glauber Leal
 
Sequencing Audio Using React and the Web Audio API
Sequencing Audio Using React and the Web Audio APISequencing Audio Using React and the Web Audio API
Sequencing Audio Using React and the Web Audio APIVincent Riemer
 
WaveEngine 2D components
WaveEngine 2D componentsWaveEngine 2D components
WaveEngine 2D componentswaveengineteam
 
Advanced Akka For Architects
Advanced Akka For ArchitectsAdvanced Akka For Architects
Advanced Akka For ArchitectsLightbend
 
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019Codemotion
 
Deep Dive into Zone.JS
Deep Dive into Zone.JSDeep Dive into Zone.JS
Deep Dive into Zone.JSIlia Idakiev
 
外部環境への依存をテストする
外部環境への依存をテストする外部環境への依存をテストする
外部環境への依存をテストするShunsuke Maeda
 
OSGi and Eclipse RCP
OSGi and Eclipse RCPOSGi and Eclipse RCP
OSGi and Eclipse RCPEric Jain
 
WaveEngine 3D components
WaveEngine 3D componentsWaveEngine 3D components
WaveEngine 3D componentswaveengineteam
 
The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210Mahmoud Samir Fayed
 

Mais procurados (20)

Programação assíncrona utilizando Coroutines
Programação assíncrona utilizando CoroutinesProgramação assíncrona utilizando Coroutines
Programação assíncrona utilizando Coroutines
 
Advanced #2 threading
Advanced #2   threadingAdvanced #2   threading
Advanced #2 threading
 
Aplicações Assíncronas no Android com Coroutines e Jetpack
Aplicações Assíncronas no Android com Coroutines e JetpackAplicações Assíncronas no Android com Coroutines e Jetpack
Aplicações Assíncronas no Android com Coroutines e Jetpack
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
 
The Ring programming language version 1.10 book - Part 59 of 212
The Ring programming language version 1.10 book - Part 59 of 212The Ring programming language version 1.10 book - Part 59 of 212
The Ring programming language version 1.10 book - Part 59 of 212
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using Room
 
Creating Ext GWT Extensions and Components
Creating Ext GWT Extensions and ComponentsCreating Ext GWT Extensions and Components
Creating Ext GWT Extensions and Components
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Aplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & JetpackAplicações assíncronas no Android com Coroutines & Jetpack
Aplicações assíncronas no Android com Coroutines & Jetpack
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
 
Sequencing Audio Using React and the Web Audio API
Sequencing Audio Using React and the Web Audio APISequencing Audio Using React and the Web Audio API
Sequencing Audio Using React and the Web Audio API
 
WaveEngine 2D components
WaveEngine 2D componentsWaveEngine 2D components
WaveEngine 2D components
 
Advanced Akka For Architects
Advanced Akka For ArchitectsAdvanced Akka For Architects
Advanced Akka For Architects
 
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019
Matteo Antony Mistretta - Refactoring into React hooks - Codemotion Rome 2019
 
Deep Dive into Zone.JS
Deep Dive into Zone.JSDeep Dive into Zone.JS
Deep Dive into Zone.JS
 
外部環境への依存をテストする
外部環境への依存をテストする外部環境への依存をテストする
外部環境への依存をテストする
 
OSGi and Eclipse RCP
OSGi and Eclipse RCPOSGi and Eclipse RCP
OSGi and Eclipse RCP
 
WaveEngine 3D components
WaveEngine 3D componentsWaveEngine 3D components
WaveEngine 3D components
 
Eddystone beacons demo
Eddystone beacons demoEddystone beacons demo
Eddystone beacons demo
 
The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210
 

Semelhante a Android Architecture Component in Real Life

Android architecture component - FbCircleDev Yogyakarta Indonesia
Android architecture component - FbCircleDev Yogyakarta IndonesiaAndroid architecture component - FbCircleDev Yogyakarta Indonesia
Android architecture component - FbCircleDev Yogyakarta IndonesiaPratama Nur Wijaya
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfssuserb6c2641
 
Military time and Standard time, JavaOne of the assignments given .pdf
Military time and Standard time, JavaOne of the assignments given .pdfMilitary time and Standard time, JavaOne of the assignments given .pdf
Military time and Standard time, JavaOne of the assignments given .pdfmarketing413921
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter ClientKenji Tanaka
 
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.UA Mobile
 
Rule Language for IoT
Rule Language for IoTRule Language for IoT
Rule Language for IoTPhil Windley
 
Circuit Breakers with Swift
Circuit Breakers with SwiftCircuit Breakers with Swift
Circuit Breakers with SwiftEmanuel Guerrero
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at NetflixC4Media
 
import java.awt.; import java.awt.event.; import javax.swing..pdf
import java.awt.; import java.awt.event.; import javax.swing..pdfimport java.awt.; import java.awt.event.; import javax.swing..pdf
import java.awt.; import java.awt.event.; import javax.swing..pdfaparnawatchcompany
 
Snapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNLSnapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNLLars Lockefeer
 
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015Un monde où 1 ms vaut 100 M€ - Devoxx France 2015
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015ThierryAbalea
 
Refactoring to Immutability
Refactoring to ImmutabilityRefactoring to Immutability
Refactoring to ImmutabilityKevlin Henney
 

Semelhante a Android Architecture Component in Real Life (20)

Android architecture component - FbCircleDev Yogyakarta Indonesia
Android architecture component - FbCircleDev Yogyakarta IndonesiaAndroid architecture component - FbCircleDev Yogyakarta Indonesia
Android architecture component - FbCircleDev Yogyakarta Indonesia
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
 
Military time and Standard time, JavaOne of the assignments given .pdf
Military time and Standard time, JavaOne of the assignments given .pdfMilitary time and Standard time, JavaOne of the assignments given .pdf
Military time and Standard time, JavaOne of the assignments given .pdf
 
Pharos
PharosPharos
Pharos
 
Android TDD & CI
Android TDD & CIAndroid TDD & CI
Android TDD & CI
 
C++ L08-Classes Part1
C++ L08-Classes Part1C++ L08-Classes Part1
C++ L08-Classes Part1
 
Qt Application Programming with C++ - Part 2
Qt Application Programming with C++ - Part 2Qt Application Programming with C++ - Part 2
Qt Application Programming with C++ - Part 2
 
I os 06
I os 06I os 06
I os 06
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter Client
 
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
 
Rule Language for IoT
Rule Language for IoTRule Language for IoT
Rule Language for IoT
 
Circuit Breakers with Swift
Circuit Breakers with SwiftCircuit Breakers with Swift
Circuit Breakers with Swift
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at Netflix
 
import java.awt.; import java.awt.event.; import javax.swing..pdf
import java.awt.; import java.awt.event.; import javax.swing..pdfimport java.awt.; import java.awt.event.; import javax.swing..pdf
import java.awt.; import java.awt.event.; import javax.swing..pdf
 
Snapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNLSnapshot Testing @ CocoaheadsNL
Snapshot Testing @ CocoaheadsNL
 
Ceilometer + Heat = Alarming
Ceilometer + Heat = Alarming Ceilometer + Heat = Alarming
Ceilometer + Heat = Alarming
 
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015Un monde où 1 ms vaut 100 M€ - Devoxx France 2015
Un monde où 1 ms vaut 100 M€ - Devoxx France 2015
 
Refactoring to Immutability
Refactoring to ImmutabilityRefactoring to Immutability
Refactoring to Immutability
 
Current State of Coroutines
Current State of CoroutinesCurrent State of Coroutines
Current State of Coroutines
 
Circuit breaker
Circuit breakerCircuit breaker
Circuit breaker
 

Mais de Somkiat Khitwongwattana

What's new in Android - Google I/O Extended Bangkok 2022
What's new in Android - Google I/O Extended Bangkok 2022What's new in Android - Google I/O Extended Bangkok 2022
What's new in Android - Google I/O Extended Bangkok 2022Somkiat Khitwongwattana
 
Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Somkiat Khitwongwattana
 
What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018Somkiat Khitwongwattana
 
New things that android developer should not miss in 2019
New things that android developer should not miss in 2019New things that android developer should not miss in 2019
New things that android developer should not miss in 2019Somkiat Khitwongwattana
 
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018Somkiat Khitwongwattana
 
Deep Dive Into Repository - Android Architecture Components
Deep Dive Into Repository - Android Architecture ComponentsDeep Dive Into Repository - Android Architecture Components
Deep Dive Into Repository - Android Architecture ComponentsSomkiat Khitwongwattana
 
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017Introduction to Architecture Components @ Google I/O Extended Bangkok 2017
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017Somkiat Khitwongwattana
 
What's new in Android O @ Google I/O Extended Bangkok 2017
What's new in Android O @ Google I/O Extended Bangkok 2017What's new in Android O @ Google I/O Extended Bangkok 2017
What's new in Android O @ Google I/O Extended Bangkok 2017Somkiat Khitwongwattana
 
Smart Lock for Password @ Game DevFest Bangkok 2015
Smart Lock for Password @ Game DevFest Bangkok 2015Smart Lock for Password @ Game DevFest Bangkok 2015
Smart Lock for Password @ Game DevFest Bangkok 2015Somkiat Khitwongwattana
 
Whats new in Android Development Tools @ I/O Rewind Bangkok
Whats new in Android Development Tools @ I/O Rewind BangkokWhats new in Android Development Tools @ I/O Rewind Bangkok
Whats new in Android Development Tools @ I/O Rewind BangkokSomkiat Khitwongwattana
 
What's new in Google Play Services @ I/O Rewind Bangkok
What's new in Google Play Services @ I/O Rewind BangkokWhat's new in Google Play Services @ I/O Rewind Bangkok
What's new in Google Play Services @ I/O Rewind BangkokSomkiat Khitwongwattana
 
ListView and Custom ListView on Android Development [Thai]
ListView and Custom ListView on Android Development [Thai]ListView and Custom ListView on Android Development [Thai]
ListView and Custom ListView on Android Development [Thai]Somkiat Khitwongwattana
 

Mais de Somkiat Khitwongwattana (17)

What's new in Android - Google I/O Extended Bangkok 2022
What's new in Android - Google I/O Extended Bangkok 2022What's new in Android - Google I/O Extended Bangkok 2022
What's new in Android - Google I/O Extended Bangkok 2022
 
Canvas API in Android
Canvas API in AndroidCanvas API in Android
Canvas API in Android
 
Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30
 
What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018
 
New things that android developer should not miss in 2019
New things that android developer should not miss in 2019New things that android developer should not miss in 2019
New things that android developer should not miss in 2019
 
Bitmap management like a boss
Bitmap management like a bossBitmap management like a boss
Bitmap management like a boss
 
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018
Hello, Android Studio 3.2 & Android App Bundle @ I/O Extended Bangkok 2018
 
Deep Dive Into Repository - Android Architecture Components
Deep Dive Into Repository - Android Architecture ComponentsDeep Dive Into Repository - Android Architecture Components
Deep Dive Into Repository - Android Architecture Components
 
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017Introduction to Architecture Components @ Google I/O Extended Bangkok 2017
Introduction to Architecture Components @ Google I/O Extended Bangkok 2017
 
What's new in Android O @ Google I/O Extended Bangkok 2017
What's new in Android O @ Google I/O Extended Bangkok 2017What's new in Android O @ Google I/O Extended Bangkok 2017
What's new in Android O @ Google I/O Extended Bangkok 2017
 
Pokemon GO 101@Nextzy
Pokemon GO 101@NextzyPokemon GO 101@Nextzy
Pokemon GO 101@Nextzy
 
Advance Android Layout Walkthrough
Advance Android Layout WalkthroughAdvance Android Layout Walkthrough
Advance Android Layout Walkthrough
 
Interface Design for Mobile Application
Interface Design for Mobile ApplicationInterface Design for Mobile Application
Interface Design for Mobile Application
 
Smart Lock for Password @ Game DevFest Bangkok 2015
Smart Lock for Password @ Game DevFest Bangkok 2015Smart Lock for Password @ Game DevFest Bangkok 2015
Smart Lock for Password @ Game DevFest Bangkok 2015
 
Whats new in Android Development Tools @ I/O Rewind Bangkok
Whats new in Android Development Tools @ I/O Rewind BangkokWhats new in Android Development Tools @ I/O Rewind Bangkok
Whats new in Android Development Tools @ I/O Rewind Bangkok
 
What's new in Google Play Services @ I/O Rewind Bangkok
What's new in Google Play Services @ I/O Rewind BangkokWhat's new in Google Play Services @ I/O Rewind Bangkok
What's new in Google Play Services @ I/O Rewind Bangkok
 
ListView and Custom ListView on Android Development [Thai]
ListView and Custom ListView on Android Development [Thai]ListView and Custom ListView on Android Development [Thai]
ListView and Custom ListView on Android Development [Thai]
 

Último

Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 

Último (20)

Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 

Android Architecture Component in Real Life

  • 1. Android Architecture Component In Real Life @akexorcist Lovely Android Developer @ Nextzy
  • 2. ● Just Work ● MVC ● Maintainable ● Testable ● MVP ● MVVM ● VIPER ● MVI ● Awesomeable ● ...
  • 3. Android Behavior • Con;guration Changes • Android Lifecycle • State Management
  • 4.
  • 7. AAC
  • 9. Do the Proof of Concept every time when you learn something new
  • 11. Timer
  • 13. class TimerListener(private val duration: Long, private val preferences: TimerPreferences, private val onTickCallback: OnTickListener? = null) : LifecycleObserver { ... private fun updateCountDownTimer() { var lastDuration: Long = preferences.get() if (lastDuration == 0L || lastDuration > duration) { lastDuration = duration } countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) { override fun onTick(millisUntilFinished: Long) { onTickCallback?.onTickUpdate(millisUntilFinished) preferences.put(millisUntilFinished) } override fun onFinish() { onTickCallback?.onTickUpdate(0) isStarted = false clearTimer() } TimerListener.kt
  • 14. class TimerListener(private val duration: Long, private val preferences: TimerPreferences, private val onTickCallback: OnTickListener? = null) : LifecycleObserver { ... private fun updateCountDownTimer() { var lastDuration: Long = preferences.get() if (lastDuration == 0L || lastDuration > duration) { lastDuration = duration } countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) { override fun onTick(millisUntilFinished: Long) { onTickCallback?.onTickUpdate(millisUntilFinished) preferences.put(millisUntilFinished) } override fun onFinish() { onTickCallback?.onTickUpdate(0) isStarted = false clearTimer() } TimerListener.kt
  • 15. class TimerListener(private val duration: Long, private val preferences: TimerPreferences, private val onTickCallback: OnTickListener? = null) : LifecycleObserver { ... private fun updateCountDownTimer() { var lastDuration: Long = preferences.get() if (lastDuration == 0L || lastDuration > duration) { lastDuration = duration } countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) { override fun onTick(millisUntilFinished: Long) { onTickCallback?.onTickUpdate(millisUntilFinished) preferences.put(millisUntilFinished) } override fun onFinish() { onTickCallback?.onTickUpdate(0) isStarted = false clearTimer() } TimerListener.kt
  • 16. class TimerListener(private val duration: Long, private val preferences: TimerPreferences, private val onTickCallback: OnTickListener? = null) : LifecycleObserver { ... private fun updateCountDownTimer() { var lastDuration: Long = preferences.get() if (lastDuration == 0L || lastDuration > duration) { lastDuration = duration } countdownTimer = object : CountDownTimer(lastDuration, DEFAULT_INTERVAL) { override fun onTick(millisUntilFinished: Long) { onTickCallback?.onTickUpdate(millisUntilFinished) preferences.put(millisUntilFinished) } override fun onFinish() { onTickCallback?.onTickUpdate(0) isStarted = false clearTimer() } TimerListener.kt
  • 17. ... @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onStart() { updateCountDownTimer() if (isStarted) { countdownTimer?.start() } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onStop() { if (isStarted) { countdownTimer?.cancel() } } ... TimerListener.kt
  • 18. ... @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onStart() { updateCountDownTimer() if (isStarted) { countdownTimer?.start() } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onStop() { if (isStarted) { countdownTimer?.cancel() } } ... TimerListener.kt
  • 19. ... @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onStart() { updateCountDownTimer() if (isStarted) { countdownTimer?.start() } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onStop() { if (isStarted) { countdownTimer?.cancel() } } ... TimerListener.kt
  • 20. ... @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onStart() { updateCountDownTimer() if (isStarted) { countdownTimer?.start() } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onStop() { if (isStarted) { countdownTimer?.cancel() } } ... TimerListener.kt
  • 21. ... fun start() { if (!isStarted) { isStarted = true countdownTimer?.start() } } fun clear() { if (isStarted) { isStarted = false countdownTimer?.cancel() } clearTimer() } ... TimerListener.kt
  • 22. ... fun start() { if (!isStarted) { isStarted = true countdownTimer?.start() } } fun clear() { if (isStarted) { isStarted = false countdownTimer?.cancel() } clearTimer() } ... TimerListener.kt
  • 23. ... fun start() { if (!isStarted) { isStarted = true countdownTimer?.start() } } fun clear() { if (isStarted) { isStarted = false countdownTimer?.cancel() } clearTimer() } ... TimerListener.kt
  • 24. class TimerLiveData(private val duration: Long, private val preferences: TimerPreferences) : LiveData<Long>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: TimerListener override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = TimerListener( duration = duration, preferences = preferences, onTickCallback = object : TimerListener.OnTickListener { override fun onTickUpdate(remainingMillis: Long) { value = remainingMillis } }) } ... TimerLiveData.kt
  • 25. class TimerLiveData(private val duration: Long, private val preferences: TimerPreferences) : LiveData<Long>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: TimerListener override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = TimerListener( duration = duration, preferences = preferences, onTickCallback = object : TimerListener.OnTickListener { override fun onTickUpdate(remainingMillis: Long) { value = remainingMillis } }) } ... TimerLiveData.kt
  • 26. class TimerLiveData(private val duration: Long, private val preferences: TimerPreferences) : LiveData<Long>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: TimerListener override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = TimerListener( duration = duration, preferences = preferences, onTickCallback = object : TimerListener.OnTickListener { override fun onTickUpdate(remainingMillis: Long) { value = remainingMillis } }) } ... TimerLiveData.kt
  • 27. class TimerLiveData(private val duration: Long, private val preferences: TimerPreferences) : LiveData<Long>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: TimerListener override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = TimerListener( duration = duration, preferences = preferences, onTickCallback = object : TimerListener.OnTickListener { override fun onTickUpdate(remainingMillis: Long) { value = remainingMillis } }) } ... TimerLiveData.kt
  • 28. class TimerLiveData(private val duration: Long, private val preferences: TimerPreferences) : LiveData<Long>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: TimerListener override fun observe(owner: LifecycleOwner, observer: Observer<in Long>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = TimerListener( duration = duration, preferences = preferences, onTickCallback = object : TimerListener.OnTickListener { override fun onTickUpdate(remainingMillis: Long) { value = remainingMillis } }) } ... TimerLiveData.kt
  • 29. ... fun start() { listener.start() } fun clear() { listener.clear() value = 0L } override fun onActive() { lifecycle.addObserver(listener) } override fun onInactive() { lifecycle.removeObserver(listener) } ... TimerLiveData.kt
  • 30. ... fun start() { listener.start() } fun clear() { listener.clear() value = 0L } override fun onActive() { lifecycle.addObserver(listener) } override fun onInactive() { lifecycle.removeObserver(listener) } ... TimerLiveData.kt
  • 31. ... fun start() { listener.start() } fun clear() { listener.clear() value = 0L } override fun onActive() { lifecycle.addObserver(listener) } override fun onInactive() { lifecycle.removeObserver(listener) } ... TimerLiveData.kt
  • 32. class TimerViewModel @Inject constructor(timerPreferences: TimerPreferences) : ViewModel() { val timerLiveData = TimerLiveData( duration = TimeUnit.SECONDS.toMillis(30), preferences = timerPreferences ) fun startTimer() { timerLiveData.start() } fun clearTimer() { timerLiveData.clear() } } TimerViewModel.kt
  • 33. class TimerViewModel @Inject constructor(timerPreferences: TimerPreferences) : ViewModel() { val timerLiveData = TimerLiveData( duration = TimeUnit.SECONDS.toMillis(30), preferences = timerPreferences ) fun startTimer() { timerLiveData.start() } fun clearTimer() { timerLiveData.clear() } } TimerViewModel.kt
  • 34. class TimerActivity : AppCompatActivity() { private lateinit var viewModel: TimerViewModel override fun onCreate(savedInstanceState: Bundle?) { ... viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java) viewModel.timerLiveData.observe(this, timerObserver) viewModel.startTimer() } private val timerObserver = Observer<Long> { remainingMillis -> if (remainingMillis > 0) { onTimerUpdate(remainingMillis) } else { onTimerEnd() } } ... TimerActivity.kt
  • 35. class TimerActivity : AppCompatActivity() { private lateinit var viewModel: TimerViewModel override fun onCreate(savedInstanceState: Bundle?) { ... viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java) viewModel.timerLiveData.observe(this, timerObserver) viewModel.startTimer() } private val timerObserver = Observer<Long> { remainingMillis -> if (remainingMillis > 0) { onTimerUpdate(remainingMillis) } else { onTimerEnd() } } ... TimerActivity.kt
  • 36. class TimerActivity : AppCompatActivity() { private lateinit var viewModel: TimerViewModel override fun onCreate(savedInstanceState: Bundle?) { ... viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java) viewModel.timerLiveData.observe(this, timerObserver) viewModel.startTimer() } private val timerObserver = Observer<Long> { remainingMillis -> if (remainingMillis > 0) { onTimerUpdate(remainingMillis) } else { onTimerEnd() } } ... TimerActivity.kt
  • 37. class TimerActivity : AppCompatActivity() { private lateinit var viewModel: TimerViewModel override fun onCreate(savedInstanceState: Bundle?) { ... viewModel = ViewModelProviders.of(this).get(TimerViewModel::class.java) viewModel.timerLiveData.observe(this, timerObserver) viewModel.startTimer() } private val timerObserver = Observer<Long> { remainingMillis -> if (remainingMillis > 0) { onTimerUpdate(remainingMillis) } else { onTimerEnd() } } ... TimerActivity.kt
  • 38. Google API Client for Android
  • 39. class GoogleApiClientListener(private val builder: GoogleApiClient.Builder, private val api: Api<out Api.ApiOptions.NotRequiredOptions>, private val callback: Callback? = null) : LifecycleObserver { private lateinit var googleApiClient: GoogleApiClient init { googleApiClient = builder.apply { addOnConnectionFailedListener { result: ConnectionResult -> callback?.onFailure(result) } addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { override fun onConnected(connectionHint: Bundle?) { callback?.onConnected(googleApiClient) } override fun onConnectionSuspended(cause: Int) { callback?.onSuspended(cause) } }) addApi(api) }.build() } GoogleApiClientListener.kt
  • 40. ... @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { if (!googleApiClient.isConnected || !googleApiClient.isConnecting) { googleApiClient.connect() } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onStop() { if (googleApiClient.isConnected) { googleApiClient.disconnect() } } fun getGoogleApiClient(): GoogleApiClient = googleApiClient interface Callback { fun onConnected(client: GoogleApiClient) fun onFailure(result: ConnectionResult) fun onSuspended(cause: Int) } GoogleApiClientListener.kt
  • 41. class GoogleApiClientLiveData(private val builder: GoogleApiClient.Builder, private val api: Api<out Api.ApiOptions.NotRequiredOptions>) : LiveData<GoogleApiResult>() { private lateinit var lifecycle: Lifecycle private lateinit var listener: GoogleApiClientListener override fun observe(owner: LifecycleOwner, observer: Observer<in GoogleApiResult>) { super.observe(owner, observer) lifecycle = owner.lifecycle listener = GoogleApiClientListener(builder, api, callback) } override fun onActive() { lifecycle.addObserver(listener) } override fun onInactive() { lifecycle.removeObserver(listener) } ... GoogleApiClientLiveData.kt
  • 42. private val callback = object : GoogleApiClientListener.Callback { override fun onConnected(client: GoogleApiClient) { value = GoogleApiResult( status = GoogleApiResult.STATUS_SUCCESS, errorCode = 0, errorMessage = null, client = listener.getGoogleApiClient()) } override fun onFailure(result: ConnectionResult) { value = GoogleApiResult( status = GoogleApiResult.STATUS_FAILURE, errorCode = result.errorCode, errorMessage = result.errorMessage, client = null) } override fun onSuspended(cause: Int) { value = GoogleApiResult( status = GoogleApiResult.STATUS_SUCCESS, errorCode = cause, errorMessage = null, client = null) } } GoogleApiClientLiveData.kt
  • 43. data class GoogleApiResult(var status: String, var errorCode: Int, var errorMessage: String?, var client: GoogleApiClient?) { companion object { const val STATUS_SUCCESS = "success" const val STATUS_FAILURE = "failure" const val STATUS_SUSPENDED = "suspended" } fun isSuccess(): Boolean = status == STATUS_SUCCESS fun isFailure(): Boolean = status == STATUS_FAILURE fun isSuspended(): Boolean = status == STATUS_SUSPENDED } GoogleApiResult.kt
  • 44. class GoogleApiViewModel @Inject constructor(private var builder: GoogleApiClient.Builder) : ViewModel() { val googleApiClientLiveData = GoogleApiClientLiveData(builder, LocationServices.API) } GoogleApiViewModel.kt
  • 45. class GoogleApiActivity : AppCompatActivity() { private lateinit var viewModel: GoogleApiViewModel override fun onCreate(savedInstanceState: Bundle?) { ... viewModel = ViewModelProviders.of(this).get(GoogleApiViewModel::class.java) viewModel.googleApiClientLiveData.observe(this, googleApiClientObserver) } private val googleApiClientObserver = Observer<GoogleApiResult> { result -> when (result.status) { GoogleApiResult.STATUS_SUCCESS -> onGoogleApiClientConnected(result.client) GoogleApiResult.STATUS_FAILURE -> onGoogleApiClientFailure(result.errorMessage) GoogleApiResult.STATUS_SUSPENDED -> onGoogleApiClientSuspend(result.errorCode) } } ... GoogleApiActivity.kt
  • 47. class RealtimeDatabaseLiveData(userId: String, firebaseDatabase: FirebaseDatabase) : LiveData<List<Post>>() { private val databaseReference = firebaseDatabase.getReference(userId).child("posts") override fun onActive() { databaseReference.addValueEventListener(valueEventListener) } override fun onInactive() { databaseReference.removeEventListener(valueEventListener) } private val valueEventListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { value = dataSnapshot.children.map { snapshot -> snapshot.getValue(Post::class.java)!! } } override fun onCancelled(databaseError: DatabaseError) {} } } RealtimeDatabaseLiveData.kt
  • 50. class SingleEventLiveData<T> : MutableLiveData<T>() { private val mPending = AtomicBoolean(false) override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { super.observe(owner, Observer { t: T? -> if (mPending.compareAndSet(true, false)) { observer.onChanged(t) } }) } override fun setValue(@Nullable t: T?) { mPending.set(true) super.setValue(t) } override fun postValue(value: T?) { mPending.set(true) super.postValue(value) } } SingleEventLiveData.kt
  • 51. class HomeEventViewModel : ViewModel() { val updateFeedEventLiveData = SingleEventLiveData<String>() fun send(message: String) { updateFeedEventLiveData.value = message } } HomeEventViewModel.kt
  • 52. 1 Event : 1 SingleEventLiveData Don't reuse
  • 54. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() }
  • 55. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 56. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 57. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 58. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 59. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 60. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 61. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() } Fragment/Activity ViewModel Repository Database Network NetworkBoundResource
  • 62. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } }.asLiveData() }
  • 63. fun getUserInfo(userId: String): LiveData<Resource<UserInfo>> { return object : NetworkBoundResource<UserInfo, UserInfo>(appExecutors) { override fun saveCallResult(item: UserInfo) { /*...*/ } override fun shouldFetch(data: UserInfo?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserInfo> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } override fun convertToResultType(response: UserInfo): UserInfo { /*...*/ } override fun callFailed(errorMessage: String) { /*...*/ } }.asLiveData() }
  • 64. fun getUserInfo(userId: String): LiveData<Resource<UserAccount>> { return object : NetworkBoundResource<UserAccount, UserInfo>(appExecutors) { override fun saveCallResult(item: UserAccount) { /*...*/ } override fun shouldFetch(data: UserAccount?): Boolean { /*...*/ } override fun loadFromDb(): LiveData<UserAccount> { /*...*/ } override fun createCall(): LiveData<UserInfo> { /*...*/ } override fun convertToResultType(response: UserInfo): UserAccount { /*...*/ } override fun callFailed(errorMessage: String) { /*...*/ } }.asLiveData() }
  • 65. fun getUserInfo(userId: String): LiveData<Resource<UserAccount>> { return object : DirectNetworkBoundResource<UserAccount, UserInfo>(appExecutors) { override fun createCall(): LiveData<UserInfo> { /*...*/ } override fun convertToResultType(response: UserInfo): UserAccount { /*...*/ } override fun callFailed(errorMessage: String) { /*...*/ } }.asLiveData() }
  • 71. Log In Homeapi/v1/login What people think about log in flow
  • 72. Log In Homeapi/v1/login api/v1/:userId/conDgs api/v1/:userId/info api/v1/:userId/banners What log in flow actually be in real life api/v1/:userId/caGs
  • 75. object Single2LiveDataConverter { fun <T> convert(single: Single<T>): LiveData<T> { return object : LiveData<T>() { private var disposable: Disposable? = null override fun onActive() { super.onActive() disposable = single .subscribeOn(Schedulers.io()) .subscribe(Consumer<T> { this.postValue(it) }) } override fun onInactive() { super.onInactive() if (disposable != null) disposable!!.dispose() } } } } Single2LiveDataConveWer.kt
  • 76. fun getNecessaryInfo(): Single<Response<LogInResult>> { return Single.zip( getUserInfo(), getBannerList(), BiFunction<Response<UserInfoResult>, Response<BannerListResult>, Response<LogInResult>> { userInfoResponse, bannerListResponse -> Response.success( LogInResult( userInfoResponse.body(), bannerListResponse.body() ) ) }) }
  • 77. override fun createCall(): LiveData<Response<LogInResult>> { return Single2LiveDataConverter.convert(getNecessaryInfo()) } Deferred2LiveData.kt
  • 80. class Deferred2LiveData<T>(var deferred: Deferred<Response<T>>) : LiveData<ApiResponse<T>>() { fun execute() { launch(CommonPool) { try { val result: Response<T> = deferred.await() postValue(ApiResponse.create(result)) } catch (e: Exception) { postValue(ApiResponse.create(e)) } } } } Deferred2LiveData.kt
  • 81. override fun createCall(): LiveData<Response<LogInResult>> { return Deferred2LiveData(async {
 LogInResult(getUserInfo().execute(), getBannerList().execute()) }) } Deferred2LiveData.kt
  • 87. class SessionDetailViewModel @Inject constructor( private val signInViewModelDelegate: SignInViewModelDelegate, private val loadUserSessionUseCase: LoadUserSessionUseCase, private val loadRelatedSessionUseCase: LoadUserSessionsUseCase, private val starEventUseCase: StarEventUseCase, private val reservationActionUseCase: ReservationActionUseCase, private val getTimeZoneUseCase: GetTimeZoneUseCase, private val snackbarMessageManager: SnackbarMessageManager, timeProvider: TimeProvider, private val networkUtils: NetworkUtils, private val analyticsHelper: AnalyticsHelper ) : ViewModel(), SessionDetailEventListener, EventActions, SignInViewModelDelegate by signInViewModelDelegate { private val loadUserSessionResult: MediatorLiveData<Result<LoadUserSessionUseCaseResult>> private val loadRelatedUserSessions: LiveData<Result<LoadUserSessionsUseCaseResult private val sessionTimeRelativeState: LiveData<TimeUtils.SessionRelativeTimeState>
  • 88. class SessionDetailFragment : DaggerFragment() { ... override fun onActivityCreated(savedInstanceState: Bundle?) { ... val starMenu = menu.findItem(R.id.menu_item_star) sessionDetailViewModel.shouldShowStarInBottomNav.observe(this, Observer { it?.let { if (it) { starMenu.setVisible(true) } else { starMenu.setVisible(false) } } }) sessionDetailViewModel.userEvent.observe(this, Observer { it?.let { if (it.isStarred) { starMenu.setIcon(R.drawable.ic_star) } else { starMenu.setIcon(R.drawable.ic_star_border) }
  • 89. sessionDetailViewModel.session.observe(this, Observer { room = it?.room shareString = if (it == null) { "" } else { getString(R.string.share_text_session_detail, it.title, it.sessionUrl) } })
  • 90. Google I/O 2018 Source Code hCps://github.com/google/iosched hCps://github.com/googlesamples/android-architecture-components Samples for AAC hCps://developer.android.com/topic/libraries/architecture/ AAC Documentation Thank you! @akexorcist