【问题标题】:Android: Async Task Thread Sleep ErrorAndroid:异步任务线程睡眠错误
【发布时间】:2017-11-06 18:47:43
【问题描述】:

我在 stackoverflow 的职业生涯中遇到了第一个问题。我希望你能帮助我。

发生了什么:

我有 Fragment,我想在其中设置一些带有一些日期的 recyclerView,它一直有效,直到我想用一些 ErrorMgs 做 ProgressDialog。

这是我的 onCreateView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.recycler_view, container, false);
    recyclerView = (RecyclerView) view.findViewById(R.id.my_recycler_view);
    textViewErrorMsg = (TextView) view.findViewById(R.id.tv_error_message);
    progressBarLoading = (ProgressBar) view.findViewById(R.id.pb_loading);
    new TakeDates().execute();
    return view;
}



我添加了 AsyncTask:

@SuppressLint("StaticFieldLeak")
class TakeDates extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog = new ProgressDialog(getActivity());
        progressDialog.getWindow().clearFlags(
                WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        progressDialog.setMessage("Loading places...");
        progressDialog.setCanceledOnTouchOutside(false);
        progressDialog.show();
    }

    @Override
    protected Void doInBackground(Void... voids) {
        try {
            Thread.sleep(2200);
            populateFromJson();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        if (progressDialog.isShowing())
            progressDialog.dismiss();
    }

}



最后一个方法是populateFromJson:

 private void populateFromJson() {
    ConnectivityManager conMgr = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    assert conMgr != null;
    NetworkInfo i = conMgr.getActiveNetworkInfo();
    if (i != null && i.isConnected() && i.isAvailable()) {
        ApiService api = RetroClient.getApiService(PLACES_URL);

        Call<PlaceList> call = api.getMyJSON();

        call.enqueue(new Callback<PlaceList>() {
            @Override
            public void onResponse(Call<PlaceList> call, Response<PlaceList> response) {
                if (response.isSuccessful()) {
                    itemList = response.body().getItemList();
                    adapter = new RecyclerViewPlaceAdapter(itemList, getContext());
                    recyclerView.setAdapter(adapter);
                    recyclerView.setHasFixedSize(true);
                    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
                    showPlaces();
                } else {
                    showErrorMessage("Try to restart your app.");
                }
            }

            @Override
            public void onFailure(Call<PlaceList> call, Throwable t) {
                showErrorMessage("Here is the main error: " + t.getMessage() + "\nTry to restart your app.");
            }
        });
    } else {
        showErrorMessage("Internet Connection Not Available" + "\nTry to find better connection to the internet.");
    }
}



现在关于错误:
1)原因:android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能接触到它的视图。
我知道错误的主要部分在这里:
添加后 SLEEP Make ERROR

@Override
    protected Void doInBackground(Void... voids) {
        try {
            Thread.sleep(2200); 
            populateFromJson();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

【问题讨论】:

  • 是在UI线程中运行的onResponse方法吗(我不这么认为)。如果不是,您将无法在该方法中对recyclerView 执行任何操作。
  • 在您的 Retrofit 调用的 onResponse() 回调中,您正在调用 recyclerView 的方法。可以从主线程调用视图相关的方法,但在您的情况下,onResponse 方法将在 AsyncTask 中执行。附言你为什么首先创建一个异步任务,Retrofit 调用的 enqueue 方法将在后台线程中执行。不需要放在异步任务中。
  • @RohanArora 我需要异步任务来进行进度对话框。在一些项目和一些教程之后,我认为 Aync 任务更好......如果我不知道,你能给我一些建议吗?谢谢!
  • @PavelPetkevich 无需为进度对话框创建 AsyncTask。只需在 onResult() 和 onFailure() 回调中的 call.enqueue() 和 progressDialog.dimiss() 之前调用 progressDialog.show()。
  • @PavelPetkevich ProgressDialogs 现在在 API 26 及更高版本中已弃用。

标签: java android multithreading android-asynctask thread-sleep


【解决方案1】:

populateFromJson() 尝试使用recicleView。 View(和 View 子类)对象不能在除 Activity 线程之外的任何线程中使用。

当您在 AsyncTask 上执行 .execute() 时,onPreExecuteonPostExecute 将由活动线程执行,doInBackground 将由子线程执行。

一旦你得到这个,你就会很容易理解为了避免这个错误你应该在onPostExecute中调用populateFromJson()

注意如果您的 doInBackground 方法真的需要等待,那么有更有效的方法可以做到这一点。看看herehere

【讨论】:

  • 好的,这意味着我应该在 onPostExecute() 中调用 populateFromJson(),在 doInBackground 中只调用 threadSleep。或者全部在 doInBackground 中制作(没有 populateFromJson() ),是否正确?:)
  • 是的。通常的模式是这样的:onPreExecute 从视图中获取数据,doInBackground 对这些数据进行大量计算,onPostExecute 在视图中显示结果。
  • 看看this。这是一种向用户显示进度的方式,例如使用进度条。
  • 非常感谢!它对我有用吗,如果可能的话,我会尝试通过您的建议链接来改进它!
【解决方案2】:

不,错误的根本原因是您正在尝试使用工作线程的视图。 应该在这个方法中得到你的调用结果

@Override
protected void onPostExecute(Void result) {
  //work with you RecyclerView here
}

【讨论】:

    【解决方案3】:

    不要使用 AsyncTask。只需在 onCreateView() 中调用 populateFromJson()

    【讨论】:

    • @greenapps 我听说 AsyncTask(他们的控制 doInBackground,onPreExecute,onPostExecute)是最好的方式,如果我想使用进度对话框......不是吗?
    • 没有。我认为不是。你想要一个吗?
    【解决方案4】:

    您不能从后台线程更新视图,让您的异步任务在 doInBackground 中返回项目列表,onPostExecuted 将接收列表作为参数,并且您可以通过该方法更新回收器视图。

    更简单的方法是用 rxjava 替换异步任务

    【讨论】:

    • 用 rxjava 这样的重量级库替换简单的异步任务是一个可怕的想法。
    • 只是认为这将有助于解决网络问题,并且由于他已经在使用改造将使使用 api 更容易,
    • 嗨,@UmarKhattab 你有一些使用示例吗?
    • 结帐this 教程,它非常简单。第一种方法只使用改造,然后使用 rxJava 和改造
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 1970-01-01
    相关资源
    最近更新 更多