Kotlin coroutines and their usage summary in Android (several third party libraries that use coroutines)

Insert picture description here

1 Dependency injection Kodin

In fact, this part has nothing to do with coroutines, but I think this article mainly introduces some Kotlin's three-party library, so it is included.

If you do n’t know what dependency injection is, you can take a look at this article: Java: Inversion of Control (IoC) and Dependency Injection (DI)
provide dependency injection in Android, everyone will think of Dragger, and Android officials also recommend using it To do dependency injection.
In the best practices section of the official documentation, the dependency injection module mentions Dragger:
use Dragger in Android applications .
Using Dependency Injection in Android | AndroidDevSummit Chinese subtitle video
Although Android has made some packaging for the Java version of Dragger for easy use, it is still too expensive to learn and a lot of concepts.
But in the Kotlin era, how can we make dependency injection a little simpler, so there is Kodin . The full name of Kodein is KOtlin DEpendency INjection. In fact, it is not a dependency injection framework. It is precisely a dependency retrieval container, which is explained in detail by the following code.

talk is cheap,show me the code:

val di = DI {
    bind<Dice>() with provider { RandomDice(0, 5) }
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
}

class Controller(private di: DI) {
    private val ds: DataSource by di.instance()
}

That's right, it's that simple. The reason why the boring and simple process is simple is to use it, and no longer care about the concepts of Module and Component in Dragger.

The official introduced its advantages:

  • It presents a very simple and easy to read declarative DSL
  • It is not restricted by type erasure (compared to Java)
  • It integrates perfectly with Android
  • It proposes a very Kotlin-style idiomatic API
  • It is fast and optimized (inline is widely used)
  • Can be used in pure Java

Requirements:
Java 8+

It is recommended to read the god of Qing Mei article:
Android developers migrate from Dagger2 to Kodein feelings
farewell Dagger2, Android's Kotlin project using dependency injection Kodein
official document reference:
Kodein DI ON Android
Kodein DI official website: https://kodein.org / di /
Kodein Github: https://github.com/Kodein-Framework/Kodein-DI


2 Picture loading framework Coil

When it comes to the image loading framework in Android, everyone will think of UIL (Universal Image Loader), Fresco, Picasso, Glide, etc. Articles with various comparisons can be found on the Internet. Why do we need a picture loading library?

Coil官网说明:Image loading for Android backed by Kotlin Coroutines.
Coil is an acronym for: Coroutine Image Loader

Code above:

// URL
imageView.load("https://www.example.com/image.jpg")

// Resource
imageView.load(R.drawable.image)

// File
imageView.load(File("/path/to/image.jpg"))

imageView.load("https://www.example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.image)
    transformations(CircleCropTransformation())
}

Officially introduced features:

  • Fast: Coil has performed many optimizations, including memory and disk caching, resampling of images in memory, multiplexing of bitmaps, automatic pause / cancel requests, etc.
  • Lightweight: Coil adds about 1500 methods to your APK (for applications that already use OkHttp and Coroutines), which is comparable to Picasso and much smaller than Glide and Fresco.
  • Easy to use: Coil's API uses Kotlin's language features to simplify and minimize boilerplate code.
  • Modern: Coil uses Kotlin and uses modern libraries including Coroutines, OkHttp, Okio and AndroidX Lifecycles.

要求:
AndroidX ;Min SDK 14+;Compile SDK: 29+;Java 8+

Currently used more Glide official website documents: http://bumptech.github.io/glide/
Corresponding Chinese version: https://muyangmin.github.io/glide-docs-cn/

3 Permission application

Regarding Android's dynamic permission application, you may have widely used RxPremissions , so why do you want to introduce the permission application framework of the coroutine version?
Of course, after having a coroutine, you can take full advantage of the coroutine.
Look at the use of RxPremissions:

final RxPermissions rxPermissions = new RxPermissions(this); // where this is an Activity or Fragment instance

// Must be done during an initialization phase like onCreate
rxPermissions
    .request(Manifest.permission.CAMERA)
    .subscribe(granted -> {
        if (granted) { // Always true pre-M
           // I can control the camera now
        } else {
           // Oups permission denied
        }
    });
    

Simple enough, if multiple permissions are applied for at the same time, the code will look like this:

public void requsetPermissions(Activity activity) {
        //RxPermission在目标activity里面添加了一个fragment用于拦截权限申请结果
        RxPermissions permissions = new RxPermissions(activity);
        permissions.setLogging(true);
        permissions.requestEach(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CALL_PHONE)
                .subscribe(new Consumer<Permission>() {
                    @Override
                    public void accept(Permission permission) throws Exception {
                        //申请和返回的权限名
                        if (permission.name.equalsIgnoreCase(Manifest.permission.READ_EXTERNAL_STORAGE)) {
                            if (permission.granted) {
                                //权限被用户通过
                            } else if (permission.shouldShowRequestPermissionRationale){
                                //权限被用户禁止,但未选中‘不在提示’,则下次涉及此权限还会弹出权限申请框
                            }else {
                                //权限被用户禁止,且选择‘不在提示’,当下次涉及此权限,不会弹出权限申请框
                            }
                        }
                        if (permission.name.equalsIgnoreCase(Manifest.permission.CALL_PHONE)) {
                            if (permission.granted) {
                            
                            } else if (permission.shouldShowRequestPermissionRationale){
                               
                            }else {
                                
                            }
                        }
                    }
                });
    }

//检查某个权限是否被申请
 permissions.isGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)

