无法追踪ContentProvider中NullPointerException的原因

问题描述 投票:0回答:1

我正在自学内容提供商的基础知识。

我有一个名为 BooksProvder 的 ContentProvider 合约类。它的基础数据位于名为“fiction”的 SQLite 数据库中,其中包含列 _ID、_TITlE、_AUTHOR 和 _YEAR。

在提供商的应用程序中,我还有:

  • 扩展 SQLiteOpenHelper 的 DBHelper
  • 扩展 ContentProvider 的 BooksProvider
  • MainActivity 扩展了 AppCompatActivity,不包含用户定义的代码,唯一的 UI 是文本框“Hello World”,因为我只对应用程序的 contentprovider 感兴趣

在图书提供商中:

  • 我创建了一个名为 dBHelper 的 DBHelper 实例,该对象具有对应用程序数据库目录中数据库的引用 (dBHelper.db)。
  • 在提供者的query()方法中我调用数据库的查询方法:
    cursor = dbHelper.db.query(BooksContract.TABLE, null, null, null,null,null,null);

但是当我尝试从测试应用程序访问提供程序时,上面的行抛出 NullPointerException,表明 db 为空。 db 不应该为空,因为我已经从 AVD 的设备文件资源管理器中找到并打开了它。我不明白为什么它是空的。欢迎任何帮助。

相关代码为:

清单:

<application
        
        <provider   android:authorities="com.mo.books_provider_app"
                    android:name=".BooksProvider"
                    android:enabled="true"
                    android:exported="true">

        </provider>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

DBHelper类:

public class DBHelper extends SQLiteOpenHelper {

    SQLiteDatabase db;
    Context context;

    public DBHelper(@Nullable Context context) {
        super(context, null, null, 1);
        this.context = context;
    }


    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

        // Check if database already exists in the app's specific directory
        File f = context.getDatabasePath("Books.db");
        if (f.exists()) {
            db = getWritableDatabase();
        } else {
            AssetCopier assetCopier = new AssetCopier(context, "Books.db", "Books.db");
            try {
                assetCopier.copyFromAssets("Books.db");
            } catch (IOException e) {
                throw new RuntimeException(e.getCause());
            }
        }
    }


    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // do nothing - there will only be one version (schema of the database)
    }

}

BooksProvder 类的相关片段:

public class BooksProvider extends ContentProvider {

    DBHelper dbHelper;
    Context context;
    UriMatcher uriMatcher;
    Cursor cursor;

    @Override
    public boolean onCreate() {
        this.context = getContext();
        dbHelper = new DBHelper(context);
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  // the uri of the whole content provider itself
        uriMatcher.addURI(BooksContract.AUTHORITY, BooksContract.PATH, BooksContract.TABLE_MATCH_CODE);
        uriMatcher.addURI(BooksContract.AUTHORITY, BooksContract.PATH + "/*", BooksContract.ROW_MATCH_CODE);
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri,
                        @Nullable String[] projection,
                        @Nullable String selection,
                        @Nullable String[] selectionArgs,
                        @Nullable String sortOrder) {

        // query the uri pattern
        switch (uriMatcher.match(uri)) {

            case BooksContract.TABLE_MATCH_CODE:  // Return the whole table, all rows, all columns
                cursor = dbHelper.db.query(BooksContract.TABLE,
                        null, null, null,null,null,null);
                break;

                case BooksContract.ROW_MATCH_CODE: // Return a particular row
                    // Extract the row ID (row number from the uri)
                    String bookId = uri.getLastPathSegment();
                    cursor = dbHelper.db.query(BooksContract.TABLE, projection, "_ID = ?", new String[] {bookId},
                            null, null, null);
                    break;

            default:
                throw new IllegalArgumentException ("Unkown URI : + uri");
        }
        return cursor;

    }
android database sqlite provider
1个回答
0
投票

如果 books.db 不存在,那么您不会实例化 db,因此它为 null 并生成 NPE。

成功复制资产后,您需要将 db 实例化为 SQLiteDatabase,这将要求 books.db 作为 SQLiteDatabase 打开。

因此,在复制资产后,将执行以下操作:-

  } else {
     // AssetCopier code not supplied in question so mimic the copy
     //AssetCopier assetCopier = new AssetCopier(context, "Books.db", "Books.db");
     // ....
     db = SQLiteDatabase.openDatabase(f.getPath(),null,SQLiteDatabase.OPEN_READWRITE); /* Open the database which now exists (at least should do it AssetCopier did it's business) */
  }

当涉及到现有文件时,您可能想(再次)使用而不是获取内存中的文件(即不是Books.db的临时空数据库)

db = SQLiteDatabase.openDatabase(f.getPath(),null,SQLiteDatabase.OPEN_READWRITE);

而不是

db = getWritableDatabase();
  • sqliteDatabase,由于超级调用的数据库名称参数为null,因此将是内存数据库。

  • 确保始终使用未持久化的内存数据库来调用 onCreate 是处理资产副本或不复制的新颖/不寻常的方式。

© www.soinside.com 2019 - 2024. All rights reserved.