腾讯x5内核(TBS)集成使用详解(webView视频播放)

腾讯x5内核(TBS)集成使用详解(webView视频播放)
腾讯x5内核(TBS)集成简单,目前在腾讯的QQ和微信中均有使用。
相对于android的系统内核来说,TBS简直太完美了
TBS(腾讯浏览服务)的优势

  1. 速度快:相比系统webview的网页打开速度有30+%的提升;
  2. 省流量:使用云端优化技术使流量节省20+%;
  3. 更安全:安全问题可以在24小时内修复;
  4. 更稳定:经过亿级用户的使用考验,CRASH率低于0.15%;
  5. 兼容好:无系统内核的碎片化问题,更少的兼容性问题;
  6. 体验优:支持夜间模式、适屏排版、字体设置等浏览增强功能;
  7. 功能全:在Html5、ES6上有更完整支持;
  8. 更强大:集成强大的视频播放器,支持视频格式远多于系统webview;
  9. 视频和文件格式的支持x5内核多于系统内核
  10. 防劫持是x5内核的一大亮点

如果是在项目中使用过android的webview之后,就能明白系统自带的各种坑。当初功能上线之后。遇到以下问题

  1. 魅族手机上部分页面加载不出来
  2. android低版本上部分js语法根本无法使用。
  3. 华为手机部分机型加载页面超级慢
  4. 复杂页面卡顿感十分明显
  5. 视频播放服务实现困难,都是原生自己实现。当触发全屏等操作的时候,效果很不好。
  6. 安全扫描的时候直接就能扫描出安全漏洞。webView注入漏洞 android 4.2以下

后来更换了腾讯x5内核(TBS),实现的效果远好于预期。

腾讯TBS官网 下载android的SDK后。解压,找到需要的jar包。
放入so文件和jar包文件分别放到src->jniLibs->armeabi和libs目录下,项目中添加这两项的依赖。

在application中添加sdk初始化的代码。

 QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {
            @Override
            public void onViewInitFinished(boolean arg0) {
                //x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。
                Log.d("app", "腾讯X5内核初始化回调 onViewInitFinished is " + arg0);
            }
            @Override
            public void onCoreInitFinished() {
                Log.d("app", "腾讯X5内核初始化回调 onCoreInitFinished");
            }
        };
        //x5内核初始化接口
        QbSdk.initX5Environment(getApplicationContext(), cb);

sdk初始化之后,构建一个BaseTencentWebActivity
界面配置文件为:

 <RelativeLayout
        android:id="@+id/rl_toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/titleheight"
        android:background="@drawable/mt_bg_title">

        <ImageButton
            android:id="@+id/ibLeft"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerVertical="true"
            android:background="@drawable/mt_bg_imagebutton"
            android:contentDescription="@string/app_name"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:src="@drawable/mt_ic_left" />

        <TextView
            android:id="@+id/tvTitle"
            style="@style/titletext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_marginLeft="40dp"
            android:layout_marginRight="40dp"
            android:ellipsize="end" />
        <ImageButton
            android:id="@+id/ibRight"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_toLeftOf="@id/ibRight1"
            android:background="@drawable/mt_bg_imagebutton"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:visibility="gone" />
    </RelativeLayout>

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="3dip"
        android:max="100"
        android:progressDrawable="@drawable/mt_progress_horizontal" />

    <com.tencent.smtt.sdk.WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

public abstract class BaseTencenWebactivity extends BaseActivity {
    protected RelativeLayout rlToolbar;
    protected ImageButton ibLeft1;
    protected ImageButton ibRight2;
    protected WebView webView;
    protected TextView tvTitle;
    protected ProgressBar progressBar;

    protected static final String HYBRID = "HYBRID";    //js调用android时候,android注入的对象名称

    public static final String FROM_WHERE = "H5";   //跳转来源H5;

    @Override
    public void setContentView() {
        setContentView(R.layout.activity_base_tencen_web);
    }

