scala.concurrent.ops.spawn
is deprecated, replaced with scala.concurrent.Future
, and will be removed from Scala 2.11.I rewrote my app with
scala.concurrent.Future
as an official Scala documentation suggests:
import ExecutionContext.Implicits.global
Future {
// ...
}
However, after I uploaded the app to Google Play, I received a ton of crash report:
java.lang.ExceptionInInitializerError
at scala.concurrent.impl.ExecutionContextImpl.createExecutorService(ExecutionContextImpl.scala:77)
at scala.concurrent.impl.ExecutionContextImpl.<init>(ExecutionContextImpl.scala:28)
at scala.concurrent.ExecutionContext$Implicits$.global$lzycompute(ExecutionContext.scala:63)
at scala.concurrent.ExecutionContext$Implicits$.global(ExecutionContext.scala:63)
at com.soundcorset.client.android.SoundcorsetService$Metronome.start(SoundcorsetService.scala:38)
...
Caused by: java.lang.Error: java.lang.NoSuchFieldException: parkBlocker
at scala.concurrent.forkjoin.ForkJoinPool.<clinit>(ForkJoinPool.java:2852)
... 20 more
Caused by: java.lang.NoSuchFieldException: parkBlocker
at java.lang.ClassCache.findFieldByName(ClassCache.java:510)
at java.lang.Class.getDeclaredField(Class.java:683)
at scala.concurrent.forkjoin.ForkJoinPool.<clinit>(ForkJoinPool.java:2847)
... 20 more
Scala library has its own copy of
ForkJoinPool
implementation, which includes dynamic invocations, that is parkBlocker
in this case. Unfortunately, some Android devices does not have this method, so we've got this awful crash report.The solution is very simple: Do not use
ExecutionContext.Implicits.global
. Declare a custom implicit ExecutionContext instead. For example:implicit val exec = ExecutionContext.fromExecutor(
new ThreadPoolExecutor(100, 100, 1000, TimeUnit.SECONDS,
new LinkedBlockingQueue[Runnable]))
Future {
// ...
}
This works perfect in production.
EDIT: If the minimum API level of your app is above 11, consider using AsyncTask.THREAD_POOL_EXECUTOR instead of making your own thread executor:
implicit val exec = ExecutionContext.fromExecutor(
AsyncTask.THREAD_POOL_EXECUTOR)
Thanks Niklas Klein for this suggestion.
Many thanks. Looks very easy. One questions remain (at least to me as a Scala newbie). What if I want the continuation to run on the UI thread in order to manipulate some UI state on the callback? How can I achieve this?
ReplyDeleterunOnUiThread would be helpful:
ReplyDeletehttps://github.com/pocorall/scaloid#asynchronous-task-processing
For sure... thanks... so sorry :-(
DeleteHi, I'm also new to scaloid and trying to use future to replace the original AsyncTask.
DeleteI'm wondering how to scala future deal with the issue that Activity got recreated before an async task finish and call the runOnUiThread()? In this case, all the objects when runOnUiThread() are pointers that out of date.
Traditional way of using AsyncTask seems to be adding a task.cancel() in onStop(), does scala future or scaloid support this?
Thanks!
This might be help:
Deleteval active = false
onDestroy(active = false)
onCreate(active = true)
future {
doSomeTask()
if(active) toast("finished")
}
And if you want to do some task that is longer than activity lifecycle, consider using Service :-D
DeleteSung-Ho: Your hint is very helpful. One small question: will this customized context affect performance on mobile device? Are there good values for those parameters?
ReplyDeleteThe performance impact is not noticable. This app is made with Scaloid, try this yourself:
ReplyDeletehttps://play.google.com/store/apps/details?id=com.soundcorset.client.android
Thanks, Suong-Ho. Will try.
DeleteWhy don't you place this useful info in the main doc: https://github.com/pocorall/scaloid/ ?
ReplyDeleteNow I updated the document. Thanks for suggestion!
DeleteYou should consider using a system Executor instead of creating your own. This way you stay compatible with Android's idea of concurrency.
ReplyDeleteimplicit lazy val ThreadPoolExecutionContext = ExecutionContext.fromExecutor( AsyncTask.THREAD_POOL_EXECUTOR )
Also I've just written a Future implementation that comes with predefined (but still overridable) ExecutionContexts to run the body code on the above ThreadPool but run the callbacks on the UI thread.
https://github.com/Taig/Android-Toolbelt/blob/master/src/main/scala/com/taig/android/concurrent/Future.scala
I didn't know about AsyncTask.THREAD_POOL_EXECUTOR. I updated the post. Thanks!
ReplyDelete