Activity/Fragmnet 傳參的新方式

在Android中兩個Activity、Activity與Fragment之間傳參是件很痛苦的事情似将,因?yàn)橐x很多的key获黔。步驟也非常的繁瑣,要存要取在验。

現(xiàn)在這個問題有了新的解決方案玷氏,就是利用Kotlin的屬性代理。

比如有兩個Activity腋舌,一個是MainActivity盏触,一個是TestActivity,從MainActivity到TestActivity.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<Button>(R.id.activityBtn).setOnClickListener {
            //跳轉(zhuǎn)到TestActivity
        }

      
    }
}

TestActivity代碼

class TestActivity : AppCompatActivity() {

    var name:String=""
    var num:Int=0

    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        textView = findViewById(R.id.textView)
        textView.text = "$name - $num"

    }
}

如果用之前常規(guī)的寫法是這樣的

val intent = Intent(this@MainActivity, TestActivity::class.java);
        intent.putExtra("name", "王小二")
        intent.putExtra("age", "25")
        startActivity(intent)

在TestActivity去取參數(shù)也麻煩、這里就不貼了
那么新方式是怎么寫呢赞辩?請看
新寫法

跳轉(zhuǎn)的代碼

TestActivity().apply {
                name = "Come from MainActivity"
                num = 100
                startActivity(this@MainActivity, this)
            }

取值的代碼

class TestActivity : AppCompatActivity() {

    var name by ActivityArgument("default")
    var num by ActivityArgument(0)

    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        textView = findViewById(R.id.textView)
        textView.text = "$name - $num"

    }
}

是不是不用再定義一大堆的靜態(tài)變量雌芽,然后存進(jìn)去再取出來了,設(shè)置一次就可以了诗宣。

實(shí)現(xiàn)原理
1.提前創(chuàng)建Activity的實(shí)例膘怕,當(dāng)系統(tǒng)要創(chuàng)建Activity實(shí)例時把我們之前創(chuàng)建的給它(要hook代碼,了解Activity啟動原理的就很好理解召庞,這里就不說了岛心,網(wǎng)上有很多這方面的資源)
2.利用Kotlin的屬性代理

show code

保存Activity實(shí)例的單例類

object ActivityInstanceManager {

  private val activityMap = HashMap<Class<*>, Activity>()

  fun putActivity(activity: Activity) {
      activityMap[activity.javaClass] = activity
  }

  fun getActivity(clazz: Class<*>): Activity? {
      return activityMap.remove(clazz)
  }

}

Activity擴(kuò)展StartActivity方法類

fun Activity.startActivity(activity: Activity, nextActivity: Activity) {
    ActivityInstanceManager.putActivity(nextActivity)
    activity.startActivity(createIntent(activity, nextActivity))
}

fun Activity.startActivityForResult(activity: Activity, nextActivity: Activity, requestCode: Int) {
    ActivityInstanceManager.putActivity(nextActivity)
    activity.startActivityForResult(createIntent(activity, nextActivity), requestCode)
}

private fun createIntent(activity: Activity, nextActivity: Activity): Intent {
    val intent = IntentInstanceManager.getIntentAndRemove(nextActivity) ?: Intent()
    intent.setClass(activity, nextActivity::class.java)
    return intent
}

屬性代理類

class ActivityArgument<T : Any>(private val default: T? = null) : ReadWriteProperty<Activity, T> {

    var value: T? = null

    override operator fun getValue(thisRef: Activity, property: KProperty<*>): T {
        if (value == null) {
            val args = thisRef.intent.extras
                ?: throw IllegalStateException("Cannot read property ${property.name} if no arguments have been set")
            if (args.containsKey(property.name)) {
                @Suppress("UNCHECKED_CAST")
                value = args.get(property.name) as T
            } else {
                value = default
            }
        }
        return value ?: throw IllegalStateException("Property ${property.name} could not be read")
    }


    override operator fun setValue(thisRef: Activity, property: KProperty<*>, value: T) {

        var intent = IntentInstanceManager.getIntent(thisRef)
        if (intent == null) {
            intent = Intent().apply {
                putExtras(Bundle())
            }
            IntentInstanceManager.putIntent(thisRef, intent)
        }

        val args = intent.extras
        val key = property.name
        when (value) {
            is String -> args.putString(key, value)
            is Int -> args.putInt(key, value)
            is Short -> args.putShort(key, value)
            is Long -> args.putLong(key, value)
            is Byte -> args.putByte(key, value)
            is ByteArray -> args.putByteArray(key, value)
            is Char -> args.putChar(key, value)
            is CharArray -> args.putCharArray(key, value)
            is CharSequence -> args.putCharSequence(key, value)
            is Float -> args.putFloat(key, value)
            is Bundle -> args.putBundle(key, value)
            is Binder -> BundleCompat.putBinder(args, key, value)
            is android.os.Parcelable -> args.putParcelable(key, value)
            is java.io.Serializable -> args.putSerializable(key, value)
            else -> throw IllegalStateException("Type ${value.javaClass.canonicalName} of property ${property.name} is not supported")
        }
        intent.putExtras(args)
    }
}
    private val intentMap = HashMap<Activity, Intent>()
    fun putIntent(activity: Activity, intent: Intent) {
        intentMap[activity] = intent
    }

    fun getIntent(activity: Activity): Intent? {
        return intentMap[activity]
    }

    fun getIntentAndRemove(activity: Activity): Intent? {
        return intentMap.remove(activity)
    }

    fun removeIntent(activity: Activity) {
        intentMap.remove(activity)
    }
}

