加载大量本地JSON时应用卡顿,求AsyncTask改造方案(含进度)
解决加载大JSON时应用冻结的问题(AsyncTask实现+进度显示)
嘿,我来帮你搞定这个问题!你现在的代码直接在**主线程(UI线程)**里读取和解析10000+条数据的JSON文件,这肯定会阻塞UI更新——安卓主线程不能处理超过200ms的耗时操作,更别说40秒了,所以应用才会看起来“冻结”。
下面我给你把代码改成AsyncTask实现,还加上了进度百分比显示,这样用户能看到加载过程,应用也不会卡顿了:
第一步:准备UI布局(添加进度显示)
先在你的布局文件里加一个水平进度条和显示百分比的文本,用来展示加载进度:
<ProgressBar android:id="@+id/progressBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:progress="0" android:max="100" style="?android:attr/progressBarStyleHorizontal"/> <TextView android:id="@+id/progressText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0%"/>
第二步:定义数据模型类
先创建一个Item类来存储JSON里的对象数据,方便后续使用:
public class Item { private int id; private String name; public Item(int id, String name) { this.id = id; this.name = name; } // Getter方法,方便获取数据 public int getId() { return id; } public String getName() { return name; } }
第三步:实现AsyncTask子类
这个类会把所有耗时的读取、解析操作放到后台线程,同时实时更新进度到UI:
public class LoadJsonTask extends AsyncTask<Void, Integer, List<Item>> { private Context mContext; private ProgressBar mProgressBar; private TextView mProgressText; private OnLoadCompleteListener mListener; // 回调接口:用来通知主线程加载完成/失败 public interface OnLoadCompleteListener { void onLoadComplete(List<Item> items); void onLoadFailed(Exception e); } // 构造方法:传入需要的UI控件和回调 public LoadJsonTask(Context context, ProgressBar progressBar, TextView progressText, OnLoadCompleteListener listener) { mContext = context; mProgressBar = progressBar; mProgressText = progressText; mListener = listener; } // 任务执行前:初始化进度条 @Override protected void onPreExecute() { super.onPreExecute(); mProgressBar.setProgress(0); mProgressText.setText("0%"); } // 后台线程执行:读取JSON、解析数据 @Override protected List<Item> doInBackground(Void... voids) { List<Item> itemList = new ArrayList<>(); try { InputStream inputStream = mContext.getAssets().open("LocalTest.json"); long totalFileSize = inputStream.available(); long readSize = 0; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; // 用1024字节缓冲区高效读取 int bytesRead; // 读取文件,实时计算进度 while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); readSize += bytesRead; int readProgress = (int) ((readSize * 100) / totalFileSize); // 发布读取进度(占总进度的50%) publishProgress(readProgress / 2); } inputStream.close(); String jsonString = outputStream.toString("UTF-8"); outputStream.close(); // 解析JSON数组,实时计算解析进度 JSONObject jsonObject = new JSONObject(jsonString); JSONArray resultArray = jsonObject.getJSONArray("result"); int totalItems = resultArray.length(); for (int x = 0; x < totalItems; x++) { JSONObject itemObj = resultArray.getJSONObject(x); int id = itemObj.getInt("id"); String name = itemObj.getString("name"); itemList.add(new Item(id, name)); // 计算解析进度(占总进度的50%) int parseProgress = (int) (((x + 1) * 100) / totalItems); int totalProgress = 50 + (parseProgress / 2); publishProgress(totalProgress); } return itemList; } catch (IOException | JSONException e) { e.printStackTrace(); return null; } } // 主线程更新进度:更新进度条和文本 @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); int progress = values[0]; mProgressBar.setProgress(progress); mProgressText.setText(progress + "%"); } // 任务完成后:通知主线程处理结果 @Override protected void onPostExecute(List<Item> items) { super.onPostExecute(items); if (items != null) { mListener.onLoadComplete(items); // 加载完成后隐藏进度控件 mProgressBar.setVisibility(View.GONE); mProgressText.setVisibility(View.GONE); } else { mListener.onLoadFailed(new Exception("加载或解析JSON失败")); } } }
第四步:在Activity中使用AsyncTask
在你的Activity里启动这个任务,并处理加载完成后的结果:
public class MainActivity extends AppCompatActivity implements LoadJsonTask.OnLoadCompleteListener { private ProgressBar mProgressBar; private TextView mProgressText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 绑定UI控件 mProgressBar = findViewById(R.id.progressBar); mProgressText = findViewById(R.id.progressText); // 启动后台加载任务 new LoadJsonTask(this, mProgressBar, mProgressText, this).execute(); } // 加载完成后的回调:处理解析好的数据 @Override public void onLoadComplete(List<Item> items) { Toast.makeText(this, "加载完成!共" + items.size() + "条数据", Toast.LENGTH_SHORT).show(); // 这里可以把数据显示到RecyclerView或其他控件里,比如: // yourRecyclerView.setAdapter(new YourAdapter(items)); } // 加载失败的回调:提示用户 @Override public void onLoadFailed(Exception e) { Toast.makeText(this, "加载失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } }
关键说明
doInBackground:所有耗时操作(读取文件、解析JSON)都在这里执行,完全不影响主线程,所以应用不会冻结。publishProgress:后台线程中调用,用来把进度数据传递到主线程,触发onProgressUpdate更新UI。- 进度拆分:把加载过程分成读取文件(50%)和解析数据(50%),用户能看到完整的进度变化,体验更好。
- 回调接口:解耦AsyncTask和Activity,避免直接持有Activity引用导致内存泄漏。
内容的提问来源于stack exchange,提问作者Taha Sami
相关产品推荐
相关产品推荐

