Aleksandar Cvetić
Aleksandar Cvetić

Reputation: 303

VideoView Android

I'm working on application that have two Views one is UI and other is VideoView. UI thread is used until some events happen and then I want to setContentView to VideoView, but I got error on setting content view from UI to VideoView. For VideoView I use code below:

Java

setContentView(R.layout.videoview);
VideoView = (VideoView) findViewById(R.id.videoview);
String path= "android.resource://"+getPackageName()+"/"+R.raw.vid;
VideoView.setVideoURI(Uri.parse(path));
VideoView.start();

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<VideoView
    android:id="@+id/videoview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
</LinearLayout>

Here is error that I'm getting:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

What am I doing wrong?

EDIT 0: Code from UI thread:

public class View extends SurfaceView implements Runnable{
    
    Thread Thread = null;
    SurfaceHolder SurfaceHolder;
    boolean Play;

    public View(Context context){
        super(context); 
        SurfaceHolder=getHolder();
    }

    @Override
    public void run() {
        while(Play){
            
            if(!SurfaceHolder.getSurface().isValid())
                continue; 
               //drawing
            if(something)
                Video();
        }

    public void Pause(){
        //called from onPause()
        Play=false;
        while(true){
            try {
                if(Thread!=null)
                Thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            break;
        }
    Thread=null;
    }

    public void Resume(){
        //called from onResume()
        Play=true;
        Thread = new Thread(this);
        Thread.start();
    }

    public void Video(){
        setContentView(R.layout.videoview);
        VideoView = (VideoView) findViewById(R.id.videoview);
        String path= "android.resource://"+getPackageName()+"/"+R.raw.vid;
        VideoView.setVideoURI(Uri.parse(path));
        VideoView.start();
    }
}

Upvotes: 1

Views: 927

Answers (1)

stealthjong
stealthjong

Reputation: 11093

You call setContentView() from the non-UI-thread.

public class View extends SurfaceView implements Runnable{  
    @Override
    public void run() {
        while(Play){
            if(!SurfaceHolder.getSurface().isValid())
                continue;
            //drawing
            if(something)
                Video();
        }
    }

    public void Resume() {
        //called from onResume()
        Play=true;
        Thread = new Thread(this);
        Thread.start();
    }

    public void Video(){
        setContentView(R.layout.videoview);
       /* more code */
    }
}

onResume is called, which creates a new Thread (Thread = new Thread(this);) which you start, so void run() is called (remember, from the non-ui-thread which you just created). run() calls video(), which, in its turn, calls setContentView(). Voila, you called setContentView() from the non-UI-thread.

You shouldnt let a view implement runnable, let alone let a View manage a Thread. It is managed by the UI-thread, no other. A possibility might be to get the context from the view and use the runOnUiThread(Runnable r) method, but better yet: let the UI-thread do UI-work like showing video's.

// I think even this might work. Not 100% sure, I do this out of the top of my head.
public void onResume() {
    if (condition)
        getContext().runOnUiThread(new Runnable() {
            public void run() {
                video();
            }
        });
}

Upvotes: 1

Related Questions