如何在AsyncTask中通过Gson字符串构建多ArrayList并修复RecyclerView填充异常
看起来你遇到了几个典型的Android异步操作和数据解析问题,我来帮你一步步解决:
问题1:AsyncTask中SharedPreferences获取错误
你在doInBackground里用getPreferences(getBaseContext().MODE_PRIVATE)是不对的——getPreferences()是Activity的方法,而且MODE_PRIVATE是全局常量,不需要从Context获取。如果你的DoSomeTasks是MoodsActivity的内部类,直接用MoodsActivity.this.getPreferences(MODE_PRIVATE)即可;如果是独立类,要传入Activity的Context对象。
问题2:后台线程不能操作UI
你在doInBackground里调用createRecycler()是错误的,因为这个方法涉及RecyclerView初始化和Adapter设置,属于UI操作,必须在主线程执行。AsyncTask的onPostExecute()方法是在主线程运行的,应该把解析好的数据返回给这个方法,再在这里更新UI。
问题3:Gson反序列化泛型类型错误
直接用gson.fromJson(history, ArrayList.class)会导致Gson无法识别Mood泛型,解析出来的列表元素是LinkedTreeMap而不是Mood对象,调用getName()等方法会抛出类型转换异常。需要用TypeToken来指定具体的泛型类型。
问题4:不必要的列表拆分
拆分出moodNames、moodGenres、moodColors三个独立列表不仅冗余,还容易出现数据不一致的问题。直接传递ArrayList<Mood>给Adapter会更简洁可靠。
修改后的完整代码示例
1. 修复后的AsyncTask(作为MoodsActivity的内部类)
public class DoSomeTasks extends AsyncTask<Void, Void, ArrayList<Mood>> { @Override protected ArrayList<Mood> doInBackground(Void... voids) { // 正确获取SharedPreferences SharedPreferences prefs = MoodsActivity.this.getPreferences(MODE_PRIVATE); // 用空数组作为默认值,避免解析空字符串抛出异常 String history = prefs.getString("history", "[]"); try { Gson gson = new Gson(); // 用TypeToken指定泛型类型,让Gson正确解析为Mood列表 Type moodListType = new TypeToken<ArrayList<Mood>>(){}.getType(); return gson.fromJson(history, moodListType); } catch (Exception e) { Log.e("MoodsActivity", "Couldn't load any moods", e); return new ArrayList<>(); // 返回空列表,避免后续空指针 } } @Override protected void onPostExecute(ArrayList<Mood> moodList) { super.onPostExecute(moodList); // 在主线程更新RecyclerView createRecycler(moodList); } }
2. 修改createRecycler方法,直接接收Mood列表
public void createRecycler(ArrayList<Mood> moodList){ Log.d("MoodsActivity", "Reached createRecycler, mood count: " + moodList.size()); RecyclerView rView = findViewById(R.id.moodlistView); // 直接把Mood列表传给Adapter RecycleViewAdapter rAdapt = new RecycleViewAdapter(moodList, this); rView.setAdapter(rAdapt); rView.setLayoutManager(new LinearLayoutManager(this)); }
3. 调整RecyclerViewAdapter,直接使用Mood对象
// 示例Adapter结构,根据你的布局需求调整控件ID public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder> { private ArrayList<Mood> mMoodList; private Context mContext; public RecycleViewAdapter(ArrayList<Mood> moodList, Context context) { mMoodList = moodList; mContext = context; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate(R.layout.your_item_layout, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Mood currentMood = mMoodList.get(position); holder.nameTv.setText(currentMood.getName()); holder.genreTv.setText(currentMood.getGenre()); // 假设Color是android.graphics.Color,转换为ARGB值设置背景 holder.colorView.setBackgroundColor(currentMood.getColor().toArgb()); } @Override public int getItemCount() { return mMoodList.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { TextView nameTv; TextView genreTv; View colorView; public ViewHolder(@NonNull View itemView) { super(itemView); nameTv = itemView.findViewById(R.id.tv_mood_name); genreTv = itemView.findViewById(R.id.tv_mood_genre); colorView = itemView.findViewById(R.id.view_mood_color); } } }
4. 启动AsyncTask的方式
在MoodsActivity的onCreate或者onResume里启动异步任务:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_moods); // 启动异步任务加载数据,避免主线程负载过高 new DoSomeTasks().execute(); // 按钮点击事件简化写法 Button preferenceButton = findViewById(buttonId); preferenceButton.setOnClickListener(v -> { Intent preferencesPage = new Intent(MoodsActivity.this, PreferencesActivity.class); startActivity(preferencesPage); }); }
额外建议
- 用
Log代替System.out.print,Android Studio的Logcat能更清晰地查看调试信息和异常堆栈。 - 如果你的minSdkVersion >= 26,建议用Coroutines替代AsyncTask(AsyncTask已被官方废弃),但上述代码在旧版本依然有效。
内容的提问来源于stack exchange,提问作者AshleeCW

