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

Android中Retrofit调用Spring REST API获取数据异常求助

问题分析与解决方案:Retrofit异步请求导致首次点击无数据

嘿,这个问题我太熟了——本质就是异步请求的时序问题,咱们一步步拆解清楚:

为什么会出现「首次点击无数据,二次点击才显示」?

你用的是Retrofit的异步回调模式,myWebService.getAllTeacher()发起网络请求后会立刻返回,不会等待请求完成。而你的getAllDepartments()方法在发起请求后马上就返回了departmentList(哦对了,你代码里还有个小bug:类里定义的是private List<Teacher> list;,但返回的是this.departmentList,这俩变量根本不搭边!)。

首次点击时:

  1. 调用dataAPI.getAllDepartments(),发起异步请求
  2. 方法立刻返回未初始化的(或null的)列表,MainActivity拿到null,自然显示不出内容
  3. 等网络请求完成后,success回调才执行,给list赋值

第二次点击时:
第一次请求的回调已经执行完了,list里有数据了,所以getAllDepartments()返回的是之前请求到的数据,就能显示了。

至于把DataAPI移到onClick里能首次加载?大概率是测试时的巧合,或者你潜意识里调整了代码逻辑——本质上异步请求的时序问题还是存在的,只是回调完成的时机刚好赶上了操作节奏而已。

怎么解决?

核心思路:不要试图从异步方法里直接返回数据,而是用回调接口让请求完成后主动通知UI更新,或者用更现代的响应式方案(比如LiveData)。

方案1:自定义回调接口(最直接的修复)

第一步:在DataAPI里定义一个回调接口,用来通知请求结果:

public interface OnDataLoadedListener {
    void onSuccess(List<Teacher> data);
    void onFailure(RetrofitError error);
}

第二步:修改DataAPI的getAllDepartments方法,去掉返回值,改成传入回调:

@RequiresApi(api = Build.VERSION_CODES.N)
public void getAllDepartments(OnDataLoadedListener listener) {
    try {
        myWebService.getAllTeacher(new Callback<List<Teacher>>() {
            @Override
            public void success(List<Teacher> data, Response response) {
                Log.d(CLASS_TAG, data.toString());
                list = data;
                // 请求成功,通知UI层
                if (listener != null) {
                    listener.onSuccess(data);
                }
            }

            @Override
            public void failure(RetrofitError error) {
                Log.e(CLASS_TAG, "请求失败", error);
                if (listener != null) {
                    listener.onFailure(error);
                }
            }
        });
    } catch (Exception e) {
        Log.e(CLASS_TAG, "异常", e);
        if (listener != null) {
            listener.onFailure(null);
        }
    }
}

第三步:修改MainActivity的点击事件,通过回调接收数据并更新UI:

@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onClick(View v) {
    textView.setText("加载中...");
    dataAPI.getAllDepartments(new OnDataLoadedListener() {
        @Override
        public void onSuccess(List<Teacher> data) {
            if (data == null || data.isEmpty()) {
                textView.setText("暂无数据");
                return;
            }
            // 用StringBuilder拼接,避免多次setText影响性能
            StringBuilder content = new StringBuilder();
            data.forEach(teacher -> content.append(teacher.toString()).append("\n"));
            textView.setText(content.toString());
        }

        @Override
        public void onFailure(RetrofitError error) {
            String errorMsg = error != null ? error.getMessage() : "未知错误";
            textView.setText("加载失败:" + errorMsg);
        }
    });
}

额外要修复的小问题

  1. 删掉DataAPI里的return this.departmentList;(因为现在用回调传递数据了),同时修正变量名不一致的问题(之前的list和departmentList混淆了)。
  2. 不要在循环里多次调用textView.setText(),用StringBuilder拼接后一次性设置,性能更好。

方案2:用LiveData(更符合Android架构的方式)

如果你的项目用了Jetpack,可以把DataAPI改成用LiveData来暴露数据,自动处理生命周期,避免内存泄漏:

public class DataAPI {
    private static final String CLASS_TAG = "DataAPI";
    RestAdapter retrofit;
    MyWebService myWebService;
    private MutableLiveData<List<Teacher>> teacherListLiveData = new MutableLiveData<>();

    public DataAPI() {
        String url = "http://10.101.12.31:8080/";
        retrofit = new RestAdapter.Builder()
                .setEndpoint(url)
                .build();
        myWebService = retrofit.create(MyWebService.class);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public LiveData<List<Teacher>> getAllDepartments() {
        try {
            myWebService.getAllTeacher(new Callback<List<Teacher>>() {
                @Override
                public void success(List<Teacher> data, Response response) {
                    Log.d(CLASS_TAG, data.toString());
                    teacherListLiveData.postValue(data);
                }

                @Override
                public void failure(RetrofitError error) {
                    teacherListLiveData.postValue(null);
                }
            });
        } catch (Exception e) {
            Log.d(CLASS_TAG, e.toString());
            teacherListLiveData.postValue(null);
        }
        return teacherListLiveData;
    }
}

然后在MainActivity里观察LiveData:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    dataAPI = new DataAPI();
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.textView);
    findViewById(R.id.button_get).setOnClickListener(new View.OnClickListener() {
        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public void onClick(View v) {
            textView.setText("加载中...");
            dataAPI.getAllDepartments().observe(MainActivity.this, teachers -> {
                if (teachers == null || teachers.isEmpty()) {
                    textView.setText("暂无数据");
                    return;
                }
                StringBuilder content = new StringBuilder();
                teachers.forEach(teacher -> content.append(teacher.toString()).append("\n"));
                textView.setText(content.toString());
            });
        }
    });
}

内容的提问来源于stack exchange,提问作者Kubek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:38:34