2017-02-25 48 views
0

我已經瀏覽了超過50頁,爲我極其簡單的應用程序尋找解決方案,但似乎沒有一個適合我。請幫忙。如何應用我從doInBackground()獲得的數據應用於我的RecyclerView?

問題:我在MainActivity的菜單中有一個刷新按鈕。當按下時,我想執行我的AsyncTask,然後更新我的RecyclerView中的所有項目。

情況。

  1. 我的應用程序通過OpenWeatherMap.org取出從API數據,然後顯示這些數據。

  2. 我有一個MainActivity類別(和我recyclerView這裏駐留在。)

  3. 對於recyclerView,我使用RecyclerView.Adapter與GridViewManager。
  4. 我有一個單獨的AsyncTask類。

所以,我曾嘗試和沒有工作:

方法1.正常方式。當選擇刷新按鈕時,調用我的AsyncTask。在我的PostExecute()中,我在MainActivity中調用setter來從doInBackGround方法分配結果數組。

在MainActivity

然後,

myAsyncTask.execute("43017,us"); 
recyclerView.recyclerView.getAdapter().notifyDataSetChanged(); 

但是這將導致notifyDataSetChanged();之前MainActivity構件vairable陣列從onPostExecute()更新到被調用。方法2.嘗試從onPostExecute方法中完全更新UI,請參閱MyAsyncTask.class中的onPostExecute方法。

那麼我知道onPostExecute,即使它被寫入不同的類,在UI線程上運行。因此,在方法中,我不喜歡的東西

MainActivity mainActivity = new MainActivity(); 
RecyclerView recyclerView = mainActivity.findViewById(R.id.recyclerView); 
RecyclerView.Adapter adapter = recyclerView.getAdapter(); 
adapter.notifyDataSetChanged(); 

在這種方法中,有記錄,我已確認onPostExecute成功更新在MainActivity成員變量,它只是notifyDataSetChanged變得太叫早,特別是onPostExecute前在後臺完成。

我希望能爲此得到一個答案,並對我的情況足夠清楚。我會在下面發佈我的MainActivity,Adapter和AsyncTask代碼。

MainActivity.java:

public class MainActivity extends AppCompatActivity { 

    private String weatherData[] = { 
     "Today - Sunny", 
      "Tomorrow - Cloudy", 
      "Tuesday - Rainy", 
      "Wednesday - Sunny", 
      "Thursday - Sunny", 
      "Friday - Sunny", 
      "Saturday - Cloudy", 
      "Sunday - Rainy :/" 
    }; 

    private RecyclerView recyclerView; 
    private mAdapter adapter; 
    private static final int SPAN_COUNT = 1; 
    private MyAsyncTask myAsyncTask = new MyAsyncTask(); 
    private Context context; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     recyclerView = (RecyclerView)findViewById(R.id.recyclerview_weatherData); 
     setLayout(getApplicationContext()); 
     adapter = new mAdapter(weatherData); 
     recyclerView.setAdapter(adapter); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     MenuInflater menuInflater = getMenuInflater(); 
     menuInflater.inflate(R.menu.menu, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     int id = item.getItemId(); 

     switch (id){ 
      case R.id.action_refresh: 
       Log.v("Menu", "Refresh button selected."); 
       //for now, take some random ZIP code 
       myAsyncTask.execute("43017,us"); 
       recyclerView.getAdapter().notifyDataSetChanged(); 
       adapter.refreshContents(weatherData); 
       for (int i = 0; i<weatherData.length; i++) { 
        Log.v("Refresh button", weatherData[i].toString()); 
       } 
     } 

     return true; 
    } 

    public void setLayout(Context context) { 
     int scrollPosition = 0; 
     //make a GridLayoutManager with 2 columns 
     LinearLayoutManager mLayoutManager = new LinearLayoutManager(context); 

     //set the mLayoutManager to the one that I just created 
     recyclerView.setLayoutManager(mLayoutManager); 
     recyclerView.scrollToPosition(scrollPosition); 

     recyclerView.setLayoutManager(new GridLayoutManager(context, SPAN_COUNT)); 

     //set the offset decoration definition to my layout 
     int middle_spacing = 30; 
     boolean includeEdge = true; 
     recyclerView.addItemDecoration(new ItemOffsetDecoration(SPAN_COUNT, middle_spacing, includeEdge)); 
    } 

    public void setWeatherData(String[] weatherData) { 
     this.weatherData = weatherData; 
    } 
    public String[] getWeatherData() { return weatherData; } 
} 

MyAsyncTask.java:

public class MyAsyncTask extends AsyncTask<String,Void,String[]> { 

    public final static String OPEN_WEATHER_MAP_API_KEY = "bc607b72747aa672bf2ac9a5f3a5fc84"; 
    String forecastJsonStr = null; 

