mDevTalk #6: 26. 1. 2017
Nová Firebase řeší spoustu běžných problémů Android vývojáře. Pokud ale máte komplexnější strukturu databáze, potřebujete to všechno spojit pomocí RxJavy. To celé chce nějakou architekturu jako MVP. A Kotlin udělá kód kratší a čitelnější.
13. How can presenter survive configuration changes
abstract class BaseFragment<P : Presenter<V>, V : MvpView> : Fragment(), LoaderManager.LoaderCallbacks<P> {
private var mPresenter: P? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
loaderManager.initLoader(PRESENTER_LOADER_ID, Bundle(), this)
}
override fun onCreateLoader(id: Int, args: Bundle): Loader<P>? {
return PresenterLoader(activity, getPresenterFactory())
}
override fun onLoadFinished(loader: Loader<P>, presenter: P) {
mPresenter = presenter
}
override fun onResume() {
mPresenter?.attachView(this as V)
}
override fun onPause() {
mPresenter?.detachView()
}
}
http://bit.ly/presenterrotation
14. How can presenter survive configuration changes
class PresenterLoader<T : Presenter<out MvpView>>(context: Context, val factory: PresenterFactory<T>) : Loader<T>(context) {
private var presenter: T? = null
override fun onStartLoading() {
if (presenter != null) {
deliverResult(presenter)
} else {
forceLoad()
}
}
override fun onForceLoad() {
presenter = mFactory.create()
presenter?.onCreatedByLoader()
deliverResult(presenter)
}
override fun onReset() {
presenter?.onDestroyedByLoader()
presenter = null
}
}
http://bit.ly/presenterrotation
15. How to coordinate multiple async calls to Firebase Realtime Database
fun observe(query: DatabaseQuery): Observable<DataSnapshot> {
return Observable.fromAsync<DataSnapshot>({
val listener = query.addValueEventListener(object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
it.onError(RuntimeException(databaseError.message))
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
it.onNext(dataSnapshot)
}
})
it.setCancellation { query.removeEventListener(listener) }
}, AsyncEmitter.BackpressureMode.BUFFER)
}
http://bit.ly/rxfirebase
16. How to coordinate multiple async calls to Firebase Realtime Database
object DatabaseRead {
fun getUserGroupColor(groupId: String): Observable<String> {
return ObjectQuery().apply { path = "userGroups/${Auth.getUserId()}/$groupId/color" }
.observe()
.toPrimitiveObservable(String::class.java)
}
fun getMember(groupId: String, memberId: String): Observable<Member> {
return ObjectQuery().apply { path = "members/$groupId/$memberId" }
.observe()
.toObjectObservable(Member::class.java)
}
}
17. How to coordinate multiple async calls to Firebase Realtime Database
private fun <T> load(observable: Observable<T>, displayToView: (T) -> Unit) {
val data = PresenterData(observable, displayToView)
data.subscription = data.observable.subscribe({
data.cachedValue = it
if (isViewAttached) {
data.displayToView(it)
}
}, {
showError(it)
})
mDataList.add(data)
}
18. How to coordinate multiple async calls to Firebase Realtime Database
fun loadMultiple(observables: List<Observable<out Any>>, displayToView: (Array<Any>) -> Unit) {
val data = PresenterMultipleData(observables, displayToView)
Observable.combineLatest(observables, {
it
}).subscribe({
data.cachedValue = it
if (isViewAttached) {
data.displayToView(it)
}
}, {
showError(it)
})
mMultipleDataList.add(data)
}
20. Concrete example - Member detail screen
class MemberDetailPresenter(val groupId: String, val memberId: String) : BasePresenter<MemberDetailMvpView>() {
override fun onCreatedByLoader() {
loadOnce(DatabaseRead.getUserGroupColor(groupId), {
getView().setGroupColor(Color.parseColor(it))
})
loadMultiple(listOf(DatabaseRead.getMember(groupId, memberId), DatabaseRead.getUserMember(groupId)) {
val member = it[0] as Member
val userMember = it[1] as String?
getView().setMember(member, member.id == userMember)
})
}
}
21. Concrete example - Member detail screen
class MemberDetailActivity : BaseActivity<MemberDetailPresenter, MemberDetailMvpView>(), MemberDetailMvpView {
override fun getLayoutRes(): Int {
return R.layout.activity_member_detail
}
override fun getPresenterFactory(): PresenterFactory<MemberDetailPresenter> {
return MemberDetailPresenter.Factory(getGroupId(), getMemberId())
}
override fun setMember(member: Member, thisIsMe: Boolean) {
vName.setText(member.name)
vThisIsMe.isChecked = thisIsMe
}
override fun setGroupColor(color: Int) {
vHeader.setBackgroundColor(color)
}
}
22. How does Firebase pricing affect
monetization?
Realtime Database:
$5/stored GB monthly
Storage:
$0.026/stored GB monthly
Our monetization:
Freemium
● Subscription
● Or free app with video ads
http://bit.ly/firebasepricing
24. TL;DR
Firebase is great
Build new apps in Kotlin
Use MVP architecture, Presenter should survive orientation change with Loader
RxJava is great for combining multiple async calls to Firebase
Think about database structure and monetization before you start building