HybridApp 概念与自定义JsBridge 框架

来源:互联网 发布:windows清理助手下载 编辑:程序博客网 时间:2024/05/29 09:38

前言:

HybridApp在过去的两年中已经成为移动界的核心话题,但是作为一名Web开发者来说要如何站在移动互联网的浪潮之巅呢?是选择学习原生开发,研究JavaObject-CC#等语言,还是选择继续使用网页开发,容忍HTML5功能的局限性?就在开发者左右为难的情况下HybridApp作为一个折中的解决方案诞生了

概念:

HybridApp是同时采用网页语言与程序语言进行开发,通过不同的应用商店进行打包与分发,应用的特性更接近原生应用而且又区别与Web应用。但是在开发过程中同时使用了网页语言,所以开发成本与难度大大降低。也就是说HybridApp兼具了NativeApp与WebApp两者的诸多优点。
对比:


  

Web App(网页应用)

Hybrid App(混合应用)

Native App(原生应用)

开发成本

维护更新

简单

简单

复杂

体验

Store或market认可

不认可

认可

认可

安装

不需要

需要

需要

跨平台

APK size

轻量


核心思路:

Hybrid App 融合 Web App的原理就是引入一个WebView组件,可以在这个组件中载入HTML页面做UI呈现(部分的),JsBridge 实现业务层与界面层的数据通讯、逻辑调用。

技术实现:

自定义JsBridgeWebView 继承自android.webkit.WebView 并内部定义一个BridgeHandler接口

@SuppressLint("SetJavaScriptEnabled")public class JsBridgeWebView extends WebView {    private List<String> mListName = new ArrayList<String>();    private class BridgeHandlerHolder implements BridgeHandler {        private BridgeHandler mBridgeHandler;        public BridgeHandlerHolder(BridgeHandler in) {            mBridgeHandler = in;        }        @Override        @JavascriptInterface        public String handler(String data0, String data1, String o) {            return null != mBridgeHandler ? mBridgeHandler.handler(data0, data1, o) : null;        }    }    public interface BridgeHandler {        public String handler(String data0, String data1, String o);    }    public void callback(Object... args) {        if (args.length > 0) {            String jsFunctionName = (String) args[0];            StringBuilder jsFunctionParam = new StringBuilder();            if (!TextUtils.isEmpty(jsFunctionName)) {                for (int i = 1; i < args.length; i++) {                    if (i == 1) {                        jsFunctionParam.append(String.format("'%s'", args[i]));                    } else if (i > 1) {                        jsFunctionParam.append(String.format(",'%s'", args[i]));                    }                }                String js = String.format("javascript:%s(%s)", jsFunctionName, jsFunctionParam);                this.loadUrl(js);            }        }    }    public JsBridgeWebView(Context context) {        super(context);        initSetting();    }    public JsBridgeWebView(Context context, AttributeSet attrs) {        super(context, attrs);        initSetting();    }    public JsBridgeWebView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initSetting();    }    public void initSetting() {        WebSettings webSettings = this.getSettings();        webSettings.setJavaScriptEnabled(true);        webSettings.setSupportZoom(true);    }    public void unRegisterHandlerAll() {        if (null != mListName) {            for (int i = 0; i < mListName.size(); i++) {                this.removeJavascriptInterface(mListName.get(i));            }            mListName.clear();        }    }    public void unRegisterHandler(String name) {        if (null == mListName) {            mListName = new ArrayList<String>();        }        if (null != mListName && mListName.contains(name)) {            mListName.remove(name);            this.removeJavascriptInterface(name);        }    }    public boolean registerHandler(String name, BridgeHandler bridgeHandler) {        boolean bReturn = true;        if (null == mListName) {            mListName = new ArrayList<String>();        }        if (null != mListName && mListName.contains(name)) {            bReturn = false;        }        if (bReturn) {            this.addJavascriptInterface(new BridgeHandlerHolder(bridgeHandler), name);            if (null != mListName) {                mListName.add(name);            }        }        return bReturn;    }}
Layout XML 使用:
<RelativeLayout>
 <com.example.qinghua_liu.myapplication.customview.JsBridgeWebView        android:id="@+id/JsBridgeWebView"        android:layout_width="match_parent"        android:layout_height="match_parent"/></RelativeLayout>
