in my project im using AndroidX Lifecycle ViewModel (2.2.0-rc01) and im in the need to write a couple of espresso test, to tell espresso that the Application tested is still busy.
The viewModelScope need to tell that espresso is busy.
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope
{
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
Is extending viewModelScope and using the CountingIdleResource a valid solution ?
Or do i just need the InstantTaskExecutorRule ?
The Coroutines Job has #invokeOnCompletion function which i could use to count down the idling resource.
thanks
It looks like there is some ongoing discussion: https://github.com/Kotlin/kotlinx.coroutines/issues/242
Related
I'm developing an android app that is a bitcoin wallet using Jetpack Compose.
I have Wallet.kt file with:
fun sync() {
Log.i(TAG, "Wallet is syncing")
wallet.sync(blockchain, LogProgress)
}
fun getBalance(): ULong = wallet.getBalance().total
then in HomeScreen.kt I have
internal class WalletViewModel() : ViewModel() {
private var _balance: MutableLiveData<ULong> = MutableLiveData(0u)
val balance: LiveData<ULong>
get() = _balance
fun updateBalance() {
Wallet.sync()
_balance.value = Wallet.getBalance()
}
then outside of this is composable function HomeScreen
internal fun HomeScreen(
navController: NavController,
walletViewModel: WalletViewModel = viewModel()
) {
val balance by walletViewModel.balance.observeAsState()
Image(Modifier.clickable{ walletViewModel.updateBalance() }
}
My problem being that when I click on that Image which has clickable, the whole app freezes, until the updateBalance() is completed.
I learned that this is because the sync() function inside Wallet.kt file is performing network task on the Main Thread and the app is in Main Thread, so the whole app has to wait until sync is done.
Can you suggest how should I implement coroutines or different way, so that the sync happens inside background thread and then updates _balance to/in the Main ?
I've tried lots of things, including suspend before sync() and async in the viewModelScope, but nothing seems to work how I want to.
Thanks
You can solve it in two ways, one is to attach the async call into the viewModelScope and mark the async call as suspend, the other one is creating a coroutine in your repository with Context and execute it in another thread.
Solution 1
fun updateBalance() {
viewModelScope.launch {
Wallet.sync()
_balance.value = Wallet.getBalance()
}
}
suspend fun sync() {
Log.i(TAG, "Wallet is syncing")
wallet.sync(blockchain, LogProgress)
}
Solution 2
suspend fun sync() {
Log.i(TAG, "Wallet is syncing")
withContext(Dispatcher.IO) {
wallet.sync(blockchain, LogProgress)
}
}
I'm new to android studio with kotlin.
I want to make multiple choice quiz app, and I use data class and object constant to supply problem. If users choose correct choice, private var mCurrentPosition(Int) get plus 1 and setQuestion() work to change the problem, choices, and correctChoice.
To prevent the progress from being reset after the app is closed, I thought it would be okay if the int of mCurrentPosition was stored, so I use onSaveIntanceState. But progress is initialized after the app is closed...
class QuizActivity : AppCompatActivity() {
private var mCurrentPosition: Int = 1
private var mQuestion300List: ArrayList<Question300>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState != null) {
with(savedInstanceState) {
mCurrentPosition = getInt(STATE_SCORE)
}
} else {
mCurrentPosition = 1
}
setContentView(R.layout.activity_quiz)
val questionList = Constant.getQuestions()
Log.i("Question Size", "${questionList.size}")
mQuestion300List = Constant.getQuestions()
setQuestion()
tv_choice1.setOnClickListener {
if (tv_correctChoice.text.toString() == "1") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice1.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
tv_choice2.setOnClickListener {
if (tv_correctChoice.text.toString() == "2") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice2.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
tv_choice3.setOnClickListener {
if (tv_correctChoice.text.toString() == "3") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice3.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState?.run {
putInt(STATE_SCORE, mCurrentPosition)
}
super.onSaveInstanceState(outState)
}
companion object {
val STATE_SCORE = "score"
}
private fun setQuestion() {
val question300 = mQuestion300List!![mCurrentPosition-1]
tv_question.text = question300!!.question
tv_choice1.text = question300.choice1
tv_choice2.text = question300.choice2
tv_choice3.text = question300.choice3
tv_correctChoice.text = question300.correctChoice
tv_now.setText("${mCurrentPosition}")
tv_choice1.setBackgroundResource(R.drawable.shape_problem)
tv_choice2.setBackgroundResource(R.drawable.shape_problem)
tv_choice3.setBackgroundResource(R.drawable.shape_problem)
}
}
here is my app code. plz give me help :) thank you
savedInstanceState is really only meant for two things
surviving rotations, where the Activity gets destroyed and recreated
the system killing your app in the background - so when you return, the Activity needs to be recreated as it was, so the user doesn't see any difference between the app just being in the background, and the app being killed to save resources
When onCreate runs, if the Activity is being recreated from a previous state, you'll get a bundle passed in as savedInstanceState - this contains all the stuff you added in onSaveInstanceState before the app was stopped earlier. But if the user has closed the app (either by backing out with the back button, or swiping the app away in the task switcher etc.) then that's counted as a fresh start with no state to restore. And savedInstanceState will be null in onCreate (which is one way you can check if it's a fresh start or not).
So if you want to persist state even after the app is explicitly closed by the user, you'll need to use something else. Here's the docs on the subject - the typical way is to use SharedPreferences for small data, some kind of database like Room for larger state. DataStore is the new thing if you wanted to try that out
The application was developed on ASP NET Core 3. To log user actions, I decided to use a single method in the Project class. Faced the problem of using one singleton dbContext from different threads.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public async Task AddUserLog(string action, string message, int userId)
{
try
{
UserLog userLog = new UserLog()
{
Action = action,
Message = message,
UserId = userId
Datepoint = DateTime.Now
};
_dbContext.UserLog.Add(userLog);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
await AddSystemLog("Project", "AddUserLog", ex.Message);
}
}
SchemeController.cs
public class SchemeController : ControllerBase
{
private readonly Project _project;
public SchemeController(Project project)
{
_project = project;
}
[Authorize(Policy = "AdvancedControl")]
[HttpPost("[action]")]
public async Task SomeMethode()
{
for (int i = 0; i < 10; i++)
{
await _project.AddUserLog("Text", "Message", 42);
}
}
}
Already at the second iteration of the loop, I catch an exception in the AddUserLog method:
"A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext."
I suggest several solutions:
Add the log to the buffer table and then save it to the database by timer. But this is not the best way out;
Block the method while it is being saved to the database.
But I don’t like any of the options.
Please tell me the correct approach in solving this issue.
So, you trying to use shared resource (singleton Project class) to perform parallel operations (save UserLogs) while your shared resource implementation is not thread-safe (exceptions raised).
You have at lease three ways to solve this:
Do not use shared resource: register Project per scope instead of singletone;
Do not perform operations in parallel: seems hard to achieve because you making webapp and you can't force user(s) to wait
Refactor your resource to be thread-safe: add locks/mutexes/buffering... inside Project
There is no one "correct" way - all 3 are correct. Choose one you like (or combine several).
Usually using scoped dbcontext is recommended (because connections are pooled), but it's the creator of app who should decide.
I got a pretty small Verticle that should connect to a database and regularly poll a table and send the objects to the event bus. Thus far I can connect to the database, but the handler afterwards gets not executed and my polling timer does not start. I guess it's something obvious and appreciate every help.
I use Vert.x 3.8 Promises as Futures are deprecated (just like in the example behind that link). As you can see in my code, using deprecated Futures works just fine! But who want's to use deprecated code, heh? Either I'm doing something wrong or that's a bug in Vert.x, what I don't assume.
My Vert.x is at 3.8.1
class JdbcVerticle extends AbstractVerticle {
private SQLConnection connection
#Override
void start(Promise<Void> startPromise) {
def jdbcParams = config().getJsonObject('connection')
// This gets executed:
testFuture().handler = { println "Test Future handler runs!" }
// This is never executed :-(
connect(jdbcParams).handler = { println "Connected..." }
}
Future<Void> connect(JsonObject jdbcParams) {
def promise = Promise.<Void>promise()
def client = JDBCClient.createShared(vertx, jdbcParams)
client.getConnection() { connection ->
if(connection.failed()) {
promise.fail(connection.cause())
} else {
this.connection = connection.result()
promise.complete()
}
}
return promise.future()
}
Future<Void> testFuture() {
def future = Future.<Void>future()
vertx.setTimer(200) { future.complete() }
return future
}
}
So I have a service set up to import a large amount of data from a file the user uploads. I want to the user to be able to continue working on the site while the file is being processed. I accomplished this by creating a thread.
Thread.start {
//work done here
}
Now the problem arises that I do not want to have multiple threads running simultaneously. Here is what I tried:
class SomeService {
Thread thread = new Thread()
def serviceMethod() {
if (!thread?.isAlive()) {
thread.start {
//Do work here
}
}
}
}
However, this doesn't work. thread.isAlive() always return false. Any ideas on how I can accomplish this?
I would consider using an Executor instead.
import java.util.concurrent.*
import javax.annotation.*
class SomeService {
ExecutorService executor = Executors.newSingleThreadExecutor()
def serviceMethod() {
executor.execute {
//Do work here
}
}
#PreDestroy
void shutdown() {
executor.shutdownNow()
}
}
Using a newSingleThreadExecutor will ensure that tasks execute one after the other. If there's a background task already running then the next task will be queued up and will start when the running task has finished (serviceMethod itself will still return immediately).
You may wish to consider the executor plugin if your "do work here" involves GORM database access, as that plugin will set up the appropriate persistence context (e.g. Hibernate session) for your background tasks.
Another way to do this is to use Spring's #Async annotation.
Add the following to resources.groovy:
beans = {
xmlns task:"http://www.springframework.org/schema/task"
task.'annotation-driven'('proxy-target-class':true, 'mode':'proxy')
}
Any service method you now annotate with #Async will run asynchronously, e.g.
#Async
def reallyLongRunningProcess() {
//do some stuff that takes ages
}
If you only want one thread to run the import at a time, you could do something like this -
class MyService {
boolean longProcessRunning = false
#Async
def reallyLongRunningProcess() {
if (longProcessRunning) return
try {
longProcessRunning = true
//do some stuff that takes ages
} finally {
longProcessRunning = false
}
}
}