Loader returns no data, or an empty cursor

不想你离开。 提交于 2019-12-25 17:04:46

问题


I've got two loaders, each of which loads data from a different content provider.

The fragment is supplied with the ID of a medication from the first content provider, and the first loader loads all of the information related to that medication. The second loader is supposed to query the second content provider for all of the alarms associated with that medication.

The first loader works just fine, and returns all of the correct data. However, the second loader appears to return a null cursor, even though I know for a fact that there is plenty of data in the table that should be relevant. I say "appears" because using getCount() on the data in in onLoadFinished for the second loader causes my app to crash, and the only reason I can think that this would occur is if the cursor were null.

Anyway, here's the code for my loaders. If you need, I can give you the code for anything else you want.

/**
 * Initializes the loaders.
 */
@Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
    CursorLoader loader = null;
    long id = getArguments().getLong(ARG_MED_ID);
    switch(loaderId) {
    case 0: // MedList Loader
        Log.d("MedManager", "Loading med data");
        Uri singleUri = ContentUris.withAppendedId(MedProvider.CONTENT_URI, id);
        String[] projection = { MedTable.MED_ID,
                MedTable.MED_NAME,
                MedTable.MED_DOSAGE,
                MedTable.MED_DATE_FILLED,
                MedTable.MED_DURATION };

        loader = new CursorLoader(getActivity(), singleUri,
                projection, null, null,
                MedTable.MED_NAME + " COLLATE LOCALIZED ASC");
        break;
    case 1: // AlarmList Loader
        Log.d("MedManager", "Theoretically loading alarm list");
        Uri baseUri = AlarmProvider.CONTENT_URI;

        // Create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String[] alarmProjection = { DailyAlarmTable.ALARM_ID,
                DailyAlarmTable.ALARM_MEDNUM,
                DailyAlarmTable.ALARM_TIME };
        String select = "((" + DailyAlarmTable.ALARM_MEDNUM + " NOTNULL) AND ("
                + DailyAlarmTable.ALARM_MEDNUM + " = " + id + "))";
        loader = new CursorLoader(getActivity(), baseUri,
                alarmProjection, select, null,
                DailyAlarmTable.ALARM_TIMESTAMP + " ASC");
        break;
    }
    return loader;
}

/**
 * Customizes the various TextViews in the layout to match
 * the values pulled from the MedTable, or swaps the alarm cursor
 * into the adapter.
 */
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    switch(loader.getId()) {
    case 0:
        setUpMedDetails(data);
        break;
    case 1:
        Log.d("MedManager", "Alarm finished loading");
        /*
         * these lines are commented out because their presence causes
         * the app to crash.
         */
        /*
        boolean isEmpty = data.getCount() < 1;
        if(isEmpty) {
            Log.d("MedManager", "No results");
        }
        */
        mAdapter.swapCursor(data);
        break;

    }
}

@Override
public void onLoaderReset(Loader<Cursor> arg0) {
    // TODO Auto-generated method stub
    if(arg0.getId() == 1) {
        mAdapter.swapCursor(null);
    }
}

EDIT: For completeness' sake, and because the possibility always exists that I'm simply a massive idiot overlooking something obvious, here's the code by which I add alarms into the table:

/**
 * This function will turn the hour and day into an "HH:mm AM/PM" string,
 * calculate the timestamp, and then inserts them into the table.
 */
@Override
public void onTimePicked(int hourOfDay, int minute) {
    Log.d("MedManager", "onTimePicked triggered");
    // Convert the hour and minute into a string
    String alarmString = formatAlarmString(hourOfDay, minute);

    // Convert the hour and minute into a timestamp
    long alarmTimestamp = getAlarmTimestamp(hourOfDay, minute);

    // Define the URI to receive the results of the insertion
    Uri newUri = null;

    // Define a contentValues object to contain the new Values
    ContentValues mValues = new ContentValues();

    // Add medId;
    long medId = getIntent().getLongExtra(MedDetailFragment.ARG_MED_ID, 0);
    mValues.put(DailyAlarmTable.ALARM_MEDNUM, medId);

    // Add the timestamp
    mValues.put(DailyAlarmTable.ALARM_TIMESTAMP, alarmTimestamp);

    // Add the time string
    mValues.put(DailyAlarmTable.ALARM_TIME, alarmString);

    // Insert the new alarm
    Toast.makeText(getApplicationContext(), "medNum = " + medId, Toast.LENGTH_SHORT).show();
    Toast.makeText(getApplicationContext(), "time = " + alarmString, Toast.LENGTH_SHORT).show();
    newUri = getContentResolver().insert(AlarmProvider.CONTENT_URI, mValues);
    String uriStr = newUri.toString();
    Toast.makeText(getApplicationContext(), "Uri = " + uriStr, Toast.LENGTH_SHORT).show();

}

As requested, here's my AlarmProvider class.

package com.gmail.jfeingold35.medicationmanager.alarmprovider;