Yes, a lot of nesting {} is inside, the code structure becomes unclear, then let's take a look at the permission application of the coroutine version.
There are many permission applications for searching coroutines on GitHub, here is one:
Assent: https://github.com/afollestad/assent

Add dependencies in Module's build.gradle:

dependencies {
  //core API   
  implementation 'com.afollestad.assent:core:3.0.0-RC4'
  //协程调用方式API
  implementation 'com.afollestad.assent:coroutines:3.0.0-RC4'
  //Google建议在用户可能不明白为什么需要该权限时,显示权限的基本原理。
  implementation 'com.afollestad.assent:rationales:3.0.0-RC4'
  
}

Use in Activity:

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rationaleHandler = createSnackBarRationale(rootView) {
      onPermission(READ_CONTACTS, "Test rationale #1, please accept!")
      onPermission(WRITE_EXTERNAL_STORAGE, "Test rationale #2, please accept!")
      onPermission(READ_SMS, "Test rationale #3, please accept!")
    }

    requestPermissionButton.clicks()//
        .debounce(200L)
        .onEach {
          val result = awaitPermissionsResult(
              READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,
              rationaleHandler = rationaleHandler
          )
          statusText.text = result.toString()
        }
        .launchIn(lifecycleScope)
  }

The clicks () method is used in the above code to convert the click event to a flow, and then debounce (200L) can be used to remove jitter to prevent fast clicks. The library used is flowBinding : "io.github.reactivecircus.flowbinding: flowbinding -android : 0.9.0 " , the rationaleHandler passed through the createSnackBarRationale () method to show the user why the permission is needed. The returned structure result is of type AssentResult , from which you can get the result of permission application

val result: AssentResult = // ...

val permissions: List<Permission> = result.permissions
val grantResults: List<GrantResult> = result.grantResults

// Takes a single permission and returns if this result contains it in its set
val containsPermission: Boolean = result.containsPermission(WRITE_EXTERNAL_STORAGE)

// You can pass multiple permissions as varargs
val permissionGranted: Boolean = result.isAllGranted(WRITE_EXTERNAL_STORAGE)

// You can pass multiple permissions as varargs
val permissionDenied: Boolean = result.isAllDenied(WRITE_EXTERNAL_STORAGE)

// Returns GRANTED, DENIED, or PERMANENTLY_DENIED
val writeStorageGrantResult: GrantResult = result[WRITE_EXTERNAL_STORAGE]

val granted: Set<Permission> = result.granted()

val denied: Set<Permission> = result.denied()

val permanentlyDenied: Set<Permission> = result.permanentlyDenied()

Where Permission and GrantResult are enumerated types:

@SuppressLint("InlinedApi")
enum class Permission(val value: String) {
  UNKNOWN(""),

  READ_CALENDAR(Manifest.permission.READ_CALENDAR),

  WRITE_CALENDAR(Manifest.permission.WRITE_CALENDAR),

  CAMERA(Manifest.permission.CAMERA),

  READ_CONTACTS(Manifest.permission.READ_CONTACTS),
  WRITE_CONTACTS(Manifest.permission.WRITE_CONTACTS),
  GET_ACCOUNTS(Manifest.permission.GET_ACCOUNTS),
  .
  .
  .
  }

enum class GrantResult {
  GRANTED,
  DENIED,
  PERMANENTLY_DENIED
}

If you don't want to use the above flowBinding library, it is also possible to call directly in the coroutine:
first awaitPermissionsResult (…) is equivalent to askForPermissions (…), just call it in the form of coroutine:

// Launch a coroutine in some scope...
launch {
   val result: AssentResult = awaitPermissionsResult(
       READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,
       rationaleHandler = rationaleHandler
   )
   // Use the result...
}

Second, awaitPermissionsGranted (...) is equivalent to the coroutine form of runWithPermissions (...):

// Launch a coroutine in some scope...
launch {
   awaitPermissionsGranted(
       READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,
       rationaleHandler = rationaleHandler
   )
   // All three permissions were granted...
}

4 Network request

The network request is of course Retrofit. In the previous article, the combination of the coroutine's suspend function and Retrofit has been introduced, so I won't go into details here. Kotlin coroutines and their use in Android are summarized (four coroutines are used in combination with Retrofit, Room, WorkManager)

5 Database

The database is recommended to use Android's official Room. In the previous article, the combination of the coroutine's suspend function and Room has been introduced, so I won't go into details here.

6 More

The use of coroutines can simplify the code. Basically, RxJava usage scenarios can be replaced by coroutines. A long time ago, it was said that RxJava has a high learning cost and is not recommended. However, almost all Android developers now know RxJava. Second, it is not a big problem to check a few operators, and RxJava has also been widely used in many projects, so should we turn RxJava into coroutines?

I think technology is just a tool. If the original way can already handle business logic well, there is no particularly big need to convert to coroutines, but this does not become a reason for us not to learn new technologies. After all, the advantages of coroutines It is still there. Of course, the cost of learning coroutines is also very high. In addition to some basic suspend functions, the use of flow, the use of channels, the use of selectors, etc., all need to be carefully studied.

When the team can recognize the benefits of coroutines and everyone is willing to try to change, then our code will become more flow flow (666).

If you know a lot of coroutine skills to improve your code, you are welcome to tell me that everyone is making progress together.

Published 82 original articles · Like 86 · Visit 110,000+

Guess you like

Origin blog.csdn.net/unicorn97/article/details/105242686