利用解构来简化Bundle读写

我想所有的Android开发者都接触过类似下面这样的代码:

Intent intent = getIntent();
Bundle bundle = intent.getExtras();
int type = bundle.getInt(KEY_TYPE, 0);
String id = bundle.getString(KEY_ID);
Serializable data = bundle.getSerializable(KEY_DATA);

这是典型的利用Bundle传参的示例,这里只有读取参数,自然有对应的写入参数,因为代码差不多,就省略了。

这段代码几乎就是纯手工代码,每个词法单元都需要手敲1-3个字符,才能敲回车来补全。所以就想尝试利用类似解构的方式来简化这个读写过程。

实现效果

先放一段目前常写的代码做对比:

//定义key的名称
private static final String KEY_TYPE = "type";
private static final String KEY_ID = "id";
private static final String KEY_DATA = "data";

//写入Bundle
bundle.putInt(KEY_TYPE, 1);
bundle.putString(KEY_ID, "u_1234");
bundle.putSerializable(KEY_DATA, new Data());

//读取Bundle
int type = bundle.getInt(KEY_TYPE, 0);
String id = bundle.getString(KEY_ID);
Serializable data = bundle.getSerializable(KEY_DATA);

修改后:

//定义bundle的传参规范
interface TestContract {
    void consume(@Key("id") String id, @Key("type") int type, @Key("data") Serializable data);
}

//写入Bundle
Bundle bundle = BundleAdapter.build(TestContract.class, test -> test.consume("u_1234", 1, new Data()));

//读取Bundle
BundleAdapter.withBundle(bundle, TestContract.class, (id, type, data) -> {/* 处理读到的参数 */});

呃,好像也没有省略多少代码,就是多加了一些参数类型约束。

完整实现(200行不到)

里面有用到Memorizer工具见Java小技巧:创建带缓存的过程