import java.util.Arrays;
import java.util.HashSet;

import com.gmail.jfeingold35.medicationmanager.database.AlarmDatabaseHelper;
import com.gmail.jfeingold35.medicationmanager.database.DailyAlarmTable;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class AlarmProvider extends ContentProvider {
// Database
private AlarmDatabaseHelper database;

// Used for the UriMatcher
private static final int ALARMS = 10;
private static final int ALARM_ID = 20;

private static final String AUTHORITY = "com.gmail.jfeingold35.medicationmanager.alarmprovider";

private static final String BASE_PATH = "medicationmanager";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY 
        + "/" + BASE_PATH);

public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
        + "/alarms";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
        + "/alarm";

private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
    sUriMatcher.addURI(AUTHORITY, BASE_PATH, ALARMS);
    sUriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", ALARM_ID);
}

@Override
public boolean onCreate() {
    database = new AlarmDatabaseHelper(getContext());
    return false;
}

/**
 * Perform a query from the alarm database
 */
@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    // Using SQLiteQueryBuilder instead of the query() method
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

    // Check if the caller requested a column which doesn't exist
    checkColumns(projection);

    // Set the table
    queryBuilder.setTables(DailyAlarmTable.TABLE_ALARM);

    int uriType = sUriMatcher.match(uri);
    switch(uriType) {
    case ALARMS:
        break;
    case ALARM_ID:
        // Adding the ID to the original query
        queryBuilder.appendWhere(DailyAlarmTable.ALARM_ID + "="
                + uri.getLastPathSegment());
        break;
    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    SQLiteDatabase db = database.getWritableDatabase();
    Cursor cursor = queryBuilder.query(db, projection, selection,
            selectionArgs, null, null, sortOrder);
    // Make sure that potential listeners are getting notified
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    return null;
}

/**
 * Delete from the alarm database
 */
public int delete(Uri uri, String selection, String[] selectionArgs) {
    int uriType = sUriMatcher.match(uri);
    SQLiteDatabase db = database.getWritableDatabase();
    int rowsDeleted = 0;
    switch(uriType) {
    case ALARMS:
        rowsDeleted = db.delete(DailyAlarmTable.TABLE_ALARM, selection,
                selectionArgs);
        break;
    case ALARM_ID:
        String id = uri.getLastPathSegment();
        if(TextUtils.isEmpty(selection)) {
            rowsDeleted = db.delete(DailyAlarmTable.TABLE_ALARM, 
                    DailyAlarmTable.ALARM_ID + "=" + id, null);
        } else {
            rowsDeleted = db.delete(DailyAlarmTable.TABLE_ALARM, 
                    DailyAlarmTable.ALARM_ID + "=" + id + " and " + selection,
                    selectionArgs);
        }
        break;
    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return rowsDeleted;
}

@Override
public String getType(Uri uri) {
    return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    int uriType = sUriMatcher.match(uri);
    SQLiteDatabase db = database.getWritableDatabase();
    long id = 0;
    switch(uriType) {
    case ALARMS:
        id = db.insert(DailyAlarmTable.TABLE_ALARM, null, values);
        break;
    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return Uri.parse(BASE_PATH + "/" + id);
}

@Override
public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
    int uriType = sUriMatcher.match(uri);
    SQLiteDatabase db = database.getWritableDatabase();
    int rowsUpdated = 0;
    switch(uriType) {
    case ALARMS:
        rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
                values,
                selection,
                selectionArgs);
        break;
    case ALARM_ID:
        String id = uri.getLastPathSegment();
        if(TextUtils.isEmpty(selection)) {
            rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
                    values,
                    DailyAlarmTable.ALARM_ID + "=" + id,
                    null);
        } else {
            rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
                    values,
                    DailyAlarmTable.ALARM_ID + "=" + id + " and " + selection,
                    selectionArgs);
        }
        break;
    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return rowsUpdated;
}

/**
 * Confirms that the columns the user requested exist.
 * @param projection
 */
public void checkColumns(String[] projection) {
    String[] available = { DailyAlarmTable.ALARM_ID,
            DailyAlarmTable.ALARM_MEDNUM,
            DailyAlarmTable.ALARM_TIMESTAMP,
            DailyAlarmTable.ALARM_TIME };
    if(projection != null) {
        HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
        HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
        // Check if all columns which are requested are available
        if(!availableColumns.containsAll(requestedColumns)) {
            throw new IllegalArgumentException("Unknown columsn in projection");
        }
    }
}

}


回答1:


Oh oh, okay I found that you return null in your query method of AlarmProvider class :)). Let's return cursor for this




回答2:


If the onLoadFinished() method passes you a null cursor, then that means that the ContentProvider's query() method has returned null. You need to fix your query() method so that it doesn't return null in this case.



来源:https://stackoverflow.com/questions/18414490/loader-returns-no-data-or-an-empty-cursor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!