最近突然要开发一个收集手机中所有应用信息,然后分别做权限处理的第三方应用,而我很不巧地负责数据库这一块(几乎全忘记了)。所以只能恶补了半天之后开始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,不关闭也不会有太多的问题。
网友评论