在后台加载数据,更新适配器?
Loading Data in Background, Updating 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 线程后台任务已完成;我可以修改我的代码以将数据加载到另一个不是 Adapter 的数据结构中,然后 UI 线程一旦知道任务完成就可以将它添加到 Adapter 中。我不知道如何在不等待 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;
}
}
}
}
}
尝试创建一个函数 setListAdapter,您可以在其中创建适配器的新实例来传递新数据,如下所示:
public void setListAdapter (AnnouncementBlock listdata)
{
If (listAdapter != null){
listAdapter = null;
}
listAdapter = new MyListAdapter (listdata);
}
然后在你的 postExecute
中调用这个方法
您只需向您的 asynTask 添加一个侦听器即可做到这一点。当您收到回调时,请确保更新代码在 UI 线程上运行。
一些代码
1 - 回调接口
interface OnAddListener {
void onAdd (AnnouncementBlock announcementBlock);
}
2 - 改变你的 class
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 - 您可以从 activity 获得回调。我倾向于喜欢 activity 直接实现接口的方法(可能是为了避免 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);
}
});
}
}
我正在使用 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 线程后台任务已完成;我可以修改我的代码以将数据加载到另一个不是 Adapter 的数据结构中,然后 UI 线程一旦知道任务完成就可以将它添加到 Adapter 中。我不知道如何在不等待 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;
}
}
}
}
}
尝试创建一个函数 setListAdapter,您可以在其中创建适配器的新实例来传递新数据,如下所示:
public void setListAdapter (AnnouncementBlock listdata)
{
If (listAdapter != null){
listAdapter = null;
}
listAdapter = new MyListAdapter (listdata);
}
然后在你的 postExecute
中调用这个方法您只需向您的 asynTask 添加一个侦听器即可做到这一点。当您收到回调时,请确保更新代码在 UI 线程上运行。
一些代码
1 - 回调接口
interface OnAddListener {
void onAdd (AnnouncementBlock announcementBlock);
}
2 - 改变你的 class
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 - 您可以从 activity 获得回调。我倾向于喜欢 activity 直接实现接口的方法(可能是为了避免 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);
}
});
}
}