What is EasyBundle
EasyBundle is one of the basic components in the open source basic component integration library EasyAndroid . Its role is: to elegantly access Bundle data
EasyAndroid is an integrated component library. The integrated components in this library all contain the following features, so you can use it with confidence~~
- Streamlined : As an integrated library, I don't want to have that kind of large components, and try to control the size of the integrated library as much as possible. No redundant code
- Cohesion : Minimize or even avoid a single component's dependence on other modules. Achieve independence between components.
Thanks to the coding
, if you only need to use EasyBundle. Then you can directly copy the EasyBundle source code file to your project and use it directly, it is no problem.
characteristic
- Unified access api
- Supports storage of any type of data, breaking the Bundle data limit
- Automatic type conversion. Read as you like
- Two-way data injection between Bundle and entity class
usage
Usage overview
Let's take
a look at how to use it first
. So that everyone can EasyBundle
have a general idea of the usage
Suppose we have the following batch of data, which needs to be stored
Types of | value |
---|---|
Int | age |
String | name |
- Native storage : Different apis need to be selected according to different storage types
val bundle = getBundle()
bundle.putInt("age", age)
bundle.putString("name", name)
- Use EasyBundle for storage : unified storage api. Direct storage
val bundle:Bundle = EasyBundle.create(getBundle())
.put("age", age)
.put("name", name)
.getBundle()
- Native reading : you need to
api
to read according to the container
val bundle = getBundle()
val age:Int = bundle.getInt("age")
val name:String = bundle.getString("name")
- Use EasyBundle to read : unified read api. Read directly
val easyBundle = EasyBundle.create(getBundle())
val age = easyBundle.get<Int>("age")
val name = easyBundle.get<String>("name")
- Native page value
class ExampleActivity:Activity() {
var age:Int = 0
var name:String = ""
override fun onCreate(saveInstanceState:Bundle?) {
super.onCreate(saveInstanceState)
intent?.let{
age = it.getIntExtra("age", 0)
name = it.getStringExtra("name")
}
}
}
- Use EasyBundle for page value
class BaseActivity() {
override fun onCreate(saveInstanceState:Bundle?) {
super.onCreate(saveInstanceState)
// intent BundleField
EasyBundle.toEntity(this, intent?.extras)
}
}
class ExampleActivity:BaseActivity() {
// BundleField
@BundleField
var age:Int = 0
@BundleField
var name:String = ""
...
}
- Native method for field protection
class ExampleActivity:Activity() {
var age:Int = 0
var name:String = ""
//
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.let{
it.putInt("age", age)
it.putString("name", name)
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
saveInstanceState?.let {
age = it.getIntExtra("age", 0)
name = it.getStringExtra("name")
}
}
}
- Use EasyBundle for field protection configuration
//
class BaseActivity() {
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
EasyBundle.toBundle(this, outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
EasyBundle.toEntity(this, savedInstanceState)
}
}
The above is the main usage of EasyBundle. I hope everyone can get a general understanding of the main functions of EasyBundle.
EasyBundle instance creation instructions
EasyBundle encapsulates the access operation of Bundle, then we will definitely need to bind a Bundle to operate accordingly
val easyBundle:EasyBundle = EasyBundle.create(bundle)
Then, after the data is manipulated through easyBundle, the bundle data after the operation is taken out for use:
val bundle:Bundle = easyBundle.bundle
If the bundle passed during creation is null
. Will create a new one bundle
for data storage
fun create(source:Bundle? = null): EasyBundle {
return EasyBundle(source?: Bundle())
}
and so. Let's go back and look at the storage example code above, it is very clear:
val bundle:Bundle = EasyBundle.create(getBundle())
.put("age", age)
.put("name", name)
.getBundle()
Unified access api
From the above example, we can see that: Compared with the native method (which requires data access for
use api
), EasyBundle
the access api is unified:
3.ways of unified storage
- Use the
put(key:String, value:Any)
method directly to store one by one:
easyBundle.put(key1, value1)
.put(key2, value2)//
put(vararg params:Pair<String, Any>)
Simultaneous storage of multiple data through the provided method with variable parameters
easyBundle.put(
key1 to value1,
key2 to value2
...
)
- Directly store the map data passed by others
put(params:Map<String, Any>)
val map:Map<String, Any> = getMap()
easyBundle.put(map)
Unified read
Unified data storage entry. Of course, EasyBundle
the data reading entry is also unified:
When you need to read. The get<T>(key:String)
specified data can be read through inline functions .
For example, read the Parcelable User
instance:
val user = easyBundle.get<User>("user")
And in the java environment. Because there is no inline function available, you can also use the get(key:String, type:Class<*>)
method to read
User user = easyBundle.get("user", User.class)
Break the Bundle storage data limit
As we all know, the Bundle's access api is so complicated, it mainly needs to be filtered out
.
So often. Sometimes when we are developing, we suddenly need to
pass one to the next page. At this time, you will need to serialize and modify this class.
Although it is actually very simple to implement the serialization interface for the class. But it often needs to be realized, which is also annoying.
The solution is actually very simple, refer to the classic network communication model: use JSON as the transit type for communication
Take the following User as an example:
class User() {
val name:String? = null
}
Make storage
easyBundle.put("user", user)
When stored for user automatically
find this type bundle
, the user will be by fastjson
or gson
be json
carried out after the storage.
Core source code display
fun put(name:String, value:Any?) {
...
when (value) {
// Bundle api
is Int -> bundle.putInt(name, value)
is Long -> bundle.putLong(name, value)
...
// Bundle JSON
else -> bundle.putString(name, toJSON(value))
}
}
Read
val user:User = easyBundle.get<User>("user")
When reading, it is taken out of the bundle json
. User
Does not match the specified type . Then it will pass fastjson
or gson
proceed json
later. Go back again:
In addition to the JSON
schemes illustrated here . The other is
:
For example, a string of numbers is placed in the current bundle:
easyBundle.put("number", "10086")
Although we store data in String type. But the content can actually be converted to int. Then we can also int
read directly :
val number:Int = easyBundle.get<Int>("number")
The way. Use it under items that use routing. perfectly worked:
Because in the routing framework, most of the parameter part of the url is directly parsed and passed in the format of String
Core source code display:
fun <T> get(key:String, type:Class<T>):T? {
var value = bundle.get(key) ?: return returnsValue(null, type) as T?
//
if (type.isInstance(value)) {
return value as T
}
if (value !is String) {
// String json
value = toJSON(value)
}
//
val result = when(type.canonicalName) {
//
"byte", "java.lang.Byte" -> value.toByte()
"short", "java.lang.Short" -> value.toShort()
...
// JSON
else -> parseJSON(value, type)
}
return result as T
}
Description of the json transfer data in EasyBundle
In EasyBundle. There is no direct dependency fastjson
and gson
parsing library. But by doing it at runtime json
. Use what is supported by the current operating environment json
:
// fastjson
private val FASTJSON by lazy { return@lazy exist("com.alibaba.fastjson.JSON") }
// gson
private val GSON by lazy { return@lazy exist("com.google.gson.Gson") }
// json gson
private fun toJSON(value:Any) = when {
GSON -> Gson().toJson(value)
FASTJSON -> JSON.toJSONString(value)
else -> throw RuntimeException("Please make sure your project support [FASTJSON] or [GSON] to be used")
}
private fun parseJSON(json:String, clazz: Class<*>) = when {
GSON -> Gson().fromJson(json, clazz)
FASTJSON -> JSON.parseObject(json, clazz)
else -> throw RuntimeException("Please make sure your project support [FASTJSON] or [GSON] to be used")
}
Therefore, there is no need to worry about introducing new unneeded libraries. Moreover, I believe that most of the projects are certainly fastjson
with gson
at least one parsing library.
Two-way data injection
EasyBundle
Provide BundleField
comments. Used to provide
functionality.
Bi-injection means: either data can be
injected into bundle
, or it can be bundle
injected into
:
For example, this is an ordinary bean class that stores user information:
class User(var name:String, var arg:Int, var address:String)
then. In normal mode. When we need to store these data in the bundle:
val user = getUser()
bundle.putString("name", user.name)
bundle.putInt("age", user.age)
bundle.putString("address", user.address)
Or, you need to fetch the corresponding data from the bundle and assign it to the user:
user.name = bundle.getString("name")
user.age = bundle.getInt("age")
user.address = bundle.getString("address")
However, if you use the EasyBundle
provided
function, it's very simple:
1. For the fields that need to be injected. Add a comment:
class User(@BundleField var name:String,
@BundleField var arg:Int,
@BundleField var address:String)
2. Inject the data from the User into the bundle for saving
EasyBundle.toBundle(user, bundle)
3. Read the data from the bundle and inject it into the User instance:
EasyBundle.toEntity(user, bundle)
The effect is consistent with the original writing above. And
.
Re-specify the key value
Generally speaking. When used directly @BundleField
. The key value used by default is
.
But sometimes, we will need to reset the key value:
class Entity(@BundleField("reset_name") var name:String)
Anti-crash switch
In the process of data access, it is difficult to avoid access exceptions. For example. You saved it "Hello,World"
, but when you got it, you got it Int
. Or save it as json. But when reading, json parsing error. These situations will cause unexpected exceptions to be thrown
So BundleField
the throwable
parameters are provided:
@BundleField(throwable = false)
var user:User
throwable
The type is Boolean. Represents when an exception occurs during access. Whether to throw this exception upward. (Default is false)
Use scenarios for data injection
Although the above is a long section, if there is no support for specific usage scenarios. Some friends may not understand: You have said so much, but what is the use of eggs?
Below I will give some examples of usage scenarios. Make some specific instructions:
1. Page jump Intent pass value
This can actually be said to be the main usage scenario. Use it in Activity to get the data passed at startup:
class UserActivity:Activity() {
@BundleField
lateinit var name:String
@BundleField
lateinit var uid:String
override fun onCreate(saveInstanceState:Bundle?) {
// intent
EasyBundle.toEntity(this, intent?.extras)
}
}
of course. In fact, there is a new page every time. It EasyBundle.toEntity
hurts to write it all once
In fact, the injection method can be put into the base class. Do it
class BaseActivity:Activity() {
override fun onCreate(saveInstanceState:Bundle?) {
// intent
EasyBundle.toEntity(this, intent?.extras)
...
}
}
and. Using this method has a very significant advantage: for example, for the UserActivity
page shown above . The data required for this page is the name
same uid
, at a glance~
2. On-site status protection
According to the original way. When we are doing on-site protection, we will need to go
one by one saveInstanceState
, and when we need to restore data, we need to go one by one.
.
For example, like the following page:
class PersonalActivity:Activity() {
//
lateinit var name:String
var isSelf:Boolean = false
...
//
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState.putString("name", name)
outState.putBoolean("isSelf", isSelf)
}
//
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
if (saveInstanceState == null) return
name = saveInstanceState.getString("name")
isSelf = saveInstanceState.getBoolean("isSelf")
}
}
These are just two variables that need to be saved. If there is a lot of data in an environment. This piece has to write people crazy. . .
And EasyBundle
the two-way data injection function can get very good performance here:
class PersonalActivity:Activity() {
//
@BundleField
lateinit var name:String
@BundleField
var isSelf:Boolean = false
...
//
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
EasyBundle.toBundle(this, outState)
}
//
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
EasyBundle.toEntity(this, savedInstanceState)
}
}
Of course, the recommended approach is to
make the upper code more concise:
class BaseActivity:Activity() {
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
EasyBundle.toBundle(this, outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
EasyBundle.toEntity(this, savedInstanceState)
}
}
Of course, you can also expand to any place you need to use it.
3. Compatible with routing jump parameter transfer
As mentioned above, the compatible logic is EasyBundle
supported
. This compatible logic is mainly used to issue routing parameters transmission
For example, we have the following route jump link:
val url = "Haoge://page/user?name=Haoge&age=18"
As can be seen from the link, there are actually two parameters we need to pass: String
type name
and Int
typeage
But the routing framework does not have this visual inspection function, so basically. The data passed in the intent after parsing is of String
type name
andage
So follow the normal logic: we are on the target page. The right age
value. You will need to read out the data before you
can use it
class UserActivity:BaseActivity() {
lateinit var name:String
lateinit var age:Int
override fun onCreate(saveInstanceState:Bundle?) {
// intent
name = intent.getStringExtra("name")
age = intent.getStringExtra("age").toInt()//
}
}
When using the injection function, you don't have to think about it so much, it's straightforward! ! !
class UserActivity:BaseActivity() {
@BundleField
lateinit var name:String
@BundleField//
lateinit var age:Int
}
4. Specify default values
@BundleField
var age:Int = 18//
Obfuscated configuration
Because the automatic injection operation uses reflection to operate. So if you need to confuse the project. Remember to add the following obfuscation rules:
-keep class com.haoge.easyandroid.easy.BundleField
-keepclasseswithmembernames class * {
@com.haoge.easyandroid.easy.BundleField <fields>;
}
More usage scenarios. Looking forward to your discovery~~~