致命例外:JavaBridge - 只有创建视图层次结构的原始线程才能接触其视图

Posted

技术标签:

【中文标题】致命例外:JavaBridge - 只有创建视图层次结构的原始线程才能接触其视图【英文标题】:FATAL EXCEPTION: JavaBridge - Only the original thread that created a view hierarchy can touch its views 【发布时间】:2017-05-23 15:40:57 【问题描述】:

我正在开发用于播放媒体的 cordova 插件。(视频)

我尝试使用适用于 android 的 mediaPlayer 类播放视频。

没有错误,我成功完成了准备步骤。

但在“onPrepared”函数中,我尝试使用“mediaController.show() & mediaPlayer.start()” api,

有错误[logcat]:

E/AndroidRuntime: FATAL EXCEPTION: JavaBridge
                  Process: yongju.example.media, PID: 3779
                  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6891)
                      at android.view.ViewRootImpl.recomputeViewAttributes(ViewRootImpl.java:3247)
                      at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1324)
                      at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1324)
                      at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1324)
                      at android.view.View.setFlags(View.java:11580)
                      at android.view.View.setKeepScreenOn(View.java:7554)
                      at android.view.SurfaceView$1.handleMessage(SurfaceView.java:135)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.os.HandlerThread.run(HandlerThread.java:61)

有我的代码 [cordova 插件]:

package com.cordova.plugin.toast;


import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;

import android.content.Context;

import android.media.AudioManager;
import android.net.Uri;
import android.os.Looper;
import android.util.Log;

import org.apache.cordova.CallbackContext;

import org.apache.cordova.CordovaActivity;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;

import android.view.View;
import android.widget.FrameLayout;
import android.widget.MediaController;

import android.media.MediaPlayer;
import android.view.SurfaceHolder;

import android.os.Handler;

import android.widget.VideoView;

import java.io.IOException;

