Spinner ArrayAdapter适配String List遇空指针问题求助
解决Volley异步回调下Spinner ArrayAdapter空指针问题
你踩了Android异步操作的经典坑——Volley网络请求是异步执行的,你在onCreate里直接初始化ArrayAdapter的时候,回调onSuccess4还没跑完,choices数组根本没被赋值,还是null,这就导致ArrayAdapter构造时触发空指针异常(从错误日志里的ArrayAdapter.<init>也能看出来)。
核心问题分析
Volley的getAllCourses方法不会阻塞主线程,它会在后台发起网络请求,请求完成后才会调用onSuccess4回调。而你原代码里,在回调还没执行的时候就直接用choices去初始化Adapter,此时choices是null,ArrayAdapter的构造函数要求数据源不能为null,所以直接抛出NullPointerException。
解决思路与修正代码
有两种常用的解决方案,选哪种都可以:
方案1:先绑定空Adapter,请求完成后更新数据
把Adapter作为Activity的成员变量,先初始化一个空的Adapter绑定到Spinner,等网络请求返回数据后,再更新Adapter的内容:
// 把Adapter声明为Activity成员变量 private ArrayAdapter<String> aa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.course_choice_activity); cChoice = (Spinner) findViewById(R.id.course_spinner); fChoices = new ArrayList<>(); c = new Course(); progressDialog = new ProgressDialog(this); cl = new ArrayList<>(); sm = new SessionManager(getApplicationContext()); // 1. 先初始化空的Adapter并绑定到Spinner aa = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, new String[0]) { @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { View view = super.getDropDownView(position, convertView, parent); TextView tv = (TextView) view; // 保留你的行背景色逻辑 tv.setBackgroundColor(position % 2 == 1 ? Color.parseColor("#FFC9A3FF") : Color.parseColor("#FFAF89E5")); return view; } }; aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); cChoice.setAdapter(aa); // 2. 设置Spinner选择监听 cChoice.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { String choice = (String) parent.getItemAtPosition(pos); Toast.makeText(getApplicationContext(), choice != null ? "Selected : " + choice : "An error has occurred..", Toast.LENGTH_SHORT).show(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); // 3. 发起Volley异步请求 rm = new RequestManager(CourseChoice.this); rm.getAllCourses(new VolleyCallbackOp() { public void onSuccess4(List<Course> cl) { // 把Course列表转为String数组(简化写法) String[] choices = cl.stream() .map(Course::toString) // 依赖Course的toString返回课程名 .toArray(String[]::new); // 更新Adapter数据并通知UI刷新 aa.clear(); aa.addAll(choices); aa.notifyDataSetChanged(); } }); }
方案2:在回调内直接创建并设置Adapter
如果不需要提前显示空Spinner,也可以直接在onSuccess4回调里创建Adapter并绑定到Spinner:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.course_choice_activity); cChoice = (Spinner) findViewById(R.id.course_spinner); fChoices = new ArrayList<>(); c = new Course(); progressDialog = new ProgressDialog(this); cl = new ArrayList<>(); sm = new SessionManager(getApplicationContext()); // 发起Volley请求 rm = new RequestManager(CourseChoice.this); rm.getAllCourses(new VolleyCallbackOp() { public void onSuccess4(List<Course> cl) { String[] choices = new String[cl.size()]; for (int a = 0; a < cl.size(); a++) { Course c = cl.get(a); choices[a] = c.toString(); } // 在这里创建Adapter并绑定到Spinner ArrayAdapter<String> aa = new ArrayAdapter<String>(CourseChoice.this, android.R.layout.simple_spinner_item, choices) { @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { View view = super.getDropDownView(position, convertView, parent); TextView tv = (TextView) view; tv.setBackgroundColor(position % 2 == 1 ? Color.parseColor("#FFC9A3FF") : Color.parseColor("#FFAF89E5")); return view; } }; aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); cChoice.setAdapter(aa); // 设置选择监听(也可以移到外面,只要Spinner已经初始化) cChoice.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { String choice = (String) parent.getItemAtPosition(pos); Toast.makeText(getApplicationContext(), choice != null ? "Selected : " + choice : "An error has occurred..", Toast.LENGTH_SHORT).show(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } }); }
额外优化建议
- 处理请求失败场景:给你的
VolleyCallbackOp加一个onError方法,在网络请求失败时显示错误提示,避免Spinner一直空着 - 避免内存泄漏:如果你的Activity可能在请求完成前销毁,记得取消Volley请求;或者用弱引用包裹Activity上下文
- 简化数组转换:用Java Stream API可以替代循环,代码更简洁(如方案1中的写法)
- 空数据判断:在
onSuccess4里先判断cl是否为空,避免创建长度为0的数组或者空指针
内容的提问来源于stack exchange,提问作者firstofthebadones
相关产品推荐
相关产品推荐

