You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:02:11