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
}
}
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()
}
}
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
© 2024 OneMinuteCode. All rights reserved.