介绍架构设计的文章已经有很多了,我想从线程切换的角度再来分解下各个架构设计的特点
mvc模式
- Model:数据层(网络、本地、业务逻辑)
- View:视图层(XML、Activity、Fragment)
- Controller:控制层(Activity 同时充当)
model代码如下
data class Person(val name:String,val passWord:String)
interface PersonMode{
fun login(person: Person,personResult: PersonResult)
}
interface PersonResult{
fun loginResult(result:Boolean)
}
class PersonData : PersonMode {
val result:(String,String)->Boolean = {name,password ->
name?.length!=0&&password?.length!=0}
override fun login(person: Person,personResult: PersonResult) {
val thread = Thread {
Log.d("PersonData","threadId ${Thread.currentThread().id}")
System.out.print("threadId ${Thread.currentThread().id}")
Thread.sleep(2000)
val loginResult = result(person.name,person.passWord)
personResult.loginResult(loginResult)
}.start()
}
}
模拟异步线程获取数据
control代码如下
class MainActivity : AppCompatActivity() ,PersonResult{
private lateinit var personData: PersonData
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout)
textView = findViewById(R.id.result)
textView.setOnClickListener {
personData.login(Person("joy","123456"),this)
}
personData = PersonData()
}
override fun loginResult(result: Boolean) {
Log.d("MainActivity","${Thread.currentThread().id}")
System.out.print("${Thread.currentThread().id}")
runOnUiThread {
textView.text = "result $result"
}
}
}
通过runOnUiThread实现线程切换,在主线程中更新view,简单来说就是开启子线程获取数据
mvp模式
引入 Presenter 层,隔离 View 与 Model。 Presenter 负责业务逻辑、数据协调。
model代码如下
data class MVPPerson(val name:String,val passWord:String)
interface View{
fun getName():String
fun getPassWord():String
fun showLoginResult(result:String)
}
interface Presenter{
fun login()
}
interface Model {
fun login(mvpPerson: MVPPerson, callback: LoginCallback)
}
interface LoginCallback {
fun onSuccess(message: String)
fun onError(error: String)
}
class MVPModel:Model{
val result:(String,String)->Boolean = {name,password ->
name?.length!=0&&password?.length!=0}
override fun login(mvpPerson: MVPPerson, callback: LoginCallback) {
val thread = Thread{
Log.d("MVPModel","thread ${Thread.currentThread().id}")
Thread.sleep(2000)
val mvpResult = result(mvpPerson.name,mvpPerson.passWord)
when(mvpResult){
true->callback.onSuccess("登录成功")
false->callback.onError("登录失败")
}
}.start()
}
}
class MVPPresenter(val view: View) : Presenter{
private val mvpModel = MVPModel()
override fun login() {
val mvpPerson = MVPPerson(view.getName(),view.getPassWord())
mvpModel.login(mvpPerson,object:LoginCallback{
override fun onSuccess(message: String) {
view.showLoginResult(message)
}
override fun onError(error: String) {
view.showLoginResult(error)
}
})
}
}
view代码如下
class MainActivity : AppCompatActivity() ,PersonResult,View{
private lateinit var personData: PersonData
private lateinit var textView: TextView
private lateinit var mvpPresenter: MVPPresenter
private val handler = Handler(Looper.getMainLooper())
private val mHandler:Handler = object :Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
1001-> textView.text = msg.data.getString("text")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout)
textView = findViewById(R.id.result)
textView.setOnClickListener {
mvpPresenter.login()
}
personData = PersonData()
mvpPresenter = MVPPresenter(this)
}
override fun loginResult(result: Boolean) {
Log.d("MainActivity","${Thread.currentThread().id}")
System.out.print("${Thread.currentThread().id}")
runOnUiThread {
textView.text = "result $result"
}
}
override fun getName(): String {
return "jiejie"
}
override fun getPassWord(): String {
return "123456"
}
override fun showLoginResult(result:String) {
Log.d("MainActivity","${Thread.currentThread().id}")
System.out.print("${Thread.currentThread().id}")
var msg = Message()
msg.data.putString("text",result)
msg.what = 1001
mHandler.sendMessage(msg)
}
}
通过持有主线程loop的Handler,发送msg实现线程切换
MVVM模式
通过 双向数据绑定 实现 UI 自动响应数据变化。ViewModel 管理状态,View 仅负责展示。
model代码如下
data class MVVMPerson(val name:String,val passWord:String)
class MVVMModel: ViewModel() {
private val mvvmRepository = MVVMRepository()
private val loginInfo = MutableLiveData<String>()
fun login(mvvmPerson: MVVMPerson){
Thread{
Log.d("MVVMModel","threadId ${Thread.currentThread().id}")
Thread.sleep(2000)
when(mvvmRepository.loginConfig(mvvmPerson)){
true->loginInfo.postValue("登录成功")
false->loginInfo.postValue("登录失败")
}
}.start()
}
fun getLoginInfo():MutableLiveData<String>{
return loginInfo
}
}
class MVVMRepository{
val result:(String,String)->Boolean = {name,password ->
name?.length!=0&&password?.length!=0}
fun loginConfig(mvvmPerson: MVVMPerson): Boolean {
return result(mvvmPerson.name, mvvmPerson.passWord)
}
}
view代码如下
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout)
textView = findViewById(R.id.result)
textView.setOnClickListener {
mvvmModel.login(MVVMPerson("joy","123456"))
}
personData = PersonData()
mvpPresenter = MVPPresenter(this)
mvvmModel = ViewModelProvider(this)[MVVMModel::class]
getLoginInfo()
}
fun getLoginInfo(){
mvvmModel.getLoginInfo().observe(this){
Log.d("MainActivity","threadId ${Thread.currentThread().id}")
textView.text = it
}
}
通过livedata实现线程切换,调用时序是
graph TD
MutableLiveData.postValue --> LiveData.postValue
-->ArchTaskExecutor.postToMainThread -->DefaultTaskExecutor.postToMainThread
-->通过创建UI线程的handler提交runnable对象到消息队列里执行
-->mPostValueRunnable
-->dispatchingValue分发消息到observe里
-->界面更新数据
mvi模式 (Model-View-Intent)
采用 单向数据流(Unidirectional Data Flow) :
用户意图(Intent)→ 状态更新(State)→ UI 渲染(Render)
model代码如下
data class MVIPerson(val name:String,val passWord:String)
sealed class LoginState{
object Idle : LoginState()
object Loading : LoginState()
data class Success(val mviPerson: MVIPerson) : LoginState()
data class Error(val message: String) : LoginState()
}
sealed class LoginIntent{
object Idle : LoginIntent()
data class Login(val mviPerson: MVIPerson) : LoginIntent()
}
class MVIRepository{
val result:(String,String)->Boolean = {name,password ->
name?.length!=0&&password?.length!=0}
suspend fun loginConfig(mviPerson: MVIPerson): Boolean {
kotlinx.coroutines.delay(1500)
return result(mviPerson.name, mviPerson.passWord)
}
}
class MVIModel:ViewModel(){
private val mviRepository = MVIRepository()
val loginInfo = MutableStateFlow<LoginState>(LoginState.Idle)
val state: StateFlow<LoginState> = loginInfo
fun processIntent(intent: LoginIntent) {
when (intent) {
is LoginIntent.Idle -> {
loginInfo.value = LoginState.Idle
}
is LoginIntent.Login -> {
login(intent.mviPerson)
}
}
}
private fun login(mviPerson: MVIPerson) {
loginInfo.value = LoginState.Loading
viewModelScope.launch(Dispatchers.IO) {
Log.d("MVIModel","${Thread.currentThread().id} ${Thread.currentThread().name}")
try {
val result = mviRepository.loginConfig(mviPerson)
withContext(Dispatchers.Main){
if (result) {
loginInfo.value = LoginState.Success(mviPerson)
} else {
loginInfo.value = LoginState.Error("登录失败")
}
}
} catch (e: Exception) {
loginInfo.value = LoginState.Error("网络请求失败: ${e.message}")
}
}
}
}
view代码如下
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout)
textView = findViewById(R.id.result)
textView.setOnClickListener {
mviModel.processIntent(LoginIntent.Login(MVIPerson("joy","123456")))
}
personData = PersonData()
mvpPresenter = MVPPresenter(this)
mvvmModel = ViewModelProvider(this)[MVVMModel::class]
mviModel = ViewModelProvider(this)[MVIModel::class]
getLoginInfo()
setupObservers()
}
fun getLoginInfo(){
mvvmModel.getLoginInfo().observe(this){
Log.d("MainActivity","threadId ${Thread.currentThread().id}")
textView.text = it
}
}
private fun setupObservers() {
lifecycleScope.launch {
mviModel.state.collectLatest { state->updateUI(state) }
}
}
private fun updateUI(state: LoginState){
Log.d("MainActivity","${Thread.currentThread().id} ${state}")
when(state){
is LoginState.Idle->{
textView.text = "没有登录"
}
is LoginState.Loading->{
textView.text = "登录中"
}
is LoginState.Success->{
textView.text = "登录成功 ${state.mviPerson.name} ${state.mviPerson.passWord}"
}
is LoginState.Error->{
textView.text = "登录错误"
}
}
}
使用viewModelScope指定调度器,实现线程切换
