2017-02-25 14 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

相关问题