Android P中MatrixCursor实现是否变更?求替代方案
1. Android P中是MatrixCursor实现变更,还是反射已被完全弃用?
首先明确:反射并没有被完全弃用,但Android P开始严格限制了对非SDK接口(也就是私有API)的访问,这才是你遇到崩溃的核心原因。
你之前通过反射调用的MatrixCursor.get(int)是一个私有方法,属于非SDK接口范畴。在Android P及更高版本中,系统加入了非SDK接口访问限制机制,会主动阻止应用通过反射、JNI等方式调用这类未公开的API,轻则抛出权限异常,重则直接导致应用崩溃。
当然,MatrixCursor的内部实现可能也有细节变更,但这不是问题的关键——就算实现没改,系统的访问限制也会让你的反射代码彻底失效。Android官方一直明确不建议依赖私有API,因为它们没有兼容性保障,随时可能被修改、移除或限制访问。
2. 是否有可存储自定义对象的Cursor替代方案?
针对存储自定义对象的需求,这里有几个基于公开API的靠谱方案,覆盖不同场景:
方案一:序列化自定义对象后存入MatrixCursor(最直接的兼容方案)
把自定义对象序列化成JSON字符串或字节数组,用MatrixCursor的公开put方法存储,读取时再反序列化回来。比如用Gson做JSON序列化:
// 存储自定义对象 CustomObject obj = new CustomObject(); Gson gson = new Gson(); String jsonStr = gson.toJson(obj); MatrixCursor cursor = new MatrixCursor(new String[]{"custom_column"}); cursor.addRow(new Object[]{jsonStr}); // 读取自定义对象 cursor.moveToFirst(); String retrievedJson = cursor.getString(0); CustomObject retrievedObj = gson.fromJson(retrievedJson, CustomObject.class);
如果追求更高性能,也可以用Parcelable序列化转字节数组:
// 存储 ByteArrayOutputStream bos = new ByteArrayOutputStream(); Parcel parcel = Parcel.obtain(); obj.writeToParcel(parcel, 0); bos.write(parcel.marshall()); parcel.recycle(); cursor.addRow(new Object[]{bos.toByteArray()}); // 读取 byte[] bytes = cursor.getBlob(0); Parcel parcel = Parcel.obtain(); parcel.unmarshall(bytes, 0, bytes.length); parcel.setDataPosition(0); CustomObject retrievedObj = CustomObject.CREATOR.createFromParcel(parcel); parcel.recycle();
方案二:自定义Cursor子类
如果需要更灵活的操作,可以继承AbstractCursor,自己实现数据存储逻辑,内部用集合保存自定义对象:
public class CustomObjectCursor extends AbstractCursor { private List<CustomObject> mDataList; private String[] mColumnNames; public CustomObjectCursor(String[] columnNames, List<CustomObject> dataList) { mColumnNames = columnNames; mDataList = dataList; } @Override public int getCount() { return mDataList.size(); } @Override public String[] getColumnNames() { return mColumnNames; } // 自定义方法直接获取对象,无需序列化 public CustomObject getCurrentCustomObject() { return mDataList.get(getPosition()); } // 实现Cursor要求的基础方法(按需重写) @Override public String getString(int column) { return new Gson().toJson(mDataList.get(getPosition())); } @Override public long getLong(int column) { return 0; } @Override public int getInt(int column) { return 0; } @Override public boolean isNull(int column) { return false; } // 其他基础方法按需实现... }
使用时直接创建这个Cursor实例,就能直接操作自定义对象,无需依赖私有API。
方案三:使用Room Persistence Library(官方推荐的本地存储方案)
如果你的场景是本地数据库存储,Room是Android官方推荐的ORM框架,天生支持自定义类型存储,只需添加类型转换器:
// 自定义类型转换器,处理对象与字符串的互转 public class CustomObjectConverter { @TypeConverter public static CustomObject fromString(String value) { return new Gson().fromJson(value, CustomObject.class); } @TypeConverter public static String toString(CustomObject obj) { return new Gson().toJson(obj); } } // 在Room数据库类中注册转换器 @Database(entities = {YourEntity.class}, version = 1) @TypeConverters({CustomObjectConverter.class}) public abstract class AppDatabase extends RoomDatabase { public abstract YourDao yourDao(); } // 实体类中直接使用自定义对象 @Entity public class YourEntity { @PrimaryKey public int id; public CustomObject customObj; // 直接存储自定义对象 }
Room会自动处理序列化和反序列化,不需要手动操作Cursor,开发效率更高,也更符合Android最佳实践。
内容的提问来源于stack exchange,提问作者Ed Holloway-George

