【问题标题】:How to do a query with Cursor inside a thread? (android)如何在线程内使用 Cursor 进行查询? (安卓)
【发布时间】:2015-09-05 05:59:40
【问题描述】:

我最近启动了我的第一个大学应用程序,作为我应用程序的一部分,我想使用this guide. 访问手机的联系人

在指南中,onActivityResult 如下所示:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request it is that we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // Get the URI that points to the selected contact
            Uri contactUri = data.getData();
            // We only need the NUMBER column, because there will be only one row in the result
            String[] projection = {Phone.NUMBER};

            // Perform the query on the contact to get the NUMBER column
            // We don't need a selection or sort order (there's only one result for the given URI)
            // CAUTION: The query() method should be called from a separate thread to avoid blocking
            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
            // Consider using CursorLoader to perform the query.
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();

            // Retrieve the phone number from the NUMBER column
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);

            // Do something with the phone number...
        }
    }
}

它说我应该使用线程或 CursorLoader 进行查询,但到目前为止我无法找到一个好的解决方案。如果我将查询方法放在一个线程中,那么我无法从中访问数据:

    Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        Cursor cursor = getContentResolver()
                                .query(contactUri, projection, null, null, null);
                    }
                };
                Thread queryThread = new Thread(r);
                queryThread.start();
cursor.moveToFirst();
int column = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

所以在这段代码中,Android Studio 无法解析符号“光标”:( 到目前为止,我还没有找到我理解的关于如何使用 CursorLoaders 执行此操作的指南。

【问题讨论】:

  • 顺便说一句,我实际上尝试了指南中的代码,它对我来说很好,但这可能是因为我的手机上没有很多联系人...... ^ ^

标签: android android-cursor android-cursorloader


【解决方案1】:

您似乎不明白启动一个单独的线程意味着什么。在您的示例中,游标不仅超出范围,而且还被异步初始化,这意味着在您调用 start() 后它不会立即可用。 start() 总是立即完成,只是导致线程在一段时间后执行,而不会阻塞主线程。

在 android 中,对于大多数用例来说,使用 AsyncTask 而不是手动创建单独的线程是最方便的。请参阅Processes and Threads guide 了解更多信息。

【讨论】:

    【解决方案2】:

    您可以使用AsyncTask 来完成这项工作!

         class WorkCursor extends AsyncTask<Cursor,Object,String> {
    
                String[] projection;
                Uri contactUri;
    
                public WorkCursor(String[] projection,Uri contactUri){
                    this.contactUri = contactUri;
                    this.projection = projection;
                }
    
    
                @Override
                protected String doInBackground(Cursor... cursors) {
    
                    //This is done in the background
    
                    Cursor cursor = MyActivity.this.getContentResolver()
                            .query(contactUri, projection, null, null, null);
    
                    int column = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
                    String number = cursor.getString(column);
    
                    return number;
                }
    
                @Override
                protected void onPostExecute(String number) {
                    super.onPostExecute(number);
    
                    //This is done on the UI thread
                    functionCall(number);
    
                }
            }
    
            public void functionCall(String number){
                //This is the UI thread
                //You can do whatever you with your number
                Toast.makeText(this,"This is the number: "+number,Toast.LENGTH_SHORT).show();
            }
    

    然后像这样调用Asynctask:

    new WorkCursor(projection,contactUri).execute();
    

    另一种方法是像你一样做,但在线程内完成所有工作,然后在 UI 线程上运行结果,如下所示:

    new Thread(new Runnable() {
                @Override
                public void run() {
                    Cursor cursor = getContentResolver()
                            .query(contactUri, projection, null, null, null);
    
                    cursor.moveToFirst();
                    int column = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
                    final String number = cursor.getString(column);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            functionCall(number);
                        }
                    });
                }
            }).run();
    

    字符串编号必须是最终的,才能在另一个(可在 UI 上运行)线程中访问!

    免责声明:代码未经测试。

    【讨论】:

    • 我尝试了你的第二个解决方案,我知道contactUri也应该是最终的:O(错误:(84、40)错误:从内部类中访问局部变量contactUri;需要声明final( 到目前为止,我在实现 WorkCursor 类时也遇到了麻烦,由于某种原因,getContentResolver、contactUri 和投影没有得到解决。
    • 哦,对了。是的,必须是,您从另一个线程访问的任何内容都必须声明为 final。只要你不改变它的价值,你就可以这样做。投影变量也应该声明为 final。应该没问题,因为您要声明不会为此目的而改变的变量。试一试。
    • 那是因为这些变量不在同一个范围内,代码不完整。您需要传递变量,例如在构造函数中并且 getContentResolver 不接受 Asynctask。我会编辑答案。
    • 您的第二个解决方案适用于最终的 contactUri 和投影声明,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-31
    • 2012-11-26
    • 2012-01-07
    相关资源
    最近更新 更多