0

I have the problem, that my ListView shows the data only one time. If am closing the application and open it again, it doesn't show anything.

I have a Content Provider for retrieving the data from a SQLite database. I have a SyncAdapter which is getting the data from a Server and puts it into the SQLite database. Then I use a SimpleCursorAdapter for showing the data into a ListView. The query is an 'inner join'.

MainActivity Code:

    public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    private static final int URL_LOADER = 1;
    private ListView employeeList;
    public static SimpleCursorAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        employeeList = (ListView) findViewById(R.id.listView);

        /**
         * Creating a dummyAccount for the content provider
         */
        Account account = new Account("dummyAccount", TrackingContentProvider.AUTHORITY);
        AccountManager accountManager = (AccountManager) this.getSystemService(ACCOUNT_SERVICE);
        accountManager.addAccountExplicitly(account, null, null);

        /**
         * Set the sync adapter to an automatic state
         */
        ContentResolver.setSyncAutomatically(account, TrackingContentProvider.AUTHORITY, true);

        /**
         * The projection for the SimpleCursorLoader
         */
        String[] from = new String[] {EmployeesTable.COLUMN_FIRST_NAME, EmployeesTable.COLUMN_LAST_NAME, StatesTable.COLUMN_STATE, EmployeesTable.COLUMN_ID};

        /**
         * The ids of the views where the content is going to be put in
         */
        int[] to = new int[] {R.id.tV_emplFirstNameInsert, R.id.tV_emplLastNameInsert, R.id.tV_stateInsert};

        /**
         * Make a new cursor adapter for the list view
         */
        adapter = new SimpleCursorAdapter(this, R.layout.employee_list_item, null, from, to, 0);

        /**
         * Calls the loader manager and query another time
         */
        getSupportLoaderManager().restartLoader(URL_LOADER, null, MainActivity.this);

        /**
         * Sets the list view to the SimpleCursorAdapter
         */
        employeeList.setAdapter(adapter);

        registerClickCallback();
    }

    /**
     * Registers the clicks on the elements
     */
    private void registerClickCallback() {
        employeeList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                startActivity(new Intent(MainActivity.this, LocationActivity.class));
            }
        });
    }

    /**
     * Is called by the loader manager
     * @param id The unique id of the loader
     * @param args Bundle of arguments
     * @return The cursor of the query
     */
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        String[] projection = new String[] {"E." + EmployeesTable.COLUMN_ID, "E." + EmployeesTable.COLUMN_FIRST_NAME,
                "E." + EmployeesTable.COLUMN_LAST_NAME, "S." + StatesTable.COLUMN_STATE};
        String clause = "S." + StatesTable.COLUMN_ID + " = " + "E." + EmployeesTable.COLUMN_STATE_ID;
        return new CursorLoader(this,
                TrackingContentProvider.EMPLOYEE_LIST_CONTENT_URI, projection, clause, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        getSupportLoaderManager().restartLoader(URL_LOADER, null, MainActivity.this);
    }
}

The Content Provider queries the data with a SQLiteQueryBuilder. The table I used for the query looks like this:

private static final String EMPLOYEE_LIST_INNER_JOIN = "" +
        EmployeesTable.TABLE_NAME + " AS E " +
        "INNER JOIN " +
        StatesTable.TABLE_NAME + " AS S";

The onPerformSync looks like this:

@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
    try {
        deleteDatabase(provider);
        insertEmployeesTable(provider);
        insertStatesTable(provider);
        insertLocationsTable(provider);
    } catch (RemoteException | IOException e) {
        Log.e("Sync: Error:", e.toString());
    }
}

So every sync, it deletes all entries of the database and inserts the data new. It works, I checked the database after sync.

After the application is started for the first time, it doesn't shows anything, since the sync adapter is syncing. Then I close the app and opens it again and it shows the data. Then the sync adapter syncs another time (same data) but it doesn't shows the data anymore. Any suggestions? I am now trying to find the error for 3 days...

EDIT 1: ContentProvider Code:

