【问题标题】:LoaderManager not receiving data change notification when using custom query in content provider在内容提供程序中使用自定义查询时,LoaderManager 未收到数据更改通知
【发布时间】:2013-02-09 09:18:09
【问题描述】:

总的来说,我对 Android 和 Java 不是很熟悉,所以我的说法可能是错误的。很抱歉。

随便。我有一个 ListActivity 从 LoaderManager 获取数据。 LoaderManager 使用 ContentProvider https://github.com/jakenjarvis/Android-OrmLiteContentProvider

当使用 contentUri (net.mydomain.app.listitems) 获取数据时,一切正常,其定义方式与示例设置中的定义几乎相同。但是我需要来自连接查询的数据并为该查询添加了一个 URI (net.mydomain.app.listitems/items_joined)。 uri 在 Contract 类中定义并命名为 itemsJoinedUri。使用此 URI 时,视图中的列表不会在向表中插入新数据时更新。

在我的 ContentProvider 类的 onQuery 方法中,如果“模式代码”与 itemsJoinedUri 匹配,我会执行自定义查询。其他副我将工作传递给超级方法。

public class DataProvider extends OrmLiteSimpleContentProvider<DatabaseHelper> {

    @Override
    protected Class<DatabaseHelper> getHelperClass() {
        return DatabaseHelper.class;
    }

    @Override
    public boolean onCreate() {
        Controller = new MatcherController()
            .add(ListItem.class, SubType.Directory, "", Contract.ListItem.CONTENT_URI_PATTERN_MANY)
            .add(ListItem.class, SubType.Item, "#", Contract.ListItem.CONTENT_URI_PATTERN_ONE)
            .add(ListItem.class, SubType.Directory, "items_joined", Contract.ListItem.CONTENT_URI_PATTERN_WITH_ITEMS_JOINED)
            .initialize();
        return true;
    }

    @Override
    public Cursor onQuery(DatabaseHelper helper, MatcherPattern target, QueryParameters parameter) {
        Cursor result = null;
        //SQLiteQueryBuilder builder = new SQLiteQueryBuilder();

        switch(target.getPatternCode()) {
        case 310:
            String MY_QUERY = "SELECT items.name as name, listitems._id as _id FROM listitems INNER JOIN items ON items._id = listitems.item_id WHERE list_id = ?";
            SQLiteDatabase db = helper.getReadableDatabase();
            result = db.rawQuery(MY_QUERY, parameter.getSelectionArgs());
            break;
        default:
            result = super.onQuery(helper, target, parameter);
            break;
        }
        return result;
    }
}

相关部分的我的合同实施:

public class Contract {
    public static final String DATABASE_NAME = "mydatabase.db";
    public static final int DATABASE_VERSION = 1;

    public static final String AUTHORITY = "net.mydomain.app";
    .
    .
    .
    public static class ListItem implements BaseColumns {
        public static final String TABLENAME = "listitems";
        public static final String CONTENT_URI_PATH = TABLENAME;
        public static final String MIMETYPE_TYPE = TABLENAME;
        public static final String MIMETYPE_NAME = AUTHORITY + ".provider";

        // field info
        public static final String ITEM = "item_id";
        public static final String LIST = "list_id";
        public static final String QUANTITY = "quantity";

        // content uri pattern code
        public static final int CONTENT_URI_PATTERN_MANY = 300;
        public static final int CONTENT_URI_PATTERN_ONE = 301;
        public static final int CONTENT_URI_PATTERN_WITH_ITEMS_JOINED = 310;

        // Refer to activity.
        public static final Uri contentUri = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(AUTHORITY)
            .appendPath(CONTENT_URI_PATH)
            .build();

        public static final Uri itemsJoinedUri = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(AUTHORITY)
            .appendPath(CONTENT_URI_PATH)
            .appendPath("items_joined")
            .build();
    }
}

ListActivity 的 onCreateLoader 方法在这里。我想使用但没有正确更新列表的那个被注释掉了。

//  @Override
//  public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
//      String[] selectArgs = { "" + list_id };
//      Uri baseUri = Contract.ListItem.itemsJoinedUri;
//      return new CursorLoader(this, baseUri, null, null, selectArgs, null);
//  }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
        String select = "( " + Contract.ListItem.LIST + " = 2 )";
        Uri baseUri = Contract.ListItem.contentUri;
        String[] projection = new String[] {
                Contract.ListItem._ID,
                Contract.ListItem.LIST
        };
        return new CursorLoader(this, baseUri, projection, select, null, null);
    }

列表中的新项目使用代码getContentResolver().insert(Contract.ListItem.contentUri, values); 插入到不同的活动中。该 Activity 在层次结构中比 ListActivity 更深一层,因此我可以使用后退按钮返回 ListActivity,此时我看到列表尚未更新。

所以数据是使用 contentUri 插入的,但使用我自己的 itemsJoinedUri 读取。这可能是这里的问题吗?

编辑

我添加了一个简单的 clicklistener 来从列表中删除一行。这使用 contentUri 并且列表在删除后自动更新。所以我猜想从另一个 Activity 移回列表时列表没有更新的问题可能与 ListActivity 生命周期的错误实现有关。

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String[] args = { "" + id};
    getContentResolver().delete(Contract.ListItem.contentUri, "_id = ?", args);
}

【问题讨论】:

    标签: android android-contentprovider ormlite


    【解决方案1】:

    问题在于生命周期管理。我没有正确的 onRestart() 实现。添加后一切正常。

    @Override
    public void onRestart() {
        super.onRestart();
        getLoaderManager().getLoader(0).forceLoad();
    }
    

    【讨论】:

    • 其实这个解决方案只是一种变通方法。当我使用 contentUri 查询数据时,不需要 onRestart() 方法中的 forceLoad。
    • 你找到更好的解决方案了吗?
    • 记不清了,但我的最后一条评论建议使用 contentUri 以避免在 onRestart 中使用 forceLoad。 contentUri 应该怎么用,我不记得了。
    【解决方案2】:

    在您注释掉的部分中,您仅发送选择参数,但不发送选择字符串。选择参数不能单独出现,还需要设置选择字符串,例如:

    return new CursorLoader(this, baseUri, null, "_id = ?", selectArgs, null);
    

    这行得通吗?

    【讨论】:

    • 不,它没有帮助。我在 DataProvider 类中的查询不使用选择字符串,因为它不使用查询生成器。查询正常,只有通知没有通过。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    • 2015-03-24
    • 2015-12-06
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多