How to access other custom method outside work manager? - android

I am working on app in which first i have to start the process and then update the value so how to access other methods of custom method o workmanager thanks
class SmsWorkManager(val context : Context, workerParameters:WorkerParameters) : CoroutineWorker(context ,workerParameters) {
override suspend fun doWork(): Result {
println("do some task ")}
fun updateMethod(){
println("how to access this method")}
}
// class Instannce for work maanager
val workManager = WorkManager.getInstance(this )
// val oneTimeRequest =OneTimeWorkRequest.Builder(SmsWorkManager::class.java)
workManager.enqueue(oneTimeRequest.build())

You need to return a Result after work completion & you can simply use the updateMethod() inside your `doWork() like below:
class SmsWorkManager(val context: Context, params: WorkerParameters) :
CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
println("do some task ")
updateMethod()
return Result.success()
}
fun updateMethod(){
println("how to access this method")
}
}
Also, if you are not doing any IO task then you should use a Worker instead of a CoroutineWorker.

Related

SynchronousExecutor not running WorkManager synchronously for CoroutineWorkers

Following Android's documentation, I created the following minimal viable example:
#RunWith(AndroidJUnit4::class)
class UpdatePlatformEndpointTest {
private lateinit var context: Context
#Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
val config = Configuration.Builder()
.setExecutor(SynchronousExecutor())
.build()
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
}
#Test
fun example() {
val workManager = WorkManager.getInstance(ApplicationProvider.getApplicationContext())
val workRequest = OneTimeWorkRequestBuilder<W>()
.build()
val operation = workManager.enqueue(workRequest)
operation.result.get()
val workInfo = workManager.getWorkInfoById(workRequest.id).get()
val outputData = workInfo.outputData
assertEquals(workInfo.state, WorkInfo.State.SUCCEEDED)
}
class W(
appContext: Context,
params: WorkerParameters
) : Worker(appContext, params) {
override fun doWork(): Result {
Thread.sleep(5000)
return Result.success()
}
}
}
This works, the tests takes about 5 seconds to complete now. However, changing the Worker class W to the following, the test will fail because the CoroutineWorker has the state of RUNNING.
class W(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
delay(5000)
return Result.success()
}
}
A quick workaround would be to extend the Worker class and add a runBlocking inside of it.
class W(
appContext: Context,
params: WorkerParameters
) : Worker(appContext, params) {
override fun doWork(): Result = runBlocking {
delay(5000)
return Result.success()
}
}
How could I create CoroutineWorkers and still have a working test setup?
P.S. I created a bug ticket in Google's IssueTracker, however I would love to have workaround that could also be documented for other developers. The bug ticket also contains a sample project to tinker with this issue.

Compose WorkManager not getting triggered

I have an app that uses a Worker to update its services via the internet.
However, the worker is not getting triggered.
Worker.kt:
class MyWorker(
private val container: AppContainer,
ctx: Context,
params: WorkerParameters
) : CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
return#withContext try {
val response = container.onlineRepository.getData()
// Load the data
container.offlineRepository.load(
data = response.data
)
Result.success()
} catch (throwable: Throwable) {
Log.e(
TAG, throwable.message, throwable
)
Result.failure()
}
}
}
}
DataActivity.kt:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val worker = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(this).enqueue(worker)
setContent {
DataApp()
}
}
When i check the logs, nothing is being logged because it is not entering the doWork()
Can someone please help ?
In yourMyWorker class constructor, you are requiring thecontainer: AppContainer argument which is not supplied on instantiation. It's better to use WorkerParameters to achieve this.
You could use this:
// Passing params
Data.Builder data = new Data.Builder();
data.putString("my_key", my_string);
val worker = OneTimeWorkRequestBuilder<MyWorker>()
.setInputData(data.build())
.build()
WorkManager.getInstance(this).enqueue(worker)
However, WorkManager's Data class only accepts some specific types as values as explained in the reference documentation.
On top of that, there's a size limit of about 10 KB, specified by the constant MAX_DATA_BYTES.
If the data is not too big, you may want to serialize it to a String and use that as inputData in your WorkRequest.

How can I call a composable function outside of an activity?

In my case I would like to call the composable from a OneTimeWorkRequest.
As far as I'm aware composables are typically called from Activities with setContent(). However I do not have access to this within the Worker and not sure how to instantiate a 'blank/dummy' activity within the worker.
#Composable
fun Hello() {
Text(text = "Hello")
}
class MyWorker(private val appContext: Context, private val params: WorkerParameters)
: CoroutineWorker(appContext,params) {
override suspend fun doWork(): Result {
Hello() /*<---error here "#Composable invocations can only happen from the context of a #Composable function"*/
return Result.success()
}
}

Coroutine Worker gets killed after kill the app

Lets say that i have an activity that starts a worker. inside the worker i do a pseudo suspend proccess and then i print out a result from the database. Here is the code
The activity which starts the worker is
class SplashActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
val oneTimeRequest = OneTimeWorkRequest.Builder(MyWorker::class.java).setInputData(Data.Builder().apply {
putInt("data", 1)
}.build()).addTag("worktag").build()
WorkManager.getInstance(applicationContext).enqueue(oneTimeRequest)
}
}
The worker is the below
class MyWorker #AssistedInject constructor(
#Assisted private val appContext: Context,
#Assisted private val params: WorkerParameters,
private val serverRepository: ServerRepository
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
GlobalScope.launch {
for (i in 0..10) {
println("$i")
delay(1000)
}
val servers = serverRepository.getServers()
runOnUiThread {
Toast.makeText(appContext, "${servers.firstOrNull()?.serverAddress}", Toast.LENGTH_SHORT).show()
}
}
return Result.success()
}
}
So the result is that i see in the logcat the system.out with 1,2,3... and then i see a toast messages.
However, when i totally kill the app from the recent while the counter still counts, i never see the toast message.
Why is this happening since i have a GlobalScope coroutine?
And what is the right way to do this??
I was trying to achieve a similar goal. I managed my work by using ForegroundService.
You can find more here
https://developer.android.com/guide/components/foreground-services

How to get live data from WorkManager in Android

I am trying to make API call from doWork() method of WorkManager. I receive MutableLiveData with list from response. How to set this complex object as output from WorkManager.
Please find below implementation for the same :
class FetchWorkManager(context: Context, params: WorkerParameters): Worker(context,params) {
var postInfoLiveData: LiveData<List<PostInfo>> = MutableLiveData()
#SuppressLint("RestrictedApi")
override fun doWork(): Result {
fetchInfoFromRepository()
//setting output data
val data = Data.Builder()
.putAll(postInfoLiveData)
//.put("liveData",postInfoLiveData)
.build()
return Result.success(data)
}
fun fetchInfoFromRepository(){
val retrofitRepository = RetrofitRepository()
postInfoLiveData = retrofitRepository.fetchPostInfoList()
}
}
Can anyone help me in resolving this issue.
i am not sure but it should be like this :)
workManager?.getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
?.observe(this, Observer {
if (it?.state == null)
return#Observer
when (it.state) {
State.SUCCEEDED -> {
val successOutputData = it.outputData
}
State.FAILED -> {
val failureOutputData = it.outputData
}
}
})
It is not intended behaviour to return result from Worker with LiveData member. The result from the Worker should be returned as a return value of startWork method. To construct Result object with some data ListenableWorker.Result.success method can be used.
const val WORKER_RESULT_INT = "WORKER_RESULT_INT"
class WorkerWithOutput(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// do some work
return Result.success(Data.Builder().putInt(WORKER_RESULT_INT, 123).build())
}
}
And to get this data from outside one of getWorkInfoXXX methods should be used.
fun getResult(context: Context, owner: LifecycleOwner, id: UUID) {
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(id)
.observe(owner, Observer {
if (it.state == WorkInfo.State.SUCCEEDED) {
val result = it.outputData.getInt(WORKER_RESULT_INT, 0)
// do something with result
}
})
}
Activity or fragment can be passed as LifecycleOwner (depending on your case). WorkRequest.getId is used to get id of the work.
It is worth noting that there is ListenableWorker.setProgressAsync which also can be useful in such circumstances.
I am not sure if this would work since I have not tried it yet. and I know it is a late answer but I would encourage you to try to use CoroutineWorker as below:
class MyWorker(context: Context, params: WorkerParameters):
CoroutineWorker(context, params){
override suspend fun doWork(): Result {
val data = withContext(Dispatchers.IO) {
// you can make network request here (best practice?)
return#withContext fetchInfoFromRepository()
// make sure that fetchInfoFromRepository() returns LiveData<List<PostInfo>>
}
/* Then return it as result with a KEY (DATA_KEY) to use in UI. */
val result = workDataOf(DATA_KEY to data)
return Result.success(result)
}
}
ref: https://developer.android.com/topic/libraries/architecture/workmanager/advanced/coroutineworker

Categories

Resources