Why is my JSON Volley request causing ANR if it is Asynchronous in itself and the data transfer into my database is handled within an AsyncTask?(JAVA)

huangapple go评论72阅读模式
英文:

Why is my JSON Volley request causing ANR if it is Asynchronous in itself and the data transfer into my database is handled within an AsyncTask?(JAVA)

问题

以下是您提供的代码部分的翻译:

这是我的控制器的OnCreate方法,我在其中调用AsyncTask:

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()) {
    //将JSON文件的下载移出UI线程
    startAsyncTask();
}
//用数据库数据填充GUI表格

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

下面是执行JSON请求并将新创建的项目填充到Room SQLite数据库的方法。这在AsyncTask中调用:

private void getData() throws InterruptedException {
    //用JSON对象填充数据库
    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, "获取数据失败", Toast.LENGTH_SHORT).show();
        }
    });
    //Thread.sleep(1000);
    queue.add(jsonArrayRequest);
}

这是我的AsyncTask,在我的控制器的OnCreate方法中调用。PreExecute应该打开进度条,然后DoInBackground应该进行数据传输,然后Post应该在DoInBackground完成后发生,并关闭进度条。然后,代码应返回到OnCreate方法并完成ASyncTask调用后的代码:

public void startAsyncTask() {
    ItemsAsyncTask itemsAsyncTask = new ItemsAsyncTask(this);
    //不确定这个整数参数是做什么的,但有一个YouTube视频说这非常重要。
    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;
        }
        //这应该在屏幕上开始进度条,然后在整个doInBackground方法中持续存在。
        activity.itemRV.setVisibility(View.INVISIBLE);
        activity.progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    protected String doInBackground(Integer... integers) {
        //这会调用执行数据传输的getData方法,远离UI线程。
        try {
            ItemListScreen activity = reference.get();
            if (activity == null || activity.isFinishing()) {
                return "返回";
            }
            activity.getData();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "返回";
    }

    @Override
    protected void onPostExecute(String s) {
        //在doInBackground方法完成后,这应该移除进度条,然后继续执行OnCreate方法的其余部分,其中GUI表格将填充数据库对象
        super.onPostExecute(s);
        ItemListScreen activity = reference.get();
        if (activity == null || activity.isFinishing()) {
            return;
        }
        activity.itemRV.setVisibility(View.VISIBLE);
        activity.progressBar.setVisibility(View.INVISIBLE);
    }
}

总之,尽管在AsyncTask中进行了Volley请求,但仍然会导致ANR。在ANR发生时仍会发生JSON下载到数据库的情况(我在Android Studio中查看数据库填充),并且Android设备会显示一个弹出消息,提示“应用未响应”,但Pre和Post方法在首次调用OnCreate时会立即闪烁在屏幕上,然后立即消失。因此,好像Post方法在DoInBackground完成之前就被调用了,或者类似的情况。有人知道为什么会发生这些情况吗?

此外,在首次打开应用程序并执行从URL下载JSON的挑战要求之后,数据库已经填满了,所以不会发生ANR。这只会在第一次使用应用程序并需要执行从URL下载JSON的挑战要求时发生。

英文:

Just an FYI, this is my first time working with JSON, Volley, or ASyncTask.

For a coding challenge I have to download a JSON array from a url into an android application and display the list of items after doing some editing and filtering. I am connecting to the JSON file via Volley in Java. In my "OnResponse" method within my JSONArrayRequest I am taking the JSON items, converting them into java items, and then adding them to a Room SQLite database. This heavy task caused an ANR so I moved the conversion and transfer into the database to an ASyncTask. It is still causing an ANR. How is this possible if all heavy methods are occurring Asynchronously? Relevant code and explanations of code below:

This is my OnCreate method for my controller where I call the AsyncTask:

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);
}

PHOTO VERSION:

enter image description here

Below is the method that performs the JSONRequest and fills a Room SQLite database with the newly created items. This is called within the ASyncTask.