public class TrackingContentProvider extends ContentProvider {
private static final int TRACKING_CONTENT = 100;
private static final int TRACKING_CONTENT_ID = 200;
private static final int EMPLOYEE_LIST_CONTENT = 300;
private static final int EMPLOYEE_LIST_CONTENT_ID = 400;

/**
 * Table declaration
 */
private static final int EMPLOYEES_TABLE_CONTENT = 500;
private static final int EMPLOYEES_TABLE_CONTENT_ID = 600;
private static final int STATES_TABLE_CONTENT = 700;
private static final int STATES_TABLE_CONTENT_ID = 800;
private static final int LOCATIONS_TABLE_CONTENT = 900;
private static final int LOCATIONS_TABLE_CONTENT_ID = 1000;

public static final String AUTHORITY = "***";
private static final String TRACKING_BASE_PATH = "tracking";
private static final String EMPLOYEE_LIST_BASE_PATH = "employees";

/**
 * Table base paths
 */
private static final String EMPLOYEES_TABLE_BASE_PATH = "employeesTable";
private static final String STATES_TABLE_BASE_PATH = "statesTable";
private static final String LOCATIONS_TABLE_BASE_PATH = "locationsTable";

/**
 * Table INNER JOIN declarations
 */
private static final String EMPLOYEE_LIST_INNER_JOIN = "" +
        EmployeesTable.TABLE_NAME + " AS E " +
        "INNER JOIN " +
        StatesTable.TABLE_NAME + " AS S";

public static final Uri TRACKING_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TRACKING_BASE_PATH);
public static final Uri EMPLOYEE_LIST_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + EMPLOYEE_LIST_BASE_PATH);

/**
 * Tables Uris
 */
public static final Uri EMPLOYEES_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + EMPLOYEES_TABLE_BASE_PATH);
public static final Uri STATES_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + STATES_TABLE_BASE_PATH);
public static final Uri LOCATIONS_TABLE_URI = Uri.parse("content://" + AUTHORITY + "/" + LOCATIONS_TABLE_BASE_PATH);

private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

static {
    sUriMatcher.addURI(AUTHORITY, TRACKING_BASE_PATH, TRACKING_CONTENT);
    sUriMatcher.addURI(AUTHORITY, TRACKING_BASE_PATH + "/#", TRACKING_CONTENT_ID);
    sUriMatcher.addURI(AUTHORITY, EMPLOYEE_LIST_BASE_PATH, EMPLOYEE_LIST_CONTENT);
    sUriMatcher.addURI(AUTHORITY, EMPLOYEE_LIST_BASE_PATH + "/#", EMPLOYEE_LIST_CONTENT_ID);

    /**
     * Table matcher
     */
    sUriMatcher.addURI(AUTHORITY, EMPLOYEES_TABLE_BASE_PATH, EMPLOYEES_TABLE_CONTENT);
    sUriMatcher.addURI(AUTHORITY, EMPLOYEES_TABLE_BASE_PATH + "/#", EMPLOYEES_TABLE_CONTENT_ID);
    sUriMatcher.addURI(AUTHORITY, STATES_TABLE_BASE_PATH, STATES_TABLE_CONTENT);
    sUriMatcher.addURI(AUTHORITY, STATES_TABLE_BASE_PATH + "/#", STATES_TABLE_CONTENT_ID);
    sUriMatcher.addURI(AUTHORITY, LOCATIONS_TABLE_BASE_PATH, LOCATIONS_TABLE_CONTENT);
    sUriMatcher.addURI(AUTHORITY, LOCATIONS_TABLE_BASE_PATH + "/#", LOCATIONS_TABLE_CONTENT_ID);
}

