Skip to content

Commit

Permalink
使用RxJava代替LiveData实现Redux
Browse files Browse the repository at this point in the history
  • Loading branch information
wangzhiyu1 committed May 19, 2020
1 parent 4f063d4 commit 017756c
Show file tree
Hide file tree
Showing 37 changed files with 507 additions and 209 deletions.
45 changes: 29 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private val viewModel by lazy {
[Redux入门二](http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html)
[Redux入门三](http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html)

本项目中的`Redux`部分旨在提供一个轻量级的全局事件总线功能
本项目中的`Redux`部分旨在提供一个轻量级的全局状态机和事件总线功能

使用`Redux`时需要自定义`Redux`子类、`AppState``Action``Reducer``Middleware`

Expand All @@ -201,6 +201,7 @@ private val viewModel by lazy {
data class AppState(
val result: Int = 0,
val loginFail: Unit? = null,
val backPressureNum: Long = 0L,
@DoPersist val username: String = ""
)
```
Expand Down Expand Up @@ -252,8 +253,7 @@ class PlusMiddleware : Middleware<AppState> {
class MyRedux(context: Application) : BaseRedux<AppState>(
context = context,
initialState = AppState(),
reducer = MyReducer(),
middlewareList = listOf(PlusMiddleware(), MultipleMiddleware())
reducer = MyReducer()
) {

companion object {
Expand Down Expand Up @@ -285,37 +285,50 @@ MyRedux.instance.dispatch(ChangeNum(currNum), listOf(PlusMiddleware(), MultipleM
```
```kotlin
// 监听State
MyRedux.instance.pick(AppState::result).observe(this, Observer {
disposable = MyRedux.instance.pick(AppState::result).subscribe {
if (currNum == 1) {
tvResult.text = "($currNum + 1) * 2 = 4"
tvResult.text = "(1 + 1) * 2 = 4"
} else {
tvResult.text = "($currNum + 1) * 2 = ${it.a}"
}
currNum++
})
}
```
如果本次`dispatch`的事件不需要中间件处理,则可以不传这个参数:
```kotlin
MyRedux.instance.dispatch(Logout)
```
发送Action的过程可以同步完成,也可以异步完成:
```kotlin
Single.just(ChangeNum(number))
Observable.just(1)
.subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.subscribe({
MyRedux.instance.dispatch(it)
}, {
println("------>>error: ${it.message}")
})
.subscribe {
MyRedux.instance.dispatch(ToastEvent, listOf())
}
```
除此之外,也可以通过`currState`对象获取到当前全局状态对象:
```kotlin
currentUserName.text = "当前用户名:${MyRedux.instance.currState.username}"
```
除此之外,也可以通过`currState()`方法获取到当前全局状态对象:

#### 8)注意内存泄漏
**注意:在使用RxMVVM中的Redux功能时,请注意Disposable的取消订阅操作,否则会导致内存泄漏。**
```kotlin
println("------>>${MyRedux.instance.currState().result}")
private var disposable: Disposable? = null
disposable = MyRedux.instance.pick(AppState::toastEvent).subscribe {
it.a?.let {
Toast.makeText(this, "消费ToastEvent事件", Toast.LENGTH_SHORT).show()
}
}
disposable?.takeIf { !it.isDisposed }?.dispose()
```

## Change Logs
#### v1.6.5
* Redux核心从LiveData切换到RxJava
* Redux支持背压
* Redux部分API更改

#### v1.6.4
* ViewModel中支持从非主线程中推送数据

Expand Down
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@

<activity android:name=".redux.PersistReduxActivity" />

<activity android:name=".redux.BackPressureReduxActivity" />

<activity android:name=".redux.AsyncReduxActivity" />

</application>

</manifest>
6 changes: 0 additions & 6 deletions app/src/main/java/my/itgungnir/rxmvvm/App.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package my.itgungnir.rxmvvm

import androidx.multidex.MultiDexApplication
import com.squareup.leakcanary.LeakCanary
import my.itgungnir.rxmvvm.common.redux.MyRedux

class App : MultiDexApplication() {
Expand All @@ -10,10 +9,5 @@ class App : MultiDexApplication() {
super.onCreate()

MyRedux.init(this)

if (LeakCanary.isInAnalyzerProcess(this)) {
return
}
LeakCanary.install(this)
}
}
28 changes: 19 additions & 9 deletions app/src/main/java/my/itgungnir/rxmvvm/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import my.itgungnir.rxmvvm.app4.AppActivity4
import my.itgungnir.rxmvvm.app5.AppActivity5
import my.itgungnir.rxmvvm.app6.AppActivity6
import my.itgungnir.rxmvvm.app7.AppActivity7
import my.itgungnir.rxmvvm.redux.AsyncReduxActivity
import my.itgungnir.rxmvvm.redux.BackPressureReduxActivity
import my.itgungnir.rxmvvm.redux.NonPersistReduxActivity
import my.itgungnir.rxmvvm.redux.PersistReduxActivity

Expand All @@ -20,39 +22,47 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

button1.setOnClickListener {
redux_non_persist.setOnClickListener {
startActivity(Intent(this, NonPersistReduxActivity::class.java))
}

button2.setOnClickListener {
redux_persist.setOnClickListener {
startActivity(Intent(this, PersistReduxActivity::class.java))
}

button3.setOnClickListener {
redux_back_pressure.setOnClickListener {
startActivity(Intent(this, BackPressureReduxActivity::class.java))
}

redux_async.setOnClickListener {
startActivity(Intent(this, AsyncReduxActivity::class.java))
}

mvvm01.setOnClickListener {
startActivity(Intent(this, AppActivity1::class.java))
}

button4.setOnClickListener {
mvvm02.setOnClickListener {
startActivity(Intent(this, AppActivity2::class.java))
}

button5.setOnClickListener {
mvvm03.setOnClickListener {
startActivity(Intent(this, AppActivity3::class.java))
}

button6.setOnClickListener {
mvvm04.setOnClickListener {
startActivity(Intent(this, AppActivity4::class.java))
}

button7.setOnClickListener {
mvvm05.setOnClickListener {
startActivity(Intent(this, AppActivity5::class.java))
}

button8.setOnClickListener {
mvvm06.setOnClickListener {
startActivity(Intent(this, AppActivity6::class.java))
}

button9.setOnClickListener {
mvvm07.setOnClickListener {
startActivity(Intent(this, AppActivity7::class.java))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AppViewModel2 : BaseViewModel<AppState2>(initialState = AppState2()) {
}, {
setState {
copy(
error = kotlin.Throwable(message = "生成随机数失败!")
error = Throwable(message = "生成随机数失败!")
)
}
})
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/my/itgungnir/rxmvvm/app2/FragBottom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class FragBottom : Fragment() {
super.onViewCreated(view, savedInstanceState)

viewModel.pick(AppState2::randomNum)
.observe(this, Observer { num ->
.observe(viewLifecycleOwner, Observer { num ->
num?.a?.let {
randomNum.text = it.toString()
}
})

viewModel.pick(AppState2::error)
.observe(this, Observer { error ->
.observe(viewLifecycleOwner, Observer { error ->
error?.a?.message?.let {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AppViewModel3 : BaseViewModel<AppState3>(initialState = AppState3()) {
}, {
setState {
copy(
error = kotlin.Throwable(message = "生成随机数失败!")
error = Throwable(message = "生成随机数失败!")
)
}
})
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/my/itgungnir/rxmvvm/app3/FragBottom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ class FragBottom : Fragment() {
private fun observeVM() {

viewModel.pick(AppState3::randomNum)
.observe(this, Observer { randomNum ->
.observe(viewLifecycleOwner, Observer { randomNum ->
randomNum?.a?.let {
number.text = it.toString()
}
})

viewModel.pick(AppState3::error)
.observe(this, Observer { error ->
.observe(viewLifecycleOwner, Observer { error ->
error?.a?.message?.let {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/my/itgungnir/rxmvvm/app4/FragChild.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ class FragChild : Fragment() {
}

innerViewModel.pick(ChildState::randomNum)
.observe(this, Observer { randomNum ->
.observe(viewLifecycleOwner, Observer { randomNum ->
randomNum?.a?.let {
number.text = it.toString()
}
})

innerViewModel.pick(ChildState::error)
.observe(this, Observer { error ->
.observe(viewLifecycleOwner, Observer { error ->
error?.a?.message?.let {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView
*
* Created by ITGungnir on 2019-12-07
*/
class AppListAdapter5(var dataList: List<String>) : RecyclerView.Adapter<AppListAdapter5.VH>() {
class AppListAdapter5(private var dataList: List<String>) : RecyclerView.Adapter<AppListAdapter5.VH>() {

override fun getItemCount(): Int = dataList.size

Expand Down
58 changes: 58 additions & 0 deletions app/src/main/java/my/itgungnir/rxmvvm/redux/AsyncReduxActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package my.itgungnir.rxmvvm.redux

import android.annotation.SuppressLint
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_redux_async.*
import my.itgungnir.rxmvvm.R
import my.itgungnir.rxmvvm.common.redux.AppState
import my.itgungnir.rxmvvm.common.redux.MyRedux
import my.itgungnir.rxmvvm.common.redux.action.Reset
import my.itgungnir.rxmvvm.common.redux.action.ToastEvent

/**
* Description:
*
* @author ITGungnir
* @date 2020/5/19
*/
class AsyncReduxActivity : AppCompatActivity() {

private var disposable: Disposable? = null

@SuppressLint("CheckResult", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_redux_async)

currentUserName.text = "当前用户名:${MyRedux.instance.currState.username}"

syncToast.setOnClickListener {
MyRedux.instance.dispatch(ToastEvent, listOf())
}

asyncToast.setOnClickListener {
Observable.just(1)
.subscribeOn(Schedulers.io())
.subscribe {
MyRedux.instance.dispatch(ToastEvent, listOf())
}
}

disposable = MyRedux.instance.pick(AppState::toastEvent).subscribe {
it.a?.let {
Toast.makeText(this, "消费ToastEvent事件", Toast.LENGTH_SHORT).show()
}
}
}

override fun onDestroy() {
MyRedux.instance.dispatch(Reset, listOf())
disposable?.takeIf { !it.isDisposed }?.dispose()
super.onDestroy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package my.itgungnir.rxmvvm.redux

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.jakewharton.rxbinding2.view.RxView
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.activity_redux_back_pressure.*
import my.itgungnir.rxmvvm.R
import my.itgungnir.rxmvvm.common.redux.AppState
import my.itgungnir.rxmvvm.common.redux.MyRedux
import my.itgungnir.rxmvvm.common.redux.action.AddNum
import java.util.concurrent.TimeUnit

/**
* Description:
*
* @author ITGungnir
* @date 2020/5/19
*/
class BackPressureReduxActivity : AppCompatActivity() {

private var disposable: Disposable? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_redux_back_pressure)

initViews()
observeVM()
}

@SuppressLint("CheckResult")
private fun initViews() {
RxView.clicks(btnAdd)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe {
for (i in 0..1000) {
MyRedux.instance.dispatch(AddNum, listOf())
}
}
}

@SuppressLint("CheckResult")
private fun observeVM() {
disposable = MyRedux.instance.pick(AppState::backPressureNum).subscribe {
println("---------->>${it.a}")
tvResult.text = it.a.toString()
}
}

override fun onDestroy() {
disposable?.takeIf { !it.isDisposed }?.dispose()
super.onDestroy()
}
}
Loading

0 comments on commit 017756c

Please sign in to comment.