How do I stop the main thread in the android ViewModelScope coltin?

Asked 2 years ago, Updated 2 years ago, 329 views

I wrote the following code as an application to access files on the server using ViewModelScope on Android.
ConnectSmb1() in SmbViewModel.kt works fine as a method to access files on the server, but connectSmb2() returns false and fails.
In connectSmb1(), after you finish working on the worker thread that accesses the server file, you put a five-second pause in sleep() in the main thread.
The connectSmb2() is an attempt to insert async(Dispatchers.IO) {}.await() to allow the main thread to work while waiting for the end of the work in the worker thread.
Please tell me the code that works without sleep().

MainActivity.kt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import PACKAGE.smbclient2.databinding.ActivityMainBinding

classMainActivity:AppCompatActivity(){

    //////////////////////////////////////////////
    valDOMAIN: String="192.168.1.1"
    val SMBROOT: String="/SMB/SERVER/FILE/"
    val USER: String="FOO"
    val PASSWORD: String="BAR"
    //////////////////////////////////////////////

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

        val binding = ActivityMainBinding.inflate (layoutInflater)
        setContentView (binding.root)

        // prepare for connecting SMB server
        varsmbUrl="smb://" + DOMAIN + SMBROOT

        // getViewModel for SmbConnection
        valsmbViewModelFactory=SmbViewModelFactory(USER, PASSWORD, DOMAIN, smbUrl)
        valuembViewModel:SmbViewModel=ViewModelProvider(this,smbViewModelFactory).get(SmbViewModel::class.java)

        // connect SMB server
        if(!smbViewModel.connectSmb2(){
            finishAndRemoveTask()
        }

        /*
         * get data in SMB server
         */
        // Create the observer which updates the UI.
        val pathObserver=Observer<String>{sPath->
            binding.tvSmbPath.text=sPath
        }
        valueObserver=Observer<Long>{sSize->
            binding.tvSmbSize.text=sSize.toString()
        }

        smbViewModel.smbPath.observe (this, pathObserver)
        smbViewModel.smbSize.observe(this,sizeObserver)
    }
}

SmbViewModel.kt

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import jcifs.CIFSContext
import jcifs.config.PropertyConfiguration
import jcifs.context.BaseContext
import jcifs.smb.NtlmPasswordAuthentication
import jcifs.smb.SmbFile
import kotlinx.coroutines.*
import java.util.*

class SmbViewModel (val user: String, val password: String, val domain: String, val smbUrl: String): ViewModel() {

    // Configure LiveData
    private val_smbPath:MutableLiveData<String>by lazy{
        MutableLiveData<String>()
    }
    valuembPath —LiveData<String>
        get() =_smbPath

    private val_smbSize:MutableLiveData<Long>by lazy{
        MutableLiveData<Long>()
    }
    valuembSize —LiveData<Long>
        get() =_smbSize

    /*
     * Connecting to SMB Server
     */

    // Connectivity to SMB Server Example 1
    funconnectSmb1(): Boolean {
        varretBool: Boolean=false

        viewModelScope.launch (Dispatchers.IO) {
            val prop=Properties()//java.util.Properties
            prop.setProperty("jcifs.smb.client.minVersion", "SMB202")
            prop.setProperty("jcifs.smb.client.maxVersion", "SMB300")
            valbc = BaseContext (PropertyConfiguration(prop))
            valcreds=NtlmPasswordAuthentication(bc,domain,user,password)
            val auth —CIFSContext=bc. with Credentials (creds)
            val smb = SmbFile(smbUrl, auth)
            if(smb.exists()){
                // Set data from SMB server in LiveData
                _smbPath.postValue(smb.path)
                _smbSize.postValue(smb.length())
                retBool=true
            }
        }
        Thread.sleep(5000)
        returnBool
    }


    // Connectivity to SMB Server Example 2
    funconnectSmb2(): Boolean {
        varretBool: Boolean=false

        viewModelScope.launch {
            valueAsync = async (Dispatchers.IO) {
                val prop=Properties()//java.util.Properties
                prop.setProperty("jcifs.smb.client.minVersion", "SMB202")
                prop.setProperty("jcifs.smb.client.maxVersion", "SMB300")
                valbc = BaseContext (PropertyConfiguration(prop))
                valcreds=NtlmPasswordAuthentication(bc,domain,user,password)
                val auth —CIFSContext=bc. with Credentials (creds)
                val smb = SmbFile(smbUrl, auth)
                if(smb.exists()){
                    // Set data from SMB server in LiveData
                    _smbPath.postValue(smb.path)
                    _smbSize.postValue(smb.length())
                    true
                } else{
                    false
                }
            }
            retBool=retAsync.await()
        }
        returnBool
    }

    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()
    }
}

