使用 Kotlin 優化 Intent 數據傳遞

gdmutou 7年前發布 | 19K 次閱讀 Intent Kotlin

首先,這篇文章可能會涉及到的知識點有:

  • 伴生對象
  • 對象表達式與對象聲明
  • 擴展函數
  • 擴展屬性
  • 高階函數( lambda 表達式)
  • 帶接收者的函數字面值
  • 委托屬性

Java 的實現方式

在 Android 開發中, Activity 之間的數據傳遞是不可避免的。初次接觸 Android 編程的時候,許多教程會寫出這樣的代碼:

// 第一個 Activity 
Intent intent = new Intent(context, AimActivity.class);
intent.putExtra("msg", message);
startActivity(intent);

// AimActivity 
this.message = getIntent().getStringExtra("msg");

這樣寫當然沒有錯,的確達到了傳遞數據的目的,但是,這卻會讓代碼難以維護。稍微有些開發經驗的,都會選擇將 Extra 信息的鍵值抽出變為常量,并且把許多類似的鍵值放在同一個地方,避免出現鍵值的沖突。

為了更好的可讀性,還有一些人會這樣去封裝 Intent :

private static final String MSG_KEY = "key for message";

@Nullable publicstaticStringgetMessage(@NonNull Intent intent){
  return intent.getStringExtra(MSG_KEY);
}

publicstaticvoidsetMessage(@NonNull Intent intent, String message){
  intent.putExtra(MSG_KEY, message);
}

這樣一來,讀寫 Intent 的確如讀寫類成員屬性一般了,可是,不得不說,這也極大地增加了代碼的編寫量。當然,或者可以通過注解、動態生成代碼的方式來解決這一問題,但這不是本篇文章的解決方案。

Kotlin 的登場

在 Java 的實現方式中,最適合閱讀、最符合面向對象思想的數據傳遞方式當屬最后一種,那,下面就用一些 Kotlin 的特性來實現:

object IntentOptions {
  private const val MSG_KEY = "key for message"

  funIntent.getMessage(): String? = getStringExtra(MSG_KEY)

  funIntent.setMessage(message:String?) {
    putExtra(MSG_KEY, message)
  }
}

// 數據的存儲與獲取
with(IntentOptions) {
  intent.setMessage("message")
  message = intent.getMessage()
}

可以看到,利用 Kotlin 提供的擴展函數的特性,我們就可以在 Intent 對象上直接使用 getter/setter 了,這是 Java 語言做不到的。

需要注意的是 with 表達式的使用,這涉及到 擴展函數 的作用域問題。在 Kotlin 中,擴展函數的作用范圍只在其定義范圍內。一般而言,我們把許多擴展函數定義在了頂級作用域中,這樣在整個 App ,被擴展的對象實例都可以使用該函數。但是考慮到,我們不能在 Intent 對象上無限制地添加 getter/setter ,所以利用 object ,讓這些擴展函數只在該對象的作用域內有效。在實際使用中,這個 object 可以是某個 Activity 的伴生對象。

到這里,我們可以更進一步,使用 擴展屬性 ,讓賦值/取值的過程更符合 Kotlin 的語言規范:

object IntentOptions {
  private const val MSG_KEY = "key for message"

  var Intent.message: String?
    get() = getStringExtra(MSG_KEY)
    set(message) {
      putExtra(MSG_KEY, message)
    }
}

// 使用
with(IntentOptions) {
  intent.message = "message"
  message = intent.message
}

嗯,不論寫那個 object 的過程怎樣,至少,在使用這樣的 Intent 方面,體驗到了讀寫原生對象的便利啊。

Kotlin 中的委托

委托?為什么要用委托?上面的代碼我們使用委托的模式改寫一下:

classIntentExtraStringDelegate(val key: String) {
  fungetValue(intent:Intent): String? = 
    intent.getStringExtra(name)

  funsetValue(intent:Intent, value:String?) {
    intent.putExtra(name, value)
  }
}

object IntentOptions {
  private val messageDelegate = IntentExtraStringDelegate("key for message")

  var Intent.message: String?
    get() = messageDelegate.getValue(this)
    set(message) = messageDelegate.setValue(this, message)
}

可以看到,委托就是把 Intent 內容的讀寫操作抽取出來,以便復用。進行了這樣的抽取之后,我們又可以在 Kotlin 的語言特性中找到這樣的實現:

classIntentExtraString(private val key: String) {
  operator fungetValue(intent:Intent, property:KProperty<*>): String? = 
    intent.getStringExtra(key)

  operator funsetValue(intent:Intent, property:KProperty<*>, value:String?) {
    intent.putExtra(key, value)
  }
}

// 使用
object IntentOptions {
  var Intent.message by IntentExtraString("key for message")
}

啊哈,這樣一來,相當于僅僅是對 Intent 內的變量進行了一下聲明,就可以在對應的范圍內使用了!而存儲的鍵值,也完全可以省略,直接使用字段名:

classIntentExtraString(private val key: String? = null) {
  private val KProperty<*>.extraName: String
    get() = this@IntentExtraString.key ?: name

  operator fungetValue(intent:Intent, property:KProperty<*>): String? = 
    intent.getStringExtra(property.extraName)

  operator funsetValue(intent:Intent, property:KProperty<*>, value:String?) {
    intent.putExtra(property.extraName, value)
  }
}

一般而言,在 Activity 可以這樣使用:

classActivity:AppCompatActivity() {
  companion object IntentOptions {
    var Intent.id by IntentExtraString()
    var Intent.name by IntentExtraString()
    var Intent.message by IntentExtraString()
  }

  funtest(intent:Intent) {
    intent.id = "1"
    intent.name = "pass"
    intent.message = "message"
  }
}

funtestOutSide(intent:Intent) = with(Activity.IntentOptions) {
  val id = intent.id
  val name = intent.name
  val message = intent.message
}

以上,就是使用 Kotlin 優化 Intent 數據傳輸的基本思路。當然,在這個思路下,還有許多可以封裝的,比如,自定義類型數據的傳輸——可以去看一看原作者的開源庫 android-extras-delegates

 

來自:http://blog.saplf.top/2017/03/19/kotlin-intent-extension/

 

 本文由用戶 gdmutou 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!