致命例外: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 - 只有创建视图层次结构的原始线程才能接触其视图的主要内容,如果未能解决你的问题,请参考以下文章
AndroidRuntime:致命例外:androidmapsapi-ZoomTableManager
致命例外:推理。无法从 Java 缓冲区复制到 TensorFlowLite 张量
致命异常:java.lang.IllegalStateException - 无法为 LinearLayout 创建层(仅在 Galaxy j4+、j6+ 中崩溃)