SmbViewModelFactory.kt

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

classSmbViewModelFactory(valuser:String, val password:String, valdomain:String, val smbUrl:String):ViewModelProvider.Factory {
    override fun<T:ViewModel>create(modelClass:Class<T>):T{
        return SmbViewModel(user, password, domain, smbUrl) as T
    }
}

android

2022-09-30 21:59

2 Answers

It's self-less.
https://stackoverflow.com/questions/67343540/how-to-send-the-result-of-viewmodelscope-launch-as-the-return-value-of-a-funct stated that the only way to pass the data in launch{} out of launch is to use LiveData.
We were able to achieve this with the following code:
In launch{} of worker thread, we set the result to isConnectedSmb of LiveData and get the value of isConnectedSmb from the main thread using the observe method.


MainActivity.kt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import net.sytes.rokkosan.smbclient2.databinding.ActivityMainBinding

classMainActivity:AppCompatActivity(){

    //////////////////////////////////////////////
    valDOMAIN: String="192.168.1.1"
    val SMBROOT: String="/SMB/SERVER/FILE/"
    val USER: String="FOO"
    val PASSWORD: String="BAR"
    //////////////////////////////////////////////

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

        val binding = ActivityMainBinding.inflate (layoutInflater)
        setContentView (binding.root)

        // prepare for connecting SMB server
        val smbUrl = "smb://" + DOMAIN + SMBROOT

        // getViewModel for SmbConnection
        valsmbViewModelFactory=SmbViewModelFactory(USER, PASSWORD, DOMAIN, smbUrl)
        valuembViewModel:SmbViewModel=ViewModelProvider(this,smbViewModelFactory).get(SmbViewModel::class.java)

        // connect SMB server
        smbViewModel.connectSmb()
        valisConnectedObserver=Observer<Boolean>{isConnected->
            if(!isConnected){
                finishAndRemoveTask()
            }
        }
        smbViewModel.isConnectedSmb.observe(this, isConnectedObserver)

        /*
         * get data in SMB server
         */
        // Create the observer which updates the UI.
        val pathObserver=Observer<String>{sPath->
            // Update the UI
            binding.tvSmbPath.text=sPath
        }
        valueObserver=Observer<Long>{sSize->
            // Update the UI
            binding.tvSmbSize.text=sSize.toString()
        }

        smbViewModel.smbPath.observe (this, pathObserver)
        smbViewModel.smbSize.observe(this,sizeObserver)
    }
}

SmbViewModel.kt

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import jcifs.config.PropertyConfiguration
import jcifs.context.BaseContext
import jcifs.smb.NtlmPasswordAuthenticator
import jcifs.smb.SmbFile
import kotlinx.coroutines.*
import java.util.*

class SmbViewModel (val user: String, val password: String, val domain: String, val smbUrl: String): ViewModel() {

    // Configure LiveData
    private val_smbPath:MutableLiveData<String>by lazy{
        MutableLiveData<String>()
    }
    valuembPath —LiveData<String>
        get() =_smbPath

    private val_smbSize:MutableLiveData<Long>by lazy{
        MutableLiveData<Long>()
    }
    valuembSize —LiveData<Long>
        get() =_smbSize

    private val_isConnectedSmb =MutableLiveData<Boolean>()
    valisConnectedSmb —LiveData<Boolean>
        get() =_isConnectedSmb

    // Connecting to SMB Server
    funconnectSmb(){
        viewModelScope.launch (Dispatchers.IO) {
            val prop=Properties()//java.util.Properties
            prop.setProperty("jcifs.smb.client.minVersion", "SMB202")
            prop.setProperty("jcifs.smb.client.maxVersion", "SMB300")
            valbaseCxt = BaseContext (PropertyConfiguration(prop))
            val auth=
                baseCxt.with Credentials (NTlmPasswordAuthenticator(domain, user, password))
            val smb = SmbFile(smbUrl, auth)
            if(smb.exists()){
                // Set data from SMB server in LiveData
                _smbPath.postValue(smb.path)
                _smbSize.postValue(smb.length())
                _isConnectedSmb.postValue(true)
            } else{
                _isConnectedSmb.postValue(false)
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()
    }
}


2022-09-30 21:59

It's self-less.
Instead of stopping the main thread, we needed a view that Activity would do the required work as soon as the results of the work on the worker thread came out.
You can pass the results of the worker thread to Activity via LiveData, and Observer can do a lot of things.
The following reference page is posted.
https://rasumus.hatenablog.com/entry/2021/09/03/221016


2022-09-30 21:59

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.