在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
}
}