Android layer type与WebView白屏以及WebView不随动画而动的问题

本文探讨了WebView在特定条件下出现白屏的问题及其解决方法。分析了不同LayerType(NONE、SOFTWARE、HARDWARE)的特点,并说明了在硬件加速环境下如何通过调整LayerType避免白屏现象。

转自:

http://blog.youkuaiyun.com/a345017062/article/details/7478667

问题:WebView白屏(有数据)

报错:<spanheiti sc="" light'"="">[ERROR:in_process_view_renderer.cc(189)] Failed to request GL process. Deadlock likely: 0

解决:WebView设置setLayerType(View.LAYER_TYPE_SOFTWARE,null)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
分析: WebView 继承View,View中有三种layer type分别为

    /**
     * Indicates that the view does not have a layer.
     *
     * @see #getLayerType()
     * @see #setLayerType(int, android.graphics.Paint)
     * @see #LAYER_TYPE_SOFTWARE
     * @see #LAYER_TYPE_HARDWARE
     */
    public static final int LAYER_TYPE_NONE = 0;

    /**
     * <p>Indicates that the view has a software layer. A software layer is backed
     * by a bitmap and causes the view to be rendered using Android's software
     * rendering pipeline, even if hardware acceleration is enabled.</p>
     *
     * <p>Software layers have various usages:</p>
     * <p>When the <a target=_blank href="http://www.codes51.com/article/search_Application/" target="_blank" style="color: rgb(0, 0, 0); text-decoration: none;"><strong>Application</strong></a> is not using hardware acceleration, a software layer
     * is useful to apply a specific color filter and/or blending mode and/or
     * translucency to a view and all its children.</p>
     * <p>When the <a target=_blank href="http://www.codes51.com/article/search_Application/" target="_blank" style="color: rgb(0, 0, 0); text-decoration: none;"><strong>Application</strong></a> is using hardware acceleration, a software layer
     * is useful to render drawing primitives not supported by the hardware
     * accelerated pipeline. It can also be used to cache a complex view tree
     * into a texture and reduce the complexity of drawing operations. For instance,
     * when animating a complex view tree with a <span id="11_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="11_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=translation&k0=translation&kdi0=0&luki=4&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="11" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">translation</span></a></span>, a software layer can
     * be used to render the view tree <span id="12_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="12_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=only&k0=only&kdi0=0&luki=3&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="12" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">only</span></a></span> once.</p>
     * <p>Software layers should be avoided when the affected view tree updates
     * often. Every update will <a target=_blank href="http://www.codes51.com/article/search_require/" target="_blank" style="color: rgb(0, 0, 0); text-decoration: none;"><strong>require</strong></a> to re-render the software layer, which can
     * potentially be slow (particularly when <span id="5_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="5_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=hardware&k0=hardware&kdi0=0&luki=7&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="5" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">hardware</span></a></span> acceleration is turned on
     * since the layer will have to be uploaded into a hardware texture after every
     * update.)</p>
     *
     * @see #getLayerType()
     * @see #setLayerType(int, android.graphics.Paint)
     * @see #LAYER_TYPE_NONE
     * @see #LAYER_TYPE_HARDWARE
     */
    public static final int LAYER_TYPE_SOFTWARE = 1;

    /**
     * <p>Indicates that the view has a <span id="6_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="6_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=hardware&k0=hardware&kdi0=0&luki=7&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="6" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">hardware</span></a></span> layer. A hardware layer is backed
     * by a hardware specific texture (generally Frame Buffer Objects or FBO on
     * OpenGL hardware) and causes the view to be rendered using Android's hardware
     * rendering pipeline, but <span id="7_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="7_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=only&k0=only&kdi0=0&luki=3&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="7" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">only</span></a></span> if hardware acceleration is turned on for the
     * view hierarchy. When hardware acceleration is turned off, hardware layers
     * behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p>
     *
     * <p>A <span id="8_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="8_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=hardware&k0=hardware&kdi0=0&luki=7&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="8" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">hardware</span></a></span> layer is useful to apply a specific color filter and/or
     * blending mode and/or translucency to a view and all its children.</p>
     * <p>A hardware layer can be used to cache a complex view tree into a
     * texture and reduce the complexity of drawing operations. For instance,
     * when animating a complex view tree with a <span id="9_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="9_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=translation&k0=translation&kdi0=0&luki=4&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="9" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">translation</span></a></span>, a hardware layer can
     * be used to render the view tree <span id="10_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="10_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=19&is_app=0&jk=cef02009fbfa4ac9&k=only&k0=only&kdi0=0&luki=3&mcpm=0&n=10&p=baidu&q=85048100_cpr&rb=0&rs=1&seller_id=1&sid=c94afafb920f0ce&ssp2=1&stid=9&t=tpclicked3_hc&td=1797308&tu=u1797308&u=http%3A%2F%2Fwww%2Ecodes51%2Ecom%2Farticle%2Fdetail%5F178654%2Ehtml&urlid=0" target="_blank" mpid="10" style="color: rgb(0, 0, 0); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">only</span></a></span> once.</p>
     * <p>A hardware layer can also be used to increase the rendering quality when
     * rotation transformations are applied on a view. It can also be used to
     * prevent potential clipping issues when applying 3D transforms on a view.</p>
     *
     * @see #getLayerType()
     * @see #setLayerType(int, android.graphics.Paint)
     * @see #LAYER_TYPE_NONE
     * @see #LAYER_TYPE_SOFTWARE
     */
    public static final int LAYER_TYPE_HARDWARE = 2;