Activity Code:
public class ActivityMain4Activity extends Activity {    private RelativeLayout mainLayout;    private JsBridgeWebView jsBridgeWebView;    private Context mContext;    private WebViewHandler mWebViewHandler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main4);        mContext = this;        jsBridgeWebView = (JsBridgeWebView) findViewById(R.id.JsBridgeWebView);        mWebViewHandler = new WebViewHandler(jsBridgeWebView);        //jsBridgeWebView.initSetting();        mainLayout = (RelativeLayout) findViewById(R.id.main_layout);        jsBridgeWebView.registerHandler("Register", new JsBridgeWebView.BridgeHandler() {            @Override            public String handler(String data0, String data1, String o) {                Toast toast = Toast.makeText(mContext, String.format("register data. Name:%s ,Psw:%s", data0, data1),                        Toast.LENGTH_SHORT);                toast.show();                final Object jscallbackFuncName = o;                new Thread(new Runnable() {                    @Override                    public void run() {                        Message message = mWebViewHandler.obtainMessage();                        message.obj = jscallbackFuncName;                        mWebViewHandler.sendMessageDelayed(message, 3000);                    }                }).start();                return "";            }        });        jsBridgeWebView.loadData("", "text/html", null);        jsBridgeWebView.loadUrl("file:///android_asset/www/register.html");        jsBridgeWebView.setWebViewClient(new WebViewClient() {        });    }    private static class WebViewHandler extends Handler {        WeakReference<JsBridgeWebView> mJsBridgeWebView;        public WebViewHandler(JsBridgeWebView view) {            mJsBridgeWebView = new WeakReference<>(view);        }        public void handleMessage(Message msg) {            final JsBridgeWebView webview = mJsBridgeWebView.get();            if (null != webview) {                String jscallbackFuncName = msg.obj.toString();                webview.callback(jscallbackFuncName, "1001");            }            //handle you message here!        }    }}
register.html:
<!DOCTYPE html><html><head><base target="_blank"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><style type="text/css">   *{margin:0;padding: 0;}   .lanren{width: 221px;border: solid #E5E5E5; border-width: 1px 0 0 1px;margin:50px auto;}   .lanren ul{overflow: hidden;}   .lanren ul li{float:left;width:200px;height:100px;padding:15px 10px 10px 10px;border-right:1px solid #EDEDED;border-bottom:1px solid #EDEDED;   overflow: hidden;position: relative;list-style: none;}   .lanren ul li img{float: left;margin-right:10px;}   .lanren ul li p{text-align: left;color: #666;font-size: 12px;margin-bottom: 5px;line-height: 140%;max-height: 32px;overflow: hidden;}   .lanren ul li b{color: #e12228;font-size: 16px;font-weight: 700;}   </style></head> <body><br/><div style="margin-left:15px;">用户名:<br/><input type="text" id ="namec"/><br/><br/>密码:<br/><input type="password" id ="psw"/><br/><br/><br/><input type="button" id="play" value="注册" onclick="callAndroidRe();">   <br/><br/><div  id="res"></div><br/><br/>广告推广</div><div class="lanren">   </div>   <script src="jquery.min.js"></script>   <script>   $(function(){      $('.lanren li').hover(function(){         $(this).find('img').stop().animate({            'margin-left':'-7px',            'margin-right':'17px'         })      },function(){         $(this).find('img').stop().animate({            'margin-left':'0',            'margin-right':'10px'         })      })   })   </script>   <!--代码部分end-->   </body></html><script language="JavaScript" type="text/javascript">var res = document.getElementById('res');var namec = document.getElementById('namec');var psw = document.getElementById('psw');function jsCallback (userid){   res.innerHTML= '注册成功! 您的ID是:'+userid;}var Person = function (userid) { this.userid = userid;}.method('getName', function () {   res.innerHTML= '注册成功! 您的ID是:'+this.userid; }).method('setName', function (userid) {   this.userid = userid;     return this;  });function callAndroidRe(){//alert('qh');res.innerHTML= '注册中';    var id = window.Register.handler(namec.value,psw.value,'jsCallback');    //res.innerHTML= '注册成功! 您的ID是:'+id;}</script>
HTML 写的注册界面:
调用JsBridge,注册成功:
总结:
两个注意点:
1.如果你的程序目标平台是17或者是更高,你必须要在暴露给网页可调用的方法(这个方法必须是公开的)加上
@JavascriptInterface注释。如果你不这样做的话,在4.2以以后的平台上,网页无法访问到你的方法。
2. webView 的方法调用必须要在同一线程(例子中是主线程)所以用WorkThread 消息给主线程的Handler 进行
webView 的方法调用。Handler 使用过程中注意内存泄露的问题。
延伸:
上面例子也可认为是个人实现的一个JSbridge 的微型框架。回到Hybrid APP框架话题上来,都说混合开发把UI的开发
交给H5,业务部分给移动开发去做,曾经有牛人问我一个这样的问题,说有没有考虑过,让移动开发去做UI(流畅),而把
业务逻辑交给html(ajax)去做,仔细想想,虽然技术上也可实现,但是现实意义在哪里,我还真没想透。如果您有见解,欢迎留言。







0 0