You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:PlaceAutocompleteAdapter主线程外调用及AsyncTask实现咨询

Fixing PlaceAutocompleteAdapter with AsyncTask

Hey there! Let's sort out how to wrap that Google Places API call in an AsyncTask so you avoid that "must be called off main thread" error. I've been in your shoes before—getting that error when you're just starting out with Places API can be confusing, but this fix is straightforward.

First, let's break down the issue: the line in question (193) calls Places.GeoDataApi.getAutocompletePredictions(), which makes a network request. Network operations block the thread they're on, so Android forces us to run them off the main UI thread to keep the app responsive and prevent ANRs (Application Not Responding) crashes.

Here's how to modify your PlaceAutocompleteAdapter to use AsyncTask properly:

Step 1: Add an AsyncTask inner class to your Adapter

Inside your PlaceAutocompleteAdapter class, create a private inner AsyncTask that handles fetching predictions in the background. This task will take the search query as input, run the API call, and return the list of predictions to update the UI.

private class FetchPredictionsTask extends AsyncTask<CharSequence, Void, List<AutocompletePrediction>> {

    @Override
    protected List<AutocompletePrediction> doInBackground(CharSequence... params) {
        // Exit early if there's no search query
        if (params.length == 0 || params[0].length() == 0) {
            return null;
        }

        // Run the Places API call off the main thread
        PendingResult<AutocompletePredictionBuffer> results =
                Places.GeoDataApi.getAutocompletePredictions(
                        mGoogleApiClient,
                        params[0].toString(),
                        mBounds,
                        mPlaceFilter);

        // Wait for results with a timeout (adjust the 60-second value as needed)
        AutocompletePredictionBuffer buffer = results.await(60, TimeUnit.SECONDS);

        // Handle API errors
        com.google.android.gms.common.api.Status status = buffer.getStatus();
        if (!status.isSuccess()) {
            buffer.release();
            Log.e(TAG, "Error fetching predictions: " + status.toString());
            return null;
        }

        // Convert the buffer to a usable list and release resources to avoid leaks
        return buffer.copyToList(new ArrayList<AutocompletePrediction>());
    }

    @Override
    protected void onPostExecute(List<AutocompletePrediction> predictions) {
        // Update the adapter's data source and notify the UI to refresh
        mResultList = predictions;
        notifyDataSetChanged();
    }
}

Step 2: Modify filtering logic to use the AsyncTask

First, add a member variable to track the current running task—this prevents outdated predictions from popping up after the user types a new query:

private FetchPredictionsTask mCurrentTask;

Then update your performFiltering method (part of the Filterable interface) to cancel any existing tasks and start a new one:

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    // Cancel any ongoing task to avoid stale results
    if (mCurrentTask != null) {
        mCurrentTask.cancel(true);
    }

    // Start a new task to fetch predictions for the current query
    mCurrentTask = new FetchPredictionsTask();
    mCurrentTask.execute(constraint);

    // Return an empty temporary result (the actual data will come via onPostExecute)
    FilterResults results = new FilterResults();
    results.values = mResultList;
    results.count = mResultList != null ? mResultList.size() : 0;
    return results;
}

Step 3: Clean up tasks to avoid memory leaks

Add a method to your adapter to cancel the task when it's no longer needed (like when the parent activity/fragment is destroyed):

public void cancelCurrentTask() {
    if (mCurrentTask != null) {
        mCurrentTask.cancel(true);
        mCurrentTask = null;
    }
}

Then call this method from your activity's onDestroy() or fragment's onDestroyView():

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mAutocompleteAdapter != null) {
        mAutocompleteAdapter.cancelCurrentTask();
    }
}

Key Tips to Remember:

  • Always cancel previous tasks before starting a new one—this ensures the user sees predictions for their latest query, not an old one.
  • Never forget to release the AutocompletePredictionBuffer after copying results—this is critical to avoid memory leaks.
  • The await() timeout prevents your app from hanging indefinitely if the API call takes too long.

That should eliminate the thread error and keep your place autocomplete feature running smoothly!

内容的提问来源于stack exchange,提问作者Matej Košút

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:35:36