美文网首页Android开发程序员Android 技术开发
【Android】自定义ContentProvider时踩的一个

【Android】自定义ContentProvider时踩的一个

作者: blink_dagger | 来源:发表于2016-11-29 08:51 被阅读0次

最近突然要开发一个收集手机中所有应用信息,然后分别做权限处理的第三方应用,而我很不巧地负责数据库这一块(几乎全忘记了)。所以只能恶补了半天之后开始coding,一开始很完美,自己试着运行了几遍后就很自信地提交给测试mm了,然后很顺利地过了测试。把软件释放给pm之后,报了一个java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.的错误,然后就是悲哀地被领导邮件轰炸!所以这里总结一下ContentProvider的简单使用!

1.继承ContentProvider,并实现增删改查、创建、更新等相关方法

有的时候数据存入后,没有及时更新的话,就需要我们手动地getContentResolver().notifyChange去通知provider更新。这部分代码几乎都一样,大致如:

    @Override
    public int delete(Uri uri, String s, String[] as) {
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentvalues) {
        sqlDB = dbHelper.getWritableDatabase();
        long rowId = sqlDB.insert(TABLE_NAME, "", contentvalues);
        if (rowId > 0) {
            Uri rowUri = ContentUris.appendId(
                    MyInfo.Info.CONTENT_URI.buildUpon(), rowId).build();
            getContext().getContentResolver().notifyChange(rowUri, null);
            return rowUri;
        }
        throw new SQLException("Failed to insert row into " + uri);
    }

    @Override
    public boolean onCreate() {
        dbHelper = new DatabaseHelper(getContext());
        return (dbHelper == null) ? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        qb.setTables(TABLE_NAME);
        Cursor c = qb.query(db, projection, selection, null, null, null,
                sortOrder);
        //c.setNotificationUri(getContext().getContentResolver(), uri);     
        return c;
    }

    @Override
    public int update(Uri uri, ContentValues contentvalues, String s,
            String[] as) {
        SQLiteDatabase db=dbHelper.getReadableDatabase();
        int num = db.update(TABLE_NAME, contentvalues,s, as);       
        db.close();
        return num;
    }
2.将创建数据库的工作托付给SQLiteOpenHelper

在自定义Provider的onCreate中,我们直接new一个数据库就可以了,然后增删改查时,工作就能都交给db来做了。这里我直接写了一个内部SQLiteOpenHelper类:

    private static class DatabaseHelper extends SQLiteOpenHelper {
        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            // 创建用于存储数据的表
            db.execSQL("Create table "
                    + TABLE_NAME
                    + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, info_package TEXT NOT NULL,info_name TEXT NOT NULL,info_states INTEGER NOT                      NULL);");
        }
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }
3.对外暴露数据库的操作API

为了避免增删改查异步执行,导致数据的混乱,所以这里肯定要全部synchronized,然后外部直部直接传入相关的info就可以了。这里我直接写了一个util类来封装,栗子:

public class DataUtils {
    private Context mContext;

    public DataUtils(Context context) {
        super();
        this.mContext = context;
    }
    private synchronized boolean isContain(Info info) {

    }
    private synchronized void upDateRecord(Info info) {
    
    }
    private synchronized int getState(Info info) {

    }
    private synchronized void insertRecord(Info info) {

    }

    public void saveData(Info info) {
        if(isContain(info)){
            upDateRecord(info);
        }else{
            insertRecord(info);
        }
    }

    public int getData(Info info) {
        int state = 0;
        if(isContain(info)){
            state = getState(info);
        }
        return state;
    }
    
}

--*
--*

接着在清单文件中注册下,就能直接使用了,我是直接在Activity resume的时候起线程去进行数据库的操作,这里就不贴具体代码了。自己运行时候功能一切正常,可是到了pm那边经过反人类的测试,一下子就发现毛病了。当我们的Activity还没完全resume完成的时候,如果直接退出该界面,就报java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.的错误了。

一开始,我觉得是线程和synchronized是不是哪里出了问题,把代码一个一个注释掉了还是有错。只能科学上网查了下,发现可能是db的连接断开了,所以对它进行操作就报非法状态错误了。同时在官网文档中也发现了吻合的解释:
IllegalStateException
if the last reference to the object has already been released.

void close ()
Releases a reference to the object, closing the object if the last reference was released. Calling this method is equivalent to calling

看样子是我哪里把数据库给手动close掉了,一找才发现是自己手贱,习惯性地在使用完db就释放了:

    @Override
    public int update(Uri uri, ContentValues contentvalues, String s,
            String[] as) {
        SQLiteDatabase db=dbHelper.getReadableDatabase();
        int num = db.update(TABLE_NAME, contentvalues,s, as);       
        db.close();
        return num;
    }

把close给注释掉了,就没报上述错误了,看来是在ContentProvider中,我们只能关闭cursor,而不能手动关闭db。瞎猜想可能是为了节省再次访问或者更新时打开db的内存开销,反正一个应用一般只对应一个db,不关闭也不会有太多的问题。

相关文章

网友评论

    本文标题:【Android】自定义ContentProvider时踩的一个

    本文链接:https://www.haomeiwen.com/subject/wegopttx.html