Skip to content

Commit e8c6eb7

Browse files
committed
Add doc for EasyBundle
1 parent d56477d commit e8c6eb7

File tree

4 files changed

+327
-5
lines changed

4 files changed

+327
-5
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,32 @@ executor.async(callable:Callable<T>, result:(T) -> Unit)// 启动异步回调任
233233
executor.setDelay(delay).execute(runnable)// 延时启动任务
234234
```
235235

236+
### [EasyBundle](./docs/EasyBundle.md)
237+
238+
> 用于使Bundle数据存取操作变得`简单``方便``灵活``强大`
239+
240+
1. 简化Bundle数据存取api:
241+
2. 打破Bundle数据格式限制。支持对非可序列化对象进行存取。
242+
3. 支持注入操作。在进行页面跳转传值时。将会非常好用。
243+
244+
用法示例:
245+
246+
```
247+
// 1. 存储任意数据对象到bundle中去
248+
EasyBundle.create(bundle)// 绑定bundle容器
249+
.put(key, value)// 指定任意数据进行存储。包括非可序列化对象
250+
251+
// 2. 从bundle中读取并自动转换为具体对象数据
252+
EasyBundle.create(bundle).get<User>("user")
253+
254+
// 3. 支持数据自动注入
255+
class ExampleActivity:BaseActivity() {
256+
// 从intent中读取name数据并注入到name字段中去
257+
@BundleField
258+
var name:String? = null
259+
}
260+
```
261+
236262
### [MVP](./docs/MVP.md)
237263

238264
> 提供的一种简单的MVP分层架构实现。

app/src/main/java/com/haoge/sample/easyandroid/activities/EasyBundleActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ data class ParcelableSubclass(val name: String = "this is a subclass of Parcela
143143

144144
data class SerializableSubclass(val name: String = "this is a subclass of Serializable"):Serializable
145145

146+
// 非可序列化测试类
146147
class Info(val name:String?) {
147148
constructor():this("默认名字")// JSON反序列化时需要空构造
148149

@@ -152,6 +153,7 @@ class Info(val name:String?) {
152153

153154
}
154155

156+
// Bundle数据注入测试类。
155157
class TestInjector(@BundleField var name:String,
156158
@BundleField var age:Int,
157159
@BundleField var parcelable:ParcelableSubclass,

docs/EasyBundle.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# EasyBundle
2+
3+
EasyBundle顾名思义,主要是对Bundle的封装。作用在于使Bundle的存取操作变得`灵活、方便、简洁`
4+
5+
[Sample Activity](../app/src/main/java/com/haoge/sample/easyandroid/activities/EasyBundleActivity.kt)
6+
7+
## 特性
8+
9+
1. 简化Bundle数据存取api:
10+
> 不需要再根据待存取的数据类型。进行`putXXX/getXXX`的方法选择了。统一为`put/get`
11+
2. 打破Bundle数据格式限制。支持对非可序列化对象进行存取。
12+
> 非可序列化对象将会转换为json后进行存储
13+
3. 支持注入操作。在进行页面跳转传值时。将会非常好用。
14+
15+
## 用法
16+
17+
### 创建EasyBundle实例
18+
19+
```
20+
// 传入具体的Bundle实例进行处理。当传入为null时,将默认创建一个空的Bundle实例提供使用
21+
val easyBundle = EasyBundle.create(bundle)
22+
23+
... // 具体操作区
24+
25+
// 操作完成后,获取操作后的bundle实例进行使用
26+
val bundle = easyBundle.bundle
27+
```
28+
29+
### 统一存取api
30+
31+
EasyBundle简化了存取api。 **不用像原生Bundle一样,需要根据指定数据类型选择使用不同的api进行使用:**
32+
33+
以存取`String、Parcelable、Serializable`实例为例:
34+
35+
```
36+
val string = "Hello world"
37+
val parcelable = ParcelableSubclass()
38+
val serializable = SerializableSubclass()
39+
```
40+
41+
#### 统一存储
42+
43+
EasyBundle提供了三种重载方法进行数据存储
44+
45+
1. 统一使用put方法进行存储, 且支持链式调用
46+
47+
```
48+
easybundle.put(key1, string)
49+
.put(key2, parcelable)
50+
.put(key3, serializable)
51+
```
52+
53+
2. 或者,你也可以使用提供的带可变参数的方法进行多数据存储
54+
55+
```
56+
easyBundle.put(key1 to String,
57+
key2 to parcelable,
58+
key3 to serializable)
59+
```
60+
61+
3. 当然,你也可以传入一个存在的map实例
62+
63+
```
64+
easyBundle.put(mapOf<String, Any>(
65+
key1 to string,
66+
key2 to parcelable,
67+
key3 to serializable))
68+
```
69+
70+
#### 统一读取
71+
72+
1. 通过内联函数指定`数据泛型`进行读取
73+
74+
```
75+
val string = easyBundle.get<String>(key1)
76+
val parcelable = easyBundle.get<ParcelableSubclass>(key2)
77+
val serializable = easyBundle.get<SerializableSubclass>(key3)
78+
```
79+
80+
2. 或者。直接通过`指定class`进行读取
81+
82+
```
83+
val String = easyBundle.get(key1, String::class.java)
84+
val parcelable = easyBundle.get(key2, ParcelableSubclass::class.java)
85+
val serializable = easyBundle.get(key3, SerializableSubclass::class.java)
86+
```
87+
88+
### 打破bundle数据存储限制
89+
90+
EasyBundle破除了存储的入口限制,所以也理所应当的,**破除了Bundle的数据存储限制**
91+
92+
意思即是:**EasyBundle允许你向Bundle内部存储任意的数据类型实例**
93+
94+
以下方的`非可序列化类`为例:
95+
96+
```
97+
class Info {
98+
val name:String? = "Info's name"
99+
}
100+
```
101+
102+
- 进行存储:
103+
104+
```
105+
val info = Info()
106+
easyBundle.put("info", info)
107+
```
108+
109+
- 进行读取
110+
111+
```
112+
val info = easyBundle.get<Info>("info")
113+
```
114+
115+
可以看到:Info本身并未实现序列化接口。但是也是可以通过EasyBundle直接进行存取操作的。
116+
117+
这是因为`EasyBundle`采用的是`JSON`作为数据中转格式:
118+
- 在进行存储时:将不能被Bundle直接存储的`(非可序列化对象)`转为`JSON`数据,再进行存储
119+
- 在进行读取时:取出的数据与实际要求的类型不匹配。通过'JSON'数据作为中转,并解析出要求的数据对象返回
120+
121+
我们来通过部分`核心代码`来进行说明:
122+
123+
- 存储时:
124+
125+
```
126+
fun put(name:String, value:Any?):EasyBundle {
127+
when (value) {
128+
// 对于bundle支持的数据格式,直接使用对应的api进行存储
129+
is Int -> bundle.putInt(name, value)
130+
...
131+
// 对于不满足条件的,进行json转码后再进行存放
132+
else -> bundle.putString(name, toJSON(value))
133+
}
134+
135+
return this
136+
}
137+
```
138+
139+
- 读取时:
140+
141+
```
142+
fun <T> get(key:String, type:Class<T>):T? {
143+
var value = bundle.get(key) ?: return returnsValue(null, type) as T?
144+
// 当取出数据类型与指定类型匹配时。直接返回
145+
if (type.isInstance(value)) {
146+
return value as T
147+
}
148+
149+
if (value !is String) {
150+
// 不匹配类型,使用json作为数据中转站
151+
value = toJSON(value)
152+
}
153+
value = value as String
154+
155+
// 处理两种情况下的数据自动转换:
156+
val result = when(type.canonicalName) {
157+
// 兼容基本数据类型
158+
"byte", "java.lang.Byte" -> value.toByte()
159+
...
160+
// 对不匹配类型数据。使用json进行反序列化解析。
161+
else -> parseJSON(value, type)
162+
}
163+
return result as T
164+
}
165+
```
166+
167+
源码很简单。相信很容易看懂。
168+
169+
而在读取时,也对基本数据类型做判断兼容的好处是:可以做到很好的兼容市面上的路由框架。
170+
171+
#### 路由传参的兼容方案
172+
173+
我们都知道。路由的传参,有相当一部分的数据是通过`url`自带的`params`进行的数据传递。
174+
而这些`params`解析后放入intent的数据。基本上都是`String`类型。所以在数据接收页,
175+
普遍的还会需要自己去进行数据解析,这样很容易导致可维护性降低。
176+
177+
所以EasyBundle自带的读取时解析数据。在这种场景下就能得到很好的应用:
178+
179+
以下方所示的链接为例:
180+
181+
```
182+
val uri = Uri.parse("haoge://page/example").buildUpon()
183+
.appendQueryParameter("int", "12")
184+
.appendQueryParameter("user", JSON.toJSONString(User("Haoge")))
185+
.appendQueryParameter("name", "Haoge")
186+
.build()
187+
```
188+
189+
为了便于展示说明。这里采用builder的方式进行了url的创建。传递一个`基本数据类型`一个`JSON`数据,
190+
191+
所以。在解析url时,这部分的参数将会被解析后存入intent中进行传递:
192+
193+
```
194+
val intent = getIntent()
195+
intent.putExtra("int", uri.getQueryParameter("int"))
196+
intent.putExtra("user", uri.getQueryParameter("user"))
197+
intent.putExtra("name", uri.getQueryParameter("name"))
198+
```
199+
200+
然后在参数接收页。按照常规做法。我们应该是要先自己从intent中读取数据。然后自己转换成对应数据后再进行使用:
201+
202+
但是使用EasyBundle即可以不用那么麻烦:
203+
204+
```
205+
val easyBundle = EasyBundle.create(intent.extras)
206+
val int = easyBundle.get<Int>("int")
207+
val user = easyBundle.get<User>("user")
208+
val name = easyBundle.get<String>("name")
209+
```
210+
211+
### 使用BundleField做自动数据注入
212+
213+
`EasyBundle`提供`BundleField`注解作自动数据注入
214+
215+
类似于ButterKnife。EasyBundle可以很方便的,从`bundle`容器中,将数据自动注入到实体类中的`对应成员变量`中去。
216+
217+
最经典的用法是进行页面传参时进行使用:仍以上方路由传参的几个参数作为说明:
218+
219+
```
220+
class InjectorActivity:Activity() {
221+
// 配置可注入的参数. 添加BundleField注解即可
222+
@BundleField("name")
223+
var name:String? = null
224+
@BundleField("int")
225+
var int:Int = 0
226+
@BundleField("user")
227+
var user:User? = null
228+
229+
override fun onCreate(saveInstanceState:Bundle?) {
230+
super.onCreate(saveInstanceState)
231+
// 执行注入操作
232+
EasyBundle.toEntity(this, intent?.extras)
233+
}
234+
}
235+
```
236+
237+
这样的操作可以大大的提高代码的可读性。不用再去自己单独手动进行读取了。
238+
239+
当然,每个类都去手动调用`toEntity`方法,也是很蛋疼的。所以`EasyBundle`也支持将注入api入口配置到基类中去。
240+
241+
而且,结合`EasyBundle`的另一个`反向注入`api:`toBundle`。能够达到很方便的`现场数据保存`的效果
242+
243+
```
244+
abstract class BaseActivity:Activity() {
245+
246+
final override fun onCreate(savedInstanceState: Bundle?) {
247+
super.onCreate(savedInstanceState)
248+
// 自动触发注入操作
249+
EasyBundle.toEntity(this, intent?.extras)
250+
}
251+
252+
// ==== 自动进行现场保护. 可选配置
253+
override fun onSaveInstanceState(outState: Bundle?) {
254+
super.onSaveInstanceState(outState)
255+
// 将当前类中的被BundleField注解的变量。注入到outState中进行保存
256+
EasyBundle.toBundle(this, outState)
257+
}
258+
259+
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
260+
super.onRestoreInstanceState(savedInstanceState)
261+
// 将savedInstanceState中的数据注入到当前类中被BundleField注解的成员变量中
262+
EasyBundle.toEntity(this, savedInstanceState)
263+
}
264+
}
265+
```
266+
267+
#### BundleField参数说明
268+
269+
BundleField提供两个参数:
270+
271+
```
272+
annotation class BundleField(val value:String = "", val throwable:Boolean = true)
273+
```
274+
275+
**1. value**: 参数的key值,当为空时,代表使用成员变量的变量名作为key值使用
276+
**2. throwable**: 在进行数据注入时,当出现异常时,是否允许抛出异常。
277+
278+
#### 指定参数默认值
279+
280+
很多时候我们会需要为某个参数指定默认值。可以通过`直接为变量配置默认值`的方式进行配置:
281+
282+
```
283+
@BundleField
284+
var name:String = "this is default name"
285+
```

utils/src/main/java/com/haoge/easyandroid/easy/EasyBundle.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@ import java.lang.StringBuilder
1010
import java.lang.reflect.Field
1111

1212
/**
13+
* 用于方便的进行Bundle数据存取
1314
* @author haoge on 2018/6/14
1415
*/
1516
class EasyBundle private constructor(val bundle: Bundle){
1617

18+
fun put(map:Map<String, Any?>):EasyBundle {
19+
for ((key, value) in map) {
20+
put(key, value)
21+
}
22+
return this
23+
}
24+
1725
fun put(vararg items:Pair<String, Any?>):EasyBundle {
1826
for ((name, value) in items) {
1927
put(name, value)
@@ -143,13 +151,13 @@ class EasyBundle private constructor(val bundle: Bundle){
143151
}
144152

145153
fun toEntity(entity:Any?, bundle: Bundle?):Any? {
146-
if (entity == null) return null
147-
return injector.toEntity(entity, bundle?: Bundle())
154+
if (entity == null || bundle == null) return null
155+
return injector.toEntity(entity, bundle)
148156
}
149157

150-
fun toBundle(entity:Any?, bundle: Bundle?):Bundle {
151-
if (entity == null) return bundle?:Bundle()
152-
return injector.toBundle(entity, bundle?: Bundle())
158+
fun toBundle(entity:Any?, bundle: Bundle?):Bundle? {
159+
if (entity == null || bundle == null) return bundle
160+
return injector.toBundle(entity, bundle)
153161
}
154162

155163
@JvmStatic
@@ -219,6 +227,7 @@ private class BundleInjector {
219227
if (pair.second.throwable) {
220228
throw e
221229
}
230+
e.printStackTrace()
222231
}
223232
}
224233
return entity

0 commit comments

Comments
 (0)