Hello Kotlin community !
I’m an Android developer switching from Java to Kotlin, and I am planning to use Coroutines to handle asynchronous code as it looks very promising.
Back in Java, to handle asynchronous code I was using the Executor
class to execute a time-consuming piece of code in another thread, away from the UI thread. I had an AppExecutors
class that I injected in my xxxRepository
classes to manage a set of Executor
. It looked like this :
public class AppExecutors
{
private static class DiskIOThreadExecutor implements Executor
{
private final Executor mDiskIO;
public DiskIOThreadExecutor()
{
mDiskIO = Executors.newSingleThreadExecutor();
}
@Override
public void execute(@NonNull Runnable command)
{
mDiskIO.execute(command);
}
}
private static class MainThreadExecutor implements Executor
{
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command)
{
mainThreadHandler.post(command);
}
}
private static volatile AppExecutors INSTANCE;
private final DiskIOThreadExecutor diskIo;
private final MainThreadExecutor mainThread;
private AppExecutors()
{
diskIo = new DiskIOThreadExecutor();
mainThread = new MainThreadExecutor();
}
public static AppExecutors getInstance()
{
if(INSTANCE == null)
{
synchronized(AppExecutors.class)
{
if(INSTANCE == null)
{
INSTANCE = new AppExecutors();
}
}
}
return INSTANCE;
}
public Executor diskIo()
{
return diskIo;
}
public Executor mainThread()
{
return mainThread;
}
}
Then I was able to write some code like this in my xxxRepository
:
executors.diskIo().execute(() ->
{
try
{
LicensedUserOutput license = gson.fromJson(Prefs.getString(Constants.SHAREDPREF_LICENSEINFOS, ""), LicensedUserOutput.class);
/**
* gson.fromJson("") returns null instead of throwing an exception as reported here :
* https://github.com/google/gson/issues/457
*/
if(license != null)
{
executors.mainThread().execute(() -> callback.onUserLicenseLoaded(license));
}
else
{
executors.mainThread().execute(() -> callback.onError());
}
}
catch(JsonSyntaxException e)
{
e.printStackTrace();
executors.mainThread().execute(() -> callback.onError());
}
});
It worked very good and Google even has something similar in their many Github Android repo examples.
So I was using callbacks. But now I am tired of the nested callbacks and I want to get rid of them. To do so, I could write in my xxxViewModel
for example :
executors.diskIo().execute(() ->
{
int result1 = repo.fetch();
String result2 = repo2.fetch(result1);
executors.mainThread().execute(() -> myLiveData.setValue(result2));
});
How is that USAGE different from Kotlin’s coroutines’ usage ? From what I saw, their biggest advantage is to be able to use asynchronous code in a sequential style. But I am able to do just that with Executor
, as you can see from the code sample right above.
So what am I missing here ? What would I gain to switch from Executor
to Coroutines ?
Note : I get the “coroutine are lightweight” argument, but in Android we don’t have to run 10 000 simultaneous tasks. 4-5 are generally enough.