Android小工具:利用解构来简化Cursor内容的读取

这篇具有很好参考价值的文章主要介绍了Android小工具:利用解构来简化Cursor内容的读取。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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) {
            List<SystemMedia> result = new ArrayList<>();
            while (cursor.moveToNext()) {
                SystemMedia media = (SystemMedia) CursorAdapter.withCursor(cursor, SystemMedia.CursorContract.class, SystemMedia::new);
                result.add(media);
            }
        }
    }
}

这样就结束了。

API说明

  1. CursorAdapter.withCursor 方法
  • 第一个Cursor参数:目标Cursor对象
  • 第二个Class参数:解构的合约接口,需要是单方法的函数式接口,类名和方法名随意,该方法的参数需要使用Key注解
  • 第三个T参数:合约接口的实现类,完成由所有字段到对象的转换,通常为全属性的构造方法的方法引用,比如SystemMedia::new
  1. Key 注解

用于标注参数对应的字段名(Cursor列名)

完整实现(差不多100行)

其中用到的Memorizer工具见Java小技巧:创建带缓存的过程文章来源地址https://www.toymoban.com/news/detail-814145.html

public abstract class CursorAdapter<T> {

    abstract T read(Cursor cursor, int index);

    public static <T> Object withCursor(Cursor cursor, Class<T> clazz, T t) {
        List<Pair<Integer, 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 Function<Cursor, Function<Class<?>, List<Pair<Integer, 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) {
                List<Pair<Integer, CursorAdapter<?>>> adapterList = new LinkedList<>();
                for (int i = 0; i < parameterTypes.length; i++) {
                    Type parameterType = parameterTypes[i];
                    Optional<Pair<Integer, 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 Function<Type, 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> CursorAdapter<T> create(BiFunction<Cursor, Integer, T> reader) {
        return new CursorAdapter<T>() {
            @Override
            T read(Cursor cursor, int index) {
                return reader.apply(cursor, index);
            }
        };
    }
}

到了这里,关于Android小工具:利用解构来简化Cursor内容的读取的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java读取word文件内容

    今天遇到一个读取word模板内容的需求,在网上找了很多种方案,有的代码比较复杂,有的读出来中文乱码,个人觉得使用Apache下面的poi包去实现起来比较简单,并且不会出现中文乱码的情况。 这次要用到一个新的类:XWPFDocument,它代表一个docx文档。是apache基金会提供的用户

    2024年02月11日
    浏览(37)
  • java读取txt文件内容

    有一个txt文件,需要按行读取内容,并按逗号分隔 共19条数据,故输出19。 大家还有什么好的读取方法吗(有没有直接读取int的方法)?可以教教我吗?

    2024年02月13日
    浏览(46)
  • java 读取pdf文件内容

    https://blog.csdn.net/ThinkPet/article/details/131256428

    2024年02月08日
    浏览(44)
  • java读取文件并输出内容

    方法一:使用BuffererReader.继承Reader类 方法二: 使用FileInputStream类

    2024年02月15日
    浏览(45)
  • java通过url读取文件内容

     使用java获取远程服务器的文件,并读取内容(该文件可以通过Url直接获取) 使用的时候稍作修改,将inputLine赋值,然后使用,否则直接在下面调用为nul,因为循环过后inputLine变成null!!!(按行读取)

    2024年02月12日
    浏览(55)
  • Java按行读取文件文本内容

    在工作和学习中,有时候会有一些场景,代码需要配合读取文件来执行,比如:读文件数据,来进行计算、组装SQL、更新操作...... 下面我们来讨论下,在Java中按行读取文件文件内容的方式有哪些? 读取的文件内容 测试代码 输出结果 分析:虽然读取1个字符,但每行后面可能

    2024年02月15日
    浏览(70)
  • java中按行读取文件内容

          java中按行来读取文件内容,一般对文件也是又要求的,比如文件编码utf-8,内容是按行可读,而不是一堆字节码。这类文件,我们按行读取,主要是方便快速查看内容,并且用这些内容来作别的用途,这类文件内容不宜过多,否则加载容易出现意想不到的问题,比如内

    2024年02月03日
    浏览(37)
  • 通过Java读取csv文件内容

    1、通过 FileReader(String fileName) 读取整个文件,并通过 BufferedReader(Reader in) 作为每行的缓冲; 2、在通过 readLine() 读取每行内容,通过空格作为分隔符,每行转为数组,并存在 ListString[] 列表中; 3、最后打印 csv文件中的内容。 csv 文件内容如下: 打印的代码如下: 输出的内容

    2024年02月15日
    浏览(33)
  • 如何使用 Java 灵活读取 Excel 内容?

    在日常工作和学习中,我们经常需要从 Excel 文件中读取数据。对于少量数据,手动复制和粘贴可能是可行的,但是对于大量的数据,这种方法会变得非常低效。在这种情况下,使用 Java 程序自动读取 Excel 文件中的数据将是更好的选择。 本教程将介绍如何使用 Java 程序读取

    2024年02月07日
    浏览(36)
  • NetMock 简介:简化 Java、Android 和 Kotlin 多平台中的 HTTP 请求测试

    NetMock可让我们摆脱在测试环境中模拟请求和响应的复杂性。 NetMock 是一个功能强大、用户友好的库,旨在简化模拟HTTP请求和响应的过程。 对开发者来说,测试HTTP请求经常会带来一些挑战,因为要在测试环境中模拟请求和响应的复杂性很高。这样就会增加手动测试的时间和精

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包