154

I'm trying to convert an Activity to fragment. The error mark on runOnUiThread. on the past:

GoogleActivityV2 extends from Activity. runOnUiThread in class ExecuteTask. class ExecuteTask nested on activity.

(Run ok) now :

GoogleActivityV2 extends from Fragment. runOnUiThread in class ExecuteTask. class ExecuteTask nested on activity. (Error on runOnUiThread)

here is my code

public class GoogleActivityV2 extends SherlockMapFragment implements OnMapClickListener , OnMapLongClickListener , OnCameraChangeListener , TextWatcher {


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
        View rootView = inflater.inflate(R.layout.activity_googlev2, container, false);
        Init();
        adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_dropdown_item_1line);
        textView = (AutoCompleteTextView) getView().findViewById(R.id.autoCompleteTextView1);
        return rootView;
    }

    public void onCameraChange(CameraPosition arg0){
        // TODO Auto-generated method stub
    }

    public void onMapLongClick(LatLng arg0){
        llLoc = arg0;
        stCommand = "onTouchEvent";
        lp = new ExecuteTask();
        lp.execute();
    }

    public void onMapClick(LatLng arg0){
        // TODO Auto-generated method stub
    }

    class ExecuteTask extends AsyncTask<String, String, String> {
        @Override
        protected void onPreExecute(){
            super.onPreExecute();
            if(stCommand.compareTo("AutoCompleteTextView") != 0) {
                pDialog = new ProgressDialog(getActivity());
                pDialog.setMessage(Html.fromHtml("<b>Search</b><br/>Loading ..."));
                pDialog.setIndeterminate(false);
                pDialog.setCancelable(false);
                pDialog.show();
            }
        }

        protected String doInBackground(String ... args){
            do something
            return null;
        }

        @Override
        protected void onPostExecute(String file_url){
            if(stCommand.compareTo("AutoCompleteTextView") != 0) pDialog.dismiss();
            runOnUiThread(new Runnable() {
                public void run(){
                    do something
                }
            });
        }
    }
    public void afterTextChanged(Editable s){
        // TODO Auto-generated method stub
    }

    public void beforeTextChanged(CharSequence s, int start, int count, int after){
        // TODO Auto-generated method stub
    }

    public void onTextChanged(CharSequence s, int start, int before, int count){
        // TODO Auto-generated method stub
    }
}

the error says: Eclipse Error

how can I fix this error?

rene
  • 41,474
  • 78
  • 114
  • 152
Tai Dao
  • 3,407
  • 7
  • 31
  • 54
  • 8
    The code you write inside onPostExecute already run inside main thread. On the other hands, you don't need to invoce runOnUiThread(). – rciovati May 07 '13 at 17:35
  • @rciovati "do something" in and out onPostExecute are different – Tai Dao May 07 '13 at 17:37

7 Answers7

327

Try this: getActivity().runOnUiThread(new Runnable...

It's because:

1) the implicit this in your call to runOnUiThread is referring to AsyncTask, not your fragment.

2) Fragment doesn't have runOnUiThread.

However, Activity does.

Note that Activity just executes the Runnable if you're already on the main thread, otherwise it uses a Handler. You can implement a Handler in your fragment if you don't want to worry about the context of this, it's actually very easy:

// A class instance
private Handler mHandler = new Handler(Looper.getMainLooper());

// anywhere else in your code
mHandler.post(<your runnable>);
// ^ this will always be run on the next run loop on the main thread.

EDIT: @rciovati is right, you are in onPostExecute, that's already on the main thread.

bclymer
  • 6,679
  • 2
  • 27
  • 36
  • 5
    Some times getActivity().runOnUiThread is resulting into NullPointerException. Could you explain? – Vadiraj Purohit Apr 27 '16 at 19:46
  • 2
    @developer1011 that will happen when the fragment has been detached from the activity. This is common in async tasks, because the activity could have been destroyed during the long running operation, so it no longer exists to `get`. Always check for null first. – bclymer Apr 27 '16 at 21:15
  • 5
    Thanks. I surrounded ```getActivity().runOnUiThread``` with ```if (isAdded())``` and it works fine – Vadiraj Purohit Apr 29 '16 at 19:54
  • 1
    @bclymer given this answer is the de facto reference on Google for UI thread in fragments, some more detail on the last section (how to implement a Handler that gets the same job done) would be nice! – LS97 Apr 10 '18 at 11:36
15

Use a Kotlin extension function

fun Fragment?.runOnUiThread(action: () -> Unit) {
    this ?: return
    if (!isAdded) return // Fragment not attached to an Activity
    activity?.runOnUiThread(action)
}

Then, in any Fragment you can just call runOnUiThread. This keeps calls consistent across activities and fragments.

runOnUiThread {
    // Call your code here
}

NOTE: If Fragment is no longer attached to an Activity, callback will not be called and no exception will be thrown

If you want to access this style from anywhere, you can add a common object and import the method:

object ThreadUtil {
    private val handler = Handler(Looper.getMainLooper())

    fun runOnUiThread(action: () -> Unit) {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            handler.post(action)
        } else {
            action.invoke()
        }
    }
}
Gibolt
  • 42,564
  • 15
  • 187
  • 127
12

For Kotlin on fragment just do this

activity?.runOnUiThread(Runnable {
        //on main thread
    })
patel dhruval
  • 1,002
  • 10
  • 12
4

In Xamarin.Android

For Fragment:

this.Activity.RunOnUiThread(() => { yourtextbox.Text="Hello"; });

For Activity:

RunOnUiThread(() => { yourtextbox.Text="Hello"; });

Happy coding :-)

Matheus Miranda
  • 1,755
  • 2
  • 21
  • 36
Ripdaman Singh
  • 596
  • 6
  • 8
2

I used this for getting Date and Time in a fragment.

private Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // Inflate the layout for this fragment
    View root = inflater.inflate(R.layout.fragment_head_screen, container, false);

    dateTextView =  root.findViewById(R.id.dateView);
    hourTv = root.findViewById(R.id.hourView);

        Thread thread = new Thread() {
        @Override
        public void run() {
            try {
                while (!isInterrupted()) {
                    Thread.sleep(1000);
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            //Calendario para obtener fecha & hora
                            Date currentTime = Calendar.getInstance().getTime();
                            SimpleDateFormat date_sdf = new SimpleDateFormat("dd/MM/yyyy");
                            SimpleDateFormat hour_sdf = new SimpleDateFormat("HH:mm a");

                            String currentDate = date_sdf.format(currentTime);
                            String currentHour = hour_sdf.format(currentTime);

                            dateTextView.setText(currentDate);
                            hourTv.setText(currentHour);
                        }
                    });
                }
            } catch (InterruptedException e) {
                Log.v("InterruptedException", e.getMessage());
            }
        }
    };
}
1

You can also post runnable using the view from any other thread. But be sure that the view is not null:

 tView.post(new Runnable() {
                    @Override
                    public void run() {
                        tView.setText("Success");
                    }
                });

According to the Documentation:

"boolean post (Runnable action) Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread."

Jawad Zeb
  • 523
  • 4
  • 18
1

This work with me perfectly in "android java":

 try {
        requireActivity().runOnUiThread(new Runnable() {
            public void run() {
                Toast toast = Toast.makeText(requireView().getContext(), msg, Toast.LENGTH_LONG);
                toast.show();
            }
        });
    } catch (Exception ignored) {
    }
Ayman.dev
  • 51
  • 12