2010-08-25 2 views
0

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

Я следовал учебник здесь, в здании моей ContentProvider: tutorial

Я хочу, чтобы проверить, что у меня есть; поэтому я пытаюсь создать экземпляр нового 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() равна null.

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

Вот моя расширенная 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 был создан, когда я запустил приложение, вместо того, чтобы объявить новый (р) и используя его?

Я обновил OnCreate код в моей деятельности к следующему, но managedQuery возвращается нуль:

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); 
+0

ли вы зарегистрировать провайдера? – fedj

+0

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

+0

Я также попытался переместить тег провайдера внутри тег приложения. Когда я пытаюсь запустить приложение, приложение выходит из строя (до достижения моей активности, которая создает экземпляр поставщика. В журнале говорится, что здесь есть синтаксическая ошибка: create table messages (_id integer primary key), integer delivery_id, целое число user_id, но не null, created_on integer not null, текст темы не null, сводный текст не null, текст сообщения не null, read integer not null, status text not null default 'STATE_OK'; – Andrew

ответ

3

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

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

Например, вам не нужен метод «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, который способен справиться с данным содержанием Ури, учитывая вы зарегистрировали его в манифесте.XML-файл, как

<provider android:name=".provider.MyContentProvider" android:authorities="com.mycompany.contentprovider.MyContentProvider" /> 

// Так же, как заметка на полях Поскольку я предпочитаю, имеющие автоматизированные модульные тесты на месте, и вы упоминаете вы просто хотите, чтобы проверить, действительно ли работает ваш 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()); 


      ... 
    } 
} 

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

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

Спасибо за ваш ответ. Это по существу то, что у меня есть (я обновил свой пост). Однако managedQuery возвращает null, и я не уверен, почему. Я добавил следующий тег в свой тег приложения в манифесте, и журнал, похоже, показывает правильную регистрацию поставщика: <поставщик android: name = "com.test.db.providers.MessagesProvider" android: authority = "com .test.db.providers.MessagesProvider "/> – Andrew

+0

AH HAH! Хорошо, я делаю все лучше, но мой обработчик запросов не признает Uri по какой-то причине. Позвольте мне убедиться, что я ничего не заблудился – Andrew

+0

Из вашего примера, который вы опубликовали, похоже, что URI в ContentProvider отличается от того, который используется в Activity. Возможно, вы просто не обновили сообщение правильно. – Juri

0

Да, я поместил следующий внутри тега непосредственно перед тегом: «<„поставщик андроид: имя =“com.test.db.providers.MessagesProvider» андроид: власти = "com.test .db.providers.MessagesProvider «/>»

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

+0

Мой тег-провайдер находится внутри моего тега приложения – Andrew

+0

OK, спасибо за проверку! Могу я попросить вас взглянуть на мою проблему? Как вы решили, вы могли бы исправить это: http://stackoverflow.com/questions/4404689/creating-using-contentprovider-for-android – user540327

Смежные вопросы