David David - 1 month ago 11
Java Question

Android "Only the original thread that created a view hierarchy can touch its views." error in Fragment

hi i've got this simple timer in my app which is runs in every 3 seconds.
it works perfectly if it's not in a fragment class.
But here in fragment i always got the error: Only the original thread that created a view hierarchy can touch its views.
Please help me how to correct it!
Thank you guys!

timer = new Timer();
timer.schedule(new TimerTask() {

@Override
public void run() {
String timeStamp = new SimpleDateFormat(
"yyyy.MM.dd HH:mm:ss").format(Calendar
.getInstance().getTime());
System.out.println("TimeStamp: " + timeStamp);
// Read And Write Register Sample
port = Integer.parseInt(gConstants.port);
String refe = "0";// HEX Address
ref = Integer.parseInt(refe, 16);// Hex to int
count = 10; // the number Address to read
SlaveAddr = 1;
astr = gConstants.ip; // Modbus Device

InetAddress addr;
try {
addr = InetAddress.getByName(astr);
con = new TCPMasterConnection(addr); // the
// connection
} catch (UnknownHostException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}

// 1.Prepare the request
/************************************/
Rreq = new ReadMultipleRegistersRequest(ref, count);
Rres = new ReadMultipleRegistersResponse();

Rreq.setUnitID(SlaveAddr); // set Slave Address
Rres.setUnitID(SlaveAddr); // set Slave Address

// 2. Open the connection
con.setPort(port);
try {
con.connect();
System.out.println("Kapcsolódva!");
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
con.setTimeout(2500);
// 3. Start Transaction
trans = new ModbusTCPTransaction(con);
trans.setRetries(5);
trans.setReconnecting(true);
trans.setRequest(Rreq);

try {
trans.execute();
} catch (ModbusIOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ModbusSlaveException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ModbusException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/* Print Response */
Rres = (ReadMultipleRegistersResponse) trans
.getResponse();

System.out.println("Connected to= " + astr
+ con.isConnected() + " / Start Register "
+ Integer.toHexString(ref));

count = 10;
for (int k = 0; k < count; k++) {
System.out.println("The value READ: "
+ Rres.getRegisterValue(k) + " "
+ Rres.getUnitID());
ki_adat = ki_adat + Rres.getRegisterValue(k) + "\n";


// Adatbázisba írás
ContentValues modbusData = new ContentValues();
modbusData.put("Value", Rres.getRegisterValue(k)); // tábla
// +
// érték
modbusData.put("timeStamp", timeStamp);
try {
gConstants.db.beginTransaction();
gConstants.db
.insert("Modbus", null, modbusData);
gConstants.db.setTransactionSuccessful();
} finally {
gConstants.db.endTransaction();
}

}
kiir.setText(ki_adat);
ki_adat = "";
}//run vége

}, 0, 3000);

Answer

This error occurs when trying to access UI elements from any thread that is not the UI thread.

To access/modify elements from a non-UI-thread, use runOnUIThread.

However as you need to change a UI element from within a fragment, runOnUIThread should be invoked onto the fragments owning activity. You can do this through getActivity().runOnUIThread().

EG:

timer.schedule(new TimerTask() {
    @Override
    public void run() {
        // Your logic here...

        // When you need to modify a UI element, do so on the UI thread. 
        // 'getActivity()' is required as this is being ran from a Fragment.
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // This code will always run on the UI thread, therefore is safe to modify UI elements.
                myTextBox.setText("my text");
            }
        });
    }
}, 0, 3000); // End of your timer code.

For further information see the following documentation:

  1. Android Fragments (specifically, getActivity()).
  2. TimerTask.
  3. Invoking a Runnable on the UI thread.
Comments