2017-01-19 4 views
0

Я использую 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.

Я предполагаю, что я просто нужен сигнал, чтобы уведомить поток пользовательского интерфейса, что фоновая задача выполнена; Я могу изменить свой код для загрузки данных в другую структуру данных, которая не является адаптером, а затем поток пользовательского интерфейса может добавить его в адаптер, как только он узнает, что задача завершена. Я не знаю, как это сделать, но не дожидаясь завершения AsyncTask, что равносильно тому, что я ожидал загрузки данных в основном потоке пользовательского интерфейса.

Как мне это сделать?

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; 
       } 
      } 
     } 
    } 
} 
+0

Использование publishProgress – Submersed

+0

Я не понимаю, почему это интересно, что вы должны ждать, пока не будет получать данные, прежде чем показывать его? – Divers

ответ

1

Вы можете сделать что просто добавив слушателя к вашему asynTask. Когда вы получите свой обратный вызов, убедитесь, что код обновления работает в потоке пользовательского интерфейса.

Некоторый код

1 - Интерфейс обратного вызова

interface OnAddListener { 
    void onAdd (AnnouncementBlock announcementBlock); 
} 

2 - Alter ваш класс

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); 
      } 
     }); 
    } 
} 
+0

Использование 'runOnUiThread', похоже, отлично работает. Благодаря! –

0

Попробуйте сделать функцию setListAdapter где вы создаете новый экземпляр адаптера проходящий новые данные, как показано ниже:

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

И называют таким образом мет в вашем postExecute

Смежные вопросы