如何在Android应用运行期间仅解析一次JSON数据?
实现Android应用JSON仅解析一次的解决方案
问题核心
当前应用在横竖屏切换、页面刷新、从子页面返回重建时,会重复执行JSON解析与网络请求,导致加载缓慢,多次切换甚至崩溃。现有代码尝试用AsyncTaskRunner加run标记控制,但未彻底解决问题,还存在数据库重复插入、主线程处理解析等冗余操作。
1. 优先读取本地数据库,避免重复网络请求
在发起网络请求前,先检查数据库是否已有数据。有数据直接读取展示,无数据再发起请求解析:
// Activity初始化时调用此方法替代直接调用parseApiData private void loadData() { // 从本地数据库读取已有数据 List<Books> savedBooks = dbHandler.readCourses(); if (savedBooks != null && !savedBooks.isEmpty()) { books.clear(); books.addAll(savedBooks); progressBar.setVisibility(View.GONE); recyclerView.setAdapter(new BookAdapter(MainActivity.this, books)); return; } // 数据库无数据时,才发起网络请求 parseApiData(); }
2. 修复JSON解析与数据库操作的冗余问题
现有代码存在重复插入数据库的错误(循环内插入一次,又开线程再插入一次),同时要把数据库操作放到后台线程,避免阻塞主线程:
// 修改parseApiData中的onResponse方法 @Override public void onResponse(JSONArray response) { if (response != null && response.length() > 0) { books.clear(); // 清空旧数据 dbHandler.removeAll(); for (int i = 0; i < response.length(); i++) { try { JSONObject obj = response.getJSONObject(i); int bookId = obj.getInt("id"); String bookName = obj.getString("title"); String bookDescription = obj.getString("description"); int bookPage = obj.getInt("pageCount"); String bookExcerpt = obj.getString("excerpt"); String bookPublishDate = obj.getString("publishDate"); Books book = new Books(bookId, bookName, bookDescription, bookPage, bookExcerpt, bookPublishDate); books.add(book); } catch (JSONException e) { e.printStackTrace(); } } // 后台线程执行数据库插入,完成后主线程更新UI new Thread(() -> { for (Books book : books) { dbHandler.addNewCourse(book.getmBookId(), book.getmBookName(), book.getmBookDescription(), book.getmBookPage(), book.getmBookExcerpt(), book.getmBookPublishDate()); } runOnUiThread(() -> { progressBar.setVisibility(View.GONE); recyclerView.setAdapter(new BookAdapter(MainActivity.this, books)); }); }).start(); } else { runOnUiThread(() -> { progressBar.setVisibility(View.GONE); Toast.makeText(MainActivity.this, "数据为空", Toast.LENGTH_SHORT).show(); }); } }
3. 避免Activity重建时重复请求
横竖屏切换会触发Activity重建,可通过两种方式解决:
方式一:配置Manifest禁用重建
在AndroidManifest.xml的对应Activity标签中添加:
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize|screenLayout"> <!-- 其他原有配置 --> </activity>
这样横竖屏切换时Activity不会重建,仅调用onConfigurationChanged方法,无需重新初始化数据。
方式二:用ViewModel持久化数据
如果需要保留Activity重建逻辑,使用ViewModel保存数据,重建后直接复用:
// 自定义ViewModel类 public class BookViewModel extends ViewModel { private MutableLiveData<List<Books>> booksLiveData; private YourDbHandler dbHandler; // 替换为你的数据库操作类 public LiveData<List<Books>> getBooks(Context context) { if (booksLiveData == null) { booksLiveData = new MutableLiveData<>(); loadBooks(context); } return booksLiveData; } private void loadBooks(Context context) { dbHandler = new YourDbHandler(context); // 后台线程读取数据库 Executors.newSingleThreadExecutor().execute(() -> { List<Books> savedBooks = dbHandler.readCourses(); if (savedBooks != null && !savedBooks.isEmpty()) { booksLiveData.postValue(savedBooks); return; } // 数据库无数据,发起网络请求 RequestQueue queue = Volley.newRequestQueue(context); JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, url, null, response -> { List<Books> newBooks = new ArrayList<>(); for (int i = 0; i < response.length(); i++) { try { JSONObject obj = response.getJSONObject(i); Books book = new Books( obj.getInt("id"), obj.getString("title"), obj.getString("description"), obj.getInt("pageCount"), obj.getString("excerpt"), obj.getString("publishDate") ); newBooks.add(book); } catch (JSONException e) { e.printStackTrace(); } } // 插入数据库后更新LiveData Executors.newSingleThreadExecutor().execute(() -> { dbHandler.removeAll(); for (Books book : newBooks) { dbHandler.addNewCourse(book.getmBookId(), book.getmBookName(), book.getmBookDescription(), book.getmBookPage(), book.getmBookExcerpt(), book.getmBookPublishDate()); } booksLiveData.postValue(newBooks); }); }, error -> Toast.makeText(context, "网络请求失败", Toast.LENGTH_SHORT).show()); queue.add(request); }); } }
在Activity中使用ViewModel:
public class MainActivity extends AppCompatActivity { private BookViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewModel = new ViewModelProvider(this).get(BookViewModel.class); viewModel.getBooks(this).observe(this, books -> { if (books != null) { progressBar.setVisibility(View.GONE); recyclerView.setAdapter(new BookAdapter(this, books)); } }); } }
4. 移除无效的AsyncTask逻辑
现有AsyncTaskRunner中的run标记是Activity成员变量,重建后会重置为默认值false,无法有效控制重复请求,建议直接删除该AsyncTask,改用上述方案。
内容的提问来源于stack exchange,提问作者Shakthi
相关产品推荐
相关产品推荐

