如何在非跳转Activity中从Model类获取数据?
解决Android跨Activity共享DataSet数据的空指针问题
首先,咱们先分析你遇到的NullPointerException大概率是哪几个原因:
- 调用
sharedPref()时传入的data是null——要么childData没初始化,要么网络请求还没跑完就急着拿数据了 childData里的DataSet对象本身是null- 你在
AnnualActivity里直接用的schoolID和sessionYear变量根本没赋值,直接导致拼接URL时空指针
因为AnnualActivity不是跳转目标,没法用Intent传数据,那咱们就得用Android里跨组件共享数据的方案,下面给你几个实用的解决办法:
方案一:用自定义Application存全局数据
这是Android里最常用的全局数据共享方式,步骤很清晰:
- 先写一个自定义的Application子类:
public class MyApp extends Application { private ArrayList<DataSet> childData; @Override public void onCreate() { super.onCreate(); // 提前初始化集合,避免空指针 childData = new ArrayList<>(); } public ArrayList<DataSet> getChildData() { return childData; } public void setChildData(ArrayList<DataSet> data) { this.childData.clear(); this.childData.addAll(data); } }
- 去
AndroidManifest.xml里注册这个Application:
<application android:name=".MyApp" <!-- 你的其他配置项 --> >
- 在网络请求成功的回调里,把
childData存到全局Application:
就在你第一个代码块的onResponse方法最后加这段:
// 把请求到的childData存入全局Application MyApp myApp = (MyApp) getApplicationContext(); myApp.setChildData(childData);
- 最后在
AnnualActivity里拿数据:
public class AnnualActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_annual); // 获取全局的childData MyApp myApp = (MyApp) getApplicationContext(); ArrayList<DataSet> childData = myApp.getChildData(); // 先做非空检查!这是避免空指针的关键 if (childData != null && !childData.isEmpty()) { for (DataSet dataSet : childData) { if (dataSet == null) continue; // 跳过空的DataSet对象 int schoolId = dataSet.getStudentSchoolId(); String sessionYear = dataSet.getSessionYear(); Log.e("AnnualActivity", "schoolId: " + schoolId + ", sessionYear: " + sessionYear); // 现在可以安全地发起你的网络请求了 JsonObjectRequest annualReq = new JsonObjectRequest(Request.Method.GET, "http://qnabubackend-env.2fuz4eh5jh.ap-south-1.elasticbeanstalk.com/school/api/annual/activity/get/list?schoolId="+schoolId+"&session="+sessionYear, null, response -> { // 处理请求成功的逻辑 }, error -> { // 处理请求失败的逻辑 }); // 记得把请求加入Volley的请求队列 Volley.newRequestQueue(this).add(annualReq); } } else { Log.e("AnnualActivity", "childData是空的!要么网络请求还没完成,要么请求失败了"); } } }
方案二:用SharedPreferences存关键数据
如果你只需要schoolId和sessionYear,不需要整个DataSet集合,直接存这俩值更轻便:
- 网络请求成功后存数据:
SharedPreferences sp = getSharedPreferences("AppData", MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); // 这里假设你要取第一个孩子的数据,要是有多个可以遍历存成数组 if (!childData.isEmpty()) { DataSet firstChild = childData.get(0); editor.putInt("saved_school_id", firstChild.getStudentSchoolId()); editor.putString("saved_session_year", firstChild.getSessionYear()); editor.apply(); // 异步存,不卡主线程 }
- 在
AnnualActivity里读数据:
SharedPreferences sp = getSharedPreferences("AppData", MODE_PRIVATE); int schoolId = sp.getInt("saved_school_id", -1); // 默认值-1表示没取到 String sessionYear = sp.getString("saved_session_year", ""); if (schoolId != -1 && !sessionYear.isEmpty()) { // 放心发起网络请求 } else { Log.e("AnnualActivity", "没拿到schoolId或sessionYear,检查下网络请求是否成功"); }
方案三:用单例类管理共享数据
和自定义Application思路差不多,用单例类来存数据,适合不想改Application的场景:
public class DataManager { private static DataManager instance; private ArrayList<DataSet> childData; private DataManager() { childData = new ArrayList<>(); } public static synchronized DataManager getInstance() { if (instance == null) { instance = new DataManager(); } return instance; } public ArrayList<DataSet> getChildData() { return childData; } public void setChildData(ArrayList<DataSet> data) { this.childData.clear(); this.childData.addAll(data); } }
存数据的时候:
DataManager.getInstance().setChildData(childData);
取数据的时候:
ArrayList<DataSet> childData = DataManager.getInstance().getChildData(); // 后续的非空检查和请求逻辑和方案一一样
几个重要的注意点
- 非空检查一定要做:不管是集合还是单个对象,访问前先判断是不是
null,避免空指针 - 注意网络请求的异步性:如果
AnnualActivity比网络请求先启动,你可以用LiveData或者EventBus来监听数据变化,等数据准备好了再处理 - 别搞内存泄漏:用全局数据的时候,别持有Activity的引用,不然容易内存泄漏
内容的提问来源于stack exchange,提问作者Kamal
相关产品推荐
相关产品推荐

