2

have a first question in my career in stackoverflow. I hope that are you help me.

What's happen:

I have Fragment where i want to set some recyclerView with some dates, it worked until I wanted to do ProgressDialog with some ErrorMgs.

This is my 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;
}



I add 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();
    }

}



And the last method is 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.");
    }
}



Now about Errors:
1) Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I understand that the main part of the Error is here:
After added SLEEP Make ERROR

@Override
    protected Void doInBackground(Void... voids) {
        try {
            Thread.sleep(2200); 
            populateFromJson();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
  • 1
    Is the onResponse method running in the UI thread (I don't think so). If it is not, the you cannot do anything with `recyclerView` in that method. – Dale Wilson Nov 06 '17 at 18:56
  • In the onResponse() callback of your Retrofit call you are calling methods of recyclerView. View related methods can be called from Main Thread but in your case onResponse method will get executed in the AsyncTask. P.S. Why did you create an async task in the first place, the enqueue method of the Retrofit call will get executed in a background thread. No need to put it in the Async task. – Rohan Arora Nov 06 '17 at 18:57
  • @RohanArora I need the Async task for progress dialog. After some projects and some tutorials i make coclusion that the Aync task fo that is better...If im not wirte, can you suggest something to me? Thanks! – Pavel Petkevich Nov 06 '17 at 19:37
  • @PavelPetkevich No need to create to create AsyncTask for Progress Dialogs. Just call progressDialog.show() just before call.enqueue() and progressDialog.dimiss() in onResult() and onFailure() callbacks. – Rohan Arora Nov 06 '17 at 19:42
  • @PavelPetkevich ProgressDialogs are now deprecated in API 26 and above. – Rohan Arora Nov 06 '17 at 19:42
  • @RohanArora Thanks, you're right, but infact no ive a good expirience with AsyncTask&Enqueue and how NOT use it:) – Pavel Petkevich Nov 06 '17 at 19:54

4 Answers4

0

No, the root cause of error is that you are trying to work with views form worker thread. Should get your call's result inside this method

@Override
protected void onPostExecute(Void result) {
  //work with you RecyclerView here
}
Kulebin
  • 246
  • 1
  • 8
0

Do not use an AsyncTask. Just call populateFromJson() in onCreateView().

greenapps
  • 11,154
  • 2
  • 16
  • 19
0

populateFromJson() try to use recicleView. View (and View subclasses) Object cannot be used from any thread except the Activity thread.

When you do .execute() on your AsyncTask onPreExecute and onPostExecute will be executed by the activity thread and doInBackground by a child Thread.

Once you get this it will be easy for you to understand that to avoid that error you should call populateFromJson() in onPostExecute

N.B. if your doInBackground method really needs to do just a wait there are more efficent way to do this. have a look here and here

Luca Reccia
  • 578
  • 3
  • 16
  • Ok, thats mean that i should to call populateFromJson() in onPostExecute(), in doInBackground only threadSleep. Or all make in doInBackground (without populateFromJson() ), is it correct ?:) – Pavel Petkevich Nov 06 '17 at 19:32
  • yeah. The usual pattern is this: onPreExecute to get data from view, doInBackground to do heavy calculation with those data and onPostExecute to show results in views. – Luca Reccia Nov 06 '17 at 19:34
  • Take a look at [this](https://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...)). It's a way to show the user the progress, for example using a progress bar. – Luca Reccia Nov 06 '17 at 19:44
  • Thanks, a lot! Is it work for me, ill try to make this better if its possible with your suggest link! – Pavel Petkevich Nov 06 '17 at 19:47
-1

You can't update the view from a background thread, have your async task return the item list in doInBackground, onPostExecuted will receive the list as a parameter and from that method you can update the recycler view.

An easier way would be to replace async task with rxjava

Umar Ahmed
  • 163
  • 1
  • 5
  • 8
  • Replacing a simple async task with a heavyweight library like rxjava is a horrible idea. – Gabe Sechan Nov 06 '17 at 19:31
  • Just thought it will help with networking problem and since he is already using retrofit will make consuming the api easier, – Umar Ahmed Nov 06 '17 at 19:38
  • Hi, @UmarKhattab have you got some Example for usage? – Pavel Petkevich Nov 06 '17 at 19:44
  • Checkout [this](https://code.tutsplus.com/tutorials/getting-started-with-retrofit-2--cms-27792) tutorial, its pretty straightforward. The first approach used just retrofit and then rxJava and retrofit – Umar Ahmed Nov 07 '17 at 08:35