private SQLiteHelper database;

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

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    int uriType = sUriMatcher.match(uri);
    checkColumns(projection);

    switch (uriType) {
        case TRACKING_CONTENT:
            break;
        case TRACKING_CONTENT_ID:
            break;
        case EMPLOYEE_LIST_CONTENT:
            queryBuilder.setTables(EMPLOYEE_LIST_INNER_JOIN);
            break;
        case EMPLOYEE_LIST_CONTENT_ID:
            queryBuilder.setTables(EMPLOYEE_LIST_INNER_JOIN);
            queryBuilder.appendWhere("E." + EmployeesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
            break;
        case STATES_TABLE_CONTENT:
            queryBuilder.setTables(StatesTable.TABLE_NAME);
            break;
        case STATES_TABLE_CONTENT_ID:
            queryBuilder.setTables(StatesTable.TABLE_NAME);
            queryBuilder.appendWhere(StatesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
            break;
        case EMPLOYEES_TABLE_CONTENT:
            queryBuilder.setTables(EmployeesTable.TABLE_NAME);
            break;
        case EMPLOYEES_TABLE_CONTENT_ID:
            queryBuilder.setTables(EmployeesTable.TABLE_NAME);
            queryBuilder.appendWhere(EmployeesTable.COLUMN_ID + " = " + uri.getLastPathSegment());
            break;
        case LOCATIONS_TABLE_CONTENT:
            queryBuilder.setTables(LocationsTable.TABLE_NAME);
            break;
        case LOCATIONS_TABLE_CONTENT_ID:
            queryBuilder.setTables(LocationsTable.TABLE_NAME);
            queryBuilder.appendWhere(LocationsTable.COLUMN_ID + " = " + uri.getLastPathSegment());
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    SQLiteDatabase db = database.getReadableDatabase();
    Cursor cursor = queryBuilder.query(
            db,
            projection,
            selection,
            selectionArgs,
            null,
            null,
            sortOrder
    );
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return cursor;
}

@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;
    String path;

    switch (uriType) {
        case STATES_TABLE_CONTENT:
            id = db.insert(StatesTable.TABLE_NAME, null, values);
            path = STATES_TABLE_BASE_PATH;
            break;
        case LOCATIONS_TABLE_CONTENT:
            id = db.insert(LocationsTable.TABLE_NAME, null, values);
            path = LOCATIONS_TABLE_BASE_PATH;
            break;
        case EMPLOYEES_TABLE_CONTENT:
            id = db.insert(EmployeesTable.TABLE_NAME, null, values);
            path = EMPLOYEES_TABLE_BASE_PATH;
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return Uri.parse(path + "/" + id);
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    int uriType = sUriMatcher.match(uri);
    SQLiteDatabase db = database.getWritableDatabase();
    int rowsDeleted = 0;

    switch (uriType) {
        case EMPLOYEES_TABLE_CONTENT:
            rowsDeleted = db.delete(EmployeesTable.TABLE_NAME,
                    selection,
                    selectionArgs);
            break;
        case EMPLOYEES_TABLE_CONTENT_ID:
            String employeeID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsDeleted = db.delete(EmployeesTable.TABLE_NAME,
                        EmployeesTable.COLUMN_ID + " = " + employeeID, null);
            }
            break;
        case STATES_TABLE_CONTENT:
            rowsDeleted = db.delete(StatesTable.TABLE_NAME,
                    selection,
                    selectionArgs);
            break;
        case STATES_TABLE_CONTENT_ID:
            String stateID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsDeleted = db.delete(StatesTable.TABLE_NAME,
                        StatesTable.COLUMN_ID + " = " + stateID, null);
            }
            break;
        case LOCATIONS_TABLE_CONTENT:
            rowsDeleted = db.delete(StatesTable.TABLE_NAME,
                    selection,
                    selectionArgs);
            break;
        case LOCATIONS_TABLE_CONTENT_ID:
            String locationID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsDeleted = db.delete(LocationsTable.TABLE_NAME,
                        LocationsTable.COLUMN_ID + " = " + locationID, null);
            }
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return rowsDeleted;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    int uriType = sUriMatcher.match(uri);
    SQLiteDatabase sqlDb = database.getWritableDatabase();
    int rowsUpdated;
    switch (uriType) {
        case EMPLOYEES_TABLE_CONTENT:
            rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
                    values,
                    selection,
                    selectionArgs);
            break;
        case EMPLOYEE_LIST_CONTENT_ID:
            String employeeID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
                        values,
                        EmployeesTable.COLUMN_ID + "=" + employeeID
                                + " AND "
                                + selection,
                        selectionArgs);
            } else {
                rowsUpdated = sqlDb.update(EmployeesTable.TABLE_NAME,
                        values,
                        EmployeesTable.COLUMN_ID + "=" + employeeID
                                + " AND "
                                + selection,
                        selectionArgs);
            }
            break;
        case STATES_TABLE_CONTENT:
            rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
                    values,
                    selection,
                    selectionArgs);
            break;
        case STATES_TABLE_CONTENT_ID:
            String stateID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
                        values,
                        StatesTable.COLUMN_ID + "=" + stateID
                                + " AND "
                                + selection,
                        selectionArgs);
            } else {
                rowsUpdated = sqlDb.update(StatesTable.TABLE_NAME,
                        values,
                        StatesTable.COLUMN_ID + "=" + stateID
                                + " AND "
                                + selection,
                        selectionArgs);
            }
            break;
        case LOCATIONS_TABLE_CONTENT:
            rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
                    values,
                    selection,
                    selectionArgs);
            break;
        case LOCATIONS_TABLE_CONTENT_ID:
            String locationID = uri.getLastPathSegment();
            if (TextUtils.isEmpty(selection)) {
                rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
                        values,
                        LocationsTable.COLUMN_ID + "=" + locationID
                                + " AND "
                                + selection,
                        selectionArgs);
            } else {
                rowsUpdated = sqlDb.update(LocationsTable.TABLE_NAME,
                        values,
                        LocationsTable.COLUMN_ID + "=" + locationID
                                + " AND "
                                + selection,
                        selectionArgs);
            }
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return rowsUpdated;
}

