question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

FastServiceLoader performs I/O on calling thread and prevents R8 optimization

See original GitHub issue

The fix to #878 that introduces a FastServiceLoader to mitigate the fact that ServiceLoader implementation on Android is slow is still inadequate for Android apps and moreover, the solution prevents the R8 optimization from working.

For Android apps to avoid doing I/O on the main thread, we should

  1. let the user disable the FastServiceLoader (this can be done now via setting a systemProp now, but is quite hidden and requires action from the developer)
  2. Enable the R8 optimization by making sure that any calls to ServiceLoader.load are of the form: ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator() Everything matters here: 2 argument constructor, class constants as arguments, and the only call to the returned ServiceLoader has to be .iterator().

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:8
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

18reactions
JakeWhartoncommented, Jun 6, 2019

You can also add

# Ensure the custom, fast service loader implementation is removed.
-assumevalues class kotlinx.coroutines.internal.MainDispatcherLoader {
  boolean FAST_SERVICE_LOADER_ENABLED return false;
}
-checkdiscard class kotlinx.coroutines.internal.FastServiceLoader

to do this at compile-time with R8 instead of at runtime with a property. Your APK also gets a teeeeeeny bit smaller!

0reactions
Tolriqcommented, Aug 29, 2019

Thanks, I can confirm that latest R8 master seems to produce a proper bytecode.

Unfortunately I’m not ready for R8 1.6 yet and will do my usual bug reports with Jinseong if any when AS 3.6 reach beta. After dozen of issues reported for 1.4/1.5 I’m playing safe now 😃

It may worth to add a note somewhere about the fact that it won’t work with 1.5 as without checking bytecode they could think it works.

.method public static constructor <clinit>()V
    .registers 8

    const-string v0, "kotlinx.coroutines.fast.service.loader"

    .line 1
    invoke-static {v0}, Lu3/a/r2/v;->a(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v0

    if-eqz v0, :cond_b

    .line 2
    invoke-static {v0}, Ljava/lang/Boolean;->parseBoolean(Ljava/lang/String;)Z

    :cond_b
    const/4 v0, 0x1

    const/4 v1, 0x2

    const/4 v2, 0x0

    :try_start_e
    new-array v0, v0, [Lkotlinx/coroutines/internal/MainDispatcherFactory;

    const/4 v3, 0x0

    .line 3
    new-instance v4, Lkotlinx/coroutines/android/AndroidDispatcherFactory;

    invoke-direct {v4}, Lkotlinx/coroutines/android/AndroidDispatcherFactory;-><init>()V

    aput-object v4, v0, v3

    invoke-static {v0}, Ljava/util/Arrays;->asList([Ljava/lang/Object;)Ljava/util/List;

    move-result-object v0

    .line 4
    invoke-interface {v0}, Ljava/util/List;->iterator()Ljava/util/Iterator;

    move-result-object v0

    invoke-static {v0}, Lq3/z/s0;->a(Ljava/util/Iterator;)Lt3/z/h;

    move-result-object v0

    invoke-static {v0}, Lq3/z/s0;->b(Lt3/z/h;)Ljava/util/List;

    move-result-object v0

    .line 5
    invoke-interface {v0}, Ljava/lang/Iterable;->iterator()Ljava/util/Iterator;

    move-result-object v3

    .line 6
    invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z

    move-result v4

    if-nez v4, :cond_34

    move-object v4, v2

    goto :goto_5b

    .line 7
    :cond_34
    invoke-interface {v3}, Ljava/util/Iterator;->next()Ljava/lang/Object;

    move-result-object v4

    .line 8
    invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z

    move-result v5

    if-nez v5, :cond_3f

    goto :goto_5b

    .line 9
    :cond_3f
    move-object v5, v4

    check-cast v5, Lkotlinx/coroutines/internal/MainDispatcherFactory;

    .line 10
    invoke-interface {v5}, Lkotlinx/coroutines/internal/MainDispatcherFactory;->getLoadPriority()I

    move-result v5

    .line 11
    :cond_46
    invoke-interface {v3}, Ljava/util/Iterator;->next()Ljava/lang/Object;

    move-result-object v6

    .line 12
    move-object v7, v6

    check-cast v7, Lkotlinx/coroutines/internal/MainDispatcherFactory;

    .line 13
    invoke-interface {v7}, Lkotlinx/coroutines/internal/MainDispatcherFactory;->getLoadPriority()I

    move-result v7

    if-ge v5, v7, :cond_55

    move-object v4, v6

    move v5, v7

    .line 14
    :cond_55
    invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z

    move-result v6

    if-nez v6, :cond_46

    .line 15
    :goto_5b
    check-cast v4, Lkotlinx/coroutines/internal/MainDispatcherFactory;
    :try_end_5d
    .catchall {:try_start_e .. :try_end_5d} :catchall_78

    if-eqz v4, :cond_72

    .line 16
    :try_start_5f
    invoke-interface {v4, v0}, Lkotlinx/coroutines/internal/MainDispatcherFactory;->createDispatcher(Ljava/util/List;)Lu3/a/b2;

    move-result-object v0
    :try_end_63
    .catchall {:try_start_5f .. :try_end_63} :catchall_64

    goto :goto_6f

    :catchall_64
    move-exception v0

    .line 17
    :try_start_65
    new-instance v3, Lu3/a/r2/o;

    invoke-interface {v4}, Lkotlinx/coroutines/internal/MainDispatcherFactory;->hintOnError()Ljava/lang/String;

    move-result-object v4

    invoke-direct {v3, v0, v4}, Lu3/a/r2/o;-><init>(Ljava/lang/Throwable;Ljava/lang/String;)V

    move-object v0, v3

    :goto_6f
    if-eqz v0, :cond_72

    goto :goto_7f

    .line 18
    :cond_72
    new-instance v0, Lu3/a/r2/o;

    invoke-direct {v0, v2, v2, v1}, Lu3/a/r2/o;-><init>(Ljava/lang/Throwable;Ljava/lang/String;I)V
    :try_end_77
    .catchall {:try_start_65 .. :try_end_77} :catchall_78

    goto :goto_7f

    :catchall_78
    move-exception v0

    .line 19
    new-instance v3, Lu3/a/r2/o;

    invoke-direct {v3, v0, v2, v1}, Lu3/a/r2/o;-><init>(Ljava/lang/Throwable;Ljava/lang/String;I)V

    move-object v0, v3

    .line 20
    :goto_7f
    sput-object v0, Lu3/a/r2/n;->a:Lu3/a/b2;

    return-void
.end method
Read more comments on GitHub >

github_iconTop Results From Across the Web

colintheshots | Colin The Shots
In this code, there was a repeated block at the end of every repository call to handle network, DNS, and timeout errors.
Read more >
Scaling Up IO Tasks in Java - Medium
Here we use Thread.sleep()to simulate a long-running IO call. This is a blocking call and the thread is stuck for the given time...
Read more >
Kotlin愛好会 vol.14でDispatchers.Mainについて談義してき ...
FastServiceLoader performs I/O on calling thread and prevents R8 optimization · Issue #1231 · Kotlin/kotlinx.coroutines · GitHub.
Read more >
log: wangdaye.com.geometricweather:30100 - F-Droid Monitor
Get:1 http://deb.debian.org/debian stretch/main amd64 ... ProGuard optimize steps (and performs some # of these optimizations on its own).
Read more >
Java源代码 - 摸瓜
C$r8$backportedMethods$utility$Long$1$hashCode.java ... KProperty0Impl$_getter$1.java; KClassImpl$Data$constructors$2.java; calls.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found