LAYER_TYPE_NONE:表明视图没有多余渲染层。

LAYER_TYPE_SOFTWARE:表明视图有一个软件渲染层。无论是否开启硬件加速,都会有一张Bitmap(software layer),并在上面对WebView进行渲染。

好处:在进行动画,使用software可以只画一次View树,很省。

什么时候不要用:

View树经常更新时不要用。尤其是在硬件加速打开时,每次更新消耗的时间更多。因为渲染完这张Bitmap后还需要再把这张Bitmap渲染到hardware layer上面去。

LAYER_TYPE_HARDWARE:

表明视图有一个硬件渲染层。硬件加速关闭时,作用同software。硬件加速打开时会在FBO(Framebuffer Object)上做渲染,在进行动画时,View树也只需要画一次。

两者区别:

1.一个是渲染到Bitmap,一个是渲染到FB上。

2.hardware可能会有一些操作不支持(出现白屏)

两者相同:

都是开了一个buffer,把View画到这个buffer上面去。

PS:GLSurfaceView和WebView默认LayerType都是NONE。


GLSurfaceView:给GLSurfaceView设置为software或者hardware后,发现什么也画不出来了。得出结论:GLSurfaceView的LayerType只能是NONE。


WebView

以前使用WebView时碰到过一个问题,如果在WebView上面使用Animation,WebView的绘画区域不动。当时的解决方案是在进行动画之前对WebView进行截屏(drawingcache)。按上面的道理试了一下,设置一个hardware或者software的layer就OK了。

现在又碰到了另外一个问题,打开硬件加速后,在一些机器上面(我的是3.2及HTC 4.4)WebView有时会出现某一块区域白屏的问题。默认的LayerType是NONE,改为hardware也不行,设置为software就解决了。当然关闭硬件加速也好了,可是那样的话程序整体就比较慢了。所以最终方案是整体硬件加速,出问题的WebView设置software。


补充1:

加上这一句,可以让3D的绘制更快一些:getHolder().setType(SurfaceHolder.SURFACE_TYPE_HARDWARE);

补充2:

问题:

在硬件加速开启的情况下GLSurfaceView一旦被从View树上摘下来,会使整个窗口背景变黑,即使设置LayerType为software也不管用。

经过两天的排查,发现了原因,我的程序是在C层由drawFrame(属于GLThread线程)来驱动进行绘画,当时GLSurfaceView被摘下来时,GLSurfaceView的destroy方法被调用,我在destory方法(属于UI线程)中直接调用了GLThread线程的结束方法。而GLSurfaceView.creat, sizeChanged,destroyed在UI线程,Render.create,sizeChanged,drawFrame在GLThread线程。因此,出现了UI线程直接调用GLThread线程的方法问题。最终通过GLSurfaceView.queueEvent向GLThread线程发送Runnable,问题得到解决。

看来,还是软渲染的容错能力比较强,一开硬件加速,底层就比较脆弱了。

