在启动 Activity、启动 Service、发送广播等,都需要借助 Intent 来操作,并且还可以在 Intent 中添加一些附加数据,以达到传值的效果。
传递常用数据类型
示例(Kotlin):
显示 Intent:
MainActivity.kt1 2 3 4 5 6 7 8
| val data = "Hello" val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("extra_data",data) intent.putExtra("string_data","hello") intent.putExtra("int_data",100) startActivity(intent)
|
SecondActivity.kt1 2 3 4
| val extraData = intent.getStringExtra("extra_data") val stringData = intent.getStringExtra("string_data") val intData = intent.getIntExtra("int_data")
|
显示 Intent:带返回值的跳转
MainActivity.kt1 2
| val intent = Intent(this, SecondActivity::class.java) startActivityForResult(intent,1)
|
SecondActivity.kt1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| override fun onClick(v: View?) { when (v?.id){ R.id.btn ->{ val intent = Intent() intent.putExtra("data_return","Hello MainActivity") setResult(Activity.RESULT_OK,intent) finish() } } }
override fun onBackPressed() { val intent = Intent() intent.putExtra("data_return","Hello MainActivity") setResult(Activity.RESULT_OK,intent) finish() }
|
MainActivity.kt1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when(requestCode){ 1 -> if (resultCode == Activity.RESULT_OK){ val returnedData = data?.getStringExtra("data_return") Log.d("TAG_MainActivity","return data is $returnedData") } } }
|
隐式 Intent:
MainActivity.kt1 2 3 4 5 6
|
val intent = Intent("com.example.activitytest.ACTION_START") intent.addCategory("com.example.activitytest.MY_CATEGORY") startActivity(intent)
|
AndroidManifest.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <activity android:name=".activity.Main3Activity" // theme 对话框形式主题 android:theme="@style/Theme.AppCompat.Dialog"> // tools:ignore="AppLinkUrlError" 忽略警告 <intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="https" /> </intent-filter> </activity> <activity android:name=".activity.Main2Activity"> <intent-filter>
<action android:name="com.example.activitytest.ACTION_START" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.example.activitytest.MY_CATEGORY" /> </intent-filter> </activity>
|
更多隐式 Intent 的用法
MainActivity.kt1 2 3 4 5 6 7 8 9 10 11
| val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com") startActivity(intent)
val intent = Intent(Intent.ACTION_DIAL) intent.data = Uri.parse("tel:1008611") startActivity(intent)
|
传递对象
使用 Intent 传递对象通常有两种方式:Serializable 和 Parcelable。
为什么要序列化?
- 永久性保存对象,保存对象的字节序列到本地文件中;
- 通过序列化对象在网络中传递对象;
- 通过序列化在进程间传递对象。
Serializable
它是序列化的意思,表示将一个对象转换成可存储或可运输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。只需要让一个类去实现 Serializable 这个接口即可完成序列化。
1 2 3 4
| class Person : Serializable { var name = "" var age = 0 }
|
MainActivity.kt1 2 3 4 5 6
| val person = Person() person.name = "Tom" person.age = 20 val intent = Intent(this,SecondActivity::Class.java) intent.putExtra("person_data",person) startActivity(intent)
|
SecondActivity.kt1 2
| val person = intent.getSerializableExtra("person_data") as Person
|
注意:这种传递对象的工作原理是先将一个对象序列化成可存储或可运输的状态,传递给另外一个 Activity 后再将其反序列化成一个新的对象。虽然这两个对象中存储的数据完全一致,但是它们实际上是不同的对象。
Java序列化技术
就是将Java对象序列化为二进制文件
序列化ID问题
虚拟机是否允许反序列化,不仅取决于“类路径“和”功能代码“是否一致,还需要两个类的序列化ID是否一致:
1 2 3 4 5 6 7
|
private static final long serialVersionUID = 1L;
|
静态变量序列化
因为,序列化保存的是对象的状态,静态变量属于类的状态,
因此,序列化并不保存静态变量。
父类的序列化与 Transient 关键字
要想将父类对象也序列化,就需要让父类也实现Serializable 接口。
如果父类不实现的话的,就 需要有默认的无参的构造函数。
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,
在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
对敏感字段加密
在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,
如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。
用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。
序列化存储规则
Java 序列化机制为了节省磁盘空间,具有特定的存储规则,
当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。
反序列化时,恢复引用关系,使得这两个对象指向唯一的对象,二者相等。该存储规则极大的节省了存储空间。
Binder 传递数据限制
Intent 传值 Bean 对象时偶尔也会导程序崩溃,因为 Intent 传递数据过大,最终原因是 Android 系统对使用 Binder 传数据进行了限制。通常情况为 1M,但是根据不同版本、不同厂商,这个值会有区别。解决办法:
- 减少通过 Intent 传递的数据,将非必须字段使用 transient 关键字修饰,避免将其序列化。
- 将对象转化为 JSON 字符串,减少数据体积。因为 JVM 加载类通常会伴随额外的空间来保存类相关信息,将类中数据转化为 JSON 字符串可以减少数据大小。比如使用 Gson.toJson 方法。(大多时候,将类转化为 JSON 字符串之后,还是会超出 Binder 限制,说明实际需要传递的数据是很大的。这种情况则需要考虑使用本地持久化来实现数据共享,或者使用 EventBus 来实现数据传递。)
Parcelable
Parcelable 的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是 Intent 所支持的数据类型,这样就能实现传递对象的功能了。对比 Serializable,Serializable 的方式较为简单,但由于会把这个对象进行序列化。因此效率会比 Parcelable 方式低一些,所以通常情况下,还是更加推荐使用 Parcelable 的方式来实现 Intent 传递对象的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Person : Parcelable { var name = "" var age = 0
override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(name) parcel.writeInt(age) }
override fun describeContents(): Int { return 0 }
companion object CREATOR : Parcelable.Creator<Person> {
override fun createFromParcel(parcel: Parcel): Person { val person = Person() person.name = parcel.readString()?:"" person.age = parcel.readInt() return person }
override fun newArray(size: Int): Array<Person?> { return arrayOfNulls(size) } } }
|
MainActivity.kt1 2 3 4 5 6
| val person = Person() person.name = "Tom" person.age = 20 val intent = Intent(this,SecondActivity::Class.java) intent.putExtra("person_data",person) startActivity(intent)
|
SecondActivity.kt1 2
| val person = intent.getParcelableExtra("person_data") as Person
|
Kotlin 提供了另外一种更加简便的用法实现 Parcelable 序列化,但前提是要传递的所有数据都必须封装在对象的主构造函数中才行。
1 2 3 4 5 6 7 8 9 10 11 12
|
@Parcelize class Person(var name: String, var age: Int) : Parcelable
|
启动 Activity 的最佳写法
MainActivity.kt1
| SecondActivity.actionStart(this)
|
SecondActivity.kt1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
companion object{ fun actionStart(context: Context,data1:String,data2:String){
val intent = Intent(context, MainActivity::class.java).apply { putExtra("param1",data1) putExtra("param2",data2) } context.startActivity(intent) } }
|