【问题标题】:Custom ContentProvider in own process not always available自己进程中的自定义 ContentProvider 并不总是可用
【发布时间】:2016-09-22 21:58:06
【问题描述】:

我正在开发一个具有 3 个主要进程的 android 应用程序。

主 UI U 在自己的进程中运行。

一个内容提供者,C,在自己的进程中运行

还有一个服务,S,在另一个进程中运行。

US 都通过内容提供程序 C 访问包含大约 6 个表的 sqlite 数据库。

我遇到的问题是 C 并不总是可用,这有时会导致某些查询失败。

我不得不做一个 hack,对 ContentProviderC 进行查询,并检查在 ContentProvider's SQLiteOpenHelper 中设置的布尔变量,当 C 的 onCreate 已被调用,其 SQLiteOpenHelper 也已完全初始化,可供 C 用于查询。

这很好用,但压力很大,我无法想象将它应用于 US 中的所有点,其中 C被访问。

如何确保 C 始终可供 US 使用? 尤其是似乎 C 现在可以上升,以后可以下降?

请注意

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"
                />

谢谢!

【问题讨论】:

    标签: java android sqlite android-contentprovider


    【解决方案1】:

    好的,所以经过一些实验后,我发现 Android 操作系统 可能会在一段时间内不使用内容提供者时终止它。我相信几天前我在 StackOverFlow 的某个地方读到过类似的内容。

    为了根据我的场景要求按需提供内容提供者, 我做了以下。

    首先,我在ContentProvider's SQLiteOpenHelper 中包含了一个名为initializationRunning 的静态布尔变量,一旦SQLiteOpenHelper 被ContentProvider 完全初始化,它就会设置为false。

    这可确保ContentProvider 及其SQLiteOpenHelper 都已做好充分准备以供使用。

    然后我创建了一个额外的 URI,使我的其他进程可以访问这个变量 (initializationRunning)。

    我通过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;
            }
    
        }
    
    }
    

    现在,在我的 Application 单例实例中,我检测当前正在运行的进程是我的 Service 的进程还是我的 UI 的进程。

    对于这两种情况,我都会这样做:

    int count = 0;
    while(  !checkContentProviderReady() ){++count;}
    

    这具有将ContentProvider 从睡眠中“唤醒”的效果,或者更确切地说,它鼓励 Android 操作系统 创建 ContentProvider 或其他东西。

    checkContentProviderReady() 方法只是一个在ContentProvider 中查询initializationRunning 变量以知道它何时为假的方法。

    紧接着,我在Application class 中启动一个线程(对于Service's 进程和主UI's 进程,它每5 秒调用一次checkContentProviderReady() 方法。

    这会迫使 Android 操作系统相信 ContentProvider 已被定期使用,因此它仍然存在。

    好吧,我不知道这是否会对您的应用有所帮助,但它确实对我的应用有所帮助。

    总结?

    在您的 ContentProvider 中创建一个不昂贵的方法,并在您的客户端中使用一个定时方法定期调用它(不要太快!)。

    在我的例子中,因为我需要知道数据库助手已经准备好,所以我还在定时方法调用中返回了数据库助手的状态。

    谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      • 2013-05-29
      相关资源
      最近更新 更多