Android “Only the original thread that created a view hierarchy can touch its views.”

后端 未结 28 3281
鱼传尺愫
鱼传尺愫 2020-11-21 04:44

I\'ve built a simple music player in Android. The view for each song contains a SeekBar, implemented like this:

public class Song extends Activity implement         


        
相关标签:
28条回答
  • 2020-11-21 05:23

    For me the issue was that I was calling onProgressUpdate() explicitly from my code. This shouldn't be done. I called publishProgress() instead and that resolved the error.

    0 讨论(0)
  • 2020-11-21 05:24

    If you are within a fragment, then you also need to get the activity object as runOnUIThread is a method on the activity.

    An example in Kotlin with some surrounding context to make it clearer - this example is navigating from a camera fragment to a gallery fragment:

    // Setup image capture listener which is triggered after photo has been taken
    imageCapture.takePicture(
           outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
    
               override fun onError(exc: ImageCaptureException) {
               Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
            }
    
            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                            val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
                            Log.d(TAG, "Photo capture succeeded: $savedUri")
                   
                 //Do whatever work you do when image is saved         
                 
                 //Now ask navigator to move to new tab - as this
                 //updates UI do on the UI thread           
                 activity?.runOnUiThread( {
                     Navigation.findNavController(
                            requireActivity(), R.id.fragment_container
                     ).navigate(CameraFragmentDirections
                            .actionCameraToGallery(outputDirectory.absolutePath))
                  })
    
    0 讨论(0)
  • 2020-11-21 05:26

    Use this code, and no need to runOnUiThread function:

    private Handler handler;
    private Runnable handlerTask;
    
    void StartTimer(){
        handler = new Handler();   
        handlerTask = new Runnable()
        {
            @Override 
            public void run() { 
                // do something  
                textView.setText("some text");
                handler.postDelayed(handlerTask, 1000);    
            }
        };
        handlerTask.run();
    }
    
    0 讨论(0)
  • 2020-11-21 05:27

    This is the stack trace of mentioned exception

            at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
            at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
            at android.view.View.requestLayout(View.java:16474)
            at android.view.View.requestLayout(View.java:16474)
            at android.view.View.requestLayout(View.java:16474)
            at android.view.View.requestLayout(View.java:16474)
            at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
            at android.view.View.requestLayout(View.java:16474)
            at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
            at android.view.View.setFlags(View.java:8938)
            at android.view.View.setVisibility(View.java:6066)
    

    So if you go and dig then you come to know

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    

    Where mThread is initialize in constructor like below

    mThread = Thread.currentThread();
    

    All I mean to say that when we created particular view we created it on UI Thread and later try to modifying in a Worker Thread.

    We can verify it via below code snippet

    Thread.currentThread().getName()
    

    when we inflate layout and later where you are getting exception.

    0 讨论(0)
  • 2020-11-21 05:28

    I had a similar issue, and my solution is ugly, but it works:

    void showCode() {
        hideRegisterMessage(); // Hides view 
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                showRegisterMessage(); // Shows view
            }
        }, 3000); // After 3 seconds
    }
    
    0 讨论(0)
  • 2020-11-21 05:30

    This is explicitly throwing an error. It says whichever thread created a view, only that can touch its views. It is because the created view is inside that thread's space. The view creation (GUI) happens in the UI (main) thread. So, you always use the UI thread to access those methods.

    In the above picture, the progress variable is inside the space of the UI thread. So, only the UI thread can access this variable. Here, you're accessing progress via new Thread(), and that's why you got an error.

    0 讨论(0)
提交回复
热议问题