混合开发短视频播放-dsBridge技术框架

基本介绍:

 混合跨平台技术框架有多种,如web开发、Hybrid开发、React Native、Flutter,此处使用Hybrid实现web与原生的混合开发。

原生与web之间需要搭建通道,才能进行通信,此处用到dsBridge框架实现两者的数据通信,以短视频播放为示例讲解混合开发Hybrid的运用。

慕课网学习视屏地址:https://www.imooc.com/video/22050  ---- 我也是看视屏学习的,但没有源码

dsBridge的使用:

先学习dsBridge的基本使用,实现两者的通信是本次案例的重点。

两端的官方使用文档地址中,详细讲述了dsBridge的集成与基本使用。

dsBridge-android端官方使用文档地址:https://github.com/wendux/DSBridge-Android/blob/master/readme-chs.md

dsBridge-IOS端官方使用文档地址:https://github.com/wendux/DSBridge-IOS

一、dsBridge的集成

① 本地依赖

将文件路径为 https://github.com/wendux/DSBridge-Android/tree/master/dsbridge 下载出来,去掉npm,作为module引入项目中。(npm文件夹中放的事dsBridge.js)

② 仓库依赖

添加 JitPack repository 到gradle脚本中

allprojects {

  repositories {

   ...

   maven { url 'https://jitpack.io' }

  }

}

dependencies {

    //compile 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'

    //support the x5 browser core of tencent

    //compile 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT'

}

短视频播放案例代码:

目录结构:

一、H5页面

<!DOCTYPE html>
<html>
<!--
简体中文页面:html lang=zh-cmn-Hans
繁体中文页面:html lang=zh-cmn-Hant
英语页面:html lang=en -->
<head lang="zh-cmn-Hans">
    <!-- 兼容搜素引擎 webkit -->
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome">
    <!--
    width:可视区域的宽度,值可为数字或关键词device-width
     height:同width
     intial-scale:页面首次被显示是可视区域的缩放级别,取值1.0则页面按实际尺寸显示,无任何缩放
     maximum-scale=1.0, minimum-scale=1.0;可视区域的缩放级别,
     maximum-scale用户可将页面放大的程序,1.0将禁止用户放大到实际尺寸之上。
     user-scalable:是否可对页面进行缩放,no 禁止缩放 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <script src="dsbridge.js"></script>
</head>
<style>
		*{padding 0; margin:0}
		.poster{
			position : relative;
			display: -webkit-box;
			display: -webkit-flex;
			display: flex;
			-webkit-box-align: center;
			-webkit-align-items: center;
			align-items: flex-start;
			-webkit-box-pack: center;
			-webkit-justify-content: center;
			justify-content: center;
			object-fit: contain;
			z-index: 2;
		}
		.button{
			top: 0px;
			left: 0px;
			right:  0px;
			bottom: 0px;
			position: absolute;
			display: -webkit-box;
			display: -webkit-flex;
			display: flex;
			-webkit-box-align: center;
			-webkit-align-items: center;
			align-items: center;
			-webkit-box-pack: center;
			-webkit-justify-content: center;
			justify-content: center;
			border-style: solid;
			z-index: 3;
		}

		.btn{
			position: relative;
			text-align: center;
			background: #D8D8D8;
			color: #222;
			padding: 20px;
			margin: 70px;
			font-size: 24px;
			border-radius: 4px;
			box-shadow: 4px 2px 10px #999;
		}

		.btn:active{
			opacity: .7;
			box-shadow: 4px 2px 10px #555;
		}

</style>
<body>
    <div id="play" class="poster">
        <img src="poster.jpg" width="100%" width="auto" height="auto"/>
        <div class="button" onclick="play()">
            <img class="playImg" src="play.png" />
        </div>
    </div>

    <div class="btn" onclick="pause()">暂停播放</div>
    <div class="btn" onclick="resume()">继续播放</div>
    <div class="btn" onclick="seekForward()">前进10秒</div>
    <div class="btn" onclick="seekBack()">后退10秒</div>
    <div class="btn" onclick="fullScreen()">全屏</div>
    <div class="btn" onclick="exitFullScreen()">退出全屏</div>