结论:一定要搞清楚哪个是UI线程,哪个是GLThread线程。


补充以上寻找问题过程中发现的知识点:

hardware acclerator是对整个窗口进行加速,在硬件加速打开时View.isHardwareAcclerator返回true。但每个View可能被渲染到的Canvas是不同的,比如View可能被通过setLayer设置layer,这时,Canvas.isHardwareAccelerator返回false

Android提供了三种硬件加速是否开启的控制级别,分别是Application,Activity,Window,View。这个可以参考DevGuide。


以上就介绍了Android layer type与WebView白屏以及WebView不随动画而动的问题,包括了方面的内容,希望对Android开发有兴趣的朋友有所帮助。

本文网址链接:http://www.codes51.com/article/detail_178654.html



webView 播放视频 有声音和进度条 没有面 package com.doshare.screen.ui; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.os.CountDownTimer; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.JavascriptInterface; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; import androidx.core.content.ContextCompat; import com.doshare.commons.enums.ErrorEnum; import com.doshare.commons.io.HttpUtils; import com.doshare.commons.io.Log; import com.doshare.commons.io.NetworkUtils; import com.doshare.commons.lang.DateUtils; import com.doshare.commons.lang.GsonUtils; import com.doshare.commons.lang.IdUtils; import com.doshare.commons.lang.StringUtils; import com.doshare.screen.AppHelper; import com.doshare.screen.Constants; import com.doshare.screen.InitApp; import com.doshare.screen.R; import com.doshare.screen.ResultBean; import com.doshare.screen.ResultEnum; import com.doshare.screen.StringCallback; import com.doshare.screen.storage.CacheHandler; import com.doshare.screen.storage.FileHandler; import com.elvishew.xlog.XLog; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 主界面内容区-浏览器 */ public class BrowserFragment extends BaseFragment { private final String REQUEST_REMOTE_DATA = "getRemoteData";//请求类型 private String currentFuncId = "";//功能Id private CommonDialogFragment commonDialog = null;//通用对话框 private Map<String, WebViewGetDataTask> webViewGetDataTaskMap = null;//浏览器获取数据异步任务 private WebView webView;//浏览器 private boolean redirectFlag = false;//重定向标识 private boolean homeShotFlag = false;//首页截屏幕标识 private boolean loadedFlag = false;//页面加载标识 private static final int MSG_LOADUI = 1; private static final int MSG_SHOWMSG = 2; private static final int MSG_SETPULLWEBVIEWCOMPLETE = 3; private static final int MSG_SCREEN_SHOT = 10;//屏幕截屏 private static final String HOME_FUNC_ID = "ClassHome"; //是否门户页面 private boolean portalFlag = false; public BrowserFragment(boolean portalFlag, String funcId){ super(); this.portalFlag = portalFlag; this.currentFuncId = funcId; webViewGetDataTaskMap = new HashMap<String, WebViewGetDataTask>(); } public String getFuncId(){ return currentFuncId; } //region 消息处理 private final Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what){ case MSG_LOADUI: break; case MSG_SHOWMSG: break; case MSG_SETPULLWEBVIEWCOMPLETE: break; case MSG_SCREEN_SHOT: if (currentFuncId.contentEquals(HOME_FUNC_ID)) mainActivity.takeScreenShot(); break; } } }; //endregion //region 覆写Fragment方法 @Override public void onCreate(Bundle savedInstanceState) { XLog.d("BrowserFragment onCreate()..."); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { XLog.d("BrowserFragment onCreateView()..."); mainActivity = (IMainActivity)getActivity(); View rootView = inflater.inflate(R.layout.fragment_browser, container, false); webView = (WebView) rootView.findViewById(R.id.webView); registerWebViewEvent(); handler.sendEmptyMessageDelayed(MSG_SCREEN_SHOT, 100); return rootView; } @Override public void onResume() { XLog.d("BrowserFragment onResume()..."); super.onResume(); } /** * 重新加载数据 * @param forceFlag 是否强制加载 */ public void reload(boolean forceFlag){ XLog.d("BrowserFragment reload()... " + forceFlag); Bundle args = getArguments(); if (args != null){ String funcId = args.getString("funcId"); if (!StringUtils.isNullOrEmpty(funcId)){ String url = funcId; if(!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file:///")){ if (url.startsWith("html://")){ url = StringUtils.trimStart(url, "html://"); String param = ""; if(url.contains("?")){ int i = url.indexOf("?"); String temp = url; url = url.substring(0,i); param = temp.substring(i); } String anchor = ""; if (url.indexOf("#") != -1){ int pos = url.indexOf("#"); anchor = url.substring(pos); if(InitApp.profile.getOrientation().equals("vertical") && !anchor.contains("_v")){ anchor = anchor + "_v"; } url = url.substring(0, pos); } if(!StringUtils.isNullOrEmpty(param)){ anchor = anchor + param; } url = "file://" + FileHandler.buildFilePath(getContext(), Environment.DIRECTORY_DOCUMENTS, Constants.RESOURCE_FILE_FOLDER + "/" + url + ".html" + anchor); XLog.d("BrowserFragment reload() url: " + url); }else{ url = "file:///android_asset/" + url; XLog.d("BrowserFragment reload() url: " + url); } } funcId = funcId +"?classId="+InitApp.profile.getClassId(); if (!StringUtils.equals(currentFuncId, funcId) || !loadedFlag) { redirectFlag = false; loadedFlag = true; reloadFlag = false; webView.clearView(); webView.loadUrl(url+"?classId="+InitApp.profile.getClassId()); } else if (loadedFlag && (reloadFlag || forceFlag)) { redirectFlag = false; reloadFlag = false; webView.reload(); } //巡堂页面打开60s后返回首页 if(funcId.equals("html://main#ccpatrol")){ backToPortalTimer.start(); } } }else if (loadedFlag && reloadFlag){ redirectFlag = false; reloadFlag = false; webView.reload(); } } /** * 改变当前网址的hash地址 * @param hash hash地址 */ public void changeHash(String hash){ XLog.d("BrowserFragment changeHash() hash: " + hash); try{ webView.loadUrl("javascript:window.changeHash('"+hash+"')"); }catch (Exception e){ e.printStackTrace(); } } /** * 注册浏览器事件 */ private void registerWebViewEvent(){ XLog.d("BrowserFragment registerWebViewEvent()... "); //webView.getSettings().setUseWideViewPort(true); webView.getSettings().setLoadWithOverviewMode(true); webView.getSettings().setAllowFileAccess(true); webView.getSettings().setSupportZoom(true); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDefaultTextEncodingName("utf-8"); webView.getSettings().setLoadsImagesAutomatically(true); webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); webView.getSettings().setMediaPlaybackRequiresUserGesture(true); webView.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent)); //webView.setBackgroundResource(R.color.transparent); //webView.getBackground().setAlpha(0); //webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null); webView.setWebChromeClient(new WebChromeClient(){ @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { Message msg = new Message(); msg.what = MSG_SHOWMSG; msg.obj = message; handler.sendMessage(msg); return true; } }); webView.addJavascriptInterface(new JavaScriptInterface(), "webView"); webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { redirectFlag = true; view.loadUrl(url); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { redirectFlag = false; super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (redirectFlag) { } } }); } @Override public void onStart() { XLog.d("BrowserFragment onStart()... "); super.onStart(); reload(false); } @Override public void onDestroy() { XLog.d("BrowserFragment onDestroy()... "); if (webViewGetDataTaskMap.size() > 0){ String[] keys = new String[webViewGetDataTaskMap.size()]; webViewGetDataTaskMap.keySet().toArray(keys); for (int i=0;i<keys.length;i++){ webViewGetDataTaskMap.get(keys[i]).cancel(true); } } handler.removeCallbacksAndMessages(null); super.onDestroy(); } private final CountDownTimer backToPortalTimer = new CountDownTimer(60 * 1000, 60 * 1000) { @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { mainActivity.sendMessage(Constants.MSG_GO_PORTAL); } }; @Override public void onHiddenChanged(boolean hidden) { XLog.d("BrowserFragment onHiddenChanged()... hidden:"+ hidden); super.onHiddenChanged(hidden); if (!hidden) reload(false); } /** * 回调 * @param callBackCode 回调代码 */ @Override public void callBack(String callBackCode){ XLog.d("BrowserFragment StringCallBack()... "); } /** * 回调 * @param messageCode 消息代码 */ @Override public void callBack(int messageCode){ XLog.d("BrowserFragment StringCallBack()... messageCode:"+messageCode); if (messageCode == MSG_SCREEN_SHOT) homeShotFlag = true; } //endregion //region 对话框 //显示等待对话框 public void showWaitingDialog(){ XLog.d("BrowserFragment showWaitingDialog()..."); commonDialog = new CommonDialogFragment(CommonDialogFragment.DialogTypes.WaitingDialog); commonDialog.show(getFragmentManager(), "CommonDialogFragment"); } //显示等待对话框 public void showWaitingMsgDialog(String msg){ XLog.d("BrowserFragment showWaitingDialog()... msg:"+msg); commonDialog = new CommonDialogFragment(CommonDialogFragment.DialogTypes.WaitingMsgDialog); commonDialog.setMessage(msg); commonDialog.show(getFragmentManager(), "CommonDialogFragment"); } //显示消息对话框 public void showPromptDialog(String title, String msg){ XLog.d("BrowserFragment showWaitingDialog()... title:"+title+" msg:" + msg); commonDialog = new CommonDialogFragment(CommonDialogFragment.DialogTypes.PromptDialog); commonDialog.setTitle(title); commonDialog.setMessage(msg); commonDialog.setPositiveButton(getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); closeDialog(); } }); commonDialog.show(getFragmentManager(), "CommonDialogFragment"); } //显示确认对话框 public void showConfirmDialog(final String funcType, String title, String msg, final String callBackMethod){ XLog.d("BrowserFragment showWaitingDialog()... title:"+title+" msg:" + msg + " funcType" + funcType); commonDialog = new CommonDialogFragment(CommonDialogFragment.DialogTypes.ConfirmDialog); commonDialog.setTitle(title); commonDialog.setMessage(msg); commonDialog.setPositiveButton(getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); closeDialog(); if (webView != null && !TextUtils.isEmpty(callBackMethod)){ webView.loadUrl("javascript:" + callBackMethod + "('" + funcType + "')"); } callBack4Confirm(funcType); } }); commonDialog.setNegativeButton(getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); closeDialog(); } }); commonDialog.show(getFragmentManager(), "CommonDialogFragment"); } //关闭对话框 public void closeDialog(){ XLog.d("BrowserFragment closeDialog()... "); androidx.fragment.app.Fragment pre = getFragmentManager().findFragmentByTag("CommonDialogFragment"); if (pre != null){ androidx.fragment.app.FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.remove(pre).commit(); } commonDialog = null; } //显示提示信息(短时间) public void showToastInShortTime(String msg){ XLog.d("BrowserFragment showToastInShortTime() ... msg:"+ msg); CommonToast.makeText(getActivity(), msg, CommonToast.LENGTH_SHORT).show(); } //显示提示信息(长时间) public void showToastInLongTime(String msg){ XLog.d("BrowserFragment showToastInLongTime() ... msg:"+ msg); CommonToast.makeText(getActivity(), msg, CommonToast.LENGTH_LONG).show(); } //确认框回调 private void callBack4Confirm(String funcType){ XLog.d("BrowserFragment callBack4Confirm() ... funcType:"+ funcType); } //endregion //region 浏览器脚本接口 final class JavaScriptInterface { //跳转至视频播放页面 @JavascriptInterface public void viewVideo(String message, String srcPath, String videoName){ XLog.d("BrowserFragment viewVideo() ... msg:"+ message + " srcPath:" + srcPath + " videoName:" + videoName); //String url = Intent intent=new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(InitApp.getContext(),ScreenVideoActivity.class); intent.putExtra("videoUrl",message); intent.putExtra("srcPath",srcPath); intent.putExtra("videoName",videoName); InitApp.getContext().startActivity(intent); } //显示消息对话框 @JavascriptInterface public void showMessage(String message){ XLog.d("BrowserFragment showMessage() ... msg:"+ message); showPromptDialog(getResources().getString(R.string.dialog_title), message); } //显示确认对话框 @JavascriptInterface public void showConfirm(String funcType, String title, String message, String callBackMethod){ XLog.d("BrowserFragment showConfirm() ... msg:"+ message + " funcType:" + funcType + " title:" + title); showConfirmDialog(funcType, TextUtils.isEmpty(title) ? getResources().getString(R.string.dialog_title) : title, message, callBackMethod); } //异步调用获取数据 @JavascriptInterface public void onscreenrequest(String interfaceCode, String interface_parameters, String onComplete, boolean showLoading){ XLog.d("BrowserFragment onscreenrequest() ... interfaceCode:"+ interfaceCode + " interface_parameters:" + interface_parameters); if (NetworkUtils.checkNetwork(getContext())){//在线异步请求 Map<String, String> params = new HashMap<>(); params.put("interface_code", interfaceCode); params.put("interface_parameters", interface_parameters); params.put("onComplete", onComplete); String taskId = IdUtils.generateUUID(); WebViewGetDataTask task = new WebViewGetDataTask(taskId, showLoading); webViewGetDataTaskMap.put(taskId, task); task.execute(params); }else{//本地缓存同步请求 try{ String result = CacheHandler.requestJson4Cache(interfaceCode, interface_parameters); webView.loadUrl("javascript:window.invokeCallback('" + onComplete + "',eval('("+result+")'))"); }catch(Exception e){ e.printStackTrace(); Log.d("CacheHandler.request4Cache", String.format("%s:%s invoke error", interfaceCode, interface_parameters)); } } } //获取访问令牌 @JavascriptInterface public String getAccessToken(){ XLog.d("BrowserFragment getAccessToken() ..."); return InitApp.profile.getToken(); } //获取后台服务地址 @JavascriptInterface public String getRemoteUrl(){ XLog.d("BrowserFragment getRemoteUrl() ..."); return AppHelper.getUrl() + "/oauth2/resources"; } @JavascriptInterface public String getDeviceCode(){ XLog.d("BrowserFragment getDeviceCode() ..."); return InitApp.profile.getDeviceCode(); } //教室 @JavascriptInterface public String getClassRoom(){ XLog.d("BrowserFragment getClassRoom() ..."); return InitApp.profile.getClassroomName(); } @JavascriptInterface public String getClassRoomId(){ XLog.d("BrowserFragment getClassRoomId() ..."); return InitApp.profile.getClassroomId(); } @JavascriptInterface public int getYearWeek(){ XLog.d("BrowserFragment getYearWeek() ..."); Calendar c = Calendar.getInstance(); return c.get(Calendar.WEEK_OF_YEAR); } //当前课程和教师 @JavascriptInterface public String getCourseAndTeacher(){ XLog.d("BrowserFragment getCourseAndTeacher() ..."); String course = ""; String teacher = ""; String nowTime = DateUtils.dateTimeToString(new Date()).split(" ")[1]; String nowTimeStr = nowTime.split(":")[0]+":"+nowTime.split(":")[1]; ListView timetableList = getActivity().findViewById(R.id.timetable_list); if(timetableList == null){ TextView tvCourse = getActivity().findViewById(R.id.tv_course_name); TextView tvTeacher = getActivity().findViewById(R.id.tv_teacher_name); if(tvCourse != null && tvTeacher != null){ return tvCourse.getText() + ":" + tvTeacher.getText(); }else{ return ""; } } ListAdapter adapter = timetableList.getAdapter(); if(adapter != null){ for(int i = 0; i < adapter.getCount(); i++){ String s = GsonUtils.object2Json(adapter.getItem(i)); Map<String, Object> map = GsonUtils.json2Map(s); String time = map.get("time").toString(); if(!StringUtils.isNullOrEmpty(time)){ String time1 = time.split("-")[0]; String time2 = time.split("-")[1]; if(nowTimeStr.compareTo(time1) >= 0 && nowTimeStr.compareTo(time2) <=0){ course = map.get("courseName").toString(); teacher = map.get("teacherName").toString(); } } } } return course + ":" + teacher; } @JavascriptInterface public String getClassId() { XLog.d("BrowserFragment getClassId() ..."); return InitApp.profile.getClassId(); } //异步调用获取文本资源 @JavascriptInterface public String onscreenloader(String name, String dataType){ XLog.d("BrowserFragment onscreenloader() ... name:" + name + " dataType:" + dataType); String filePathName = "/" + Constants.RESOURCE_FILE_FOLDER + "/" + name; return FileHandler.readText(getContext(), Environment.DIRECTORY_DOCUMENTS, filePathName); } //是否门户页所在浏览器 @JavascriptInterface public boolean isPortalBrowser(){ XLog.d("BrowserFragment isPortalBrowser() ..."); return portalFlag; } @JavascriptInterface public String getBrowserName(){ XLog.d("BrowserFragment isPortalBrowser() ..."); return portalFlag ? "portal" : "browser2"; } //页面跳转(门户页跳转到内容页,或内容页内部跳转) @JavascriptInterface public void redirect(String hash){ XLog.d("BrowserFragment redirect() ... hash:"+hash); Message msg = new Message(); msg.what = Constants.MSG_REDIRECT; Bundle args = new Bundle(); args.putString("hash", hash); msg.setData(args); mainActivity.sendMessage(msg); } //返回门户页面(内容页面返回门户页面) @JavascriptInterface public void backPortal() { XLog.d("BrowserFragment backPortal() ... "); mainActivity.sendMessage(Constants.MSG_GO_PORTAL); } //改变页面地址(仅用于改变内容页浏览器地址) @JavascriptInterface public void changeUrl(String url){ XLog.d("BrowserFragment changeUrl() ... url:"+url); if (!portalFlag) currentFuncId = url; } @JavascriptInterface public void returnAttend(){ XLog.d("BrowserFragment returnAttend() ... "); mainActivity.changePage("app://Attend", "考勤"); } } //浏览器获取数据任务内部类 //参数一:启任务执行的输入参数类型 //参数二:后台任务执行的进度参数类型 //参数三:后台计算结果的类型 class WebViewGetDataTask extends AsyncTask<Map<String, String>, Void, ResultBean> { private final String id; private final boolean showWaiting; private String callBackMehtod; public WebViewGetDataTask(){ this(IdUtils.generateUUID(), true); } public WebViewGetDataTask(String id, boolean showWaiting){ this.id = id; this.showWaiting = showWaiting; } //任务开始后立即执行,可在此处对UI进行标记处理 @Override protected void onPreExecute() { if (showWaiting){ showWaitingDialog(); } } //在onPreExecute()完成后立即执行,用于执行较为费时的操作 @Override protected ResultBean doInBackground(Map<String, String>... params) { if (!NetworkUtils.checkNetwork(getContext())){ return ResultBean.getInstance(REQUEST_REMOTE_DATA, params[0], ResultEnum.String, ErrorEnum.NETWORK_OFFLINE); } try { String interface_code = params[0].get("interface_code"); String interface_parameters = params[0].get("interface_parameters"); callBackMehtod = params[0].get("onComplete"); Map<String, String> postParams = getPostParams(interface_code, interface_parameters); StringCallback callback = new StringCallback(); String url = AppHelper.getUrl() + "/oauth2/resources"; XLog.d("BrowserFragment WebViewGetDataTask url:" + url + " params:" + postParams); HttpUtils.post(url, postParams, callback); return ResultBean.getInstance(REQUEST_REMOTE_DATA, params[0], callback); } catch (Exception e) { XLog.e("BrowserFragment WebViewGetDataTask 异常:"); XLog.e(e); return ResultBean.getInstance(REQUEST_REMOTE_DATA, params[0], ResultEnum.String, ErrorEnum.ERROR); } } //任务结束时执行,计算结果被传递到此方法中,在此处恢复UI标记 @Override protected void onPostExecute(final ResultBean result) { webViewGetDataTaskMap.remove(id); if (showWaiting) closeDialog(); if (result.isSuccess()){ XLog.d("BrowserFragment WebViewGetDataTask success:"); if (!TextUtils.isEmpty(callBackMehtod)){ String resultJson = StringUtils.replace(result.getStringResult(),"\"", "\\\""); webView.loadUrl("javascript:window.invokeCallback('" + callBackMehtod + "',eval('("+resultJson+")'))"); } }else{ XLog.e("BrowserFragment WebViewGetDataTask error:"+ result.getErrorCode() + ":" + result.getErrorDesc()); Map<String,Object> map = (Map<String, Object>) result.getRequestParameters(); String interface_code = map.get("interface_code").toString(); if(interface_code.equals("getClassPersonalCard")){ if(result.getErrorCode().equals("010004")){ CommonToast.makeText(getActivity(), "用户表资料缺失", CommonToast.LENGTH_SHORT).show(); } } } } //取消任务时执行 @Override protected void onCancelled() { XLog.d("BrowserFragment onCancelled()"); webViewGetDataTaskMap.remove(id); if (showWaiting) closeDialog(); } /** * 获取post请求参数 * @param interface_code 接口代码 * @param interface_parameters 接口参数 * @return post请求参数 */ private Map<String, String> getPostParams(String interface_code, String interface_parameters){ XLog.d("BrowserFragment getPostParams()... interface_code:" + interface_code + " interface_parameters:" + interface_parameters); Map<String, String> params = new HashMap<>(); params.put("access_token", InitApp.profile.getToken()); params.put("classId", InitApp.profile.getClassId()); params.put("interface_code", interface_code); params.put("interface_parameters", interface_parameters); return params; } } //endregion }
最新发布
06-20
### 解决方案 在 Android WebView 中播放视频时出现有声音但没有面的问题,通常以下因素有关:HTML5 视频支持、硬件加速配置、以及 WebView 的设置问题。以下是详细的解决方案: #### 1. 确保启用硬件加速 Android 应用需要启用硬件加速才能正确渲染视频面。可以通过在 `AndroidManifest.xml` 文件中为应用或特定 Activity 启用硬件加速: ```xml <application android:hardwareAccelerated="true"> ... </application> ``` 或者针对某个 Activity: ```xml <activity android:name=".YourActivity" android:hardwareAccelerated="true"> ... </activity> ``` 如果硬件加速未启用,可能会导致视频播放时只有声音而没有面[^1]。 #### 2. 配置 WebView 设置 确保 WebView 的相关设置正确无误,特别是对 HTML5 和媒体播放的支持。以下是一个完整的 WebView 初始化示例: ```java WebView webView = findViewById(R.id.webView); WebSettings webSettings = webView.getSettings(); // 启用 JavaScript 支持 webSettings.setJavaScriptEnabled(true); // 支持 HTML5 视频播放 webSettings.setPluginState(WebSettings.PluginState.ON); webSettings.setMediaPlaybackRequiresUserGesture(false); // 允许自播放 // 提高 WebView 性能 webSettings.setLoadWithOverviewMode(true); webSettings.setUseWideViewPort(true); webSettings.setAllowFileAccess(true); // 设置 WebView 客户端 webView.setWebViewClient(new WebViewClient()); webView.setWebChromeClient(new WebChromeClient() { @Override public void onShowCustomView(View view, CustomViewCallback callback) { super.onShowCustomView(view, callback); // 处理全屏模式 } @Override public void onHideCustomView() { super.onHideCustomView(); // 恢复默认视图 } }); ``` #### 3. 检查视频格式兼容性 某些视频格式可能Android WebView 原生支持。例如,FLV 和 HLS 协议在主流浏览器中普遍支持[^1]。建议使用常见的 MP4 格式(H.264 编码)来确保兼容性。 #### 4. 使用第三方播放器插件 如果 WebView 仍然无法正常播放视频,可以考虑引入第三方播放器插件,如 MuiPlayer。该插件提供了统一的样式和增强的移端播放功能,能够解决大部分兼容性问题[^3]。 安装和使用 MuiPlayer 的步骤如下: 1. 安装插件: ```bash npm i mui-player-mobile-plugin --save ``` 2. 引入并初始化: ```javascript import MuiPlayer from 'mui-player'; import MuiPlayerMobilePlugin from 'mui-player-mobile-plugin'; var mp = new MuiPlayer({ container: '#mui-player', src: '../media/media.mp4', plugins: [ new MuiPlayerMobilePlugin({ showMenuButton: true, }) ] }); ``` #### 5. 调试日志分析 如果问题仍未解决,可以主发送错误报告并检查日志输出。通过捕获 WebView 的加载错误和播放事件,定位具体原因: ```java webView.setWebChromeClient(new WebChromeClient() { @Override public void onConsoleMessage(ConsoleMessage consoleMessage) { super.onConsoleMessage(consoleMessage); Log.d("WebView", "Console: " + consoleMessage.message()); } }); ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值