public abstract class BundleAdapter<T> {
    public static <T> Bundle build(Class<T> clazz, Consumer<T> consumer) {
        Bundle bundle = new Bundle();
        T proxyInstance = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
            List<Pair<String, BundleAdapter<?>>> bundleAdapters = BundleAdapter.adapterExtractor.apply(clazz);
            if (bundleAdapters.size() == args.length) {
                IntStream.range(0, args.length).forEach(i -> {
                    Pair<String, BundleAdapter<?>> pair = bundleAdapters.get(i);
                    BundleAdapter adapter = pair.second;
                    adapter.write(bundle, pair.first, args[i]);
                });
                return null;
            } else {
                throw new IllegalStateException("adapter length is not equal to args length:" + bundleAdapters.size() + " - " + args.length);
            }
        });
        consumer.accept(proxyInstance);
        return bundle;
    }

    public static <T> void withBundle(Bundle bundle, Class<T> clazz, T t) {
        List<Pair<String, BundleAdapter<?>>> list = BundleAdapter.adapterExtractor.apply(clazz);
        Method[] methods = clazz.getMethods();
        if (methods.length == 1) {
            Object[] args = list.stream().map(pair -> pair.second.read(bundle, pair.first)).toArray();
            try {
                methods[0].invoke(t, args);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new IllegalStateException("methods length is not 1, current is " + methods.length);
        }
    }

    static final Function<Class<?>, List<Pair<String, BundleAdapter<?>>>> adapterExtractor = Memorizer.memorize(clazz -> {
        Method[] methods = clazz.getMethods();
        if (methods.length == 1) {
            Method desMethod = methods[0];
            Class<?> returnType = desMethod.getReturnType();
            if (returnType == void.class) {
                Annotation[][] parameterAnnotations = desMethod.getParameterAnnotations();
                Type[] parameterTypes = desMethod.getGenericParameterTypes();
                if (parameterTypes.length == parameterAnnotations.length) {
                    List<Pair<String, BundleAdapter<?>>> adapterList = new LinkedList<>();
                    for (int i = 0; i < parameterTypes.length; i++) {
                        Type parameterType = parameterTypes[i];
                        Optional<Pair<String, BundleAdapter<?>>> pairOptional = Arrays.stream(parameterAnnotations[i])
                                .filter(annotation -> annotation instanceof Key)
                                .map(annotation -> (Key) annotation)
                                .findFirst()
                                .map(key -> new Pair<>(key.value(), BundleAdapter.adapterBuilder.apply(parameterType)));
                        if (pairOptional.isPresent()) {
                            adapterList.add(pairOptional.get());
                        } else {
                            throw new IllegalStateException("every parameter must contains a Key annotation");
                        }
                    }
                    return adapterList;
                } else {
                    throw new IllegalStateException("parameters length is not equal to annotations length");
                }
            } else {
                throw new IllegalStateException("return type must be Void, current is " + returnType);
            }
        } else {
            throw new IllegalArgumentException("methods size must be 1, current is " + methods.length);
        }
    });

    private static final Function<Type, BundleAdapter<?>> adapterBuilder = Memorizer.memorize(type -> {
        if (Integer.class.equals(type) || int.class.equals(type)) {
            return create(Bundle::getInt, bundle -> bundle::putInt);
        } else if (Float.class.equals(type) || float.class.equals(type)) {
            return create(Bundle::getFloat, bundle -> bundle::putFloat);
        } else if (Long.class.equals(type) || long.class.equals(type)) {
            return create(Bundle::getLong, bundle -> bundle::putLong);
        } else if (Boolean.class.equals(type) || boolean.class.equals(type)) {
            return create(Bundle::getBoolean, bundle -> bundle::putBoolean);
        } else if (Byte.class.equals(type) || byte.class.equals(type)) {
            return create(Bundle::getByte, bundle -> bundle::putByte);
        } else if (String.class.equals(type)) {
            return create(Bundle::getString, bundle -> bundle::putString);
        } else if (Short.class.equals(type) || short.class.equals(type)) {
            return create(Bundle::getShort, bundle -> bundle::putShort);
        } else if (Character.class.equals(type) || char.class.equals(type)) {
            return create(Bundle::getChar, bundle -> bundle::putChar);
        } else if (CharSequence.class.equals(type)) {
            return create(Bundle::getCharSequence, bundle -> bundle::putCharSequence);
        } else if (Parcelable.class.equals(type)) {
            return create((bundle, key) -> (Parcelable) bundle.getParcelable(key), bundle -> bundle::putParcelable);
        } else if (Serializable.class.equals(type)) {
            return create(Bundle::getSerializable, bundle -> bundle::putSerializable);
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            if (int.class.equals(componentType)) {
                return create(Bundle::getIntArray, bundle -> bundle::putIntArray);
            } else if (float.class.equals(componentType)) {
                return create(Bundle::getFloatArray, bundle -> bundle::putFloatArray);
            } else if (long.class.equals(componentType)) {
                return create(Bundle::getLongArray, bundle -> bundle::putLongArray);
            } else if (byte.class.equals(componentType)) {
                return create(Bundle::getByteArray, bundle -> bundle::putByteArray);
            } else if (short.class.equals(componentType)) {
                return create(Bundle::getShortArray, bundle -> bundle::putShortArray);
            } else if (char.class.equals(componentType)) {
                return create(Bundle::getCharArray, bundle -> bundle::putCharArray);
            } else if (CharSequence.class.equals(componentType)) {
                return create(Bundle::getCharSequenceArray, bundle -> bundle::putCharSequenceArray);
            } else if (String.class.equals(componentType)) {
                return create(Bundle::getStringArray, bundle -> bundle::putStringArray);
            } else if (Parcelable.class.equals(componentType)) {
                return create(Bundle::getParcelableArray, bundle -> bundle::putParcelableArray);
            } else if (boolean.class.equals(componentType)) {
                return create(Bundle::getBooleanArray, bundle -> bundle::putBooleanArray);
            } else {
                throw new IllegalArgumentException("unsupported array type:" + componentType);
            }
        } else if (type instanceof ParameterizedType) {
            Type rawType = ((ParameterizedType) type).getRawType();
            Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (ArrayList.class.equals(rawType)) {
                if (typeArguments.length == 1) {
                    Type typeArgument = typeArguments[0];
                    if (String.class.equals(typeArgument)) {
                        return create(Bundle::getStringArrayList, bundle -> bundle::putStringArrayList);
                    } else if (Integer.class.equals(typeArgument)) {
                        return create(Bundle::getIntegerArrayList, bundle -> bundle::putIntegerArrayList);
                    } else if (CharSequence.class.equals(typeArgument)) {
                        return create(Bundle::getCharSequenceArrayList, bundle -> bundle::putCharSequenceArrayList);
                    } else if (Parcelable.class.equals(typeArgument)) {
                        return create(Bundle::getParcelableArrayList, bundle -> bundle::putParcelableArrayList);
                    } else {
                        throw new IllegalStateException("unsupported typeArgument:" + typeArgument);
                    }
                } else {
                    throw new IllegalStateException("typeArguments length must be 1, current is " + typeArguments.length);
                }
            } else {
                throw new IllegalStateException("ParameterizedType must be ArrayList");
            }
        } else {
            throw new IllegalArgumentException("unsupported type:" + type);
        }
    });

    private static <T> BundleAdapter<T> create(BiFunction<Bundle, String, T> reader, Function<Bundle, BiConsumer<String, T>> writer) {
        return new BundleAdapter<T>() {
            @Override
            T read(Bundle bundle, String key) {
                return reader.apply(bundle, key);
            }

            @Override
            void write(Bundle bundle, String key, T value) {
                writer.apply(bundle).accept(key, value);
            }
        };
    }

    abstract T read(Bundle bundle, String key);

    abstract void write(Bundle bundle, String key, T value);
}

你可能感兴趣的:(Java,java,android)