</body>
<script>
    function play(){
    //url old : http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4
    var videoInfo = "{\n" +
			"\t\"url\":  \"http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4\",\n"+
			"\t\"width\":  1080,\n"+
			"\t\"height\":  750\n"+
			"}"
      bridge.call("play",videoInfo,function(v){
            alert(v);
           // document.getElementById("play").style.display= "none";
        });
    }

    function pause(){
        bridge.call("pause","test syn call",function(v){
            //document.getElementById("play").style.display= "block";
            alert(v);
        });
    }

    function resume(){
        bridge.call("resume","test asyn call",function(v){
            alert(v);
        });
    }

    function seekForward(){
        bridge.call("seek","10",function(v){
            alert(v);
        });
    }

    function seekBack(){
        bridge.call("seek","-10",function(v){
            alert(v);
        });
    }

    function fullScreen(){
        bridge.call("fullScreen","test syn call",function(v){
            alert(v);
        });
    }

    function exitFullScreen(){
        bridge.call("exitFullScreen","test syn call",function(v){
            alert(v);
        });
    }
</script>
</html>

二、H5与Android绑定

MainActivity.java

package com.xyhe.shortvideo;

import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import wendu.dsbridge.DWebView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final DWebView dWebView = findViewById(R.id.dWebView);

        //将WebView与Html页面进行绑定
        dWebView.addJavascriptObject(new VideoApi(this), "");
        dWebView.loadUrl("file:///android_asset/video.html");
        dWebView.setWebContentsDebuggingEnabled(true);
        getWindow().setFormat(PixelFormat.TRANSLUCENT);//设置半透明
       
    }

    @Override
    public void onBackPressed() {
        if(!VideoApi.onBackPressed()){
            super.onBackPressed();
        }

    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <wendu.dsbridge.DWebView
        android:id="@+id/dWebView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
  
</FrameLayout>

三、用于h5使用的方法

VideoApi.java

package com.xyhe.shortvideo;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.widget.FrameLayout;

import org.json.JSONObject;

import wendu.dsbridge.CompletionHandler;

public class VideoApi {
    private Context mContext;
    private boolean mIsAttach = false;//是否在播放
    private static VideoPlayerView mVideoPlay;

    public VideoApi(Context context){
        mContext = context;
        mVideoPlay =  new VideoPlayerView(mContext);
    }


    /**
     * 请求播放视屏,并且添加VideoView到当前View中
     */
    @JavascriptInterface
    public String play(Object msg){
        if(mIsAttach){
            return "video already play.";
        }
        String url = "";
        int width = 0;
        int height = 0;
        try{
            JSONObject videoInfo = new JSONObject((String)msg);
            url = videoInfo.getString("url");
            width = videoInfo.getInt("width");
            height = videoInfo.getInt("height");
        }catch (Exception e){
        }
        //将VideoPlay视图添加到View中
        final Activity acticity = (Activity)mContext;
        final int finalWidth = width;
        final int finalHeight = height;
        final String finalUrl = url;
        acticity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                FrameLayout frameLayout = acticity.findViewById(R.id.frameLayout);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(finalWidth, finalHeight);
                frameLayout.addView(mVideoPlay, params);
                mIsAttach = true;
                mVideoPlay.play(finalUrl);
            }
        });
        return "play success";
    }

    /**
     * 暂停播放
     * @param msg
     * @return
     */
    @JavascriptInterface
    public String pause(Object msg){
        if(mVideoPlay!=null){
            mVideoPlay.pause();
        }
        return "call pause success";
    }

    /**
     * 继续播放
     * @param msg
     * @return
     */
    @JavascriptInterface
    public String resume(Object msg){
        if(mVideoPlay!=null){
            mVideoPlay.resume();
        }
        return "call resume success";
    }

    /**
     * 后退或前进
     * @param msg
     * @return
     */
    @JavascriptInterface
    public String seek(Object msg){
        if(mVideoPlay!=null){
            mVideoPlay.seek(Integer.valueOf((String)msg));
        }
        return "call seek success";
    }

    /**
     * 全屏
     * @param msg
     * @return
     */
    @JavascriptInterface
    public String fullScreen(Object msg){
        if(mVideoPlay!=null){
            mVideoPlay.fullScreen();
        }
        return "call fullScreen success";
    }

    /**
     * 退出全屏
     * @param msg
     * @return
     */
    @JavascriptInterface
    public String exitFullScreen(Object msg){
        if(mVideoPlay!=null){
            mVideoPlay.exitFullScreen();
        }
        return "call exitFullScreen success";
    }


    /**
     * android返回按钮:先退出全屏
     * @return
     */
    public static boolean onBackPressed() {
        if(mVideoPlay!=null){
            return mVideoPlay.onBack();
        }
        return false;
    }
}

