Расширенный ContentProvider не работает должным образом - PullRequest
0 голосов
/ 25 августа 2010

Я пытаюсь реализовать ContentProvider, обернутый вокруг базы данных SQLite.

Я следовал этому уроку при создании ContentProvider: учебник

Я хочу проверить, что у меня есть; поэтому я пытаюсь создать экземпляр моего нового ContentProvider, извлечь курсор из обработчика запросов и прикрепить его к моему CursorAdapter. В настоящее время я делаю это в onCreate своей Деятельности (я знаю, что это плохая практика, я просто тестирую, я в конечном итоге перенесу это в сервис).

Uri uri = Uri.parse("content://com.test.db.providers.Messages/messages");
String s[] = {"_id", "delivery_id", "user_id", "created_on", "subject", "summary", "messagetext", "read", "status"};
MessagesProvider p = new MessagesProvider();
if (p.open()) {
   Cursor messages = p.query(uri, s, null, null, null);
   startManagingCursor(messages);
}

Когда я запускаю свое приложение, запускается метод onCreate моего расширенного ContentProvider. Создается вспомогательный объект базы данных, создается база данных, а метод onCreate возвращает значение true. Однако, когда я пытаюсь использовать мой ContentProvider (с кодом выше), в методе open () создается вспомогательный объект базы данных, но getWritableDatabase () возвращает null. Кроме того, когда я вызываю open (), ссылка на getContext () равна нулю.

Примечание: все остальное работает нормально. Когда я вызываю query, он попадает в мой обработчик запросов, распознает Uri и пытается запустить мой код запроса (который, очевидно, разрывается, потому что объект базы данных имеет значение null).

Вот мой расширенный ContentProvider и помощник по базе данных:

package com.test.db.providers;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

import com.test.db.DbDefinitions;
import com.test.db.DbHelper;

public class MessagesProvider extends ContentProvider {

 private DbHelper mDbHelper;
    private SQLiteDatabase mDb;
    private static final UriMatcher sUriMatcher;

    private static final String PROVIDER_NAME = "com.test.db.providers.Messages";
    private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/messages");

 public static final String id = "_id";
 public static final String delivery_id = "delivery_id";
 public static final String user_id = "user_id";
 public static final String created_on = "created_on";
 public static final String subject = "subject";
 public static final String summary = "summary";
 public static final String messagetext = "messagetext";
 public static final String status = "status";

    private static final int MESSAGES = 1;
    private static final int MESSAGES_ID = 2;

    static {
     sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     sUriMatcher.addURI(PROVIDER_NAME, "messages", MESSAGES);
     sUriMatcher.addURI(PROVIDER_NAME, "messages/#", MESSAGES_ID);
    }

    public boolean open() {
        mDbHelper = new DbHelper(getContext());
        mDb = mDbHelper.getWritableDatabase();
        return (mDb == null) ? false : true;
    }
    public void close() {
     mDbHelper.close();
    }


    @Override
    public boolean onCreate () {
     mDbHelper = new DbHelper(getContext());
     mDb = mDbHelper.getWritableDatabase();
     return (mDb == null) ? false : true;
    }

    @Override
    public String getType (Uri uri) {
     switch (sUriMatcher.match(uri)) {
      case MESSAGES:
       return "vnd.android.cursor.dir/com.test.messages";
      case MESSAGES_ID:
       return "vnd.android.cursor.item/com.test.messages";
      default:
       throw new IllegalArgumentException("Unknown URI " + uri);
     }
    }

    @Override
    public Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
     switch (sUriMatcher.match(uri)) {
      case MESSAGES:
       return queryMessages(uri, projection, selection, selectionArgs, sortOrder);
      default:
       throw new IllegalArgumentException("Unknown Uri " + uri);
     }
    }

    @Override
    public Uri insert (Uri uri, ContentValues initialValues) {
     switch (sUriMatcher.match(uri)) {
   case MESSAGES:
    return insertMessages(uri, initialValues);
   default:
    throw new IllegalArgumentException("Unknown URI " + uri);
     }
    }

    @Override
    public int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) {
     switch (sUriMatcher.match(uri)) {
      case MESSAGES:
       return updateMessages(uri, values, selection, selectionArgs);
      default:
       throw new IllegalArgumentException("Unknown URI " + uri);
     }
    }

    @Override
    public int delete (Uri uri, String selection, String[] selectionArgs) {
     switch (sUriMatcher.match(uri)) {
   case MESSAGES:
    return deleteMessages(uri, selection, selectionArgs);
   default:
    throw new IllegalArgumentException("Unknown URI " + uri);
     }
    }


    /*
     * Messages
     */
    private Cursor queryMessages(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
     Cursor c = mDb.query(DbDefinitions.TABLE_MESSAGES, projection, selection, selectionArgs, null, null, sortOrder);
     if (c != null) {
      c.moveToFirst();
     }
     return c;
    }

    private Uri insertMessages(Uri uri, ContentValues initialValues) {
  ContentValues values;
  if (initialValues != null)
   values = new ContentValues(initialValues);
  else
   values = new ContentValues();
  long rowId = mDb.insert(DbDefinitions.TABLE_MESSAGES, summary, values);
  if (rowId > 0) {
   Uri messageUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
   getContext().getContentResolver().notifyChange(messageUri, null);
   return messageUri;
  }
  throw new SQLException("Failed to insert new message " + uri);
    }

    private int updateMessages(Uri uri, ContentValues values, String where, String[] whereArgs) {
     int result = mDb.update(DbDefinitions.TABLE_MESSAGES, values, where, whereArgs);
     getContext().getContentResolver().notifyChange(uri, null);
     return result;
    }

    public int deleteMessages(Uri uri, String where, String[] whereArgs) {
     // TODO flag message as deleted
     return 0;
    }
}