    private String format = "json"; 
    private String units = "metric"; 
    private int numDays = 7; 
    private String data[] =null; 
    private RecyclerView recyclerView; 
    private MainActivity mainActivity; 
    private RecyclerView.Adapter adapter; 


    @Override 
    protected String[] doInBackground(String... params) { 

     if (params.length == 0) { 
      Log.v("AsyncTask", "No parameter is taken."); 
      return null; 
     } 

     HttpURLConnection urlConnection = null; 
     BufferedReader reader = null; 

     try { 
      final String FORECAST_BASE_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?"; 
      final String QUERY_PARAM = "q"; 
      final String FORMAT_PARAM = "mode"; 
      final String UNITS_PARAM = "units"; 
      final String DAYS_PARAM = "cnt"; 
      final String APPID_PARAM = "APPID"; 

      Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon() 
        .appendQueryParameter(QUERY_PARAM, params[0]) 
        .appendQueryParameter(FORMAT_PARAM, format) 
        .appendQueryParameter(UNITS_PARAM, units) 
        .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)) 
        .appendQueryParameter(APPID_PARAM, OPEN_WEATHER_MAP_API_KEY) 
        .build(); 

      URL url = new URL(builtUri.toString()); 

      urlConnection = (HttpURLConnection) url.openConnection(); 
      urlConnection.setRequestMethod("GET"); 
      urlConnection.connect(); 

      InputStream inputStream = urlConnection.getInputStream(); 
      StringBuffer buffer = new StringBuffer(); 

      if (inputStream == null) { 
       return null; 
      } 
      reader = new BufferedReader(new InputStreamReader(inputStream)); 

      String line; 
      while ((line = reader.readLine()) != null) { 
       buffer.append(line + "\n"); 
      } 

      if (buffer.length() == 0) { 
       return null; 
      } 
      //put the buffer in String var forecastJsonStr 
      forecastJsonStr = buffer.toString(); 
      Log.v("AsyncTask", forecastJsonStr.toString()); 

     } catch (IOException e) { 
      return null; 

     } finally { 
      if (urlConnection != null) { 
       urlConnection.disconnect(); 
      } 
      if (reader != null) { 
       try { 
        reader.close(); 
       } catch (final IOException e) { 
        Log.e("Async", "Reader is null, something wrong."); 
       } 
      } 
     } 
     //Then put the string contents into an array 
     try { 
      ParseWeatherData parser = new ParseWeatherData(); 
      data = parser.getWeatherDataFromJson(forecastJsonStr, numDays); 
      return data; 
     } catch (JSONException e) { 
      e.printStackTrace(); 
     } 
      return data; 
    } 

    @Override 
    protected void onPostExecute(final String data[]) { 
     super.onPostExecute(data); 

     if (data != null) { 
      //this log works fine: the fetched data is successfully stored... 
      for (int i = 0; i<data.length; i++) { 
       Log.v("onPostExecute", data[i].toString()); 
      } 
      //how do I pass this data to the main thread? 

      mainActivity = new MainActivity(); 
      mainActivity.setWeatherData(data); 
     } 



    } 
} 

最後,mAdapter.java:

public class mAdapter extends RecyclerView.Adapter<mAdapter.ViewHolder> { 

    private String data[]; 

    public mAdapter(String data[]) { 
     this.data = data; 
    } 


    @Override 
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 

     View listView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_single_list, parent, false); 

     return new ViewHolder(listView); 
    } 

    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) { 
     holder.weather.setText(data[position]); 
     Log.v("BindView", "Item " + position + " set."); 
    } 


    @Override 
    public int getItemCount() { 
     if (data == null) { 
      Log.v("WeatherAdapter", "Oops, getting null in the adapter."); 
      return 0; 
     } else { 
      return data.length; 
     } 
    } 

    public void refreshContents(String data[]) { 
     this.data = null; 
     this.data = data; 
     notifyDataSetChanged(); 
    } 

    public static class ViewHolder extends RecyclerView.ViewHolder { 

     TextView weather, day; 

     //currently ViewHolder is set as the TextView for logging 
     public ViewHolder(View v) { 
      super(v); 
      weather = (TextView) v.findViewById(R.id.test_text); 

      // Define click listener for the ViewHolder's View. 
      v.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        Log.d(TAG, "Element " + getAdapterPosition() + " clicked."); 
       } 
      }); 
     } 

    } 
} 

預先感謝您!

+3

寫下接口回調和onPostexecute通過你的結果() 並在主要活動接收數據 –

回答

0

首先不要在MyAsyncTask中創建MainActivity的新對象。實際上發生的事情是的AsyncTask上一個新的線程上運行,所以當你做

myAsyncTask.execute("43017,us"); 
recyclerView.getAdapter().notifyDataSetChanged(); 
adapter.refreshContents(weatherData); 