四、重写播放器控件

VideoPlayView.java

package com.xyhe.shortvideo;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.MediaController;
import android.widget.VideoView;

/**
 *客户端播放器
 */
public class VideoPlayerView extends FrameLayout {
    private VideoView mVideoView;
    private boolean mIsFull = false;//是否全屏
    private int lastWidth;//全屏圈的宽
    private int lastHeight;//全屏圈的高

    public VideoPlayerView(Context context) {
        super(context);
    }

    /**
     * 播放视频
     * @param url
     */
    public void play(String url){
        mVideoView = new VideoView(getContext());
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        addView(mVideoView,params);
        MediaController controller = new MediaController(getContext());
        mVideoView.setMediaController(controller);
        controller.setAnchorView(mVideoView);
        mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                Log.v("VIDEO","onPrepare()");
            }
        });

        mVideoView.setVideoPath(url);
        mVideoView.start();
    }

    /**
     * 暂停播放
     */
    public void pause(){
        if(mVideoView!=null){
            mVideoView.pause();
        }
    }

    /**
     * 继续播放
     */
    public void resume(){
        if(mVideoView!=null){
            mVideoView.start();
        }
    }

    /**
     * 前进或倒退几秒
     * @param second
     */
    public void  seek(int second){
        if(mVideoView!=null){
            mVideoView.seekTo(mVideoView.getCurrentPosition()+second*1000);
        }
    }

    /**
     * 获取当前播放进度
     * @return
     */
    public int getCurrentPosition(){
        if(mVideoView!=null){
            return mVideoView.getCurrentPosition();
        }
        return 0;
    }

    /**
     * 全屏
     */
    public void fullScreen(){
        if(mIsFull){
            return;
        }
        final Activity  activity = (Activity) getContext();
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //做成横屏,并且全屏
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
                //留存现有的宽和高
                ViewGroup.LayoutParams params = mVideoView.getLayoutParams();
                lastWidth = params.width;
                lastHeight = params.height;
                //设置全屏宽高度
                params.width = LayoutParams.MATCH_PARENT;
                params.height = LayoutParams.MATCH_PARENT;

                setLayoutParams(params);
                mIsFull = true;
            }
        });

    }

    /**
     * 退出全屏
     */
    public void exitFullScreen(){
        if(!mIsFull){
            return;
        }
        final Activity  activity = (Activity) getContext();
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //做成竖屏
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                //设置全屏宽高度
                ViewGroup.LayoutParams params = getLayoutParams();
                params.width = lastWidth;
                params.height = lastHeight;
                setLayoutParams(params);
                mIsFull = false;
            }
        });
    }

    /**
     * 返回按钮:先退出全屏
     * @return
     */
    public boolean onBack() {
        if(mIsFull){
            exitFullScreen();
            return true;
        }
        return false;
    }
}

总结:

代码粘起来就几分钟,但是所有代码都是看视屏一点点写出来的。

这个案例重在将H5和原生开发啊(Android或IOS)结合起来,算是打开了一扇新的门。技术永远在进步,学海无涯,继续加油,我去开启下一个课程学习了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值