Kotlin-first but not kotlin-must

After Google announced at I/O that the Kotlin programming language is now the language of choice for Android app developers, more Android apps are embracing Kotlin. Kotlin’s grammar sugar makes development more efficient, speeds up development, makes it more fun, and gives us more time to write comments (laughs). But in fact, as for the choice of Kotlin and Java in Android development, I personally think that in addition to the developer’s preference for the language, the charm and characteristics of each language, and even the requirements of the project and subsequent maintenance and other factors, there is no absolute choice. What we need to do is to amplify and expand the advantages of different languages, not just choose one language. The language is not the problem, but how the person uses it is the key.

Use Lazy help to implement the initialization

Lazy () is a function that takes a Lambda expression as an argument and returns an instance of lazy <T>, which can be used as a delegate to implement the delay property: The first call to get() executes the lamda expression passed to Lazy () and records the result; subsequent calls to get() simply return the recorded result. First paste the code:

fun startInit(component: Components.()->Unit){ component.invoke(Components.get()) } class Components { companion object{ private val entry = ArrayMap<String,Any? >() private val instant by lazy { Components() } fun get() = instant fun getEntry() = entry } inline fun <reified T>single(single: ()->T){ val name = T::class.java.name getEntry()[name] = single() } } inline fun <reified T> get(name: String = T::class.java.name) : T{return Components.getEntry()[name] as T
}

inline fun <reified T> inject(name: String = T::class.java.name) : Lazy<T> {
    returnLazy (mode = LazyThreadSafetyMode. SYNCHRONIZED) {Components. GetEntry () [name] as T}} / / use example startInit {single { RoomApi.getDao() } single { RetroHttp.createApi(Main::class.java) } } private val main : Main by inject() private val dao : MainDao by inject()Copy the code

Summary: Simple code optimization, improve development efficiency

With the help of coroutines to achieve the countdown and timeout waiting tasks

I. Countdown

This has always been a common requirement for Android development. Generally, the following solutions can be adopted: 1, RxJava 2, CountDownTimer 3, Timer+TimerTask 4, thread. These are all common and feasible solutions. So with the coroutine tool, we can implement one of these solutions ourselves, write the code gracefully and paste the code as well

fun counter(dispatcher: CoroutineContext,start:Int, end:Int, delay:Long, onProgress:((value:Int)->Unit),onFinish: (()->Unit)? = null){ val out = flow<Int> {for (i instart.. end) { emit(i) kotlinx.coroutines.delay(delay) } } GlobalScope.launch { withContext(dispatcher) { out.collect { onProgress.invoke(it) } onFinish?.invoke() } } }Copy the code

Using flow to achieve this asynchronous output of numbers at the same time, to achieve a delay between each output result, so that you can better calculate the results, The GlobalScope. Launch. This method is suggested to replace me into lifecycleScope launchWhenStarted life cycle state is very important in the android, is why in recent years, Google introduced the framework:

androidx.lifecycle:lifecycle-***-***
Copy the code

The lifecycleScope launchWhenStarted from here, children’s shoes are interested in can be in-depth study, in fact it is not difficult to understand the knowledge and source code,

androidx.lifecycle:lifecycle-runtime-ktx
Copy the code

2. Timeout waiting task

In normal development, there is more or less a requirement that I need to perform a timeout task, and then the task has a maximum timeout period, after which the task will be invalidated or another task will be executed, and I will execute other logic ahead of time and so on. For example, many launch pages take ads, no matter the ads come from your home or a third party. When it comes to network access, there is always a delay problem. I need to get the ads before ENTERING the main page, and I can’t wait too long to affect the normal use of functions. That had to perform similar tasks overtime, my personal solution is as follows: or with the aid of coroutines, same GlobalScope. Launch. This method is suggested to replace me into lifecycleScope launchWhenStarted

fun waitUtil(dispatcher: CoroutineContext,outTimeMills:Long,onTime:(result:Boolean)->Unit, doWork: suspend () ->Unit){
    GlobalScope.launch(dispatcher) {
        val result = withTimeoutOrNull(outTimeMills){
            doWork.invoke() } onTime.invoke(result ! = null) } }Copy the code

Use the timeout method in the coroutine to help determine whether the execution of the task has timed out. Bottom line: Coroutine is a good framework for thread pool encapsulation. You don’t need to think of it as something very high and hard to understand. It’s important to slowly analyze the source code.

Floating point variables to DP,px,sp, etc

This is also our usual development often used, px to DP, px to sp, etc. First post the code:

val Float.dp
   get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this,Resources.getSystem().displayMetrics)

val Float.px
  get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, this,Resources.getSystem().displayMetrics)

val Float.sp
    get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this,Resources.getSystem().displayMetrics)
Copy the code