private void getData () throws InterruptedException {
//fills database with JSON objects
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, url, null, new Response.Listener&lt;JSONArray&gt;() {
@Override
public void onResponse(JSONArray response) {
for (int i = 0; i &lt; response.length(); i++) {
try {
JSONObject responseObj = response.getJSONObject(i);
int id = responseObj.getInt(&quot;id&quot;);
int listId = responseObj.getInt(&quot;listId&quot;);
String name = responseObj.getString(&quot;name&quot;);
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, &quot;Failed to get the data&quot;, Toast.LENGTH_SHORT).show();
}
});
//Thread.sleep(1000);
queue.add(jsonArrayRequest);
}

PHOTO VERSION:

enter image description here

This is my AsyncTask which is called in the OnCreate method of my controller. The PreExecute should turn on the progress bar, then the DoInBackground should make the data transfer, then the Post should occur after the DoInBackground is done and turn off the progress bar. The code should then go back to the OnCreate method and finish out the code below the ASyncTask call:

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&lt;Integer, Integer, String&gt;{
private WeakReference&lt;ItemListScreen&gt; reference;
ItemsAsyncTask(ItemListScreen activity){
reference = new WeakReference&lt;ItemListScreen&gt;(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 &quot;return&quot;;
}
activity.getData();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return &quot;return&quot;;        }
@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);
}
}

PHOTO VERSION:

enter image description here

So, in summary, My volley request is causing an ANR even though it is within an ASyncTask. The download of the JSON into the database still occurs while the ANR is happening (I am viewing the database populate in Android Studio) and the android device is showing a pop up message saying "application is not responding", but the Pre and Post methods instantly blink on the screen when the oncreate first occurs and then are instantly gone again. So it's like the Post method is being called before the DoInBackground is complete or something. Does anyone know why this things are occurring?

Also, after the application does this big download the first time the application is opened, upon further openings the database is already full so the ANR doesnt occur. It only happens the first time the app is used and it has to perform the challenge requirement of downloading the JSON from url.

I tried ASync but the ANR still occurs

EDIT: I updated my code as a user suggested and now have one small error left to solve. Even though I am calling a weak reference of activity for the response, it is still not recognizing response. Any Idea why this is the case? Here is the code:

@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 &quot;return&quot;;
}
for (int i = 0; i &lt; activity.response.length(); i++) {
try {
JSONObject responseObj = activity.response.getJSONObject(i);
int id = responseObj.getInt(&quot;id&quot;);
int listId = responseObj.getInt(&quot;listId&quot;);
String name = responseObj.getString(&quot;name&quot;);
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 &quot;return&quot;;        }

答案1

得分: 0

Your request is in the AsyncTask but the response is in the UI thread. Volley runs requests on a worker thread and then delivers the response in the main thread, so your AsyncTask is pointless.

你的请求在AsyncTask中,但响应在UI线程中。Volley在工作线程上运行请求,然后在主线程中传递响应,因此你的AsyncTask是多余的。

You need to run the code you have now in the response (inserting into the database) in the background.

你需要在响应中(插入到数据库中)在后台运行你现在的代码。

So.

  1. getData should just launch the Volley request.
  2. getData应该只是启动Volley请求。
  3. In your Volley response, start the AsyncTask.
  4. 在Volley响应中,启动AsyncTask。
  5. Update the AsyncTask to take the response JSON and put it in the database.
  6. 更新AsyncTask以接收响应JSON并将其放入数据库。
英文:

> My volley request is causing an ANR even though it is within an ASyncTask.

Your request is in the AsyncTask but the response is in the UI thread. Volley runs requests on a worker thread and then delivered the response in the main thread, so your AsyncTask is pointless.

You need to run the code you have now in the response (inserting into the database) in the background.

So.

  1. getData should just launch the Volley request.
  2. In your Volley response, start the AsyncTask
  3. Update the AsyncTask to take the response JSON and put it in the database.

huangapple
  • 本文由 发表于 2023年5月26日 10:57:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76337382.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定