自己进程中的自定义 ContentProvider 并不总是可用
Custom ContentProvider in own process not always available
我正在开发一个具有 3 个主要进程的 android 应用程序。
主要UI、U在自己的进程中运行。
内容提供者,C,运行在自己的进程中
还有一个服务,S,运行在另一个进程中。
U和S都通过内容提供者C[=访问包含大约6个表的sqlite数据库59=].
我遇到的问题是 C 并不总是可用,这有时会导致某些查询失败。
我不得不做一个 hack 来查询 ContentProvider
、C,并检查在 ContentProvider's
中设置的布尔变量SQLiteOpenHelper
当 C 的 onCreate 被调用并且其 SQLiteOpenHelper
也已完全初始化并准备好被 使用时变为真C 查询。
这很好用,但压力太大,我无法想象将它应用于 U 和 S 中的所有点,其中C 被访问。
How can I ensure that C is always up for U and S to use?
especially as it seems as if C can be up now and down later on?
请注意
ContentProvider 在实际启动和关闭时工作正常,不会抛出任何异常。
这是 C
的清单条目
<provider
android:name=".data.cprov.Provider"
android:authorities="mynet.app.data.cprov.Provider"
android:enabled="true"
android:exported="false"
android:process=":someproc"
android:permission="mynet.app.data.PERMISSION"
/>
谢谢!
好吧,经过一些实验,我发现 Android OS 可能已经连接到杀死内容提供者,如果它有有一段时间没用了。我相信几天前我在 Whosebug 的某处读到过类似的内容。
为了让内容提供者在我的场景中按需可用,
我做了以下。
首先,我在 ContentProvider's
SQLiteOpenHelper
中包含了一个名为 initializationRunning
的静态布尔变量,一旦 SQLiteOpenHelper 被 ContentProvider
完全初始化,它就被设置为 false .
这确保 ContentProvider
及其 SQLiteOpenHelper
都已做好充分准备。
然后我创建了一个额外的 URI,使我的其他进程可以访问此变量 (initializationRunning
)。
我return这个变量通过一个MatrixCursor
或者其他合适的Cursor
在 ContentProvider's
query
方法中。
public class ChatsProvider extends ContentProvider {
public static final String AUTHORITY = "mynet.app.data.cprov.Provider;
private static final String CHATS_TABLE = "CHATS";
private static final String CONTACTS_TABLE = "CONTACTS;
private static final String UPDATES_TABLE = "UPDATES";
private static final String UPLOADED_CONTACTS_TABLE = "UPLOADED_CONTACTS";
private static final String PRODUCTS_TABLE = "PRODUCTS";
private static final String ROOMS_TABLE = "ROOMS";
private static final String MOCK_TABLE = "MOCK";
public static final String MOCK_COLUMN = "MOCK_COLUMN";
public static final Uri CONTENT_URI_CHATS =
Uri.parse("content://" + AUTHORITY + "/" + CHATS_TABLE);
public static final Uri CONTENT_URI_CONTACTS =
Uri.parse("content://" + AUTHORITY + "/" + CONTACTS_TABLE);
public static final Uri CONTENT_URI_UPDATES =
Uri.parse("content://" + AUTHORITY + "/" + UPDATES_TABLE);
public static final Uri CONTENT_URI_UPLOADED_CONTACTS =
Uri.parse("content://" + AUTHORITY + "/" + UPLOADED_CONTACTS_TABLE);
public static final Uri CONTENT_URI_PRODUCTS =
Uri.parse("content://" + AUTHORITY + "/" + PRODUCTS_TABLE);
public static final Uri CONTENT_URI_ROOMS =
Uri.parse("content://" + AUTHORITY + "/" + ROOMS_TABLE);
public static final Uri CONTENT_URI_MOCK =
Uri.parse("content://" + AUTHORITY + "/" + MOCK_TABLE);
public static final int CHATS = 1;
public static final int CHATS_ID = 100;
public static final int CONTACTS = 101;
public static final int CONTACTS_ID = 200;
public static final int UPDATES = 201;
public static final int UPDATES_ID = 300;
public static final int UPLOADED_CONTACTS = 301;
public static final int UPLOADED_CONTACTS_ID = 400;
public static final int PRODUCTS = 401;
public static final int PRODUCTS_ID = 500;
public static final int ROOMS = 501;
public static final int ROOMS_ID = 600;
public static final int MOCK = 601;
public static final int MOCK_ID = 700;
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* My SQLiteOpenHelper
*/
public static DatabaseUtils databaseUtils;
static {
uriMatcher.addURI(AUTHORITY, CHATS_TABLE, CHATS);
uriMatcher.addURI(AUTHORITY, CHATS_TABLE + "/#",
CHATS_ID);
uriMatcher.addURI(AUTHORITY, CONTACTS_TABLE, CONTACTS);
uriMatcher.addURI(AUTHORITY, CONTACTS_TABLE + "/#",
CONTACTS_ID);
uriMatcher.addURI(AUTHORITY, UPDATES_TABLE, UPDATES);
uriMatcher.addURI(AUTHORITY, UPDATES_TABLE + "/#",
UPDATES_ID);
uriMatcher.addURI(AUTHORITY, UPLOADED_CONTACTS_TABLE, UPLOADED_CONTACTS);
uriMatcher.addURI(AUTHORITY, UPLOADED_CONTACTS_TABLE + "/#",
UPLOADED_CONTACTS_ID);
uriMatcher.addURI(AUTHORITY, PRODUCTS_TABLE, PRODUCTS);
uriMatcher.addURI(AUTHORITY, PRODUCTS_TABLE + "/#",
PRODUCTS_ID);
uriMatcher.addURI(AUTHORITY, ROOMS_TABLE, ROOMS);
uriMatcher.addURI(AUTHORITY, ROOMS_TABLE + "/#",
ROOMS_ID);
uriMatcher.addURI(AUTHORITY, MOCK_TABLE, MOCK);
uriMatcher.addURI(AUTHORITY, MOCK_TABLE + "/#",
MOCK_ID);
}
public ChatsProvider() {
}
@Override
public synchronized boolean onCreate() {
Utils.logErrorMessage("ContentProvider-"+this+" is up and running!", getClass());
if(databaseUtils == null){
databaseUtils = DatabaseUtils.getInstance(this.getContext());
}
return false;
}
@Override
public synchronized Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int uriType = uriMatcher.match(uri);
String table = null;
switch (uriType) {
case CHATS:
table = CHATS_TABLE;
break;
case CONTACTS:
table = CONTACTS_TABLE;
break;
case UPDATES:
table = UPDATES_TABLE;
break;
case UPLOADED_CONTACTS:
table = UPLOADED_CONTACTS_TABLE;
break;
case PRODUCTS:
table = PRODUCTS_TABLE;
break;
case ROOMS:
table = ROOMS_TABLE;
break;
case MOCK:
table = MOCK_TABLE;
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if(table.equals(MOCK_TABLE)){
String[] value = { DatabaseUtils.initializationRunning + "" };
String[] mockProjection = new String[]{"MOCK_COLUMN"};
MatrixCursor c = new MatrixCursor(mockProjection);
c.addRow(value);
return c;
}
try {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(table);
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(AppController.contentResolver,
uri);
return cursor;
}
catch (Exception e){
return null;
}
}
}
现在,在我的应用程序单例实例中,我检测当前 运行 进程是我的服务进程还是我的 UI 进程。
对于这两种情况,我都会这样做:
int count = 0;
while( !checkContentProviderReady() ){++count;}
这具有 "rousing" ContentProvider
睡眠的效果,或者更确切地说,它鼓励 Android OS创建 ContentProvider 或其他东西。
方法,checkContentProviderReady()
只是一个查询 ContentProvider
的 initializationRunning
变量以知道它何时为假的方法。
紧接着,我在 Application class
中启动一个线程(用于 Service's
进程和每 5 秒调用一次 checkContentProviderReady()
方法的主 UI's
进程.
这会强制 Android OS 相信 ContentProvider
已被定期使用,因此它仍然存在。
好吧,我不知道这是否对您的应用有帮助,但它确实对我有帮助。
总结这个?
在您的 ContentProvider
中创建一个非昂贵的方法,并在您的客户端中有一个定时方法定期调用它(不要太快!)。
在我的例子中,因为我需要知道数据库助手已经准备好,所以我还在定时方法调用中 return 编辑了数据库助手的状态。
谢谢!
我正在开发一个具有 3 个主要进程的 android 应用程序。
主要UI、U在自己的进程中运行。
内容提供者,C,运行在自己的进程中
还有一个服务,S,运行在另一个进程中。
U和S都通过内容提供者C[=访问包含大约6个表的sqlite数据库59=].
我遇到的问题是 C 并不总是可用,这有时会导致某些查询失败。
我不得不做一个 hack 来查询 ContentProvider
、C,并检查在 ContentProvider's
中设置的布尔变量SQLiteOpenHelper
当 C 的 onCreate 被调用并且其 SQLiteOpenHelper
也已完全初始化并准备好被 使用时变为真C 查询。
这很好用,但压力太大,我无法想象将它应用于 U 和 S 中的所有点,其中C 被访问。
How can I ensure that C is always up for U and S to use? especially as it seems as if C can be up now and down later on?
请注意
ContentProvider 在实际启动和关闭时工作正常,不会抛出任何异常。
这是 C
的清单条目 <provider
android:name=".data.cprov.Provider"
android:authorities="mynet.app.data.cprov.Provider"
android:enabled="true"
android:exported="false"
android:process=":someproc"
android:permission="mynet.app.data.PERMISSION"
/>
谢谢!
好吧,经过一些实验,我发现 Android OS 可能已经连接到杀死内容提供者,如果它有有一段时间没用了。我相信几天前我在 Whosebug 的某处读到过类似的内容。
为了让内容提供者在我的场景中按需可用, 我做了以下。
首先,我在 ContentProvider's
SQLiteOpenHelper
中包含了一个名为 initializationRunning
的静态布尔变量,一旦 SQLiteOpenHelper 被 ContentProvider
完全初始化,它就被设置为 false .
这确保 ContentProvider
及其 SQLiteOpenHelper
都已做好充分准备。
然后我创建了一个额外的 URI,使我的其他进程可以访问此变量 (initializationRunning
)。
我return这个变量通过一个MatrixCursor
或者其他合适的Cursor
在 ContentProvider's
query
方法中。
public class ChatsProvider extends ContentProvider {
public static final String AUTHORITY = "mynet.app.data.cprov.Provider;
private static final String CHATS_TABLE = "CHATS";
private static final String CONTACTS_TABLE = "CONTACTS;
private static final String UPDATES_TABLE = "UPDATES";
private static final String UPLOADED_CONTACTS_TABLE = "UPLOADED_CONTACTS";
private static final String PRODUCTS_TABLE = "PRODUCTS";
private static final String ROOMS_TABLE = "ROOMS";
private static final String MOCK_TABLE = "MOCK";
public static final String MOCK_COLUMN = "MOCK_COLUMN";
public static final Uri CONTENT_URI_CHATS =
Uri.parse("content://" + AUTHORITY + "/" + CHATS_TABLE);
public static final Uri CONTENT_URI_CONTACTS =
Uri.parse("content://" + AUTHORITY + "/" + CONTACTS_TABLE);
public static final Uri CONTENT_URI_UPDATES =
Uri.parse("content://" + AUTHORITY + "/" + UPDATES_TABLE);
public static final Uri CONTENT_URI_UPLOADED_CONTACTS =
Uri.parse("content://" + AUTHORITY + "/" + UPLOADED_CONTACTS_TABLE);
public static final Uri CONTENT_URI_PRODUCTS =
Uri.parse("content://" + AUTHORITY + "/" + PRODUCTS_TABLE);
public static final Uri CONTENT_URI_ROOMS =
Uri.parse("content://" + AUTHORITY + "/" + ROOMS_TABLE);
public static final Uri CONTENT_URI_MOCK =
Uri.parse("content://" + AUTHORITY + "/" + MOCK_TABLE);
public static final int CHATS = 1;
public static final int CHATS_ID = 100;
public static final int CONTACTS = 101;
public static final int CONTACTS_ID = 200;
public static final int UPDATES = 201;
public static final int UPDATES_ID = 300;
public static final int UPLOADED_CONTACTS = 301;
public static final int UPLOADED_CONTACTS_ID = 400;
public static final int PRODUCTS = 401;
public static final int PRODUCTS_ID = 500;
public static final int ROOMS = 501;
public static final int ROOMS_ID = 600;
public static final int MOCK = 601;
public static final int MOCK_ID = 700;
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* My SQLiteOpenHelper
*/
public static DatabaseUtils databaseUtils;
static {
uriMatcher.addURI(AUTHORITY, CHATS_TABLE, CHATS);
uriMatcher.addURI(AUTHORITY, CHATS_TABLE + "/#",
CHATS_ID);
uriMatcher.addURI(AUTHORITY, CONTACTS_TABLE, CONTACTS);
uriMatcher.addURI(AUTHORITY, CONTACTS_TABLE + "/#",
CONTACTS_ID);
uriMatcher.addURI(AUTHORITY, UPDATES_TABLE, UPDATES);
uriMatcher.addURI(AUTHORITY, UPDATES_TABLE + "/#",
UPDATES_ID);
uriMatcher.addURI(AUTHORITY, UPLOADED_CONTACTS_TABLE, UPLOADED_CONTACTS);
uriMatcher.addURI(AUTHORITY, UPLOADED_CONTACTS_TABLE + "/#",
UPLOADED_CONTACTS_ID);
uriMatcher.addURI(AUTHORITY, PRODUCTS_TABLE, PRODUCTS);
uriMatcher.addURI(AUTHORITY, PRODUCTS_TABLE + "/#",
PRODUCTS_ID);
uriMatcher.addURI(AUTHORITY, ROOMS_TABLE, ROOMS);
uriMatcher.addURI(AUTHORITY, ROOMS_TABLE + "/#",
ROOMS_ID);
uriMatcher.addURI(AUTHORITY, MOCK_TABLE, MOCK);
uriMatcher.addURI(AUTHORITY, MOCK_TABLE + "/#",
MOCK_ID);
}
public ChatsProvider() {
}
@Override
public synchronized boolean onCreate() {
Utils.logErrorMessage("ContentProvider-"+this+" is up and running!", getClass());
if(databaseUtils == null){
databaseUtils = DatabaseUtils.getInstance(this.getContext());
}
return false;
}
@Override
public synchronized Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int uriType = uriMatcher.match(uri);
String table = null;
switch (uriType) {
case CHATS:
table = CHATS_TABLE;
break;
case CONTACTS:
table = CONTACTS_TABLE;
break;
case UPDATES:
table = UPDATES_TABLE;
break;
case UPLOADED_CONTACTS:
table = UPLOADED_CONTACTS_TABLE;
break;
case PRODUCTS:
table = PRODUCTS_TABLE;
break;
case ROOMS:
table = ROOMS_TABLE;
break;
case MOCK:
table = MOCK_TABLE;
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if(table.equals(MOCK_TABLE)){
String[] value = { DatabaseUtils.initializationRunning + "" };
String[] mockProjection = new String[]{"MOCK_COLUMN"};
MatrixCursor c = new MatrixCursor(mockProjection);
c.addRow(value);
return c;
}
try {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(table);
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(AppController.contentResolver,
uri);
return cursor;
}
catch (Exception e){
return null;
}
}
}
现在,在我的应用程序单例实例中,我检测当前 运行 进程是我的服务进程还是我的 UI 进程。
对于这两种情况,我都会这样做:
int count = 0;
while( !checkContentProviderReady() ){++count;}
这具有 "rousing" ContentProvider
睡眠的效果,或者更确切地说,它鼓励 Android OS创建 ContentProvider 或其他东西。
方法,checkContentProviderReady()
只是一个查询 ContentProvider
的 initializationRunning
变量以知道它何时为假的方法。
紧接着,我在 Application class
中启动一个线程(用于 Service's
进程和每 5 秒调用一次 checkContentProviderReady()
方法的主 UI's
进程.
这会强制 Android OS 相信 ContentProvider
已被定期使用,因此它仍然存在。
好吧,我不知道这是否对您的应用有帮助,但它确实对我有帮助。
总结这个?
在您的 ContentProvider
中创建一个非昂贵的方法,并在您的客户端中有一个定时方法定期调用它(不要太快!)。
在我的例子中,因为我需要知道数据库助手已经准备好,所以我还在定时方法调用中 return 编辑了数据库助手的状态。
谢谢!