如何讓你的回調(diào)更具Kotlin風(fēng)味(kotlin 回調(diào)

      網(wǎng)友投稿 969 2025-03-31

      簡(jiǎn)述: 這應(yīng)該是2019年的第一篇文章了,臨近過年回家一個(gè)月需求是真的很多,正如康少說的那樣,一年的需求幾乎都在最后一兩月寫完了。所以寫文章也擱置了很久,當(dāng)然再忙每天都會(huì)刷掘金。很久就一直在使用Kotlin寫項(xiàng)目,說實(shí)話到目前為止Kotlin用的是越來越順手了(心里只能用美滋滋來形容了)。當(dāng)然這次依然講的是Kotlin,說下我這次需求開發(fā)中自己一些思考和實(shí)踐。其中讓自己感受最深的就是: "Don't Repeat Yourself"。當(dāng)你經(jīng)常寫一些重復(fù)性的代碼,不妨停下來想下是否要去改變這樣一種狀態(tài)。


      今天我們來講個(gè)非常非常簡(jiǎn)單的東西,那就是回調(diào)俗稱Callback, 在Android開發(fā)以及一些客戶端開發(fā)中經(jīng)常會(huì)使用回調(diào)。其實(shí)如果端的界面開發(fā)當(dāng)做一個(gè)黑盒的話,無非就是輸入和輸出,輸入數(shù)據(jù),輸出UI的渲染以及用戶的交互事件,那么這個(gè)交互事件大多數(shù)場(chǎng)景會(huì)采用回調(diào)來實(shí)現(xiàn)。那么今天一起來說說如何讓你的回調(diào)更具kotlin風(fēng)味:

      1、Java中的回調(diào)實(shí)現(xiàn)

      簡(jiǎn)述: 這應(yīng)該是2019年的第一篇文章了,臨近過年回家一個(gè)月需求是真的很多,正如康少說的那樣,一年的需求幾乎都在最后一兩月寫完了。所以寫文章也擱置了很久,當(dāng)然再忙每天都會(huì)刷掘金。很久就一直在使用Kotlin寫項(xiàng)目,說實(shí)話到目前為止Kotlin用的是越來越順手了(心里只能用美滋滋來形容了)。當(dāng)然這次依然講的是Kotlin,說下我這次需求開發(fā)中自己一些思考和實(shí)踐。其中讓自己感受最深的就是: "Don't Repeat Yourself"。當(dāng)你經(jīng)常寫一些重復(fù)性的代碼,不妨停下來想下是否要去改變這樣一種狀態(tài)。

      今天我們來講個(gè)非常非常簡(jiǎn)單的東西,那就是回調(diào)俗稱Callback, 在Android開發(fā)以及一些客戶端開發(fā)中經(jīng)常會(huì)使用回調(diào)。其實(shí)如果端的界面開發(fā)當(dāng)做一個(gè)黑盒的話,無非就是輸入和輸出,輸入數(shù)據(jù),輸出UI的渲染以及用戶的交互事件,那么這個(gè)交互事件大多數(shù)場(chǎng)景會(huì)采用回調(diào)來實(shí)現(xiàn)。那么今天一起來說說如何讓你的回調(diào)更具kotlin風(fēng)味:

      1、Java中的回調(diào)實(shí)現(xiàn)

      2、使用Kotlin來改造Java中的回調(diào)

      3、進(jìn)一步讓你的回調(diào)更具Kotlin風(fēng)味

      4、Object對(duì)象表達(dá)式回調(diào)和DSL回調(diào)對(duì)比

      5、Kotlin中回調(diào)使用建議

      6、Don't Repeat Yourself(DSL回調(diào)配置太模板化了,不妨來擼個(gè)自動(dòng)生成代碼的AS插件吧)

      7、DslListenerBuilder插件基本介紹和使用

      8、DslListenerBuilder插件源碼和Velocity模板引擎基本介紹

      9、總結(jié)

      一、Java中的回調(diào)實(shí)現(xiàn)

      Java中的回調(diào)一般處理步驟都是寫一個(gè)接口,然后在接口中定義一些回調(diào)函數(shù);然后再暴露一個(gè)設(shè)置回調(diào)接口的函數(shù),傳入函數(shù)實(shí)參就是回調(diào)接口的一個(gè)實(shí)例,一般情況都是以匿名對(duì)象形式存在。例如以Android中OnClickListener和TextWatcher源碼為例:

      1、OnClickListener回調(diào)的Java實(shí)現(xiàn)

      //OnClickListener的定義

      public?interface?OnClickListener?{

      void?onClick(View?v);

      }

      public?void?setOnClickListener(OnClickListener?listener)?{

      this.clickListener?=?listener;

      }

      //OnClickListener的使用

      mBtnSubmit.setOnClickListener(new?View.OnClickListener()?{

      @Override

      public?void?onClick(View?v)?{

      //add?your?logic?code

      }

      });

      2、TextWatcher回調(diào)的Java實(shí)現(xiàn)

      //TextWatcher的定義

      public?interface?TextWatcher?extends?NoCopySpan?{

      public?void?beforeTextChanged(CharSequence?s,?int?start,int?count,?int?after);

      public?void?onTextChanged(CharSequence?s,?int?start,?int?before,?int?count);

      public?void?afterTextChanged(Editable?s);

      }

      public?void?addTextChangedListener(TextWatcher?watcher)?{

      if?(mListeners?==?null)?{

      mListeners?=?new?ArrayList();

      }

      mListeners.add(watcher);

      }

      //TextWatcher的使用

      mEtComment.addTextChangedListener(new?TextWatcher()?{

      @Override

      public?void?beforeTextChanged(CharSequence?s,?int?start,?int?count,?int?after)?{

      //add?your?logic?code

      }

      @Override

      public?void?onTextChanged(CharSequence?s,?int?start,?int?before,?int?count)?{

      //add?your?logic?code

      }

      @Override

      public?void?afterTextChanged(Editable?s)?{

      //add?your?logic?code

      }

      });

      二、使用Kotlin來改造Java中的回調(diào)

      針對(duì)上述Java中的回調(diào)寫法,估計(jì)大部分人轉(zhuǎn)到Kotlin后,估計(jì)會(huì)做如下處理:

      1、如果接口只有一個(gè)回調(diào)函數(shù)可以直接使用lamba表達(dá)式實(shí)現(xiàn)回調(diào)的簡(jiǎn)寫。

      2、如果接口中含有多個(gè)回調(diào)函數(shù),都會(huì)使用object對(duì)象表達(dá)式來實(shí)現(xiàn)的。

      以改造上述代碼為例:

      1、(只有一個(gè)回調(diào)函數(shù)簡(jiǎn)寫形式)OnClickListener回調(diào)Kotlin改造

      //只有一個(gè)回調(diào)函數(shù)普通簡(jiǎn)寫形式:?OnClickListener的使用

      mBtnSubmit.setOnClickListener?{?view?->

      //add?your?logic?code

      }

      //針對(duì)OnClickListener監(jiān)聽設(shè)置Coroutine協(xié)程框架中onClick擴(kuò)展函數(shù)的使用

      mBtnSubmit.onClick?{?view?->

      //add?your?logic?code

      }

      //Coroutine協(xié)程框架:?onClick的擴(kuò)展函數(shù)定義

      fun?android.view.View.onClick(

      context:?CoroutineContext?=?UI,

      handler:?suspend?CoroutineScope.(v:?android.view.View?)?->?Unit

      )?{

      setOnClickListener?{?v?->

      launch(context)?{

      handler(v)

      }

      }

      }

      2、(多個(gè)回調(diào)函數(shù)object表達(dá)式)TextWatcher回調(diào)的Kotlin改造(object對(duì)象表達(dá)式)

      mEtComment.addTextChangedListener(object:?TextWatcher{

      override?fun?afterTextChanged(s:?Editable?)?{

      //add?your?logic?code

      }

      override?fun?beforeTextChanged(s:?CharSequence?,?start:?Int,?count:?Int,?after:?Int)?{

      //add?your?logic?code

      }

      override?fun?onTextChanged(s:?CharSequence?,?start:?Int,?before:?Int,?count:?Int)?{

      //add?your?logic?code

      如何讓你的回調(diào)更具Kotlin風(fēng)味(kotlin 回調(diào))

      }

      })

      關(guān)于object對(duì)象表達(dá)式實(shí)現(xiàn)的Kotlin中回調(diào),有不少的Kotlin的小伙伴在公眾號(hào)留言向我吐槽過,感覺這樣的寫法是直接從Java中的翻譯過來的一樣,完全看不出Kotlin的優(yōu)勢(shì)在哪。問我有沒有什么更加具有Kotlin風(fēng)味的寫法,當(dāng)然是有的,請(qǐng)接著往下看。

      三、進(jìn)一步讓你的回調(diào)更具Kotlin風(fēng)味(DSL配置回調(diào))

      其實(shí)如果你看過很多國(guó)外大佬的有關(guān)Koltin項(xiàng)目的源碼,你就會(huì)發(fā)現(xiàn)他們寫回調(diào)很少去使用object表達(dá)式去實(shí)現(xiàn)回調(diào),而是采用另一種方式去實(shí)現(xiàn),并且整體寫法看起來更具有Kotlin風(fēng)味。即使內(nèi)部用到object表達(dá)式,暴露給外層中間都會(huì)做一層DSL配置轉(zhuǎn)換,讓外部調(diào)用起來更加Kotlin化。以Github中的MaterialDrawer項(xiàng)目(目前已經(jīng)有1W多star)中官方指定MatrialDrawer項(xiàng)目Kotlin版本實(shí)現(xiàn)的MaterialDrawerKt項(xiàng)目中間一段源碼為例:

      1、DrawerImageLoader 回調(diào)定義

      //注意:?這個(gè)函數(shù)參數(shù)是一個(gè)帶返回值的lambda表達(dá)式

      public?fun?drawerImageLoader(actions:?DrawerImageLoaderKt.()?->?Unit):?DrawerImageLoader.IDrawerImageLoader?{

      val?loaderImpl?=?DrawerImageLoaderKt().apply(actions).build()?//

      DrawerImageLoader.init(loaderImpl)

      return?loaderImpl

      }

      //DrawerImageLoaderKt:?DSL?listener?Builder類

      public?class?DrawerImageLoaderKt?{

      //定義需要回調(diào)的函數(shù)lamba成員對(duì)象

      private?var?setFunc:?((ImageView,?Uri,?Drawable?,?String?)?->?Unit)??=?null

      private?var?placeholderFunc:?((Context,?String?)?->?Drawable)??=?null

      internal?fun?build()?=?object?:?AbstractDrawerImageLoader()?{

      private?val?setFunction:?(ImageView,?Uri,?Drawable?,?String?)?->?Unit?=?setFunc

      ?:?throw?IllegalStateException("DrawerImageLoader?has?to?have?a?set?function")

      private?val?placeholderFunction?=?placeholderFunc

      ?:?{?ctx,?tag?->?super.placeholder(ctx,?tag)?}

      override?fun?set(imageView:?ImageView,?uri:?Uri,?placeholder:?Drawable?,?tag:?String?)?=?setFunction(imageView,?uri,?placeholder,?tag)

      override?fun?placeholder(ctx:?Context,?tag:?String?)?=?placeholderFunction(ctx,?tag)

      }

      //暴露給外部調(diào)用的回調(diào)函數(shù),在構(gòu)建類中類似setter,getter方法

      public?fun?set(setFunction:?(imageView:?ImageView,?uri:?Uri,?placeholder:?Drawable?,?tag:?String?)?->?Unit)?{

      this.setFunc?=?setFunction

      }

      public?fun?placeholder(placeholderFunction:?(ctx:?Context,?tag:?String?)?->?Drawable)?{

      this.placeholderFunc?=?placeholderFunction

      }

      2、DrawerImageLoader回調(diào)使用

      drawerImageLoader?{

      //內(nèi)部的回調(diào)函數(shù)可以選擇性重寫

      set?{?imageView,?uri,?placeholder,?_?->

      Picasso.with(imageView.context)

      .load(uri)

      .placeholder(placeholder)

      .into(imageView)

      }

      cancel?{?imageView?->

      Picasso.with(imageView.context)

      .cancelRequest(imageView)

      }

      }

      可以看到使用DSL配置的回調(diào)更加具有Kotlin風(fēng)味,讓整個(gè)回調(diào)看起來非常的舒服,那種效果豈止絲滑。

      四、DSL配置回調(diào)基本步驟

      在Kotlin的一個(gè)類中實(shí)現(xiàn)了DSL配置回調(diào)非常簡(jiǎn)單主要就三步:

      1、定義一個(gè)回調(diào)的Builder類,并且在類中定義回調(diào)lamba表達(dá)式對(duì)象成員,最后再定義Builder類的成員函數(shù),這些函數(shù)就是暴露給外部回調(diào)的函數(shù)。個(gè)人習(xí)慣把它作為一個(gè)類的內(nèi)部類。類似下面這樣

      class?AudioPlayer(context:?Context){

      //other?logic?...

      inner?class?ListenerBuilder?{

      internal?var?mAudioPlayAction:?((AudioData)?->?Unit)??=?null

      internal?var?mAudioPauseAction:?((AudioData)?->?Unit)??=?null

      internal?var?mAudioFinishAction:?((AudioData)?->?Unit)??=?null

      fun?onAudioPlay(action:?(AudioData)?->?Unit)?{

      mAudioPlayAction?=?action

      }

      fun?onAudioPause(action:?(AudioData)?->?Unit)?{

      mAudioPauseAction?=?action

      }

      fun?onAudioFinish(action:?(AudioData)?->?Unit)?{

      mAudioFinishAction?=?action

      }

      }

      }

      2、然后,在類中聲明一個(gè)ListenerBuilder的實(shí)例引用,并且暴露一個(gè)設(shè)置該實(shí)例對(duì)象的一個(gè)方法,也就是我們常說的注冊(cè)事件監(jiān)聽或回調(diào)的方法,類似setOnClickListenter這種。但是需要注意的是函數(shù)的參數(shù)是帶ListenerBuilder返回值的lamba,類似下面這樣:

      class?AudioPlayer(context:?Context){

      //other?logic?...

      private?lateinit?var?mListener:?ListenerBuilder

      fun?registerListener(listenerBuilder:?ListenerBuilder.()?->?Unit)?{//帶ListenerBuilder返回值的lamba

      mListener?=?ListenerBuilder().also(listenerBuilder)

      }

      }

      3、最后在觸發(fā)相應(yīng)事件調(diào)用Builder實(shí)例中l(wèi)amba即可

      class?AudioPlayer(context:?Context){

      //other?logic?...

      val?mediaPlayer?=?MediaPlayer(mContext)

      mediaPlayer.play(mediaItem,?object?:?PlayerCallbackAdapter()?{

      override?fun?onPlay(item:?MediaItem?)?{

      if?(::mListener.isInitialized)?{

      mListener.mAudioPlayAction?.invoke(mAudioData)

      }

      }

      override?fun?onPause(item:?MediaItem?)?{

      if?(::mListener.isInitialized)?{

      mListener.mAudioPauseAction?.invoke(mAudioData)

      }

      }

      override?fun?onPlayCompleted(item:?MediaItem?)?{

      if?(::mListener.isInitialized)?{

      mListener.mAudioFinishAction?.invoke(mAudioData)

      }

      }

      })

      }

      4、外部調(diào)用

      val?audioPlayer?=?AudioPlayer(context)

      audioPlayer.registerListener?{

      //可以任意選擇需要回調(diào)的函數(shù),不必要完全重寫

      onAudioPlay?{

      //todo?your?logic

      }

      onAudioPause?{

      //todo?your?logic

      }

      onAudioFinish?{

      //todo?your?logic

      }

      }

      相比object表達(dá)式回調(diào)寫法,有沒有發(fā)現(xiàn)DSL回調(diào)配置更懂Kotlin. 可能大家看起來確實(shí)不錯(cuò),但是不知道它具體原理,畢竟這樣寫法太語法糖化,不太好理解,讓我們接下來一起揭開它的糖衣。

      五、揭開DSL回調(diào)配置的語法糖衣

      1、原理闡述

      DSL回調(diào)配置其實(shí)挺簡(jiǎn)單的,實(shí)際上就一個(gè)Builder類中維護(hù)著多個(gè)回調(diào)lambda的實(shí)例,然后在外部回調(diào)的時(shí)候再利用帶Builder類返回值實(shí)例的lamba特性,在該lambda作用域內(nèi)this可以內(nèi)部表達(dá)為Builder類實(shí)例,利用Builder類實(shí)例調(diào)用它內(nèi)部定義成員函數(shù)并且賦值初始化Builder類回調(diào)lambda成員實(shí)例,而這些被初始化過的lambda實(shí)例就會(huì)在內(nèi)部事件被觸發(fā)的時(shí)候執(zhí)行invoke操作。如果在該lambda內(nèi)部沒有調(diào)用某個(gè)成員方法,那么在該Builder類中這個(gè)回調(diào)lambda成員實(shí)例就是為null,即使內(nèi)部事件觸發(fā),為空就不會(huì)回調(diào)到外部。

      換句話就是外部回調(diào)的函數(shù)block塊會(huì)通過Builder類中成員函數(shù)初始化Builder類中回調(diào)lambda實(shí)例(在上述代碼表現(xiàn)就是mXXXAction實(shí)例),然后當(dāng)內(nèi)部事件觸發(fā)后,根據(jù)當(dāng)前l(fā)ambda實(shí)例是否被初始化,如果初始化完畢,就是立即執(zhí)行這個(gè)lambda也就是執(zhí)行傳入的block代碼塊

      2、代碼拆解

      為了更加清楚論證上面的闡述,我們可以把代碼拆解一下:

      mAudioPlayer.registerListener({

      //registerListener參數(shù)是個(gè)帶ListenerBuilder實(shí)例返回值的lambda

      //所以這里this就是內(nèi)部指代為L(zhǎng)istenerBuilder實(shí)例

      this.onAudioPlay?({

      //logic?block

      })

      this.onAudioPause?({

      //?logic?block

      })

      this.onAudioFinish({

      //?logic?block

      })

      })

      以onAudioPlay為例其他同理,調(diào)用ListenerBuilder中onAudioPlay函數(shù),并傳入block塊來賦值初始化ListenerBuilder類中的mAudioPlayActionlambda實(shí)例,當(dāng)AudioPlayer中的onPlay函數(shù)被回調(diào)時(shí),就執(zhí)行mAudioPlayActionlambda。

      貌似看起來object對(duì)象表達(dá)式回調(diào)相比DSL回調(diào)表現(xiàn)那么一無是處,是不是完全可以摒棄object對(duì)象表達(dá)式這種寫法呢?其實(shí)不然,object對(duì)象表達(dá)式這種寫法也是有它優(yōu)點(diǎn)的,具體有什么優(yōu)點(diǎn),請(qǐng)接著看它們兩種形式對(duì)比。

      六、object對(duì)象表達(dá)式回調(diào)和DSL回調(diào)對(duì)比

      1、調(diào)用寫法上對(duì)比

      //使用DSL配置回調(diào)

      val?audioPlayer?=?AudioPlayer(context)

      audioPlayer.registerListener?{

      //可以任意選擇需要回調(diào)的函數(shù),不必要完全重寫

      onAudioPlay?{

      //todo?your?logic

      }

      onAudioPause?{

      //todo?your?logic

      }

      onAudioFinish?{

      //todo?your?logic

      }

      }

      //使用object對(duì)象表達(dá)式回調(diào)

      val?audioPlayer?=?AudioPlayer(context)

      audioPlayer.registerListener(object:?AudioPlayListener{

      override?fun?onAudioPlay(audioData:?AudioData)?{

      //todo?your?logic

      }

      override?fun?onAudioPause(audioData:?AudioData)?{

      //todo?your?logic

      }

      override?fun?onAudioFinish(audioData:?AudioData)?{

      //todo?your?logic

      }

      })

      調(diào)用寫法對(duì)比明顯感覺DSL配置更加符合Kotlin風(fēng)格,所以DSL配置回調(diào)更勝一籌

      2、使用上對(duì)比

      使用上DSL有個(gè)明顯優(yōu)勢(shì)就是對(duì)于不需要監(jiān)聽的回調(diào)函數(shù)可以直接省略,而對(duì)于object表達(dá)式是直接實(shí)現(xiàn)一個(gè)接口回調(diào)必須重寫,雖然它也能做到任意選擇自己需要方法回調(diào),但是還是避免不了一層callback adapter層的處理。所以與其做個(gè)adapter層還不如一步到位。所以DSL配置回調(diào)更勝一籌

      3、性能上對(duì)比

      其實(shí)通過上述調(diào)用寫法上看,一眼就能看出來,DSL配置回調(diào)這種方式會(huì)針對(duì)每個(gè)回調(diào)函數(shù)都會(huì)創(chuàng)建lambda實(shí)例對(duì)象,而object對(duì)象表達(dá)式不管內(nèi)部回調(diào)的方法有多少個(gè),都只會(huì)生成一個(gè)匿名對(duì)象實(shí)例。區(qū)別就在這里,所以在性能方面object對(duì)象表達(dá)式這種方式會(huì)更優(yōu)一點(diǎn),但是通過問過一些Kotlin社區(qū)的大佬們他們還是更傾向于DSL配置這種寫法。所以其實(shí)這兩種方式都挺好的,看不同需求,自己權(quán)衡選擇即可, 反正我個(gè)人挺喜歡DSL那種。為了驗(yàn)證我們上述所說的,不妨來看下兩種方式下反編譯的代碼,看看是否是我們所說的那樣:

      //DSL配置回調(diào)反編譯code

      public?final?void?setListener(@NotNull?Function1?listener)?{

      Intrinsics.checkParameterIsNotNull(listener,?"listener");

      ListenerBuilder?var2?=?new?ListenerBuilder();

      listener.invoke(var2);

      ListenerBuilder?var10000?=?this.mListener;

      //獲取AudioPlay方法對(duì)應(yīng)的實(shí)例對(duì)象

      Function0?var3?=?var10000.getMAudioPlayAction$Coroutine_main();

      Unit?var4;

      if?(var3?!=?null)?{

      var4?=?(Unit)var3.invoke();

      }

      //獲取AudioPause方法對(duì)應(yīng)的實(shí)例對(duì)象

      var3?=?var10000.getMAudioPauseAction$Coroutine_main();

      if?(var3?!=?null)?{

      var4?=?(Unit)var3.invoke();

      }

      //獲取AudioFinish方法對(duì)應(yīng)的實(shí)例對(duì)象

      var3?=?var10000.getMAudioFinishAction$Coroutine_main();

      if?(var3?!=?null)?{

      var4?=?(Unit)var3.invoke();

      }

      }

      //object對(duì)象表達(dá)式反編譯code

      public?static?final?void?main(@NotNull?String[]?args)?{

      Intrinsics.checkParameterIsNotNull(args,?"args");

      int?count?=?true;

      PlayerPlugin?player?=?new?PlayerPlugin();

      //new?Callback一個(gè)實(shí)例

      player.setCallback((Callback)(new?Callback()?{

      public?void?onAudioPlay()?{

      }

      public?void?onAudioPause()?{

      }

      public?void?onAudioFinish()?{

      }

      }));

      }

      七、Don't Repeat Yourself(所以順便使用kotlin來擼個(gè)自動(dòng)生成ListenerBuilder的插件吧)

      使用過DSL配置回調(diào)的小伙伴們有沒有覺得寫這些代碼沒有任何技術(shù)含量的,且浪費(fèi)時(shí)間, 那么Don't Repeat Yourself從現(xiàn)在開始。如果整個(gè)DSL配置回調(diào)的過程可以做成類似toString、setter、getter方法那樣自動(dòng)生成,豈不美滋滋,所以來擼個(gè)插件吧。所以接下來大致介紹下DslListenerBuilder插件的開發(fā)。

      開發(fā)整體思路:

      實(shí)際上就是通過Swing的UI窗口配置需要信息參數(shù),然后通過Velocity模板引擎生成模板代碼,然后通過Intellij Plugin API 將生成的代碼插入到當(dāng)前代碼文件中。所以所有需要自動(dòng)生成代碼的需求都類似這樣流程。下次需要生成不一樣的代碼只需要修改Velocity模板即可。

      使用到技術(shù)點(diǎn):

      1、Kotlin基礎(chǔ)開發(fā)知識(shí)

      2、Kotlin擴(kuò)展函數(shù)

      3、Kotlin的lambda表達(dá)式

      4、Swing UI組件開發(fā)知識(shí)

      5、Intellij Plugin開發(fā)基本知識(shí)

      6、IntelliJ Plugin 常用開發(fā)API(Editor、WriteCommandAction、PsiDocumentManager、Document等API的使用)

      7、Velocity模板基本語法(#if,#foreach,#set等)

      8、Velocity模板引擎API的基本使用

      基本介紹和使用:

      這是一款自動(dòng)生成DSL ListenerBuilder回調(diào)模板代碼的IDEA插件,支持IDEA、AndroidStudio以及JetBrains全家桶。

      第一步: 首先按照IDEA一般插件安裝流程安裝好DslListenerBuilder插件。

      第二步: 然后打開具體某個(gè)類文件,將光標(biāo)定位在具體代碼生成的位置,

      第三步: 使用快捷鍵調(diào)出Generate中的面板,選擇其中的“Listener Builder”, 然后就會(huì)彈出一個(gè)面板,可以點(diǎn)擊add按鈕添加一個(gè)或多個(gè)回調(diào)函數(shù)的lamba, 也可以從面板中選擇任一一條不需要的Item進(jìn)行刪除。

      第四步: 最后點(diǎn)擊OK就可以在指定光標(biāo)位置生成需要的代碼。

      九、DslListenerBuilder插件源碼和Velocity模板引擎學(xué)習(xí)資源

      這里推薦一些有關(guān)Velocity模板引擎的學(xué)習(xí)資源,此外有關(guān)插件的更多具體實(shí)現(xiàn)內(nèi)容請(qǐng)查看下面GitHub中的源碼,如果覺得不錯(cuò)歡迎給個(gè)star~~~

      目前插件已經(jīng)上傳到JetBrains IntelliJ Plugins官方倉庫,還處于審核,過幾天就可以直接在AndroidStudio或者IntelliJ IDEA中搜索 DslListenerBuilder直接安裝了

      DslListenerBuilder插件-

      DslListenerBuilder插件源碼地址

      Velocity模板基本語法

      使用 Velocity 模板引擎快速生成代碼

      十、總結(jié)

      到這里有關(guān)Kotlin回調(diào)相關(guān)內(nèi)容已經(jīng)講得很清楚了,然后還給大家介紹了如何去開發(fā)一個(gè)自動(dòng)生成代碼的插件。整個(gè)插件開發(fā)流程同樣適用于其他的代碼生成需求。為什么要寫這么個(gè)插件呢,主要是由于最近需求太多,每次寫回調(diào)的時(shí)候都需要不斷重復(fù)去寫很多類似的代碼。有時(shí)候當(dāng)我們?cè)谥貜?fù)性做一些操作的時(shí)候,不妨去思考下用什么工具能否把整個(gè)流程給自動(dòng)化。歸根結(jié)底一句話: Don't Repeat Yourself.

      歡迎關(guān)注Kotlin開發(fā)者聯(lián)盟,這里有最新Kotlin技術(shù)文章,每周會(huì)不定期翻譯一篇Kotlin國(guó)外技術(shù)文章。如果你也喜歡Kotlin,歡迎加入我們~~~

      Kotlin系列文章,歡迎查看:

      Effective Kotlin翻譯系列

      [譯]Effective Kotlin系列之考慮使用原始類型的數(shù)組優(yōu)化性能(五)

      [譯]Effective Kotlin系列之使用Sequence來優(yōu)化集合的操作(四)

      [譯]Effective Kotlin系列之探索高階函數(shù)中inline修飾符(三)

      [譯]Effective Kotlin系列之遇到多個(gè)構(gòu)造器參數(shù)要考慮使用構(gòu)建器(二)

      [譯]Effective Kotlin系列之考慮使用靜態(tài)工廠方法替代構(gòu)造器(一)

      原創(chuàng)系列:

      Jetbrains開發(fā)者日見聞(三)之Kotlin1.3新特性(inline class篇)

      JetBrains開發(fā)者日見聞(二)之Kotlin1.3的新特性(Contract契約與協(xié)程篇)

      JetBrains開發(fā)者日見聞(一)之Kotlin/Native 嘗鮮篇

      教你如何攻克Kotlin中泛型型變的難點(diǎn)(實(shí)踐篇)

      教你如何攻克Kotlin中泛型型變的難點(diǎn)(下篇)

      教你如何攻克Kotlin中泛型型變的難點(diǎn)(上篇)

      Kotlin的獨(dú)門秘籍Reified實(shí)化類型參數(shù)(下篇)

      有關(guān)Kotlin屬性代理你需要知道的一切

      淺談Kotlin中的Sequences源碼解析

      淺談Kotlin中集合和函數(shù)式API完全解析-上篇

      淺談Kotlin語法篇之lambda編譯成字節(jié)碼過程完全解析

      淺談Kotlin語法篇之Lambda表達(dá)式完全解析

      淺談Kotlin語法篇之?dāng)U展函數(shù)

      淺談Kotlin語法篇之頂層函數(shù)、中綴調(diào)用、解構(gòu)聲明

      淺談Kotlin語法篇之如何讓函數(shù)更好地調(diào)用

      淺談Kotlin語法篇之變量和常量

      淺談Kotlin語法篇之基礎(chǔ)語法

      翻譯系列:

      [譯]記一次Kotlin官方文檔翻譯的PR(內(nèi)聯(lián)類)

      [譯]Kotlin中內(nèi)聯(lián)類的自動(dòng)裝箱和高性能探索(二)

      [譯]Kotlin中內(nèi)聯(lián)類(inline class)完全解析(一)

      [譯]Kotlin的獨(dú)門秘籍Reified實(shí)化類型參數(shù)(上篇)

      [譯]Kotlin泛型中何時(shí)該用類型形參約束?

      [譯] 一個(gè)簡(jiǎn)單方式教你記住Kotlin的形參和實(shí)參

      [譯]Kotlin中是應(yīng)該定義函數(shù)還是定義屬性?

      [譯]如何在你的Kotlin代碼中移除所有的!!(非空斷言)

      [譯]掌握Kotlin中的標(biāo)準(zhǔn)庫函數(shù): run、with、let、also和apply

      [譯]有關(guān)Kotlin類型別名(typealias)你需要知道的一切

      [譯]Kotlin中是應(yīng)該使用序列(Sequences)還是集合(Lists)?

      [譯]Kotlin中的龜(List)兔(Sequence)賽跑

      實(shí)戰(zhàn)系列:

      用Kotlin擼一個(gè)圖片壓縮插件ImageSlimming-導(dǎo)學(xué)篇(一)

      用Kotlin擼一個(gè)圖片壓縮插件-插件基礎(chǔ)篇(二)

      用Kotlin擼一個(gè)圖片壓縮插件-實(shí)戰(zhàn)篇(三)

      淺談Kotlin實(shí)戰(zhàn)篇之自定義View圖片圓角簡(jiǎn)單應(yīng)用

      Java Kotlin

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:現(xiàn)在無代碼開發(fā)平臺(tái)(無代碼軟件開發(fā)平臺(tái))
      下一篇:OA管理系統(tǒng)有什么作用?
      相關(guān)文章
      久久精品国产亚洲av成人| 亚洲AV成人潮喷综合网| 亚洲日韩小电影在线观看| 国产精品亚洲一区二区三区久久 | 亚洲精品无码久久久久YW| 亚洲xxxx18| 亚洲偷偷自拍高清| 久久亚洲国产最新网站| 亚洲国产区男人本色在线观看| 亚洲日韩国产精品乱-久| 亚洲最大中文字幕无码网站| 亚洲人成无码网站在线观看| 亚洲欧美日韩综合久久久| 亚洲成a人无码亚洲成www牛牛 | 国精无码欧精品亚洲一区| 国产亚洲精品a在线无码| 亚洲国产成人一区二区三区| 国产成人亚洲综合无码精品| 亚洲第一AAAAA片| 亚洲午夜免费视频| 亚洲精品影院久久久久久| 亚洲成a人片在线观看中文!!! | 国产成+人+综合+亚洲专| 亚洲一级免费视频| 亚洲国产成a人v在线观看| 亚洲精品无码专区在线| 小说专区亚洲春色校园| 亚洲国产精品一区二区第一页免| AV在线播放日韩亚洲欧| 亚洲va久久久噜噜噜久久天堂| 久久亚洲国产成人亚| 久久亚洲精品无码VA大香大香| 亚洲无人区视频大全| 99亚偷拍自图区亚洲| 国产精品亚洲专区无码唯爱网| 亚洲精品国产精品国自产观看| 亚洲男同帅GAY片在线观看| 亚洲国产成人精品不卡青青草原| 亚洲日本在线播放| 亚洲男同gay片| 国内精品99亚洲免费高清|