How to do a query with Cursor inside a thread? (android)

2.5k views Asked by At

I recently started my first app for college, and as part of my app I want to acces the contacts of the phone using this guide.

In the guide the onActivityResult looks like this:

@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...
        }
    }
}

It says i should use a thread or CursorLoader to do the query but so far I was unable to find a good solution for this. If I place the query method in a thread then I can't acces the data from it:

    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);

So in this code Android Studio cannot resolve the symbol "cursor" :( And so far I could not find a guide I understand on how to do this with CursorLoaders.

2

There are 2 answers

4
Rui Santos On BEST ANSWER

You can use an AsyncTask for the job!

     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();
        }

And then call the Asynctask like so:

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

One other way is to do like you did but do all the work inside the thread and then run the result on the UI thread like so:

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();

String number must be final in order to be accessible inside the other (runnable on UI) thread!

Disclaimer: Code is untested.

0
wvdz On

You don't seem to understand what it means to start a separate thread. Not only is cursor out of scope in your example, it is also initialized asynchronously, which means that it won't be available right after you call start(). start() always completes immediately and just causes the thread to be executed some time later, without blocking the main thread.

In android, for most usecases it's most convenient to use AsyncTask instead of manually creating a separate thread. See the Processes and Threads guide for more information.