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

Volley异步请求+AsyncTask仍触发ANR及response识别问题排查

问题背景

我是首次使用JSON、Volley和AsyncTask的Android开发者,要完成一个编码挑战:从URL下载JSON数组到应用,编辑过滤后展示成列表。

问题1:ANR仍出现

最初我在Volley的JsonArrayRequest的onResponse方法里把JSON数据转成Java对象并插入Room数据库,结果触发了ANR;后来把转换和入库逻辑移到AsyncTask里,还是会出现ANR。
现象:只有应用首次启动时会ANR(后续启动因为数据库已经有数据就不会出现),ANR期间数据库还在填充数据,进度条只闪一下就消失,感觉AsyncTask的onPostExecute提前执行了。

控制器的onCreate方法

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedClosableObjects()
                .penaltyLog()
                .build());


        repository = new Repository(getApplication());
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item_list_screen);


        itemRV = findViewById(R.id.itemListRecyclerView);
        progressBar = findViewById(R.id.progressBar1);

        RequestQueue queue1 = Volley.newRequestQueue(ItemListScreen.this);
        queue = queue1;

        final ItemAdapter itemAdapter = new ItemAdapter(this);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        RecyclerView recyclerView  = findViewById(R.id.itemListRecyclerView);
        recyclerView.setAdapter(itemAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        if(repository.getAllItems().isEmpty()) {
            //moves the download of the json file offof the UI thread
            startAsyncTask();
        }
        //fills GUI table with database data

        items = repository.getAllItems();
        itemAdapter.setItems(items);

数据获取及入库方法

private void getData () throws InterruptedException {
        //fills database with JSON objects
        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, url, null, new Response.Listener<JSONArray>() {
            @Override
            public void onResponse(JSONArray response) {
                for (int i = 0; i < response.length(); i++) {
                    try {
                        JSONObject responseObj = response.getJSONObject(i);
                        int id = responseObj.getInt("id");
                        int listId = responseObj.getInt("listId");
                        String name = responseObj.getString("name");
                        Item item = new Item(id, listId, name);
                        repository.insert(item);

                    } catch (JSONException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                        Toast.makeText(ItemListScreen.this, "Failed to get the data", Toast.LENGTH_SHORT).show();
                    }
                });
        //Thread.sleep(1000);
            queue.add(jsonArrayRequest);
}

AsyncTask实现

public void startAsyncTask(){
        ItemsAsyncTask itemsAsyncTask = new ItemsAsyncTask(this);
        //not sure what this integerparameter does but a youtube video said this was very important to have.
        itemsAsyncTask.execute(10);
    }
    private static class ItemsAsyncTask extends AsyncTask<Integer, Integer, String>{

        private WeakReference<ItemListScreen> reference;

        ItemsAsyncTask(ItemListScreen activity){
            reference = new WeakReference<ItemListScreen>(activity);
        }
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            ItemListScreen activity = reference.get();
            if (activity == null || activity.isFinishing()){
            return;
            }
            //This should begin the progress bar on the screen, which should continue throughout the doInBackground method.
            activity.itemRV.setVisibility(View.INVISIBLE);
            activity.progressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected String doInBackground(Integer... integers) {
            //this calls the getData method that performs the data transfer away from the UI thread.
            try {
                ItemListScreen activity = reference.get();
                if (activity == null || activity.isFinishing()){
                    return "return";
                }
                activity.getData();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "return";        }



        @Override
        protected void onPostExecute(String s) {
            //After the doInBackground method is complete, this should remove the progress bar
            // and then continue on to the rest of the OnCreate method where the GUI table will be filled witht he database objects
            super.onPostExecute(s);
            ItemListScreen activity = reference.get();
            if (activity == null || activity.isFinishing()){
                return;
            }
            activity.itemRV.setVisibility(View.VISIBLE);
            activity.progressBar.setVisibility(View.INVISIBLE);
        }
    }

问题2:response识别失败

按照建议修改代码后,又出现新问题:通过弱引用获取Activity后,代码识别不了response对象,相关代码如下:

@Override
    protected String doInBackground(Integer... integers) {
        //this calls the getData method that performs the data transfer away from the UI thread.
        try {
            ItemListScreen activity = reference.get();
            if (activity == null || activity.isFinishing()){
                return "return";
            }
            for (int i = 0; i < activity.response.length(); i++) {
                try {
                    JSONObject responseObj = activity.response.getJSONObject(i);
                    int id = responseObj.getInt("id");
                    int listId = responseObj.getInt("listId");
                    String name = responseObj.getString("name");
                    Item item = new Item(id, listId, name);
                    activity.repository.insert(item);

                } catch (JSONException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "return";        }

解决方案

针对问题1:ANR仍出现的原因及解决

原因

  1. Volley回调运行在UI线程:你把getData()放到AsyncTask的doInBackground里,但Volley的onResponse回调本身就是在UI线程执行的,里面的循环插入数据库操作直接阻塞了UI,导致ANR。
  2. AsyncTask提前结束:getData()只是把请求加入Volley队列就返回了,doInBackground执行完activity.getData()就直接return,触发onPostExecute,所以进度条提前消失,但此时Volley请求还在后台处理,数据库插入仍在UI线程运行,继续阻塞引发ANR。
  3. Room插入未异步:如果你的repository.insert(item)是同步方法,大量插入操作在UI线程执行会直接卡死界面。

解决步骤

推荐用Coroutine替代AsyncTask(AsyncTask已被官方废弃),同时确保所有耗时操作都在后台线程执行:

  1. 添加CoroutineScope到Activity
private CoroutineScope scope = CoroutineScope(Dispatchers.Main + Job());
  1. 修改getData()方法,将数据处理移到IO线程
private void getData() {
    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, url, null, response -> {
        // 在IO线程处理数据转换和数据库插入
        scope.launch(Dispatchers.IO) {
            for (int i = 0; i < response.length(); i++) {
                try {
                    JSONObject responseObj = response.getJSONObject(i);
                    int id = responseObj.getInt("id");
                    int listId = responseObj.getInt("listId");
                    String name = responseObj.getString("name");
                    Item item = new Item(id, listId, name);
                    repository.insert(item);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            // 回到主线程更新UI和列表
            launch(Dispatchers.Main) {
                progressBar.setVisibility(View.INVISIBLE);
                itemRV.setVisibility(View.VISIBLE);
                items = repository.getAllItems();
                itemAdapter.setItems(items);
            }
        }
    }, error -> {
        Toast.makeText(ItemListScreen.this, "Failed to get the data", Toast.LENGTH_SHORT).show();
        progressBar.setVisibility(View.INVISIBLE);
        itemRV.setVisibility(View.VISIBLE);
    });
    queue.add(jsonArrayRequest);
}
  1. 修改启动逻辑,去掉AsyncTask
if(repository.getAllItems().isEmpty()) {
    itemRV.setVisibility(View.INVISIBLE);
    progressBar.setVisibility(View.VISIBLE);
    getData();
} else {
    items = repository.getAllItems();
    itemAdapter.setItems(items);
}
  1. 确保Room插入异步
    如果用Java,给Repository的insert方法套一层AsyncTask:
public void insert(Item item) {
    new InsertAsyncTask(itemDao).execute(item);
}

private static class InsertAsyncTask extends AsyncTask<Item, Void, Void> {
    private ItemDao itemDao;

    InsertAsyncTask(ItemDao itemDao) {
        this.itemDao = itemDao;
    }

    @Override
    protected Void doInBackground(Item... items) {
        itemDao.insert(items[0]);
        return null;
    }
}

针对问题2:response识别失败的原因及解决

原因

response是Volley回调里的局部变量,你并没有把它定义成Activity的成员变量,所以activity.response自然找不到这个字段。

解决步骤

不要试图从Activity直接获取response,而是在Volley的onResponse回调里拿到response后,直接传给后台处理逻辑。如果坚持用AsyncTask,可以把JSONArray作为参数传给AsyncTask:

  1. 修改Volley的onResponse回调
@Override
public void onResponse(JSONArray response) {
    new InsertItemsAsyncTask(repository, ItemListScreen.this).execute(response);
}
  1. 重新定义AsyncTask
private static class InsertItemsAsyncTask extends AsyncTask<JSONArray, Void, Void> {
    private Repository repository;
    private WeakReference<ItemListScreen> activityRef;

    InsertItemsAsyncTask(Repository repository, ItemListScreen activity) {
        this.repository = repository;
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        ItemListScreen activity = activityRef.get();
        if (activity != null && !activity.isFinishing()) {
            activity.itemRV.setVisibility(View.INVISIBLE);
            activity.progressBar.setVisibility(View.VISIBLE);
        }
    }

    @Override
    protected Void doInBackground(JSONArray... jsonArrays) {
        JSONArray response = jsonArrays[0];
        for (int i = 0; i < response.length(); i++) {
            try {
                JSONObject responseObj = response.getJSONObject(i);
                int id = responseObj.getInt("id");
                int listId = responseObj.getInt("listId");
                String name = responseObj.getString("name");
                Item item = new Item(id, listId, name);
                repository.insert(item);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void unused) {
        super.onPostExecute(unused);
        ItemListScreen activity = activityRef.get();
        if (activity != null && !activity.isFinishing()) {
            activity.progressBar.setVisibility(View.INVISIBLE);
            activity.itemRV.setVisibility(View.VISIBLE);
            List<Item> items = activity.repository.getAllItems();
            activity.itemAdapter.setItems(items);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:54:59