    @Override
    public void init() {
        rlToolbar = (RelativeLayout) findViewById(R.id.rl_toolbar);
        ibLeft1 = (ImageButton) findViewById(R.id.ibLeft1);
        ibRight2 = (ImageButton) findViewById(R.id.ibRight2);
        tvTitle = (TextView) findViewById(R.id.tvTitle);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        progressBar.setVisibility(View.VISIBLE);
        progressBar.setProgress(0);

        webView = (WebView) findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setUseWideViewPort(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setBlockNetworkImage(false);
        webView.getSettings().setBlockNetworkLoads(false);
        webView.getSettings().setDomStorageEnabled(true);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setDisplayZoomControls(false);
        webView.getSettings().setGeolocationEnabled(true);
        webView.getSettings().setSupportZoom(true);
        webView.getSettings().setUserAgentString("Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn;) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.3 Mobile Safari/537.36");
        onWebViewSettingBuild();
        configPlaySetting();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
            injectJavaScript();
        }
        if (isNoWebCache()) {
            webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
        }
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if (url.startsWith("https") || url.startsWith("http") ) {
                    view.loadUrl(url);
                    progressBar.setVisibility(View.VISIBLE);
                    progressBar.setProgress(0);
                    BaseTencenWebactivity.this.shouldOverrideUrlLoading(view, url);
                }
                return false;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                progressBar.setVisibility(View.GONE);
                super.onPageFinished(view, url);
                BaseTencenWebactivity.this.onPageFinished(view, url);
            }

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                BaseTencenWebactivity.this.onPageStarted(view, url, favicon);
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                if (enabledHTTPS()) {
                    //当启用https,遇到证书错误的时候,直接调用handler.proceed实际上是存在安全因素的。
                    //实际上可以根据不同的错误类型,来做调整。比如遇到一个不可信的证书。 SslError.SSL_UNTRUSTED
                    //可以像其他浏览器那样,弹出对话框提示,提示用户证书不可信等。
                    handler.proceed();
                } else {
                    super.onReceivedSslError(view, handler, error);
                }

            }
        });
        webView.setWebChromeClient(new WebChromeClient() {

            private View myView;
            @Override
            public void onProgressChanged(WebView view, int progress) {
                progressBar.setProgress(progress);
                BaseTencenWebactivity.this.onProgressChanged(view, progress);
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                return handleJsPrompt(view, url, message, defaultValue, result);
            }

            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                return handleJsAlert(view, url, message, result);
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
                return handleJsConfirm(view, url, message, result);
            }

            @Override
            public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
                MyLog.i(getClass().getName(), String.format("[%s] sourceID: %s lineNumber: %s message: %s",
                        consoleMessage.messageLevel(), consoleMessage.sourceId(), consoleMessage.lineNumber(), consoleMessage.message()));
                return super.onConsoleMessage(consoleMessage);
            }

            @Override
            public void onReceivedTitle(WebView webView, String s) {
                tvTitle.setText(s);
            }

            //自定义视频播放  如果需要启用这个,需要设置x5,自己实现全屏播放。目前的使用的x5的视频播放
            //如果是点击h5 vedio标签的播放,需要自己实现全屏播放
            @Override
            public void onShowCustomView(View view, IX5WebChromeClient.CustomViewCallback customViewCallback) {
                super.onShowCustomView(view, customViewCallback);
                ViewGroup parent = (ViewGroup) webView.getParent();
                parent.removeView(webView);

                // 设置背景色为黑色
                view.setBackgroundColor(BaseTencenWebactivity.this.getResources().getColor(R.color.black));
                parent.addView(view);
                myView = view;
                rlToolbar.setVisibility(View.GONE);
                setFullScreen();
            }

            @Override
            public void onHideCustomView() {
                super.onHideCustomView();
                if (myView != null) {
                    ViewGroup parent = (ViewGroup) myView.getParent();
                    parent.removeView(myView);
                    parent.addView(webView);
                    myView = null;
                    rlToolbar.setVisibility(View.VISIBLE);
                    quitFullScreen();
                }
            }

        });
        tvTitle.setText(getWebViewTitle());
        String url = getUrl();
        if (!TextUtils.isEmpty(url)) {
            loadUrl(url);
        }

    }

    /**
     * 播放设置    已经开启x5全屏  小窗播放  页内播放等。
     */
    protected void configPlaySetting(){
        Bundle data = new Bundle();
//true表示标准全屏,false表示X5全屏;不设置默认false,
        data.putBoolean("standardFullScreen", false);
//false:关闭小窗;true:开启小窗;不设置默认true,
        data.putBoolean("supportLiteWnd", true);
//1:以页面内开始播放,2:以全屏开始播放;不设置默认:1
        data.putInt("DefaultVideoScreen", 1);
        webView.getX5WebViewExtension().invokeMiscMethod("setVideoParams", data);
//        standardFullScreen 全屏设置
//
//        设置为true时,我们会回调WebChromeClient的onShowCustomView方法,由开发者自己实现全屏展示;
//
//        设置为false时,由我们实现全屏展示,我们实现全屏展示需要满足下面两个条件:
//
//        a. 我们 Webview初始化的Context必须是Activity类型的Context
//
//        b. 我们 Webview 所在的Activity要声明这个属性
//
//        android:configChanges="orientation|screenSize|keyboardHidden"
//        如果不满足这两个条件,standardFullScreen 自动置为 true
//        supportLiteWnd 小窗播放设置
//
//        前提standardFullScreen=false,这个条件才生效
//
//        设置为 true, 开启小窗功能
//
//        设置为 false,不使用小窗功能
//
//        DefaultVideoScreen 初始播放形态设置
//
//        a、以页面内形态开始播放
//
//        b、以全屏形态开始播放

    }


    /**
     *设置全屏
     */
    private void setFullScreen(){
        BaseTencenWebactivity.this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }


    /**
     * 退出全屏
     */
    private void quitFullScreen() {
        // 声明当前屏幕状态的参数并获取
        final WindowManager.LayoutParams attrs = BaseTencenWebactivity.this.getWindow().getAttributes();
        attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
        BaseTencenWebactivity.this.getWindow().setAttributes(attrs);
        BaseTencenWebactivity.this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
    }

    /**
     * 播放视频  传入视频的url地址
     * @return
     */
    protected boolean playVideoByTbs(String videoUrl){
        if(TbsVideo.canUseTbsPlayer(BaseTencenWebactivity.this)){
            //播放器是否可以使用
            Bundle xtraData = new Bundle();
            xtraData.putInt("screenMode", 102);//全屏设置 和控制栏设置
            TbsVideo.openVideo(BaseTencenWebactivity.this,videoUrl, xtraData);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 是否在android中处理Js的Prompt弹出框
     * false不处理,true可拦截处理
     */
    protected boolean handleJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        return false;
    }

    /**
     * 是否在android中处理Js的Alert弹出框
     * false不处理,true可拦截处理
     */
    protected boolean handleJsAlert(WebView view, String url, String message, JsResult result) {
        return false;
    }

    /**
     * 是否在android中处理Js的Confirm弹出框
     * false不处理,true可拦截处理
     */
    protected boolean handleJsConfirm(WebView view, String url, String message, JsResult result) {
        return false;
    }

    /**
     * 注入javaScript对象
     */
    @SuppressLint("AddJavascriptInterface")
    private void injectJavaScript() {
        webView.addJavascriptInterface(getJavascriptInterface(), HYBRID);
    }

    /**
     * 返回 注入javaScript对象,必须继承 {@link BaseHYBRID}
     *
     * @return T extend BaseHYBRID
     */
    protected BaseHYBRID getJavascriptInterface() {
        return new BaseHYBRID();
    }

    protected boolean enabledHTTPS() {
        return Tools.getBooleanExtra(getIntent(), "enabledHTTPS", false);
    }
    
    

    /**
     * 调用javascript方法
     *
     * @param js
     */
    protected void loadJavaScript(String js) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //sdk>19才有
            webView.evaluateJavascript(js, new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String responseJson) {
                    //这里获取的参数就是JS函数的返回值
                }
            });
        } else {
            //SDK <= 19
            webView.loadUrl(js);
        }
    }

    protected void onWebViewSettingBuild() {

    }

    protected void loadUrl(String url) {
        webView.loadUrl(url);
    }

    /**
     * 不用缓存,true:无缓存,false:默认
     *
     * @return
     */
    protected boolean isNoWebCache() {
        return false;
    }

    protected String getWebViewTitle() {
        return getIntent().getStringExtra("title");
    }

    public String getUrl() {
        return getIntent().getStringExtra("url");
    }
    

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (webView.canGoBack()) {
                webView.goBack();
            } else {
                back();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    protected void shouldOverrideUrlLoading(WebView view, String url) {
    }

    protected void onPageFinished(WebView view, String url) {
    }

    protected void onPageStarted(WebView view, String url, Bitmap favicon) {
    }

    protected void onProgressChanged(WebView view, int progress) {
    }

    /**
     * 公共的JS行为基类,封装通用的操作给第三方
     */
    public class BaseHYBRID {

        /**
         * 退出当前页面
         */
        @JavascriptInterface
        public void exitPage() {
            back();
        }

        /**
         * 设置页面标题
         *
         * @param title
         */
        @JavascriptInterface
        public void setTitle(String title) {
            setTitleOnUiThread(title);
        }

        public void setTitleOnUiThread(final String title) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvTitle.setText(title);
                }
            });
        }
    }

    @Override
    protected void onDestroy() {
        webView.stopLoading();
        webView.removeAllViews();
        webView.destroy();
        webView = null;
        super.onDestroy();
    }

}

以上就封装了一个能够正常播放视频,能够小窗播放,h5传入视频地址就能正常播放的浏览器。其中tbsVedio是内置的一个播放方法,传入url地址就行了,如果需要播放网页中嵌入的视频,在全屏和非全屏之间的切换时,需要实现onShowCustomView和onHideCustomView两个方法。

到这里腾讯x5内核(TBS)就集成完毕了

猜你喜欢

转载自blog.csdn.net/xu_coding/article/details/88431397