范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

KotlinDSL

  什么是DSL?
  DSL 全称是  Domain Specific Language ,即领域特定语言。顾名思义 DSL 是用来专门解决某一特定问题的语言,比如我们常见的 SQL 或者 正则表达式等,DSL 没有通用编程语言(Java、Kotlin等)那么万能,但是在特定问题的解决上更高效。
  设想以下有这样一种场景,如果我们希望其他人跟随我们既定的规则,排除定义接口、类、方法这种传统方式,我们能怎么实现呢?
  自己去开发一门语言,难度可想而知。其实可以转换一下思路,我们可以基于已有的通用编程语言打造自己的 DSL,比如日常开发中我们将常见到 gradle 脚本 ,其本质就是来自 Groovy 的一套 DSL: android { 	compileSdkVersion 30 	defaultConfig {     applicationId "x.xx.xxx"     minSdkVersion 24     targetSdkVersion 30     versionCode 1     versionName "1.0"     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 	}   buildTypes {     release {       minifyEnabled false       proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"     }    } }
  使用大括号表现层级结构,使用键值对的形式设置参数,没有多余的程序符号,非常直观。还原成标准的Groovy语法则变成如下: Android(30,           DefaultConfig("com.my.app", 24, 30, 1, "1.0",                           "android.support.test.runner.AndroidJUnitRunner") ), BuildTypes(Release(false,           getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") )
  我们可以对比一下上下两段代码,很明显上面一段的代码可读性更高!
  目前Gradle已经 开始推荐使用  kts   替代  gradle  ,其实就是利用了 Kotlin 优秀的 DSL 特性。 Kotlin DSL
  Kotlin 早几年已经被Google作为Android应用软件开发的主要编程语言,在很多场景下,我们已经看到了DSL在 Android 开发中发挥的优势,并且可以有效地提升开发效率。例如 Jetpack Compose 的 UI 代码就是一个很好的示范,它借助 DSL 让 Kotlin 代码具有了不输于 XML 的表现力,同时还兼顾了类型安全,提升了 UI 开发效率。
  XML布局
  xml View
  Compose DSL布局
  compose View
  通过对比可以看到 Kotin DSL 有诸多好处: 有着近似 XML 的结构化表现力; 较少的字符串,更多的强类型,更安全; linearLayoutParams 这样的对象可以多次复用,更方便组件化和维护; 可以在定义布局的同时实现 onClick 等事件; 还可以嵌入 if ,for 这样的控制语句,用于根据状态动态控制和显示View;
  在没有DSL之前,如果我们想要实现这样的效果,代码可能会写成这样,如下:LinearLayout(context).apply { 	addView(ImageView(context).apply { 		image = context.getDrawable(R.drawable.avatar) 	}, LinearLayout.LayoutParams(context, null).apply {...})  	addView(LinearLayout(context).apply { 	... 	}, LinearLayout.LayoutParams(context,null).apply {...})  	addView(Button(context).apply { 		setOnClickListener { 			... 		} 	}, LinearLayout.LayoutParams(0,0).apply {...}) }
  虽然代码已经借助  apply   等作用域函数进行了优化,但写起来仍然很繁琐,同时阅读起来也比较困难。
  小结:
  通过上面的代码实例,我们已经能感受到Kotlin DSL带来的冲击,并且伴随着Jetpack Compose的快速迭代,所带来的性能提升、即时预览等等,在不远的未来传统的xml布局开发方式,也会被取代(Android平台Java基本已经被Kotlin取代)。Kotlin是怎么实现DSL?
  常见的 DSL 都会用大括号来表现层级。Kotlin 的高阶函数允许指定一个 lambda 类型的参数,且当 lambda 位于参数列表的最后位置时可以脱离圆括号,满足 DSL 中的大括号语法要求。
  那么我们不妨先尝试去改造一下下面这段代码:LinearLayout(context).apply {    orientation = LinearLayout.HORIZONTAL    addView(ImageView(context))  }
  为LinearLayout定义一个高阶函数HorizontalLayout,用于表示水平布局,代码如下://函数定义 fun HorizontalLayout(context: Context, init: (LinearLayout) -> Unit) : LinearLayout { 	return LinearLayout(context).apply {   	orientation = LinearLayout.HORIZONTAL     init(this)   } }  //函数调用 HorizontalLayout(context) {   ... 	it.addView(ImageView(context))   ... }
  通过上面函数调用,可以看出来虽然省略了apply,但是离我们想要的结果还是有很远差距。大括号内部,还是需要使用it来进行相关函数的调用,addView 方法也带有浓重的传统写法味道。
  我们再进一步对函数定义进行优化//函数定义 fun HorizontalLayout(context: Context, init: LinearLayout.() -> Unit) : LinearLayout { 	return LinearLayout(context).apply {   	orientation = LinearLayout.HORIZONTAL     init()   } }  //隐藏addView方法到ImageView内部,同时ViewGroup添加拓展函数 //由于不用把ImageView实例返回给父View,直接返回Unit fun ViewGroup.ImageView(init: ImageView.() -> Unit) { 	addView(ImageView(context).apply(init)) }  //优化之后的,函数调用 HorizontalLayout {   ... 	ImageView {   	...   }   ... }
  再看一下DSL改造view的setOnClickListener的代码,如下://函数定义 fun View.onClick(listener: (v: View) -> Unit) { 	setOnClickListener(listener) }  //函数调用 Button.onClick { 	...... }
  当然这个例子中由于setOnClickListener是一个SAM接口,优势并不明显。下面我们用EdttText的addTextChangedListener方法,来进一步展示DSL的优势,对于EditText代码通常如下:EditText { 	addTextChangedListener(object: TextWatcher {    	override fun beforeTextChanged(...){   		...   	 }                                    override fun onTextChanged(...){        	....        }      		override fun afterTextChanged(...){        		....        }    } }
  使用DSL进行改造fun EditText.textChangeListener(init: _TextWatcher.() -> Unit) { 	val listener = _TextWatcher()   listener.init()   addTextChangedListener(listener) }  class _TextWatcher: android.text.TextWatcher { 	private var _onTextChanged: ((Charsequence?, Int, Int, Int) -> Unit) ?= null   override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: int) {   	_onTextChanged?.invoke(s, start, before, count)   }   fun onTextChanged(listener: (Charsequence?, Int, Int, Int) -> Unit) {   	_onTextChanged = listener   }      //beforeTextChanged 和 afterTextChanged相关代码省略 }
  函数调用代码如下,同时我们在调用的过程中可以按需调用内部的三个方法,而不用每次都必须复写三个方法。EditText {     textChangedListener {         beforeTextChanged { charSequence, i, i2, i3 ->             //...         }              onTextChanged { charSequence, i, i2, i3 ->             //...         }              afterTextChanged {             //...         }     } }Kotlin DSL 更进一步
  经过前面的一步一步的优化,我们的 DSL 基本达到了预期效果,接下来通过更多 Kotlin 的特性让这套 DSL 更加好用,并且语义更加清晰。 infix 增强可读性
  Kotlin 的中缀函数可以让函数省略圆点以及圆括号等程序符号,让语句更自然,进一步提升可读性。
  比如所有的 View 都有  setTag   方法,正常使用如下: HorizontalLayout {     setTag(1, "tag1")     setTag(2, "tag2") }
  我们使用中缀函数来优化  setTag   的调用如下: class _Tag(val view: View) { 	infix fun  Int.to(that: B) = View.setTag(this, that) }  fun View.tag(block: _Tag.() -> Unit) { 	_Tag(this).apply(block) }
  DSL中调用代码如下:HorizontalLayout { 	tag {   	 1 to "tag1"      2 to "tag2"   } }@DslMarker限制作用域HorizontalLayout {// this: LinearLayout     ...     TextView {//this : TextView         // 此处仍然可以调用 HorizontalLayout         HorizontalLayout {             ...         }     } }
  上面这段代码,我们发现在TextView {...}内部可以调用HorizontalLayout{...},这明显是不符合逻辑的。由于TextView的作用域同时处于父HorizontalLayout的作用域内,所以上面代码编译器任务其内容的HorizontalLayout{...}是在调用this@LinearLayout不会报错。如果编译器不报错,那么将提升代码的bug,同时也不利于我们日常开发功能。
  Kotlin为DSL的使用场景提供了@DslMarker注解,可以对方法的作用域进行限制。添加注解的 lambda 中在省略  this   的隐式调用时只能访问到最近的类型,当调用更外层的的方法会报错。
  @DslMarker   是一个元注解,我们需要基于它定义自己的注解,如下: @DslMarker @Target(AnnotationTarget.TYPE) annotation class ViewDslMarker
  接着,在尾 lambda 的 Receiver 添加此注解,如下: fun ViewGroup.TextView(init: (@ViewDslMarker TextView).() -> Unit) {     addView(TextView(context).apply(init)) }
  TextView {...}   中如果不写  this.   则只能调用  TextView   的方法,如果想调用外层的方法,必须显示的使用  this@xxx   进行调用。 Context Receivers 传递多个上下文
  先看一段代码,如下:fun View.dp(value: Int): Int = (value * context.resources.displayMetrics.density).toInt()  HorizontalLayout {     TextView {         layoutParams = LinearLayout.LayoutParams(context, null).apply {             width = dp(60)             height = 0             weight = 1.0         }     } }  RelativeLayout {     TextView {         layoutParams = RelativeLayout.LayoutParams(context, null).apply {             width = dp(60)             height = ViewGroup.LayoutParams.WRAP_CONTENT         }     } }
  上面的代码中有几点可以使用 context 帮助改善。
  首先,代码中使用带参数的 dp(60) 进行 dip 转换。我们可以通过前面介绍的 context 语法替换为 60f.dp 这样的写法 ,避免括号的出现,写起来更加舒适。
  此外,我们知道 View 的 LayoutParams 的类型由其父 View 类型决定,上面代码中,我们在创建 LayoutParams 时必须时刻留意类型是否正确,心理负担很大。
  这个问题也可以用 context 很好的解决,如下我们为 TextView 针对不同的 context 定义 layoutParams 扩展函数:context(RelativeLayout) fun TextView.layoutParams(block: RelativeLayout.LayoutParams.() -> Unit) {     layoutParams = RelativeLayout.LayoutParams(context, null).apply(block) }  context(LinearLayout) fun TextView.layoutParams(block: LinearLayout.LayoutParams.() -> Unit) {     layoutParams = LinearLayout.LayoutParams(context, null).apply(block) }
  在 DSL 中使用效果如下:
  TextView   的  layoutParams {...}   会根据父容器类型自动返回不同的  this   类型,便于后续配置。 使用 inline 和 @PublishedApi 提高性能
  DSL 的实现使用了大量高阶函数,过多的 lambda 会产生过的匿名类,同时也会增加运行时对象创建的开销,不少 DSL 选择使用  inline   操作符,减少匿名类的产生,提高运行时性能。
  比如为  ImageView   的定义添加  inline   : inline fun ViewGroup.ImageView(init: ImageView.() -> Unit) {     addView(ImageView(context).apply(init)) }
  inline   函数内部调用的函数必须是  public   的,这会造成一些不必要的代码暴露,此时可以借助  @PublishedApi   化解。 //resInt 指定图片  inline fun ViewGroup.ImageView(resId: Int, init: ImageView.() -> Unit) {     _ImageView(init).apply { setImageResource(resId) } }  //drawable 指定图片 inline fun ViewGroup.ImageView(drawable: Drawable, init: ImageView.() -> Unit) {     _ImageView(init).apply { setImageDrawable(drawable) } }  @PublishedApi internal inline fun ViewGroup._ImageView(init: ImageView.() -> Unit) =         ImageView(context).apply {             this@_ImageView.addView(this)             init()         }
  如上,为了方便 DSL 中使用,我们定义了两个 ImageView 方法,分别用于 resId 和 drawable 的图片设置。由于大部分代码可以复用,我们抽出了一个 _ImageView 方法。但是由于要在 inline 方法中使用,所以编译器要求 _ImageView 必须是 public 类型。_ImageView 只需在库的内部服务,所以可以添加为 internal 的同时加 @PublishdApi 注解,它允许一个模块内部方法在 inline 中使用,且编译器不会报错。总结
  经过上面的步骤,我们已经基本能实现02 中Compose DSL view代码,但是Kotlin DSL的运用场景远不止UI这一种,但是基本思路都是相通的,我们再回顾一下基本步骤:使用带有尾 lambda 的高阶函数实现大括号的层级调用; 为 lambda 添加 Receiver,通过 this 传递上下文; 通过扩展函数优化代码风格,DSL 中避免出现命令式的语义; 使用 infix 减少点号圆括号等符号的出现,提高可读性; 使用 @DslMarker 限制 DSL 作用域,避免出错; 使用 Context Receivers 传递多个上下文,DSL 更聪明(非正式语法,未来有变动的可能); 使用 inline 提升性能,同时使用 @PublishedApi 避免不必要的代码暴露;

用好时间管理,创造更多的价值绳锯木断,水滴石穿,自律和坚持是成功前的修炼,优秀的人不一定自律,但自律的人一定很优秀。要想坚持自律,首先要学会管理时间,个人感觉,坚持自律和管理时间,会让每个人变得更优秀。有时间A股未来3年有望翻10倍的4大军工龙头,军工电子横空出世军工电子军工电子产业主要承担为武器装备的配套作用,产业链集中于军工产业链中上游环节,中游环节通信设备雷达红外热成像光学制导,是军工电子整机的重要子系统上游环节分为电子元器件特种集成75岁老干妈下场带货,传统品牌何以自救?编辑导语一直以来,老干妈都占领着辣酱市场大部分的市场份额,然而老干妈这种不融资不上市不做广告的品牌理念,似乎已不符合当下消费市场的趋势。传统品牌该如何自救呢?一起来看一下吧。如今的不懂人性,你将一事无成,穷苦一生7个人性揭秘,让你成为人性大师1人与人之间终究是一场利益交换亲情,友情,爱情这些从某种意义上,都是一种假设!本质上是不存在的,是因为人类赋予他的意义才存在的。我爱你,爱的是什么?是经典语录1幸福其实很简单,只要我们有一颗能感受到幸福的心。2。回忆就像断了的弦,弹不出曾经美妙的乐章。3在阳光的路上骑马,在高考的大海深处乘风破浪。4鱼迷水,互忘水。鸟儿乘风飞翔,却不知有清荷札记世间之事,千端万绪作者清荷札记世间之事,千端万绪,岁月漫长,心怀善意,韬光养晦,厚积薄发,与人相处,如沐春风!世间之事,千端万绪,人前的一番大话,远不及柴米油盐,表面的风光排场,比不上碎银几两。当命想要生活越过越好,试试这8个方法来源人民日报徘徊迷茫时,不要质疑你的付出,要知道,你的积累都在默默为你铺路。请相信,奋力奔跑,美好终会如期而至。勤于读书善于包容保持专注8个办法,助你沉淀自己。重视细节不矜细行,终红米K50至尊版对比一加AcePro,看看哪款适合你不得不承认,这两款手机就是近两天热度最高的手机,当然,也有很多朋友都在问,这两款手机怎么选?因为昨天发布会太频繁了,然剪辑视频比较耗时间,所以今天来给大家盘点一波。给大家一些参考意新iPhone或因内存涨价,国产旗舰普及大内存,还加量不加价?最近一个多月时间,苹果新机iPhone14系列接连上热搜,从最新的消息来看,今年的iPhone大概率是要涨价了。不过涨价也不能无缘无故,如果Pro版是因为把内存提高到256GB的话家用扫地机十大品牌中哪个品牌好家用扫地机十大品牌,冰尊家用扫地机独占鳌头,扫地机器人和冰箱空调等大件家电产品一样,动辄就是两三千的价格并不算太便宜。正因为如此,大家花了钱大家都希望买到正确的产品,其实可以参考下120W闪充1亿像素三星120Hz屏天玑920液冷芯,红米良心价随着我们对手机的使用越来越频繁,手机已经不仅仅是日常工作和生活的通信工具了,在我们的一天中尤为重要,人们对手机的要求更高了,不仅要处理性能好,拍照好,长续航,充电快,屏幕清晰流畅,
小个子穿衣显矮的3个细节,你踩雷了吗?穿衣搭配讲究的不仅是体面,它的最大作用堪比于变身,可以快速瘦胳膊瘦腿,让我们看起来更纤细修长。尤其是小个子女生,穿衣合体与不合体,视觉上身高能差好几厘米。这次就给大家分享困扰小个子来点新鲜的!魏牌玛奇朵DHTPHEV助力开启肆意自驾游回乡之后,幸福的假期生活才算正式开始,除了可以睡到自然醒,吃到很久没尝的家乡美食,陪伴父母逛街散步,当然少不了的还有和昔日好友的聚会。以前我们逢年过节的聚会虽然也是仪式感拉满,但是第八号当铺太玄学了!4位女演员现状就像被角色诅咒一样要说都市异闻类的魔幻题材电视剧,不得不提一部台剧第八号当铺。爱情才华什么都能典当,和魔鬼做交易就能如愿以偿,这种新奇的剧情设定也让无数观众欲罢不能,前两年还曾传出过第八号当铺将被翻颖儿新造型彻底封神!梳油发大背头变女霸总,中空穿西装太美艳现如今女性在穿搭这方面更在意高级感的塑造,女性的穿搭在选择单品和风格上是有很大的空间。如果能够更加灵活地运用穿搭,就懂得去营造自己的美,在服装的选择上,不仅仅要时尚好看还要有一种与宋轶的白雪公主造型好美,浅粉色大裙摆礼服好高级礼服的款式各式各样,不过要说最受女性关注的礼服,还是大裙摆的礼服。大裙摆礼服和美眉们心中的公主裙非常相似,给人一种贵气小公主的感觉,非常容易让人心动。个性且高级的大裙摆礼服裙虽然不风衣搭什么下装更时尚?除了阔腿裤外还有它们,每种搭法都好高级作为早春出镜率最高的单品之一,运用好风衣可以让素人成为人群中更亮眼时尚的风景。那么普通人怎么穿风衣才能与旁人拉开差距呢?推荐大家从下装入手,通过选择适合自己又时尚的下装来打造潮流范脚后跟干裂,或并非是皮肤缺水所致,本文告诉你答案,建议多了解相信大家在日常生活中都经历过脚后跟干裂的情况,尤其是到了秋冬季节,由于天气比较干燥,皮肤干裂的情况更加明显,有很多朋友都经历过,当冬天脱掉秋裤时,裤子上总会占有一些皮肤碎屑,看起来女人的精致是对自己的褒奖,初春备上这些优雅简约穿搭,提前时髦对于女生来说,要想穿的有品位精致优雅风格的穿搭是最为适合的,而且不挑年纪,都说女人的精致是对自己的褒奖,每天穿的靓丽一点,心情也魅力。下面这些精致优雅搭配示范,简约又高级,任何年龄想不到裤子袜子的穿法火了,新潮时髦又很洋气,入手不亏裤子无疑是四季中最常见的基础款单品,但越是基础的单品越考验穿搭功底,裤子更是如此。要想将基础款的裤装穿出让人眼前一亮的效果,那我们就得在搭配的穿法上多加注意。今年就火了一种穿法用裤穿红色衣服很显土气?那是你不会搭配,这样穿才时髦好看相信很多人的衣柜里都有不少黑白色的服饰单品,毕竟这类衣服都比较实用百搭,但穿久了难免会审美疲劳,其实可以偶尔改变一下穿搭思路,选择一些色彩艳丽的款式来搭配,大胆尝试下不同的风格,或升温了!这款单鞋早穿早时髦升温提醒!又快要到了单鞋盛行的季节了,我不允许你们还不知道!强占潮流先机的时刻到了,集美们都准备好了吗?当然,在这个关键时刻,咱绝不能少了早穿早时髦的ZHR镜面两穿深口单鞋的助力,