好了 核心的就是些

還有的就是hook Instrumentation 類創(chuàng)建Activity

class InstrumentationProxy(val mInstrumentation: Instrumentation) : Instrumentation() {

    override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity {
        println("className = ${className}")
        val clazz = Class.forName(className)
        return ActivityInstanceManager.getActivity(clazz) ?: super.newActivity(cl, className, intent)
    }
}

替換系統(tǒng) Instrumentation

private fun hookActivityThreadInstrumentation() {
        try {
            val activityThreadClazz = Class.forName("android.app.ActivityThread")
            val activityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread")
            activityThreadField.isAccessible = true
            val activityThread = activityThreadField.get(null)
            val instrumentationField = activityThreadClazz.getDeclaredField("mInstrumentation")
            instrumentationField.isAccessible = true
            val instrumentation = instrumentationField.get(activityThread) as Instrumentation
            val proxy = InstrumentationProxy(instrumentation)
            instrumentationField.set(activityThread, proxy)
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

這樣就能把我們提前創(chuàng)建的Activity的實(shí)例給系統(tǒng)了
到這里就完了 以后啟動Activity就可以這樣寫了

 TestActivity().apply {
                name = "Come from MainActivity"
                num = 100
                startActivity(this@MainActivity, this)
            }

是不是可以提前下班、篮灼、忘古、

Fragment也是一樣的,因?yàn)椴恍枰猦ook 就更簡單了

class TestFragment : Fragment() {

    companion object {
        fun attach(act: FragmentActivity, containerId: Int) {
            val fragment = TestFragment().apply {
                age = 18
                name = "小花"
            }
            act.supportFragmentManager.beginTransaction().replace(containerId, fragment).commit()
        }
    }

    var name by FragmentArgument("lily")
    var age by FragmentArgument(16)

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_test, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val textView = view!!.findViewById<TextView>(R.id.contentTv)
        textView.text = "$name - $age"
    }


}

看下代碼就明白了

屬性代理還有SharedPreferences 的應(yīng)用
可以這樣寫

private var keystore by PreferenceArgument(this, "keystore", "abc")

原理是一樣的 就貼下代碼吧

class PreferenceArgument<T>(context: Context, private val name: String, private val default: T) :
    ReadWriteProperty<Any?, T> {

    private val prefs by lazy {
        context.getSharedPreferences("${context.packageName}_preferences", Context.MODE_PRIVATE)
    }

    override fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreferences(name, default)

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreferences(name, value)
    }

    private fun putPreferences(name: String, value: T) = with(prefs.edit()) {
        when (value) {
            is Int -> putInt(name, value)
            is Long -> putLong(name, value)
            is Float -> putFloat(name, value)
            is String -> putString(name, value)
            is Boolean -> putBoolean(name, value)
            else ->
                throw  IllegalArgumentException("This type can not be saved into preferences")
        }
        apply()
    }

    private fun findPreferences(name: String, default: T) = with(prefs) {
        var result = when (default) {
            is Int -> getInt(name, default)
            is Long -> getLong(name, default)
            is Float -> getFloat(name, default)
            is String -> getString(name, default)
            is Boolean -> getBoolean(name, default)
            else ->
                throw  IllegalArgumentException("This type can not be saved into preferences")
        }

        result as T
    }
}

完整代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诅诱,一起剝皮案震驚了整個濱河市髓堪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娘荡,老刑警劉巖干旁,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異炮沐,居然都是意外死亡争群,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門大年,熙熙樓的掌柜王于貴愁眉苦臉地迎上來换薄,“玉大人,你說我怎么就攤上這事翔试∏嵋” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵垦缅,是天一觀的道長冲泥。 經(jīng)常有香客問我,道長壁涎,這世上最難降的妖魔是什么柏蘑? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮粹庞,結(jié)果婚禮上咳焚,老公的妹妹穿的比我還像新娘。我一直安慰自己庞溜,他們只是感情好革半,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布碑定。 她就那樣靜靜地躺著,像睡著了一般又官。 火紅的嫁衣襯著肌膚如雪延刘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天六敬,我揣著相機(jī)與錄音碘赖,去河邊找鬼。 笑死外构,一個胖子當(dāng)著我的面吹牛普泡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播审编,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼撼班,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了垒酬?” 一聲冷哼從身側(cè)響起砰嘁,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎勘究,沒想到半個月后矮湘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡口糕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年缅阳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片走净。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡券时,死狀恐怖孤里,靈堂內(nèi)的尸體忽然破棺而出伏伯,到底是詐尸還是另有隱情,我是刑警寧澤捌袜,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布说搅,位于F島的核電站,受9級特大地震影響虏等,放射性物質(zhì)發(fā)生泄漏弄唧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一霍衫、第九天 我趴在偏房一處隱蔽的房頂上張望候引。 院中可真熱鬧,春花似錦敦跌、人聲如沸澄干。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽麸俘。三九已至辩稽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間从媚,已是汗流浹背逞泄。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拜效,地道東北人喷众。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像拂檩,于是被迫代替她去往敵國和親侮腹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容