郑州企业建站详情,4a网站建设公司,吉林网站建设,网站前置审批怎么做Cursor这个类是Android开发者难以避免的#xff0c;比如数据库、ContentResolver内容的读取#xff0c;但通过这个类读取内容非常的繁琐#xff0c;针对要读取的每一个字段都会有这样一段代码#xff1a;
int idIndex cursor.getColumnIndex(id); //获取字段…Cursor这个类是Android开发者难以避免的比如数据库、ContentResolver内容的读取但通过这个类读取内容非常的繁琐针对要读取的每一个字段都会有这样一段代码
int idIndex cursor.getColumnIndex(id); //获取字段对应的列index列index通常并不需要每次都获取
if(idIndex 0){ //判断列index的合法性String id cursor.getString(idIndex); //获取对应列的内容
}这种代码基本没法复用而且还都是纯手工代码自动生成比较麻烦我希望可以像用json映射那样每个字段/列一行代码就完成这个任务所以本文就仿照以前解构Bundle一样来解构Cursor完整实现差不多100行。
实现效果
以MediaStore读取照片为例先编写内容要映射到的Java数据类重点在于其中的CursorContract
public class SystemMedia implements Serializable {private long id;private String data;private long size;private String displayName;private String mimeType;private long dateAdded;private long dateModified;private long bucketId;private String bucketDisplayName;private String album;private int height;private int width;private int orientation;public interface CursorContract { //重点这个类声明映射的合约需要提供一个同样参数的构造方法以方便使用SystemMedia consume(Key(MediaStore.MediaColumns._ID) long id,Key(MediaStore.MediaColumns.DATA) String data,Key(MediaStore.MediaColumns.SIZE) long size,Key(MediaStore.MediaColumns.DISPLAY_NAME) String displayName,Key(MediaStore.MediaColumns.MIME_TYPE) String mimeType,Key(MediaStore.MediaColumns.DATE_ADDED) long dateAdded,Key(MediaStore.MediaColumns.DATE_MODIFIED) long dateModified,Key(MediaStore.MediaColumns.BUCKET_ID) long bucketId,Key(MediaStore.MediaColumns.BUCKET_DISPLAY_NAME) String bucketDisplayName,Key(MediaStore.MediaColumns.HEIGHT) int height,Key(MediaStore.MediaColumns.WIDTH) int width,Key(MediaStore.MediaColumns.ALBUM) String album,Key(MediaStore.MediaColumns.ORIENTATION) int orientation);}public SystemMedia(long id, String data, long size, String displayName, String mimeType, long dateAdded, long dateModified, long bucketId, String bucketDisplayName, int height, int width, String album, int orientation) {this.id id;this.data data;this.size size;this.displayName displayName;this.mimeType mimeType;this.dateAdded dateAdded;this.dateModified dateModified;this.bucketId bucketId;this.bucketDisplayName bucketDisplayName;this.height height;this.width width;this.album album;this.orientation orientation;}public SystemMedia() {}//省略 getter 和 setter
}然后我们的查询代码就变成了
public void query(Context context) {try (Cursor cursor context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null,null, null, null)) {if (cursor ! null) {ListSystemMedia result new ArrayList();while (cursor.moveToNext()) {SystemMedia media (SystemMedia) CursorAdapter.withCursor(cursor, SystemMedia.CursorContract.class, SystemMedia::new);result.add(media);}}}
}这样就结束了。
API说明
CursorAdapter.withCursor 方法
第一个Cursor参数目标Cursor对象第二个Class参数解构的合约接口需要是单方法的函数式接口类名和方法名随意该方法的参数需要使用Key注解第三个T参数合约接口的实现类完成由所有字段到对象的转换通常为全属性的构造方法的方法引用比如SystemMedia::new
Key 注解
用于标注参数对应的字段名Cursor列名
完整实现差不多100行
其中用到的Memorizer工具见Java小技巧创建带缓存的过程
public abstract class CursorAdapterT {abstract T read(Cursor cursor, int index);public static T Object withCursor(Cursor cursor, ClassT clazz, T t) {ListPairInteger, CursorAdapter? list CursorAdapter.adapterExtractor.apply(cursor).apply(clazz);Method[] methods clazz.getMethods();if (methods.length 1) {Object[] args list.stream().map(pair - pair.first 0 ? pair.second.read(cursor, pair.first) : null).toArray();try {return 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 FunctionCursor, FunctionClass?, ListPairInteger, CursorAdapter? adapterExtractor Memorizer.weakMemorize(cursor - Memorizer.memorize(clazz - {Method[] methods clazz.getMethods();if (methods.length 1) {Method desMethod methods[0];Annotation[][] parameterAnnotations desMethod.getParameterAnnotations();Type[] parameterTypes desMethod.getGenericParameterTypes();if (parameterTypes.length parameterAnnotations.length) {ListPairInteger, CursorAdapter? adapterList new LinkedList();for (int i 0; i parameterTypes.length; i) {Type parameterType parameterTypes[i];OptionalPairInteger, CursorAdapter? pairOptional Arrays.stream(parameterAnnotations[i]).filter(annotation - annotation instanceof Key).map(annotation - (Key) annotation).findFirst().map(key - new Pair(cursor.getColumnIndex(key.value()), CursorAdapter.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 IllegalArgumentException(methods size must be 1, current is methods.length);}}));private static final FunctionType, CursorAdapter? adapterBuilder Memorizer.memorize(type - {if (int.class.equals(type) || Integer.class.equals(type)) {return create(Cursor::getInt);} else if (float.class.equals(type) || Float.class.equals(type)) {return create(Cursor::getFloat);} else if (long.class.equals(type) || Long.class.equals(type)) {return create(Cursor::getLong);} else if (short.class.equals(type) || Short.class.equals(type)) {return create(Cursor::getShort);} else if (String.class.equals(type)) {return create(Cursor::getString);} else if (double.class.equals(type) || Double.class.equals(type)) {return create(Cursor::getDouble);} else if (type instanceof GenericArrayType) {Type componentType ((GenericArrayType) type).getGenericComponentType();if (byte.class.equals(componentType)) {return create(Cursor::getBlob);} else {throw new IllegalStateException(unsupported componentType: componentType);}} else {throw new IllegalArgumentException(unsupported type : type);}});private static T CursorAdapterT create(BiFunctionCursor, Integer, T reader) {return new CursorAdapterT() {OverrideT read(Cursor cursor, int index) {return reader.apply(cursor, index);}};}
}