package com.test.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DbHelper extends SQLiteOpenHelper {

 public DbHelper(Context context) {
  super(context, DbDefinitions.DATABASE_NAME, null, DbDefinitions.DATABASE_VERSION);
 }

 @Override
    public void onCreate(SQLiteDatabase db) {
     db.execSQL(DbDefinitions.DB_CREATE);
     db.execSQL(DbDefinitions.DB_TEST_DATA);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
     // TODO run upgrade string
     db.execSQL("DROP TABLE IF EXISTS " + DbDefinitions.TABLE_MESSAGES);
     onCreate(db);
    }
}

Мне интересно, должен ли я как-то ссылаться на какой-либо экземпляр MessagesProvider, который был создан при запуске приложения, вместо того, чтобы объявлять новый (p) и использовать его?

Я обновил код onCreate в своей Activity следующим образом, но managedQuery вернул null:

Uri uri = Uri.parse("content://com.test.db.providers.Messages/messages");
String s[] = {"_id", "delivery_id", "user_id", "created_on", "subject", "summary", "messagetext", "read", "status"};

Cursor messages = managedQuery(uri, s, null, null, null);
if (messages != null)
    startManagingCursor(messages);

ExampleCursorAdapter msg = 
    new ExampleCursorAdapter(this, messages);
setListAdapter(msg);

Ответы [ 2 ]

3 голосов
/ 25 августа 2010

Прежде чем написать что-нибудь еще: взгляните на пример Блокнот с сайта Android Developer.На мой взгляд, это отличный пример того, как увидеть, как реализуются ContentProviders.

Изучив этот пример, я бы остановился на том, как они пишут ContentProviders, а также на том, как они вызывают его из пользовательского интерфейса для полученияdata.

Например, вам не понадобится метод open ().То, что вы можете сделать в своей Активности, это просто

@Override
public void onCreate(Bundle savedInstanceState){
   ...

   if (getIntent().getData() == null) {
       getIntent().setData(MyMetaData.CONTENT_URI);
   }

   Cursor cursor = managedQuery(getIntent().getData(), null, null, null, null);

   //create an appropriate adapter and bind it to the UI
   ...
}

. Это автоматически вызовет ContentProvider, который может обрабатывать заданный URI контента, если вы зарегистрировали его в файле manifest.xml, например

* 1011.*

// Так же, как sidenote Поскольку я предпочитаю иметь автоматические модульные тесты на месте, и вы упомянули, что вы просто хотите проверить, действительно ли работает ваш ContentProvider, вы можете также написать модульный тестпротив этого, более конкретно ProviderTestCase2:

public class MessagesProviderTest extends ProviderTestCase2<MessagesProvider> {
    private MockContentResolver mockResolver;

    public MessagesProviderTest() {
        super(MessagesProvider.class, MessagesMetaData.AUTHORITY);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mockResolver = getMockContentResolver();
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        mockResolver = null;

            //clean the old db
        getContext().getDatabasePath("test.messages.db").delete();
    }

    public void testRetrieveMessages() {
        //TODO insert some using ContentValues

        //try to retrieve them
        Cursor readMessagesCursor = mockResolver.query(....);
        assertTrue("The cursor should contain some entries", readMessagesCursor.moveToFirst());


           ...
    }
 }

Это просто как sidenote, но я действительно рекомендую это, потому что таким образом вы можете

  1. Проверить, работает ли ваш ContentProviderбез необходимости реализации какой-либо фиктивной операции, службы или чего-либо еще
  2. Вы всегда можете перезапустить тест после изменения реализации ContentProvider и посмотреть, не сломали ли вы что-нибудь.
0 голосов
/ 13 декабря 2010

Да, я поместил в тег непосредственно перед тегом следующее: "<" provider android: name = "com.test.db.providers.MessagesProvider" android: authority = "com.test.db.providers. MessagesProvider "/>"

Ты все еще делаешь это? Как обычно, метка провайдера должна быть размещена внутри метки приложения.

...