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

从Widget启动App时Retrofit获取数据遇NullPointerException问题

问题分析与解决方案:Widget启动App时Retrofit异步请求导致的NullPointerException

核心原因

你遇到的空指针问题,本质是Retrofit网络请求的异步特性和Activity生命周期的同步执行之间的冲突:
当从Widget启动App时,你在onCreate方法里发起了Retrofit请求,但这个请求是异步执行的——代码会直接跳过回调逻辑,继续往下执行getSupportActionBar().setTitle(reciepeObject.getName())。此时reciepeObject还没被onResponse回调赋值,自然是null,触发空指针异常。

你看到onResponse里的Toast能正常显示,是因为那是请求完成后才会触发的回调,而此时Activity的onCreate早就执行完毕,崩溃已经发生了。


一、修复空指针的正确代码实现

我们需要把依赖reciepeObject的UI操作,移到异步请求完成后的回调里,同时增加空值防护:

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.master_list); 
    getSupportActionBar().setDisplayHomeAsUpEnabled(true); 

    // 先设置临时标题,避免空白或崩溃
    getSupportActionBar().setTitle("加载中...");

    // 先判断Intent额外参数是否存在,避免空指针
    Bundle extras = getIntent().getExtras();
    if (extras != null && extras.getString("stringFromWidget") != null) { 
        fromWidget = true; 

        // 发起网络请求
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class); 
        Call<List<ApiResponce>> call = apiService.getResponce(); 
        call.enqueue(new Callback<List<ApiResponce>>() { 
            @Override 
            public void onResponse(Call<List<ApiResponce>> call, Response<List<ApiResponce>> response) { 
                // 多层空值防护:确保请求成功、返回体非空、列表长度足够
                if (response.isSuccessful() && response.body() != null && response.body().size() > 3) {
                    reciepeObject = response.body().get(3); 
                    // 所有依赖reciepeObject的操作都放在这里
                    Toast.makeText(MasterListClass.this, reciepeObject.getName(), Toast.LENGTH_SHORT).show();
                    getSupportActionBar().setTitle(reciepeObject.getName());
                    // 这里还可以更新数据库缓存,方便下次Widget启动快速读取
                } else {
                    // 处理数据异常的情况
                    getSupportActionBar().setTitle("加载失败");
                    Toast.makeText(MasterListClass.this, "数据加载异常", Toast.LENGTH_SHORT).show();
                }
            } 

            @Override 
            public void onFailure(Call<List<ApiResponce>> call, Throwable t) { 
                Log.e("networkError", t.getMessage());
                getSupportActionBar().setTitle("加载失败");
                Toast.makeText(MasterListClass.this, "网络请求失败", Toast.LENGTH_SHORT).show();
            } 
        }); 
    } 
    // 非Widget启动的情况,直接从Intent获取对象
    else { 
        reciepeObject = getIntent().getParcelableExtra(getString(R.string.object)); 
        if (reciepeObject != null) {
            getSupportActionBar().setTitle(reciepeObject.getName());
        } else {
            getSupportActionBar().setTitle("数据异常");
        }
    } 
}

二、Widget点击后App获取数据的最佳实践

  1. 优先使用本地缓存提升体验
    你之前尝试从数据库读取缓存的思路非常好,可以先显示缓存数据,再发起网络请求更新:
// 先读取数据库缓存,快速显示
Cursor c = getContentResolver().query(DBContract.CONTENT_URI, null, null, null);
if (c != null && c.moveToFirst()) {
    String cachedName = c.getString(c.getColumnIndex(DBContract.RECIPE_NAME));
    getSupportActionBar().setTitle(cachedName);
    c.close();
}
// 再发起网络请求,更新缓存和UI
Call<List<ApiResponce>> call = apiService.getResponce();
// ... 在回调里更新数据库和UI
  1. 精准请求数据
    如果Widget可以传递食谱ID这类标识,不要请求整个列表再取第3个,直接根据ID请求对应数据,减少带宽消耗和加载时间。

  2. 处理配置变更
    如果Activity在请求过程中发生屏幕旋转等配置变更,建议使用ViewModel或保留Fragment管理异步请求,避免请求丢失和内存泄漏。


三、额外的空指针防护建议

  • 每次调用getIntent().getExtras()前,先判断是否为null
  • 在onResponse里必须检查response.isSuccessful()、response.body()是否为null,以及列表长度是否足够,防止数组越界
  • 所有使用reciepeObject的地方,都先加非空判断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:16:05