Reputation: 91
I am currently practicing HandlerThread
in Android with an easy camera example - the problem is if i create my CameraController
object in onCreate
and send a message to the Handler Thread in order to process my openCamera()
method ( which contains direct UI update code)
It is allowing me to updated the UI although i shouldn't be allowed to do that - since technically I`m supposed to be in a different thread.
If I move the code onCheckedChanged
method (it is commented in the below snippet) the application will crash saying that I am trying to update the UI thread from a different thread.
public class MainActivity extends AppCompatActivity implements Handler.Callback {
private ToggleButton toggleButton;
private TextView textView;
private CameraController mTestCam;
//generate messages
private static final int MSG_OPEN_CAM = 0;
private static final int MSG_CLOSE_CAM = 1;
private static final int MSG_OPEN_FLASH= 2;
private static final int MSG_CLOSE_FLASH = 3;
//verify
private static final int MSG_OPEN_CAM_DONE = 4; // nu trebuie
private static final int MSG_CLOSE_CAM_DONE = 5;
private static final int MSG_OPEN_FLASH_DONE= 6;
private static final int MSG_CLOSE_FLASH_DONE= 7;
//used for MainActivity HandleMessage
protected Handler mHandler;
//camera controler thread
private CameraControllerThread mCameraControlThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//android marshmallow - in order for service to run you need runtime permissions
//testCam.openCamera(); //moved to method
toggleButton = (ToggleButton) findViewById(R.id.toggleButton);
textView = (TextView) findViewById(R.id.textView_text);
mHandler = new Handler(this);
mCameraControlThread = new CameraControllerThread("Camera Control Thread");
mCameraControlThread.start();
//moved to onStart
mTestCam = new CameraController();
Message openCameraMsg = mCameraControlThread.mWorkerHandler.obtainMessage(this.MSG_OPEN_CAM);
openCameraMsg.sendToTarget();
toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if(isChecked){
mTestCam = new CameraController();
Message openCameraMsg = mCameraControlThread.mWorkerHandler
.obtainMessage(MSG_OPEN_CAM);
openCameraMsg.sendToTarget();
//mTestCam.openFlash();
} else {
//close flash
//mTestCam.stopFlash();
}
}
});
}
private void openCamera(){
synchronized (mTestCam){
mTestCam.openCamera();
}
Toast.makeText(this, "This is my text", Toast.LENGTH_LONG).show();
textView.setVisibility(View.VISIBLE);
textView.setText("Camera Open");
}
@Override
public boolean handleMessage(Message message) {
return false;
}
private class CameraControllerThread extends HandlerThread implements Handler.Callback{
protected Handler mWorkerHandler;
public CameraControllerThread(String name){
super(name);
}
@Override
protected void onLooperPrepared() {
mWorkerHandler = new Handler(getLooper(), this);
}
@Override
public boolean handleMessage(Message message) {
switch(message.what){
case MSG_OPEN_CAM:
openCamera();
break;
}
return true;
}
}
}
I guess my question is - why if I instantiate my CameraController
and send the message from onCreate
- it is allowing me to update the UI - and when i do the same thing from the onCheckedChangeListener
it does not allow me?
Upvotes: 3
Views: 347
Reputation: 3852
Quoting Android documentation:
[Accessing the Android UI toolkit from outside the UI thread] can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.
Anything can happen after a race condition.
Upvotes: 2