background

Recently we’ve been working on a blockchain wallet project, a new App using a brand new technology stack. For Android we use Kotlin+RxJava+Android Architecture Components and for iOS we use Swift+RxSwift. This article does not discuss the architecture of the App, only the features of Kotlin used in the project.

In Android apps, it’s no exaggeration to say that more than 95% of our code is developed using Kotlin. Therefore, it is necessary to make a simple summary of the use of Kotlin in this phase.

Kotlin features to use:

I. Extension functions

Kotlin allows developers to add new functions to a class without changing the existing class. This feature is called an extension function.

Here’s a simple example. If you want to close an I/O stream, you might write a utility method using Java.

    /** * Safely shut down the IO stream *@param closeable
     */
    public static void closeQuietly(Closeable closeable) {
        if(closeable ! =null) {
            try {
                closeable.close();
            } catch(IOException e) { e.printStackTrace(); }}}Copy the code

For Kotlin, the function closeQuietly() can be extended to Closeable.

funCloseable? .closeQuietly(a) {
    try {
        this? .close() }catch (e: Throwable) {
    }
}
Copy the code

After that, any class that implements the Closeable interface can use its own closeQuietly() method to close the stream. We don’t need that tool method anymore.

In the project, we use the extension function to do the encapsulation of Glide, Glide greatly simplifies the use.

/** * Placeholder rectangle */
fun ImageView.load(url: String) {
    get(url).placeholder(R.drawable.shape_default_rec_bg)
            .error(R.drawable.shape_default_rec_bg)
            .into(this)}/** * Placeholder rounded rectangle */
fun ImageView.loadRound(url: String) {
    get(url).placeholder(R.drawable.shape_default_round_bg)
            .error(R.drawable.shape_default_round_bg)
// .apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0)))
            .transform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0))
            .into(this)}/** * Placeholder circle */
fun ImageView.loadCircle(url: Drawable) {
    get(url).placeholder(R.drawable.shape_default_circle_bg)
            .error(R.drawable.shape_default_circle_bg)
            .into(this)}fun ImageView.loadCircle(url: String) {
    get(url).placeholder(R.drawable.shape_default_circle_bg)
            .error(R.drawable.shape_default_circle_bg)
            .into(this)}fun ImageView.get(url: String): GlideRequest<Drawable> = GlideApp.with(context).load(url)
fun ImageView.get(url: Drawable): GlideRequest<Drawable> = GlideApp.with(context).load(url)
Copy the code

In addition, we also use extension functions in many places.

I updated my Kolin tool library, by the way, it includes various utils and various extension at https://github.com/fengzhizi715/SAF-Kotlin-Utils

Two. Tag closures

I didn’t understand the concept at first. By chance, I saw our friends write this code when using RxBus:

RxBus.get().register(LogoutEvent::class.java) { refresh() }
Copy the code

I was confused at that time, because RxBus was written by me, and I remember there was no such method. Click on the register() method and check it out.

    public <T> Disposable register(Class<T> eventType, Consumer<T> onNext) {
        return toObservable(eventType).observeOn(AndroidSchedulers.mainThread()).subscribe(onNext);
    }
Copy the code

Thanks to the use of Kotlin, the use of the register method can be simplified as follows:

RxBus.get().register(LogoutEvent::class.java.{
            refresh()
        })
Copy the code

Since the last argument to register() is a method or a closure, you can bring the method or closure to the outside. Become what you see in the project:

RxBus.get().register(LogoutEvent::class.java) { refresh() }
Copy the code

This is the trailing closure, which makes your code look cleaner.

With, with, with

With takes an object as an argument to a function, which can be referred to by this within the function block. Methods or properties of an object can be called directly from within a function block.

/** * Calls the specified function [block] with the given [receiver] as its receiver and returns its result. */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T. () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
Copy the code

Some Adapter before using with

class AppPublisherAdapter : BaseAdapter<BoundAppInfoResponse.AppInfo>() {

    override fun getLayoutId(viewType: Int): Int = R.layout.cell_app_publisher

    override fun onBindViewHolderImpl(holder: BaseViewHolder, position: Int,content: BoundAppInfoResponse.AppInfo) {
        holder.itemView.tv_game_name.text = content.name

        if (content.is_bound) {
            holder.itemView.tv_bound_user_name.text = content.bound_user_name
            holder.itemView.tv_bound_user_name.setTextColor(context.resources.getColor(R.color.color_bound_user_name))
        } else {
            holder.itemView.tv_bound_user_name.text = context.getString(R.string.bind_on_account)
            holder.itemView.tv_bound_user_name.setTextColor(context.resources.getColor(R.color.color_bind_on_account))
        }
        holder.itemView.iv_game_icon.load(content.logo_url)
    }
}
Copy the code

After using with, the function block can omit the “content.”

class AppPublisherAdapter : BaseAdapter<BoundAppInfoResponse.AppInfo>() {

    override fun getLayoutId(viewType: Int): Int = R.layout.cell_app_publisher

    override fun onBindViewHolderImpl(holder: BaseViewHolder, position: Int, content: BoundAppInfoResponse.AppInfo) {

        with(content) {
            holder.itemView.tv_game_name.text = name

            if (is_bound) {
                holder.itemView.tv_bound_user_name.text = bound_user_name
                holder.itemView.tv_bound_user_name.setTextColor(context.color(R.color.color_bound_user_name))
            } else {
                holder.itemView.tv_bound_user_name.text = context.string(R.string.bind_on_account)
                holder.itemView.tv_bound_user_name.setTextColor(context.color(R.color.color_bind_on_account))
            }
            holder.itemView.iv_game_icon.load(logo_url)
        }
    }
}
Copy the code

Other four.

This section is not a feature of Kotlin, but a tool I developed with Kotlin. Examples include logging framework L and Retrofit’s logging interceptor. These libraries, which were developed a long time ago, have recently been slightly upgraded.

L making address: https://github.com/fengzhizi715/SAF-Kotlin-log

Retrofit logging interceptor making address: https://github.com/fengzhizi715/saf-logginginterceptor

A rendering of the log interceptor:

conclusion

Kotlin absorbs the advantages of multiple languages and has many exciting features compared to Java, greatly improving development efficiency. The features described in this article are just a drop in the ocean. Next, I’ll sort out more of the Kotlin features used in my projects.

BTW, I am writing this as the first domestic version of the wallet has just been completed and the first round of testing is underway.

Related articles in this series:

Use Kotlin to efficiently develop Android App(5) the final part

Developing Android Apps efficiently with Kotlin (part 4)

Developing Android Apps efficiently with Kotlin (part 3)

Developing Android Apps efficiently with Kotlin (part 2)


Java and Android technology stack: weekly updates push original technical articles, welcome to scan the public qr code below and follow, look forward to growing with you and progress together.