/**
 * Checks if the column exists
 * @param projection The array of the existing columns
 */
private void checkColumns(String[] projection) {
    String[] available = {
            "E." + EmployeesTable.COLUMN_ID,
            "E." + EmployeesTable.COLUMN_LAST_NAME,
            "E." + EmployeesTable.COLUMN_FIRST_NAME,
            "E." + EmployeesTable.COLUMN_STATE_ID,
            "E." + EmployeesTable.COLUMN_ENTRY_DATE,

            "S." + StatesTable.COLUMN_ID,
            "S." + StatesTable.COLUMN_STATE,

            "L." + LocationsTable.COLUMN_ID,
            "L." + LocationsTable.COLUMN_DATE,
            "L." + LocationsTable.COLUMN_EMPLOYEE_ID,
            "L." + LocationsTable.COLUMN_LONGITUDE,
            "L." + LocationsTable.COLUMN_LATITUDE,

            EmployeesTable.COLUMN_ID,
            EmployeesTable.COLUMN_LAST_NAME,
            EmployeesTable.COLUMN_FIRST_NAME,
            EmployeesTable.COLUMN_STATE_ID,
            EmployeesTable.COLUMN_ENTRY_DATE,

            StatesTable.COLUMN_ID,
            StatesTable.COLUMN_STATE,

            LocationsTable.COLUMN_ID,
            LocationsTable.COLUMN_DATE,
            LocationsTable.COLUMN_EMPLOYEE_ID,
            LocationsTable.COLUMN_LONGITUDE,
            LocationsTable.COLUMN_LATITUDE,

    };

    if (projection != null) {
        HashSet<String> requestedColumns = new HashSet<>(Arrays.asList(projection));
        HashSet<String> availableColumns = new HashSet<>(Arrays.asList(available));

        if (!availableColumns.containsAll(requestedColumns)) {
            throw new IllegalArgumentException("Unknown columns in projection");
        }
    }
}

}

UPDATE:
I finally found the mistake. During the synchronization, there is a mistake with the auto increment. So, he won't find a entry with this Inner Join statement. finished.

Simon Pio.
  • 115
  • 1
  • 14
  • in `onLoadFinished` call `Databaseutils.dumpCursor`, what is on the `logcat`? – pskink Jul 09 '16 at 11:23
  • Logcat doesn't show any error, I will try your suggestion. – Simon Pio. Jul 09 '16 at 11:28
  • watch the logcat after adding `dumpCursor` – pskink Jul 09 '16 at 11:29
  • 07-09 13:34:45.048 32527-32527/****I/System.out: >>>>> Dumping cursor android.content.ContentResolver$CursorWrapperInner@2e3e195 07-09 13:34:45.048 32527-32527/**** I/System.out: <<<< – Simon Pio. Jul 09 '16 at 11:38
  • ok so it is an empty Cursor, most likely your `String clause` is wrong, try using `selectionArgs` parameter in `CursorLoader` constructor: """selectionArgs You may include ?s in selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.""" – pskink Jul 09 '16 at 12:00
  • I made something like this: String clause = "? = ?"; String[] clauseArgs = new String[] {"S." + StatesTable.COLUMN_ID, "E." + EmployeesTable.COLUMN_STATE_ID}; return new CursorLoader(this, TrackingContentProvider.EMPLOYEE_LIST_CONTENT_URI, projection, clause, clauseArgs, null); Cursor is still empty -.- – Simon Pio. Jul 09 '16 at 12:10
  • ooops sorry, i was wrong: you dont have any arguments in your query, post your custom `ContentProvider` code – pskink Jul 09 '16 at 12:41
  • can you use `queryBuilder.buildQuery` or `buildQueryString` and log the constructed SELECT statement? – pskink Jul 09 '16 at 13:05
  • I logged it: SELECT E._id, E.firstName, E.lastName, S.state FROM employees AS E INNER JOIN states AS S WHERE (S._id = E.stateID). This problem just appears, after the second sync. But the database is populated. I guess, the SimpleCursorAdapter is the problem in anyway – Simon Pio. Jul 09 '16 at 13:23
  • Updated my mistake, thank you for your effort pskink! – Simon Pio. Jul 09 '16 at 14:57

0 Answers0