Actually this kind of expansion method is often used in the daily Kotlin development, detailed disambiguation visible bosses throwing objects in detail video: www.bilibili.com/video/BV16K…

Jump Kotlin’s ForEach in the fit position

List. ForEach {} : {break} : {break} : {break} : {break}; My personal optimization plan is as follows:

inline fun <T> Iterable<T>.forEachBreak(action: (T) -> Boolean ){
    kotlin.run breaking@{
        for (element in this)
            if(! action(element)){return@breaking
            }
    }
}
Copy the code

Break out of the loop when the final return value is false. Of course, you can use break,countine normally if you use for (element in this). Here’s an example:


val list = ArrayList<Int>()
    for(i in0.. 10){ list.add(i) } list.forEach {if(it>5){
            break; // The compiler will report an error'break' and 'continue' are only allowed inside a loop
        }
        print("Test 1 Loop in $it")}for(i in list){
        if(i>5){
            break
        }
        print("Test 2 Loop in $i")
    }
    
       list.forEachBreak {
        if(it>5){
           return@forEachBreak false
        }
        println("Test 3 Loop in $it")
        true
    }
    
Copy the code

The two outputs are the same except for the one that reported the error

Test 2 Loop in 0
Test 2 Loop in 1
Test 2 Loop in 2
Test 2 Loop in 3
Test 2 Loop in 4
Test 2 Loop in 5
Test 3 Loop in 0
Test 3 Loop in 1
Test 3 Loop in 2
Test 3 Loop in 3
Test 3 Loop in 4
Test 3 Loop in 5
Copy the code

Conclusion: don’t be lazy, write more lines of code, sometimes lazy syntax sugar may not taste good

Optimized use of EditText’s addTextChangedListener and TabLayout’s addOnTabSelectedListener

The following code is big, sometimes really don’t want to write code (dog head), first post:

1, EditText addTextChangedListener

fun EditText.textWatcher(textWatch: SimpleTextWatcher.() -> Unit) { val simpleTextWatcher = SimpleTextWatcher(this) textWatch.invoke(simpleTextWatcher) } class SimpleTextWatcher(var view: EditText) { private var afterText: (Editable? .() -> Unit)? = null fun afterTextChanged(afterText: (Editable? .() -> Unit)) { this.afterText = afterText } private var beforeText: ((s: CharSequence? , start: Int, count: Int, after: Int) -> Unit)? = null fun beforeTextChanged(beforeText: ((s: CharSequence? , start: Int, count: Int, after: Int) -> Unit)) { this.beforeText = beforeText } private var onTextChanged: ((s: CharSequence? , start: Int, before: Int, count: Int) -> Unit)? = null fun onTextChanged(onTextChanged: ((s: CharSequence? , start: Int, before: Int, count: Int) -> Unit)) { this.onTextChanged = onTextChanged } init { view.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { afterText? .invoke(s) } override fun beforeTextChanged(s: CharSequence? , start: Int, count: Int, after: Int) { beforeText? .invoke(s, start, count, after) } override fun onTextChanged(s: CharSequence? , start: Int, before: Int, count: Int) { onTextChanged? .invoke(s, start, before, count) } }) } }Copy the code

AddOnTabSelectedListener for TabLayout

With the basics above, the following is simple:

fun TabLayout.onTabSelected(tabSelect: TabSelect.() -> Unit) { tabSelect.invoke(TabSelect(this)) } class TabSelect(tab: TabLayout) { private var tabReselected: ((tab: TabLayout.Tab) -> Unit)? = null private var tabUnselected: ((tab: TabLayout.Tab) -> Unit)? = null private var tabSelected: ((tab: TabLayout.Tab) -> Unit)? = null fun onTabReselected(tabReselected: (TabLayout.Tab.() -> Unit)) { this.tabReselected = tabReselected } fun onTabUnselected(tabUnselected: (TabLayout.Tab.() -> Unit)) { this.tabUnselected = tabUnselected } fun onTabSelected(tabSelected: (TabLayout.Tab.() -> Unit)) { this.tabSelected = tabSelected } init { tab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabReselected(tab: TabLayout.Tab?) { tab? .apply { tabReselected? .invoke(tab) } } override fun onTabUnselected(tab: TabLayout.Tab?) { tab? .apply { tabUnselected? .invoke(tab) } } override fun onTabSelected(tab: TabLayout.Tab?) { tab? .apply { tabSelected? .invoke(TAB)}}})}} // Invoke tab.onTabSelected {onTabSelected {pos = position}}Copy the code

Bottom line: Both of the above are typical DSL syntax. Using Kotlin’s DSL, with Kotlin’s elegance, proper method naming, and proper design, can make your code better understood by the developer or maintainer. After all, if you have a large chunk of code in there, it is a bit of a chore to maintain.

conclusion

In general, the content shared is not much, which is often used in our daily life. There will be a lot of different findings if we type and write more.