Android设计架构里的线程切换分析

60 阅读2分钟

介绍架构设计的文章已经有很多了,我想从线程切换的角度再来分解下各个架构设计的特点

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 {
//            personData.login(Person("joy","123456"),this)
            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}")
//        handler.post {
//            textView.text = result
//        }
//        handler.sendEmptyMessage(1001)
        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 {
//            personData.login(Person("joy","123456"),this)
//            mvpPresenter.login()
            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 {
//            personData.login(Person("joy","123456"),this)
//            mvpPresenter.login()
//            mvvmModel.login(MVVMPerson("joy","123456"))
            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指定调度器,实现线程切换

image.png