在使用ListView和LoaderManager填充片段中的片段时,我会得到这个错误。如果我从适配器中删除SQLiteCursorLoader并直接填充,那么所有这些都能正常工作。如果我尝试一个自定义适配器,我会得到同样的错误。错误似乎是,mUri成员的CursorLoader从未填充并被保留为空,SQLiteCursorLoader从不调用设置mUri的CursorLoader上的构造函数。
public CursorLoader(Context context, Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new ForceLoadContentObserver();
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}相反,它调用只传递上下文的构造函数的版本。
public CursorLoader(Context context) {
super(context);
mObserver = new ForceLoadContentObserver();
}这是错误
03-08 15:00:36.906 2601-2683/com.overunitystudios.medbuddy E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.overunitystudios.medbuddy, PID: 2601
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:309)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: uri
at com.android.internal.util.Preconditions.checkNotNull(Preconditions.java:60)
at android.content.ContentResolver.query(ContentResolver.java:474)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:64)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:56)
at android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:312)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:69)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:66)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818) 下面是包含ListView的片段的代码
public class fragment_medications extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private database_helper_medications dbHelper;
private SimpleCursorAdapter listAdapter;
private SQLLiteCursorLoader loader=null;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//return super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_medications_layout, container,false);
dbHelper = new database_helper_medications(getActivity());
listAdapter = new SimpleCursorAdapter(getActivity(),R.layout.list_item_medications,null, new String[] {database_helper_medications.FIELD_MEDICATION}, new int[] {R.id.tvMedication},0);
ListView lvMedications = (ListView) view.findViewById(R.id.lvMedications);
lvMedications.setAdapter(listAdapter);
return view;
}
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
}
//TODO: 03-08 00:16:48.563 3508-3516/com.overunitystudios.medbuddy W/SQLiteConnectionPool: A SQLiteConnection object for database '/data/user/0/com.overunitystudios.medbuddy/databases/medbuddy' was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
@Override
public void onDestroy() {
super.onDestroy();
if (dbHelper!=null) dbHelper.close();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
loader=new SQLLiteCursorLoader(getActivity(), dbHelper, dbHelper.DATABASE_SELECT_ALL, null);
return(loader);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
this.loader=(SQLLiteCursorLoader)loader;
listAdapter.changeCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
listAdapter.changeCursor(null);
}
}数据库助手
public class database_helper_medications extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "medbuddy";
private static final String TABLE_NAME = "medications";
private static final int DATABASE_VERSION = 1;
public final static String FIELD_ID="_id"; //Changed table to use a column named "_id" instead of "id" as CursorAdapter must have an _id column in the table it is using.
public final static String FIELD_MEDICATION="name";
private SQLiteDatabase database;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table if not exists " + TABLE_NAME + " ( " + FIELD_ID + " integer primary key," + FIELD_MEDICATION + " text not null);";
public static final String DATABASE_SELECT_ALL = "select " + FIELD_ID + "," + FIELD_MEDICATION + " from " + TABLE_NAME + " order by " + FIELD_MEDICATION;
public database_helper_medications(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
database = this.getWritableDatabase();
}
// Method is called during creation of the database
@Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
// Method is called during an upgrade of the database,
@Override
public void onUpgrade(SQLiteDatabase database,int oldVersion,int newVersion){
database.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME + "");
onCreate(database);
}
public long createSingleRecord(Integer id, String medication){
ContentValues values = new ContentValues();
values.put(FIELD_ID, id);
values.put(FIELD_MEDICATION, medication);
return database.insert(TABLE_NAME, null, values);
}
public long createSingleRecord(String medication){
ContentValues values = new ContentValues();
//values.put(FIELD_ID, id); //We dont need to do this as it's marked autoincrement.
values.put(FIELD_MEDICATION, medication);
return database.insert(TABLE_NAME, null, values);
}
public Cursor selectAllRecords() {
String[] cols = new String[] {FIELD_ID, FIELD_MEDICATION};
Cursor mCursor = database.query(true, TABLE_NAME, cols,null, null, null, null, null, null);
if (mCursor != null)
mCursor.moveToFirst();
return mCursor; // iterate to get each value.
}
public void DumpToLog()
{
Log.i("DumpToLog", DatabaseUtils.dumpCursorToString(selectAllRecords()));
}
}这里是SQLLiteCursorLoader (我重命名为流行的SQLiteCursorLoader)
public class SQLLiteCursorLoader extends CursorLoader
{
SQLiteOpenHelper db=null;
String rawQuery=null;
String[] args=null;
/**
* Creates a fully-specified SQLiteCursorLoader. See
* {@link SQLiteDatabase#rawQuery(SQLiteDatabase, String, String[])
* SQLiteDatabase.rawQuery()} for documentation on the
* meaning of the parameters. These will be passed as-is
* to that call.
*/
public SQLLiteCursorLoader(Context context, SQLiteOpenHelper db, String rawQuery, String[] args)
{
super(context);
this.db=db;
this.rawQuery=rawQuery;
this.args=args;
}
/**
* Runs on a worker thread and performs the actual
* database query to retrieve the Cursor.
*/
//@Override
protected Cursor buildCursor() {
return(db.getReadableDatabase().rawQuery(rawQuery, args));
}
/**
* Writes a semi-user-readable roster of contents to
* supplied output.
*/
@Override
public void dump(String prefix, FileDescriptor fd,PrintWriter writer, String[] args)
{
super.dump(prefix, fd, writer, args);
writer.print(prefix);
writer.print("rawQuery=");
writer.println(rawQuery);
writer.print(prefix);
writer.print("args=");
writer.println(Arrays.toString(args));
}
public void insert(String table, String nullColumnHack, ContentValues values)
{
buildInsertTask(this).execute(db, table, nullColumnHack, values);
}
public void update(String table, ContentValues values,String whereClause, String[] whereArgs)
{
buildUpdateTask(this).execute(db, table, values, whereClause,whereArgs);
}
public void replace(String table, String nullColumnHack,ContentValues values)
{
buildReplaceTask(this).execute(db, table, nullColumnHack, values);
}
public void delete(String table, String whereClause,String[] whereArgs)
{
buildDeleteTask(this).execute(db, table, whereClause, whereArgs);
}
public void execSQL(String sql, Object[] bindArgs)
{
buildExecSQLTask(this).execute(db, sql, bindArgs);
}
protected ContentChangingTask buildInsertTask(SQLLiteCursorLoader loader)
{
return(new InsertTask(loader));
}
protected ContentChangingTask buildUpdateTask(SQLLiteCursorLoader loader)
{
return(new UpdateTask(loader));
}
protected ContentChangingTask buildReplaceTask(SQLLiteCursorLoader loader)
{
return(new ReplaceTask(loader));
}
protected ContentChangingTask buildDeleteTask(SQLLiteCursorLoader loader)
{
return(new DeleteTask(loader));
}
protected ContentChangingTask buildExecSQLTask(SQLLiteCursorLoader loader)
{
return(new ExecSQLTask(loader));
}
protected static class InsertTask extends ContentChangingTask
{
InsertTask(SQLLiteCursorLoader loader)
{
super(loader);
}
@Override
protected Void doInBackground(Object... params) {
SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
String table=(String)params[1];
String nullColumnHack=(String)params[2];
ContentValues values=(ContentValues)params[3];
db.getWritableDatabase().insert(table, nullColumnHack, values);
return(null);
}
}
protected static class UpdateTask extends ContentChangingTask
{
UpdateTask(SQLLiteCursorLoader loader)
{
super(loader);
}
@Override
protected Void doInBackground(Object... params) {
SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
String table=(String)params[1];
ContentValues values=(ContentValues)params[2];
String where=(String)params[3];
String[] whereParams=(String[])params[4];
db.getWritableDatabase().update(table, values, where, whereParams);
return(null);
}
}
protected static class ReplaceTask extends ContentChangingTask
{
ReplaceTask(SQLLiteCursorLoader loader)
{
super(loader);
}
@Override
protected Void doInBackground(Object... params) {
SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
String table=(String)params[1];
String nullColumnHack=(String)params[2];
ContentValues values=(ContentValues)params[3];
db.getWritableDatabase().replace(table, nullColumnHack, values);
return(null);
}
}
protected static class DeleteTask extends ContentChangingTask
{
DeleteTask(SQLLiteCursorLoader loader)
{
super(loader);
}
@Override
protected Void doInBackground(Object... params) {
SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
String table=(String)params[1];
String where=(String)params[2];
String[] whereParams=(String[])params[3];
db.getWritableDatabase().delete(table, where, whereParams);
return(null);
}
}
protected static class ExecSQLTask extends ContentChangingTask
{
ExecSQLTask(SQLLiteCursorLoader loader)
{
super(loader);
}
@Override
protected Void doInBackground(Object... params) {
SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
String sql=(String)params[1];
Object[] bindParams=(Object[])params[2];
db.getWritableDatabase().execSQL(sql, bindParams);
return(null);
}
}
}我将不包括布局XML,正如我前面说过的,如果我直接从适配器填充并且不使用加载程序,它就能工作。
发布于 2016-03-08 16:02:20
从CursorLoader API,
公共CursorLoader (上下文) 在API 11中添加创建一个空的、未指定的CursorLoader。您必须遵循setSelection(String),,然后调用setUri(Uri)、等来指定要执行的查询。
因此,在自定义SQLLiteCursorLoader类中,必须在super(context);之后显式调用构造函数中的super(context);。
更新
看看你在下面的评论中提到的github项目,我发现了一个不同之处。
你的
public class SQLLiteCursorLoader extends CursorLoader 而github的
public class SQLiteCursorLoader extends AbstractCursorLoader你猜怎么着,CursorLoader有一个方法(链接)
/* Runs on a worker thread */
@Override
public Cursor loadInBackground() {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder);
if (cursor != null) {
// Ensure the cursor window is filled
cursor.getCount();
registerContentObserver(cursor, mObserver);
}
return cursor;
}它使用mUri,这就是它不能为null的原因,而AbstractCursorLoader (链接)中的等效方法是:
@Override
public Cursor loadInBackground() {
Cursor cursor=buildCursor();
if (cursor!=null) {
// Ensure the cursor window is filled
cursor.getCount();
}
return(cursor);
}因此,如果您更正SQLLiteCursorLoader类中的继承(使其扩展AbstractCursorLoader),您最终将解决问题。
https://stackoverflow.com/questions/35871133
复制相似问题