Search code examples
javaandroidcursorinter-process-communicat

Implementation of AbstractCursor for communication between Applications calls getString instead of getInt


I'm writing a simple "client/server" communication using a Content Provider and custom cursor extending from AbstractCursor. It provides a simple key/pair sharing between two applications, with each column being a key and each value is an int (actually I need booleans, but I map them as 0's and 1's in an Int)

The client app opens a ContentProviderClient using the content resolver, correctly reads the column names, but when calling getInt to get the values, on the server app side I see that getString is being called instead of getInt.

I can communicate the values by turning them into Strings, but is there a way of forcing getInt to be called directly, or must I always do the string conversion when communicating different apps?

Here is the Provider class code:

private static class StringBooleanCursor extends AbstractCursor {

    private final String[] keys;
    private final int[] values;

    public StringBooleanCursor(Map<String, Boolean> data) {
        List<String> lKeys = new ArrayList<String>(data.keySet());
        keys = new String[lKeys.size()];
        lKeys.toArray(keys);

        values = new int[keys.length];
        for (int i = 0 ; i < keys.length ; i++) {
            values[i] = data.get(keys[i]) ? 1 : 0;
        }
    }

    @Override public int getCount() {
        return 1;
    }

    @Override public String[] getColumnNames() {
        return keys;
    }

    @Override public String getString(int column) {
        Log.d(TAG, "getString " + column);
        return Integer.toString(values[column]);
    }

    @Override public short getShort(int column) {
        Log.d(TAG, "getShort " + column);
        return 0;
    }

    @Override public int getInt(int column) {
        Log.d(TAG, "getInt " + column + ": " + values[column]);
        return values[column];
    }

    @Override public long getLong(int column) {
        Log.d(TAG, "getLong " + column);
        return 0;
    }

    @Override public float getFloat(int column) {
        Log.d(TAG, "getFloat " + column);   
        return 0;
    }

    @Override public double getDouble(int column) {
        Log.d(TAG, "getDouble " + column);
        return 0;
    }

    @Override public boolean isNull(int column) {
        Log.d(TAG, "isNull " + column);
        return column < 0 || column >= keys.length;
    }
}

And the relevant part of the client:

        cursor = cpc.query(configUri, null, null, null, null);
        if (cursor != null && cursor.moveToNext()) {
            String[] keys = cursor.getColumnNames();
            for (int i = 0 ; i < keys.length ; i++) {
                Log.d(TAG, "cursor.getInt(" + i + "): " + cursor.getInt(i));
                String key = keys[i];
                boolean value = cursor.getInt(i) != 0;
                Log.d(TAG, key + " <- " + value);
            }
        }

Solution

  • As Selvin pointed out, I have to override getType. The default implementation in AbstractCursor is something like this:

        public int getType(int column) {
            // Reflects the assumption that all commonly used field types (meaning everything
            // but blobs) are convertible to strings so it should be safe to   call
            // getString to retrieve them.
            return FIELD_TYPE_STRING;
        }
    

    So I overwriting it with something like (in my case):

        @Override public int getType(int column) {
            return FIELD_TYPE_INTEGER;
        }
    

    now generates calls to getLong(int) instead of getString(int)