Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

大话Android Hybrid

$
0
0

前言

其实之前一直都很抵制hybrid开发,因为作为一个Android开发程序员,总是觉得原生的更好(其实是不想丢饭碗),但是一个闲着没事干,就写了一个demo搭了个webview,然后把html文件放到asset下面,一加载惊呆宝宝了,简直跟原生的没有区别啊,体验跟原生基本一样(andrid 5.0以后webview的速度比之前的版本有很大的提升),至此我就走上了学习混合开发的道路.

前期准备 WebView

其实我相信很多跟我一样刚入门混合开发的人,对于应该要学习哪部分知识都会感到迷惑,在这里我先谈谈我的经验:

  1. html基础,不用说很厉害,但是至少你要知道整个html的体系还有css,div等控件的使用,还有对html节点的一些基本操作
  2. javaScript基础,这里说的基础就是语法之类的,js这一部分其实挺重要的,跟上面一样,但是js你懂得越多,少走的坑也就真的越少,切身体会啊!!!
  3. 对一些常见的前段框架的运用,比如jquery,sui-mobile,第三点倒不是很重要
  4. webview的原理,这一点挺重要的,因为它涉及到webview中的js怎么去与android的native交互的原理,懂得原理你可以在混合开发中更加”自由地”做出你想要的东西

把网页搬到自己的app上面

假如我们的第一个需求是:把一个网页搬到自己的app上面。那此时,我们只需要下面这段代码就行

public class BolgActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bolg);
        webView = (WebView) findViewById(R.id.webview);
        //这段代码的作用是让webview不要使用系统自带浏览器
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url){
                return false;
            }
        });
        webView.loadUrl("http://www.baidu.com");
    }
}

运行结果:
图片名称
在一般情况下,我们的做法都是把html文件放在app的asset目录下,然后通过下面的代码来加载html文件,这样大大提高了加载速度

mWebView.loadUrl("file:///android_asset/index.html");

native提供帮助

很明显这不能满足我们的需求,这样你的应用和浏览器又有什么区别,那现在我们提高要求。假如现在我们有一个新的需求:
要求网页弹出一个加载框(要求不是网页的加载框,而是android系统原生的加载框)
在完成这个需求之前,就必须先讲讲webview中的页面怎么去和android原生交互了.我们可以先把webview想象成一个容器,那么html页面就是运行在这个容器上面的.
那么我们的webview想要去调用html页面里的js就通过下面这段代码

webView.loadUrl("javascript: log()");

那么js中要调用android里面的代码就是通过webview注入一个对象,然后让js调用,下面我用伪代码表示一下:

{
    //假设这里是activity里面
    webView.addJavascriptInterface(new JsInterface(), "interface");
    class JsInterface{
        void showToast(String msg){
            Toast.make(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
        }   
    }
}
-------------------------------------------------------
{
    //假设这里是在一段JavaScript函数里面
    interface.showToast("I'm js");
}

上面讲的是第一种js和native交互的方式,其实我说的很直白,如果看不懂可以看看下面这篇博文:
js与webview的交互(并不是很喜欢这种转载的,但由于原创的那篇打不开,将就一下吧)

进阶 JsBridge

ps:上面我们讲了js与native交互的一种方案,但在实际运用中我发现并不是很好用,js与native的调用一多,会显得代码非常混乱,当然后面也有个方案能解决,甚至比我接下来要讲的方案还要好,这里先讲下我现在在使用的方案。
相信我的这篇blog是你查阅了大量资料以后才无意中看到的,那么你肯定是知道JsBridge了。先看看代码

{
    //这是在native中的代码,mWebView为JsBridge封装过的webview
    mWebView.callHandler("myInit", "我是数据", new CallBackFunction() {
            @Override
            public void onCallBack(String data) {

            }
        });
}
-----------------------------------------------------------------------------------------------------
{
    //这是在Js中的代码,bridge是native注入在js中的对象
    bridge.registerHandler("functionInJs", function(data, responseCallback) {
                document.getElementById("content").innerHTML = ("data from Java: = " + data);
                var responseData = "Javascript Says  我要你的地址!";
                responseCallback(responseData);
            });
}

上面这段代码什么意思呢?就是在js中通过bridge注册一个函数,然后在native中通过封装好的webview.callHandler函数来调用js中已经注册好的函数,那么反过来Js想调用native的函数,也是同样的道理,只要在native中注册好函数,在js中通过bridge.callHandler来调用就行了。
这样给我们的开发提供了极大的方便,而且避免了很多代码混乱的情况,那么它的基本原理是什么呢?下面我还是用最简单地语言来讲一讲。

  • 在我们的webview中,可以设置自定义的WebChromeClient,这个东西就是加载在webview中的html中有弹窗事件的时候会回调的
  • WebChromeClient里面有很多种回调,其中有一个回调方法onJsPrompt,这个方法就是我们的突入点(其他方法也都可以,但是可能会干扰到原来的逻辑,这个比较少用,所以我们用这个)

语言是不是很简单暴力?如果你还看不懂,看看下面的实例:

{
    //假设这里是在js里面
    var uri = "hybrid://objectName:sid/methodName?params";
    var value = "xxxxxxx";
    window.prompt(uri, value);
}
--------------------------------------------------------------
{
    //这是在activity里面
    public class InjectedChromeClient extends WebChromeClient {
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                //这里传进来的参数就是从js的window.prompt(uri, value)传过来的参数
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }
    }
}