public class Media extends CordovaPlugin implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, MediaController.MediaPlayerControl 

    class MediaPlayerState 
        protected String NONE = "NONE";
        protected String IDLE = "IDLE";
        protected String READY = "READY"; // prepared
        protected String PLAYING = "PLAYING";
        protected String PAUSED = "PAUSED";
    

    private MediaPlayerState mPlayerState = new MediaPlayerState();
    private static final String TAG = "Media";

    private FrameLayout fraLayout;
    private CordovaWebView webView;

    private MediaPlayer mediaPlayer;
    private MediaController mcontroller;

    private VideoView videoView;
    private SurfaceHolder surfaceHolder;
    private String src, state;

    private CordovaActivity nowActivity;
    private Context context;

    private CallbackContext callbackContext;

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) 
        super.initialize(cordova, webView);
        this.webView = webView;
        nowActivity = (CordovaActivity)this.cordova.getActivity();
        context = this.cordova.getActivity().getApplicationContext();
    

    @Override
    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException 
        this.callbackContext = callbackContext;
        if (action.equals("create")) 
            Log.v(TAG, "**************Success 'create'**************");

            createVideoContainer();
        

        else if (action.equals("open")) 
            Log.v(TAG, "**************Success 'open'**************");

            openVideoStream(args);
        

        else if (action.equals("play")) 
            Log.v(TAG, "**************Success 'play'**************");

            playVideoStream(args);
        

        else if (action.equals("stop")) 
            Log.v(TAG, "**************Success 'stop'**************");

            stopVideoStream(args);
        

        else if (action.equals("pause")) 
            Log.v(TAG, "**************Success 'pause'**************");

            pauseVideoStream(args);
        

        else 
            return false;
        

        return true;
    

    private void createVideoContainer() 
        state = mPlayerState.NONE;

        fraLayout = (FrameLayout) this.webView.getView().getParent();
        FrameLayout.LayoutParams fraLayoutParam = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        fraLayoutParam.setMargins(80, 80, 80, 80);

        videoView = new VideoView(cordova.getActivity());
        videoView.setLayoutParams(fraLayoutParam);
        videoView.setVisibility(View.VISIBLE);

        surfaceHolder = videoView.getHolder();

        surfaceHolder.addCallback(this);

        mcontroller = new MediaController(this.cordova.getActivity());
    

    private void openVideoStream(JSONArray args) throws JSONException 
        String id = args.getString(0);
        src = args.getString(1);
        Log.v(TAG, "media::open() - id =" + id);
    

    private void playVideoStream(JSONArray args) throws JSONException 
        String id = args.getString(0);
        Log.v(TAG, "media::play() - id =" + id);

        if(state == mPlayerState.IDLE) 
            // If 'prepared' is not completed, must again 'prepare'
        
        else
            mediaPlayer.start();
            state = mPlayerState.PLAYING;
        
    

    private void pauseVideoStream(JSONArray args) throws JSONException 
        String id = args.getString(0);
        Log.v(TAG, "media::pause() - EVENT_STATE -> PAUSED");

        mediaPlayer.pause();
        state = mPlayerState.PAUSED;
    

    private void stopVideoStream(JSONArray args) throws JSONException 
        String id = args.getString(0);
        Log.v(TAG, "media::stop() - EVENT_STATE -> IDLE");

        mediaPlayer.stop();
        state = mPlayerState.IDLE;
        //videoView.setVisibility(View.INVISIBLE);
    

    @Override
    public void surfaceCreated(SurfaceHolder holder) 
        Log.v(TAG, "*********************************************************media::surfaceCreated - Start MediaPlayer Object create : " + src);
        if (mediaPlayer == null) 
            mediaPlayer = new MediaPlayer();
        
        else 
            mediaPlayer.reset();
        

        state = mPlayerState.IDLE;

        try 
            String fileName = "android.resource://yongju.example.media/raw/sample";
            Uri fileUri = Uri.parse(fileName);

            mediaPlayer.setDataSource(context, fileUri); // for testing In local file
            mediaPlayer.setDisplay(holder);      // Call Screen
         catch (IOException e) 
            e.printStackTrace();
        

        mediaPlayer.setOnCompletionListener(completionListener);
        mediaPlayer.setOnVideoSizeChangedListener(sizeChangeListener);
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setScreenOnWhilePlaying(true);
        //mediaPlayer.setAudiostreamType(AudioManager.STREAM_MUSIC);

        mediaPlayer.prepareAsync();                      // Ready for loading Video
    

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
        Log.v(TAG, "*********************************************************media::surfaceChanged");
    

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) 
        Log.v(TAG, "*********************************************************media::surfaceDestroyed");
        mediaPlayer.release();
    

    MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() 
        /**
         * Listener for playing video is completed
         * @param mediaPlayer : Now mediaPlayer that is controlled
         */
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) 
        
    ;

    MediaPlayer.OnVideoSizeChangedListener sizeChangeListener = new MediaPlayer.OnVideoSizeChangedListener() 
        @Override
        public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) 
        
    ;

    @Override
    public void onPrepared(MediaPlayer mp) 
        Log.v(TAG, "*********************************************************media::prepare completed");
        state = mPlayerState.READY;
        mcontroller.setMediaPlayer(this);
        mcontroller.setAnchorView(videoView);
        mcontroller.setEnabled(true);

        cordova.getActivity().runOnUiThread(new Runnable() 
            @Override
            public void run() 
                if (cordova.getActivity().isFinishing() == false) 
                    mcontroller.show();
                    mp.start();
                

            
        );
    

    @Override
    public void start() 
        mediaPlayer.start();
    

    @Override
    public void pause() 
        mediaPlayer.pause();
    

    @Override
    public int getDuration() 
        return 0;
    

    @Override
    public int getCurrentPosition() 
        return mediaPlayer.getDuration();
    

    @Override
    public void seekTo(int pos) 
        mediaPlayer.seekTo(pos);
    

    @Override
    public boolean isPlaying() 
        return mediaPlayer.isPlaying();
    

    @Override
    public int getBufferPercentage() 
        return 0;
    

    @Override
    public boolean canPause() 
        return true;
    

    @Override
    public boolean canSeekBackward() 
        return true;
    

    @Override
    public boolean canSeekForward() 
        return true;
    

    @Override
    public int getAudioSessionId() 
        return 0;
    

          return mediaPlayer.getDuration();
        

        @Override
        public void seekTo(int pos) 
            mediaPlayer.seekTo(pos);
        

        @Override
        public boolean isPlaying() 
            return mediaPlayer.isPlaying();
        

        @Override
        public int getBufferPercentage() 
            return 0;
        

        @Override
        public boolean canPause() 
            return true;
        

        @Override
        public boolean canSeekBackward() 


    return true;
    

    @Override
    public boolean canSeekForward() 
        return true;
    

    @Override
    public int getAudioSessionId() 
        return 0;
    

请告诉我为什么。

【问题讨论】:

【参考方案1】:

您尝试不从主线程启动 MediaPlayer。只需调用 runOnUiThread 函数,一切都会好起来的。

【讨论】:

您可以更新代码以便我看到更改吗?如果您仍然收到此错误,则表示您仍在尝试在某处的后台线程上执行 mediaplayer。【参考方案2】:
activity.runOnUiThread(new Runnable() 
            @Override
            public void run() 
                //your implementetion
            
        );

【讨论】:

以上是关于致命例外:JavaBridge - 只有创建视图层次结构的原始线程才能接触其视图的主要内容,如果未能解决你的问题,请参考以下文章

只有创建视图层次结构的原始线程才能触及其视图异常[重复]

php Codeigniter致命错误是一个例外

AndroidRuntime:致命例外:androidmapsapi-ZoomTableManager

致命例外:推理。无法从 Java 缓冲区复制到 TensorFlowLite 张量

致命异常:java.lang.IllegalStateException - 无法为 LinearLayout 创建层(仅在 Galaxy j4+、j6+ 中崩溃)

CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能接触视图