在后台加载数据,更新适配器?

时间:2017-01-19 22:03:52

标签: java android android-asynctask android-adapter

我使用ArrayAdapter显示项目列表,我有一个AsyncTask线程,通过HttpURLConnection加载这些项目。我希望这个项目列表是用户在启动应用程序时看到的第一件事。我知道让应用程序等待启动以完成此负载是不好的做法,这就是为什么我使负载成为AsyncTask的原因。我有本地数据存储在SharedPreferences中,同时显示。

不幸的是,我无法直接从AsyncTask中修改适配器。

java.lang.IllegalStateException: Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.

我想我只需要一个信号来通知UI线程后台任务已完成;我可以修改我的代码以将数据加载到另一个不是适配器的数据结构中,然后UI线程可以在知道任务完成后将其添加到适配器。我不知道如何在不等待AsyncTask完成的情况下执行此操作,这与我在等待主UI线程中加载数据的情况相同。

我应该怎么做?

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        ...
        LoadAnnouncementDataFromAPI announcementAPIQuery = new LoadAnnouncementDataFromAPI();
        announcementAPIQuery.execute();
        ...
    }

    ...

    private class LoadAnnouncementDataFromAPI extends AsyncTask<String, String, String> {

        private String announcementAPI = "http://10.0.2.2:3000/announcements";

        @Override
        protected String doInBackground(String... params) {
            try {
                URL url = new URL(announcementAPI);
                HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

                StringBuilder stringBuilder = new StringBuilder();
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append("\n");
                }
                bufferedReader.close();
                return stringBuilder.toString();
            } catch (IOException e) {
                return "";
            }
        }

        @Override
        protected void onPostExecute(String queryResponse) {
            if (queryResponse != null && !queryResponse.equals("")) {
                try {
                    JSONArray apiResponse = new JSONArray(queryResponse);
                    for (int i = 0; i < apiResponse.length(); i++) {
                        JSONObject announcementJSON = apiResponse.getJSONObject(i);
                        if (!announcementJSON.getString("id").equals("")) {
                            mAnnouncementAdapter.items.add(new AnnouncementBlock(announcementJSON));
                        }
                    }
                    mAnnouncementAdapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    return;
                }
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

只需向asynTask添加一个监听器即可。当您收到回调时,请确保更新代码在UI线程上运行。

部分代码

1 - 回调界面

interface OnAddListener {
    void onAdd (AnnouncementBlock announcementBlock);
}

2 - 改变你的课程

class LoadAnnouncementDataFromAPI extends AsyncTask<String,String,String> {
    OnAddListener listener;

    public void setOnAddListener(OnAddListener listener) {
        this.listener = listener;
    }

    LoadAnnouncementDataFromAPI(OnAddListener listener) {
        this.listener = listener;
        // Or may be pass it to the constructor...
    }

    protected String doInBackground(String... params) {
        // Your logic
        // And then!
        if(listener!=null){
            listener.onAdd(new AnnouncementBlock(announcementJSON));
        }
        // ...
    }
}

3 - 您可以从活动中获得回调。我倾向于喜欢活动直接实现接口的方法(可能避免使用final关键字),但你可以这样做。

// Notice that the activity is implementing the OnAddListener interface.
class YourActivity extends Activity implements OnAddListener {

    // ... Some logic life cycle callbacks

    // I suppose you're doing something similar
    private void startUpdating(){
        mLoadAnnouncementDataFromAPI.setListener(this); // set the listener.
    }

    // Your callback.
    @Override
    public void onAdd(final AnnouncementBlock announcementBlock) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // Now you're sure it's running in the UI thread ;)
                mAnnouncementAdapter.items.onAdd(announcementBlock);
            }
        });
    }
}

答案 1 :(得分:0)

尝试创建一个函数setListAdapter,在其中创建适配器的新实例,传递新数据如下:

public void setListAdapter (AnnouncementBlock listdata)   
    {
        If (listAdapter != null){
           listAdapter = null;
        }
     listAdapter = new MyListAdapter (listdata);
}

并在postExecute中调用metho