JsBridge的工作原理就是这样,那么现在看来好像对我们没什么用呀?下面我举个实际应用的例子说一下,就拿上面那个例子来说:要求网页弹出一个加载框(要求不是网页的加载框,而是android系统原生的加载框)
(先不要讲太复杂的代码,从上面JsBridge的知识我们已经知道了js里面能传递数据给webview)

{
    //假设这个方法是js中负责发送数据给webview的
    function postToNative(data){..省略..}
    var data = 
    {
        "tagName":"showLoadingDialog",
        "pars":{
            "content":"正在加载中",
            "title":"请等待",
            //这个callback就是当加载框被取消的时候native回调js中的函数
            "callback":"onLoadingCancel"
        }   
    };
    postToNative(data);
}

{
    //假设这个方法实在native中接收webview中的js发过来的数据
    void receiveJsData(String data){
        //上面js的函数可以看出,js向我们发送了一段json数据
        //所以我们只需解析这段数据,便可知道js需要native做什么事
    }
}

总结

对比

觉得上面讲的都是废话?
其实网上关于hybrid开发的文章我敢说我基本都看过了,国外的文章也有看了看,在实际开发中,我自己也写了很多demo,下面是我总结的关于一些JsBridge的优缺点:
优点

  1. 相比传统的方式方便很多,代码更加简洁
  2. 其实第一点已经完爆一切了,写不出第二点了哈哈

缺点
1. 调试困难,不知是否是博主基础太差,过程中有许多意料不到的错(当然是关于js的),不过到后面基本都能解决了
2. 如果要做到一个页面可以兼容所有web页面,那需要注册的函数太多了,因为每个页面需要的功能都不一样
3. 生命周期有点难掌控,假如你在JsBridge未初始化之前就调用,那么会导致JsBridge初始化失败(之前这个坑陷了好久后面才发现的)

实际应用

在实际开发中,我的方法是通过json来灵活地定义每个动作,也就是利用json数据,来告诉native我需要做什么动作。通过这种方式,一个app甚至只要一个activity就能解决所有的页面,此时有人要问了,那我每个页面的ui就都一样了吗?比如说toolbar,alertDialog。
然而并不是这样的,通过我上面将的知识点,可以在js初始化的时候,定义一段json数据,用户初始化ui,例如下面的:

var data =
{
    "title":"话题列表",
    "right":{
        "icon":"R.mipmap.ic_launcher",
        "description":"描述",
        "callback":"onRightBtnClick"
    }
};

然后在native中解析这段数据,来达到初始化ui的效果,这种方式我也已经运用在我的项目中。
相信你看了这边文章,你已经基本知道了webview在实际开发中的应用和各种方案,如果觉得文章有哪些地方写的不好,请留言说明。

ps:代码下次贴出,在写个几个基类供参考,写得还不是很完善,需要的也提出来把!

作者:qq122627018 发表于2016/11/18 20:53:14 原文链接
阅读:49 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>