cannot touch view from another thread, how to make

2019-08-21 23:30发布

问题:

I cant figure out why i cant set text to my textView tv. getting:

E/AndroidRuntime(686): android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.

I tried many ways to make it right. As you can see i tried Handler because i had the same problem with toasts. Now toast works but setText doesnt :(( Please someone help me, how should i configure this handler?

public class calculate extends Activity implements OnClickListener {
    private myService myService; //bound service instance
    private boolean serviceStarted;
    View show_map;
    View data;
    View start;
    View stop;
    public TextView tv;
    private Location loc;
    private boolean initiated=false;
    private float distance=0;
    UIHandler uiHandler;
    route_calc rc;

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.calculate);
    tv=(TextView)findViewById(R.id.textView1);
    show_map=findViewById(R.id.button1);
    show_map.setOnClickListener(this);
    data=findViewById(R.id.button2);
    data.setOnClickListener(this);
    start=findViewById(R.id.button3);
    start.setOnClickListener(this);
    stop=findViewById(R.id.button4);
    stop.setVisibility(View.INVISIBLE);
    stop.setOnClickListener(this);
    HandlerThread uiThread = new HandlerThread("UIHandler");
    uiThread.start();
    uiHandler = new UIHandler( uiThread.getLooper());

}

public void onDestroy(){
    super.onDestroy();
}


@Override
public void onClick(View v) {
    Intent i;
    switch(v.getId()){
    case R.id.button1:
        i=new Intent(this,Map.class);
        startActivity(i);
        break;
    case R.id.button2:
        i=new Intent(this,data.class);
        startActivity(i);
        break;
    case R.id.button3:
        startService();

        break;
    case R.id.button4:
        stopService();
        break;
    }

}

//connection between this activity and service myService
ServiceConnection myServConn = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        myService = null;
    }
    @Override
    public void onServiceConnected(ComponentName arg0, IBinder binder) {
        myService = ((myService.MyBinder)binder).getMyService();
    }
};

private void startService() {
    Intent intent = new Intent(this, myService.class); 
    startService(intent);
    //Bind MyService here
    bindService(intent, myServConn, BIND_AUTO_CREATE);
    stop.setVisibility(View.VISIBLE);
    serviceStarted = true;
    rc = new route_calc();
    rc.start();
}

private void stopService() {
    if(serviceStarted) {
        Intent intent = new Intent(this, myService.class);
        //Unbind MyService here
        unbindService(myServConn);
        stopService(intent);
        stop.setVisibility(View.INVISIBLE);

        serviceStarted = false;
    }
}

void showToast(String s){
    handleUIRequest(s);
}

void setText(){
    handleUISetText();
}

class route_calc extends Thread{
    Location begin;
    public void run() {
        float temp;


        while(!initiated){
            try{

                loc=myService.getLocation();

            }
            catch(Exception e){

            }

            if(loc!=null){
                begin=loc;
                initiated=true;
                showToast("zadzialalo");
            }

        }
        while(true){
            loc=myService.getLocation();
            temp=begin.distanceTo(loc);
            distance=distance+temp;
            tv.setText("przejechales "+distance+" m");
            System.err.println(distance);
            begin=loc;
            try {
                this.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}


private final class UIHandler extends Handler
{
    public static final int DISPLAY_UI_TOAST = 0;
    public static final int TV_SET_TEXT = 1;

    public UIHandler(Looper looper)
    {
        super(looper);
    }


    public void handleMessage(Message msg)
    {
        switch(msg.what)
        {
        case UIHandler.DISPLAY_UI_TOAST:
        {
            Context context = getApplicationContext();
            Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
            t.show();
        }

        case UIHandler.TV_SET_TEXT:
        {

            tv.setText("przejechałeś "+distance+" m");
        }
        default:
            break;
        }
    }
}

protected void handleUIRequest(String message)
{
    Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
    msg.obj = message;
    uiHandler.sendMessage(msg);
}

protected void handleUISetText(){
    Message msg=uiHandler.obtainMessage(UIHandler.TV_SET_TEXT);
    uiHandler.sendMessage(msg);
}

}

回答1:

It seems like you put your entire Activity here, and that it also includes a service, and you didn't try to narrow down your problem.

in your route_calc thread you call showToast, this is probably one of your problems, you should call showToast (or any other UI function) from your Handler.

Something like this:

Do anything you want on your thread:

   new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        someHeavyStuffHere(); //Big calculations or file download here.
                        handler.sendEmptyMessage(SUCCESS);
                    }
                    catch (Exception e)
                    {
                        handler.sendEmptyMessage(FAILURE);
                    }
                }
            }).start();

When your data is ready, tell the handler to put it in a view and show it:

protected Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            if (msg.what == SUCCESS)
            {
                setCalculatedDataToaView(); // the data you calculated from your thread can now be shown in one of your views.
            }
            else if (msg.what == FAILURE)
            {
                errorHandlerHere();//could be your toasts or any other error handling...
            }
        }
    };