其他
昨天有同学说Kotlin语法不舒服?那你一定没试过DSL
https://www.jianshu.com/u/257511d0c878
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tanjiajun.androidgenericframework"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
androidExtensions {
experimental = true
}
dataBinding {
enabled = true
}
packagingOptions {
pickFirst 'META-INF/kotlinx-io.kotlin_module'
pickFirst 'META-INF/atomicfu.kotlin_module'
pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module'
}
}
platform :ios, '10.0'
use_frameworks!
target 'GenericFrameworkForiOS' **do**
pod 'SnapKit', '~> 4.0'
**end**
扩展函数
ViewModelFactory((applicationContext as AndroidGenericFrameworkApplication).userRepository)
Lambda表达式
Lambda表达式分类
普通的Lambda表达式
带接收者的Lambda表达式
apply函数
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
let函数
* Calls the specified function [block] with `this` value as its argument and returns its result.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let).
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
函数类型实例调用
中缀表示法
它们必须是成员函数和扩展函数。 它们必须只有一个参数。 其参数不得接受可变数量的参数,而且不能有默认值。
this.plus(x)
// 可以中缀表示法调用函数
1 plus 2
// 等同于这样
1.plus(2)
until函数用法
for (i in 0 until 4) {
tlOrder.addTab(tlOrder.newTab().setText("订单$i"))
}
until函数源码
* Returns a range from this value up to but excluding the specified [to] value.
*
* If the [to] value is less than or equal to `this` value, then the returned range is empty.
*/
public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
to函数用法
to函数源码
* Creates a tuple of type [Pair] from this and [that].
*
* This can be useful for creating [Map] literals with less noise, for example:
* @sample samples.collections.Maps.Instantiation.mapFromPairs
*/
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
回调处理
Java中的回调实现
定义一个接口 在接口中定义一些回调方法 定义一个设置回调接口的方法,这个方法的参数是回调接口的实例,一般以匿名对象的形式存在。
实现TextWatcher接口
etCommonCallbackContent.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// no implementation
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// no implementation
}
@Override
public void afterTextChanged(Editable s) {
// no implementation
}
});
实现TabLayout.OnTabSelectedListener接口
tlOrder.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// no implementation
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// no implementation
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// no implementation
}
});
Kotlin中的回调实现
实现TextWatcher接口
TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// no implementation
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// no implementation
}
override fun afterTextChanged(s: Editable?) {
tvCommonCallbackContent.text = s
}
})
实现TabLayout.OnTabSelectedListener接口
override fun onTabReselected(tab: TabLayout.Tab?) {
// no implementation
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
// no implementation
}
override fun onTabSelected(tab: TabLayout.Tab?) {
vpOrder.currentItem = tab?.position ?: 0
}
})
Kotlin DSL
TextWatcherBuilder
import android.text.Editable
import android.text.TextWatcher
/**
* Created by TanJiaJun on 2019-10-01.
*/
private typealias BeforeTextChangedCallback =
(s: CharSequence?, start: Int, count: Int, after: Int) -> Unit
private typealias OnTextChangedCallback =
(s: CharSequence?, start: Int, before: Int, count: Int) -> Unit
private typealias AfterTextChangedCallback = (s: Editable?) -> Unit
class TextWatcherBuilder : TextWatcher {
private var beforeTextChangedCallback: BeforeTextChangedCallback? = null
private var onTextChangedCallback: OnTextChangedCallback? = null
private var afterTextChangedCallback: AfterTextChangedCallback? = null
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) =
beforeTextChangedCallback?.invoke(s, start, count, after) ?: Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) =
onTextChangedCallback?.invoke(s, start, before, count) ?: Unit
override fun afterTextChanged(s: Editable?) =
afterTextChangedCallback?.invoke(s) ?: Unit
fun beforeTextChanged(callback: BeforeTextChangedCallback) {
beforeTextChangedCallback = callback
}
fun onTextChanged(callback: OnTextChangedCallback) {
onTextChangedCallback = callback
}
fun afterTextChanged(callback: AfterTextChangedCallback) {
afterTextChangedCallback = callback
}
}
fun registerTextWatcher(function: TextWatcherBuilder.() -> Unit) =
TextWatcherBuilder().also(function)
OnTabSelectedListenerBuilder
import com.google.android.material.tabs.TabLayout
/**
* Created by TanJiaJun on 2019-09-07.
*/
private typealias OnTabCallback = (tab: TabLayout.Tab?) -> Unit
class OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {
private var onTabReselectedCallback: OnTabCallback? = null
private var onTabUnselectedCallback: OnTabCallback? = null
private var onTabSelectedCallback: OnTabCallback? = null
override fun onTabReselected(tab: TabLayout.Tab?) =
onTabReselectedCallback?.invoke(tab) ?: Unit
override fun onTabUnselected(tab: TabLayout.Tab?) =
onTabUnselectedCallback?.invoke(tab) ?: Unit
override fun onTabSelected(tab: TabLayout.Tab?) =
onTabSelectedCallback?.invoke(tab) ?: Unit
fun onTabReselected(callback: OnTabCallback) {
onTabReselectedCallback = callback
}
fun onTabUnselected(callback: OnTabCallback) {
onTabUnselectedCallback = callback
}
fun onTabSelected(callback: OnTabCallback) {
onTabSelectedCallback = callback
}
}
fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) =
OnTabSelectedListenerBuilder().also(function)
先定义一个类去实现回调接口,并且实现它的回调方法。 观察回调方法的参数,提取成一个函数类型(function type),并且按照需要使用类型别名给函数类型起一个别称,并且用私有修饰。 在类里面声明一些可空的函数类型的可变(var)私有成员变量,并且在回调函数中拿到对应的变量实现它的invoke函数,传入对应的参数。 在类中定义一些跟回调接口一样名字,但是参数是对应的函数类型的函数,并且将函数类型赋值给当前类的对应的成员变量。 定义一个成员函数,参数是一个带有我们定好那个类的接受者对象并且返回Unit的Lambda表达式,在函数里创建相应的对象,并且使用also函数把Lambda表达式传进去。
TextWatcher
registerTextWatcher {
afterTextChanged { tvDSLCallbackContent.text = it }
})
TabLayout.OnTabSelectedListener
onTabSelected { vpOrder.currentItem = it?.position ?: 0 }
})
this.afterTextChanged({ s: Editable? ->
tvDSLCallbackContent.text = s
})
}))
object对象表达式回调和DSL回调对比
DSL写法比object写法会更加符合Kotlin风格。 object写法要实现所有方法,DSL写法可以按照需要实现想要的方法。 从性能上对比,DSL写法对每个回调函数都会去创建Lambda表达式的实例对象,而object写法不管有多少个回调方法,都只生成一个匿名对象实例,所以object写法比DSL写法性能好。
object对象表达式回调
public void beforeTextChanged(@Nullable CharSequence s, int start, int count, int after) {
}
public void onTextChanged(@Nullable CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(@Nullable Editable s) {
TextView var10000 = tvCommonCallbackContent;
Intrinsics.checkExpressionValueIsNotNull(var10000, "tvCommonCallbackContent");
var10000.setText((CharSequence)s);
}
}));
DSL回调
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke((TextWatcherBuilder)var1);
return Unit.INSTANCE;
}
public final void invoke(@NotNull TextWatcherBuilder $this$registerTextWatcher) {
Intrinsics.checkParameterIsNotNull($this$registerTextWatcher, "$receiver");
$this$registerTextWatcher.beforeTextChanged((Function4)(new Function4() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1, Object var2, Object var3, Object var4) {
this.invoke((CharSequence)var1, ((Number)var2).intValue(), ((Number)var3).intValue(), ((Number)var4).intValue());
return Unit.INSTANCE;
}
public final void invoke(@Nullable CharSequence s, int start, int count, int after) {
TextView var10000 = tvDSLCallbackContent;
Intrinsics.checkExpressionValueIsNotNull(var10000, "tvDSLCallbackContent");
var10000.setText(s);
}
}));
$this$registerTextWatcher.onTextChanged((Function4)(new Function4() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1, Object var2, Object var3, Object var4) {
this.invoke((CharSequence)var1, ((Number)var2).intValue(), ((Number)var3).intValue(), ((Number)var4).intValue());
return Unit.INSTANCE;
}
public final void invoke(@Nullable CharSequence s, int start, int before, int count) {
TextView var10000 = tvDSLCallbackContent;
Intrinsics.checkExpressionValueIsNotNull(var10000, "tvDSLCallbackContent");
var10000.setText(s);
}
}));
$this$registerTextWatcher.afterTextChanged((Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke((Editable)var1);
return Unit.INSTANCE;
}
public final void invoke(@Nullable Editable it) {
TextView var10000 = tvDSLCallbackContent;
Intrinsics.checkExpressionValueIsNotNull(var10000, "tvDSLCallbackContent");
var10000.setText((CharSequence)it);
}
}));
}
})));
题外话
void onLoginClick(View view);
default void onLogoutClick(View view){
}
}
fun onLoginClick(view: View)
@JvmDefault
fun onLogoutClick(view: View){
}
}
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\bf\u0018\u00002\u00020\u0001J\u0010\u0010\u0002\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H&J\u0010\u0010\u0006\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H\u0017ø\u0001\u0000\u0082\u0002\u0007\n\u0005\b\u0091(0\u0001¨\u0006\u0007"},
d2 = {"Lcom/tanjiajun/kotlindsldemo/MainActivity$Handlers;", "", "onLoginClick", "", "view", "Landroid/view/View;", "onLogoutClick", "app_debug"}
)
public interface Handlers {
void onLoginClick(@NotNull View var1);
@JvmDefault
default void onLogoutClick(@NotNull View view) {
Intrinsics.checkParameterIsNotNull(view, "view");
}
}
Specifies that a JVM default method should be generated for non-abstract Kotlin interface member. Usages of this annotation require an explicit compilation argument to be specified: either -Xjvm-default=enable or -Xjvm-default=compatibility.
with -Xjvm-default=enable, only default method in interface is generated for each @JvmDefault method. In this mode, annotating an existing method with @JvmDefault can break binary compatibility, because it will effectively remove the method from the DefaultImpls class.
with -Xjvm-default=compatibility, in addition to the default interface method, a compatibility accessor is generated in the DefaultImpls class, that calls the default interface method via a synthetic accessor. In this mode, annotating an existing method with @JvmDefault is binary compatible, but results in more methods in bytecode.
Removing this annotation from an interface member is a binary incompatible change in both modes. Generation of default methods is only possible with JVM target bytecode version 1.8 (-jvm-target 1.8) or higher.
@JvmDefault methods are excluded from interface delegation.
repositories {
google()
jcenter()
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += '-Xjvm-default=compatibility'
}
}
}
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\bf\u0018\u00002\u00020\u0001J\u0010\u0010\u0002\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H&J\u0010\u0010\u0006\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H\u0017ø\u0001\u0000\u0082\u0002\u0007\n\u0005\b\u0091(0\u0001¨\u0006\u0007"},
d2 = {"Lcom/tanjiajun/kotlindsldemo/MainActivity$Handlers;", "", "onLoginClick", "", "view", "Landroid/view/View;", "onLogoutClick", "app_debug"}
)
public interface Handlers {
void onLoginClick(@NotNull View var1);
@JvmDefault
default void onLogoutClick(@NotNull View view) {
Intrinsics.checkParameterIsNotNull(view, "view");
}
}
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\bf\u0018\u00002\u00020\u0001J\u0010\u0010\u0002\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H&J\u0010\u0010\u0006\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H\u0017ø\u0001\u0000\u0082\u0002\u0007\n\u0005\b\u0091(0\u0001¨\u0006\u0007"},
d2 = {"Lcom/tanjiajun/kotlindsldemo/MainActivity$Handlers;", "", "onLoginClick", "", "view", "Landroid/view/View;", "onLogoutClick", "app_debug"}
)
public interface Handlers {
void onLoginClick(@NotNull View var1);
@JvmDefault
default void onLogoutClick(@NotNull View view) {
Intrinsics.checkParameterIsNotNull(view, "view");
}
@Metadata(
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 3
)
public static final class DefaultImpls {
@JvmDefault
public static void onLogoutClick(MainActivity.Handlers $this, @NotNull View view) {
$this.onLogoutClick(view);
}
}
}
https://github.com/TanJiaJunBeyond/KotlinDSLDemo