然後myAsyncTask在不同的線程運行,並在下一行開始.execute前右執行,而無需等待asynctask完成哪怕等待asynctask完成後,創建一個新的對象來更新MainActivity中的數據是行不通的。因此,您可以通過onPostExecute方法更新您的清單,方法是將您的活動從參數傳遞到myAsyncTask。不要在一開始初始化myAsyncTask,剛剛從private MyAsyncTask myAsyncTask = new MyAsyncTask();刪除= new MyAsyncTask()MainActivity現在有了這個替換MainActivity行:

取而代之的是在您的MainActivity

myAsyncTask.execute("43017,us"); 
recyclerView.getAdapter().notifyDataSetChanged(); 
adapter.refreshContents(weatherData); 
for (int i = 0; i<weatherData.length; i++) { 
    Log.v("Refresh button", weatherData[i].toString()); 
} 

myAsyncTask = new MyAsyncTask(this); 
myAsyncTask.execute("43017,us"); 

並刷新內容你可以讓你的adapter公有的,因此你可以調用從myAsyncTask本身notifydatasetchanged,但如果你想跟着.execute後您編寫的代碼,那麼你可以把它移到一個新的方法類似下面

public void refreshList(){ 
    recyclerView.getAdapter().notifyDataSetChanged(); 
    adapter.refreshContents(weatherData); 
    for (int i = 0; i<weatherData.length; i++) { 
     Log.v("Refresh button", weatherData[i].toString()); 
    } 
} 

,現在剩下的就是讓的情況下你MainActivityMyAsyncTask並刷新您的列表onPostExecute。因此,請更改,如下列:

創建MyAsyncTask的構造

public class MyAsyncTask extends AsyncTask<String,Void,String[]> { 
    MainActivity mainActivity; 
    public MyAsyncTask(MainActivity mainActivity){ 
     this.mainActivity = mainActivity; 
    } 

在MyAsyncTask

mainActivity = new MainActivity(); 
mainActivity.setWeatherData(data); 

替換此有了這個

mainActivity.setWeatherData(data); 
mainActivity.refreshList(); 

我沒有測試它自己,但我認爲這應該可以解決您的問題。你可以嘗試調試這個,看看它是如何工作的。如果您遇到任何問題,請在下面評論。

編輯

基於由@Ganesh帕蒂爾您可以創建這個解決方案的接口以及評論。對於使用的AsyncTask接口的參考,您可以點擊此鏈接: https://stackoverflow.com/a/28958913/7071039

但要保持它的簡單,我沒有使用interface和剛剛通過的MainActivityMyAsyncTask

編輯的背景基礎上,評論

親愛的PC HUB,首先非常感謝你的詳細解答。 所有的解釋都有意義,我使用了代碼。我的應用程序 現在不會崩潰,並且所有數據都正確傳遞/檢索,但 以某種方式mainActivity.refreshList()和 recyclerView.getAdapter()。notifyDataSetChanged()仍然不起作用。 因此,我的recyclerView仍然不會更改內容...我不知道該怎麼做。 - Rikuto越後屋

改變你的refreshList到這個

public void refreshList(){ 
    /* Check your Weather Data size in this method to find out weather your data is changing or not */ 
    Log.d("Tag","SIZE OF WEATHER DATA : "+weatherData.length); 
    /* You already have the adapter object so you don't need to 
     get it using recyclerview.getAdapter. Just do it directly like this */ 
    adapter.notifyDataSetChanged(); 

    // Not changing this as this will not stop your list from refreshing :P 
    for (int i = 0; i<weatherData.length; i++) { 
     Log.v("Refresh button", weatherData[i].toString()); 
    } 
} 

另外,還要確保您已在MyAsyncTaskmainActivity.refreshList();之前叫mainActivity.setWeatherData(data);

如果仍然不能解決你的問題,然後分享您的更新代碼,以便我們可以看到,爲什麼你的名單不會得到更新:)

+0

親愛的PC HUB,首先非常感謝你的非常詳細的答案。所有的解釋都是有道理的,我使用了這些代碼。我的應用程序現在不會崩潰,並且所有數據都正確傳遞/檢索,但不知何故'mainActivity.refreshList()'和'recyclerView.getAdapter()。notifyDataSetChanged()'仍然不起作用。因此,我的recyclerView仍然不會改變內容...我不知道該怎麼做。 – Etchee

+0

請檢查更新的代碼...並讓我知道如果它仍然不能解決您的問題。做一些調試,如果你不能解決它,那就分享你的代碼,這樣我就可以看到錯誤在哪裏^ _^ –

+0

謝謝!就像你提到的那樣,我最終將MainActivity傳遞給了適配器,並且它工作正常。我非常感謝你的幫助。 – Etchee

相關問題