/* * Copyright (C) 2018 Haoge https://github.com/yjfnypeu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("unused") package com.haoge.easyandroid.easy import android.os.Bundle import android.os.IBinder import android.os.Parcelable import android.text.TextUtils import android.util.Size import android.util.SizeF import android.util.SparseArray import com.alibaba.fastjson.JSON import com.google.gson.Gson import java.io.Serializable import java.lang.StringBuilder import java.lang.reflect.Field import java.lang.reflect.ParameterizedType import java.lang.reflect.Type /** * 用于方便的进行Bundle数据存取。 * @author haoge on 2018/6/14 */ class EasyBundle private constructor(val bundle: Bundle){ /** 将map中的所有数据均存放至容器中*/ fun put(map:Map<String, Any?>):EasyBundle { map.forEach { put(it.key, it.value) } return this } /** 直接一起存储不定数量的键值对数据到容器中*/ fun put(vararg items:Pair<String, Any?>):EasyBundle { items.forEach { put(it.first, it.second) } return this } /** * 将指定[key]-[value]键值对数据存储到Bundle容器中 * * **存储规则:** * * 1. 当[value]的数据类型支持直接被bundle进行存储时,直接进行存储 * 2. 当[value]的数据类型不支持被bundle进行存储是,则将先将value转换为json后再进行存储 */ fun put(key:String, value:Any?):EasyBundle { if (TextUtils.isEmpty(key) || value == null) { return this } var store = true // 根据value的类型,选择合适的api进行存储 @Suppress("UNCHECKED_CAST") when (value) { is Int -> bundle.putInt(key, value) is Long -> bundle.putLong(key, value) is CharSequence -> bundle.putCharSequence(key, value) is String -> bundle.putString(key, value) is Float -> bundle.putFloat(key, value) is Double -> bundle.putDouble(key, value) is Char -> bundle.putChar(key, value) is Short -> bundle.putShort(key, value) is Boolean -> bundle.putBoolean(key, value) is Parcelable -> bundle.putParcelable(key, value) is SparseArray<*> -> bundle.putSparseParcelableArray(key, value as SparseArray<out Parcelable>) is Array<*> -> when { value.isArrayOf<CharSequence>() -> bundle.putCharSequenceArray(key, value as Array<out CharSequence>) value.isArrayOf<String>() -> bundle.putStringArray(key, value as Array<out String>?) value.isArrayOf<Parcelable>() -> bundle.putParcelableArray(key, value as Array<out Parcelable>?) else -> store = false } is Size -> bundle.putSize(key, value) is SizeF -> bundle.putSizeF(key, value) is IntArray -> bundle.putIntArray(key, value) is LongArray -> bundle.putLongArray(key, value) is FloatArray -> bundle.putFloatArray(key, value) is DoubleArray -> bundle.putDoubleArray(key, value) is CharArray -> bundle.putCharArray(key, value) is ShortArray -> bundle.putShortArray(key, value) is BooleanArray -> bundle.putBooleanArray(key, value) is IBinder -> bundle.putBinder(key, value) is Serializable -> when (value) { is Collection<*>, is Map<*, *> -> store = false else -> bundle.putSerializable(key, value) } else -> store = false } if (store.not()) { bundle.putString(key, toJSON(value)) } return this } /** 获取指定[key]对应的值,若获取失败,则返回默认值[defValue]*/ inline fun <reified T> get(key: String, defValue:T):T { return get<T>(key)?:defValue } /** 获取指定[key]对应的值,可为null*/ inline fun <reified T> get(key:String):T? { val type = object : TypeGeneric<T>(T::class.java){}.getType() return get(key, type) as T? } /** 获取指定[key]对应的值,类型为[clazz], 若获取失败,则返回默认值[defValue]*/ fun <T> get(key: String, clazz: Class<T>, defValue:T):T { return get(key, clazz)?:defValue } /** 获取指定[key]对应的值,类型为[clazz], 可为null*/ fun <T> get(key: String, clazz:Class<T>):T? { @Suppress("UNCHECKED_CAST") return get(key, type = clazz) as T? } /** 获取指定[key]对应的值,类型为[type], 可为null*/ fun get(key:String, type:Type):Any? { val rawType = getRawClass(type) var value = bundle.get(key) ?: return returnsValue(null, rawType) // 当取出数据类型与指定类型匹配时。直接返回 if (rawType.isInstance(value)) { return value } if (value !is String) { // 对于数据类型不为String的,先行转换为json。 value = toJSON(value) } if (value.isEmpty()) { // 过滤空数据 returnsValue(null, rawType) } // 处理两种情况下的数据自动转换: @Suppress("IMPLICIT_CAST_TO_ANY") return when(rawType.canonicalName) { // String自动转换基本数据类型 "byte", "java.lang.Byte" -> value.toByte() "short", "java.lang.Short" -> value.toShort() "int", "java.lang.Integer" -> value.toInt() "long", "java.lang.Long" -> value.toLong() "float", "java.lang.Float" -> value.toFloat() "double", "java.lang.Double" -> value.toDouble() "char", "java.lang.Character" -> value.toCharArray()[0] "boolean", "java.lang.Boolean" -> value.toBoolean() // 特殊处理StringBuilder、StringBuffer "java.lang.StringBuilder" -> StringBuilder(value) "java.lang.StringBuffer" -> StringBuffer(value) else -> parseJSON(value, type) } } private fun getRawClass(type:Type):Class<*> { return when(type) { is Class<*> -> type is ParameterizedType -> getRawClass(type.rawType) else -> throw RuntimeException("Only support of Class and ParameterizedType") } } // 兼容java环境使用,对返回数据进行二次处理。避免对基本数据类型返回null导致crash private fun returnsValue(value:Any?, type:Class<*>):Any? { if (value != null) return value return when (type.canonicalName) { "byte" -> 0.toByte() "short" -> 0.toShort() "int" -> 0 "long" -> 0.toLong() "float" -> 0.toFloat() "double" -> 0.toDouble() "char" -> '0' "boolean" -> false else -> null } } private fun toJSON(value:Any):String { return 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, type: Type):Any { return when { GSON -> Gson().fromJson(json, type) FASTJSON -> JSON.parseObject(json, type) else -> throw RuntimeException("Please make sure your project support [FASTJSON] or [GSON] to be used") } } companion object { // 标识符:当前运行环境是否依赖了fastjson private val FASTJSON by lazy { return@lazy exist("com.alibaba.fastjson.JSON") } private val GSON by lazy { return@lazy exist("com.google.gson.Gson") } private val injector = BundleInjector() @JvmStatic fun create(source:Bundle? = null): EasyBundle { return EasyBundle(source?: Bundle()) } @JvmStatic fun toEntity(entity:Any?, bundle: Bundle?):Any? { if (entity == null || bundle == null) return entity return injector.toEntity(entity, bundle) } @JvmStatic fun toBundle(entity:Any?, bundle: Bundle?):Bundle? { if (entity == null || bundle == null) return bundle return injector.toBundle(entity, bundle) } private fun exist(name:String):Boolean = try{ Class.forName(name) true } catch (e:Exception) { false } } } @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD) annotation class BundleField(val value:String = "", val throwable:Boolean = false) private class BundleInjector { // 缓存注解与字段的匹配信息。进行加速 private val container = mutableMapOf<Class<*>, Map<String, Pair<Field, BundleField>>>() fun parseFields(clazz:Class<*>):Map<String, Pair<Field, BundleField>> { if (container.containsKey(clazz)) { return container.getValue(clazz) } // 将自身以及父类中配有BundleField注解的字段进行解析存储。 var type = clazz val fields = HashMap<String, Pair<Field, BundleField>>() while (true) { val name = type.canonicalName if (name.startsWith("android") || name.startsWith("java") || name.startsWith("javax") || name.startsWith("kotlin")) { // 对系统类进行跳过 break } for (field in type.declaredFields) { val bundleField = field.getAnnotation(BundleField::class.java) ?: continue if (field.isAccessible.not()) { field.isAccessible = true } fields[if (bundleField.value.isEmpty()) field.name else bundleField.value] = Pair(field, bundleField) } type = type.superclass } container[clazz] = fields return fields } // 将bundle中的数据注入到entity的对应字段中去。 fun toEntity(entity:Any, bundle: Bundle):Any { val map = parseFields(entity.javaClass) val easyBundle = EasyBundle.create(bundle) for ((name, pair) in map) { try { if (bundle.containsKey(name).not()) continue val value = easyBundle.get(name, pair.first.type) ?: continue pair.first.set(entity, value) } catch (e:Exception) { if (pair.second.throwable) { throw e } e.printStackTrace() } } return entity } // 将entity中的指定数据注入到bundle中去 fun toBundle(entity:Any, bundle: Bundle):Bundle { val map = parseFields(entity.javaClass) val easyBundle = EasyBundle.create(bundle) for ((name, pair) in map) { try { val value = pair.first.get(entity) ?: continue easyBundle.put(name, value) } catch (e:Exception) { if (pair.second.throwable) { throw e } } } return bundle } } abstract class TypeGeneric<T>(private val raw:Class<*>) { fun getType():Type { val type = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] return if (type is Class<*> || type is ParameterizedType) type else raw } }