Android开发之Hybrid开发

来源:互联网 发布:python return{} 编辑:程序博客网 时间:2024/05/21 07:57

本文将介绍Android中hybrid开发相关的知识点。hybrid开发实际上是混合开发的意思,这里的混合是H5开发与Native开发混合的意思。下面的文章中我们将逐个介绍一下hybrid开发的概念、hybrid开发的优势、Android中如何实现hybrid开发、简单的hybrid开发的例子,以及在产品实践中对hybrid开发的应用,希望通过本篇文章的介绍让您能够对Android中的hybrid开发有一个基本的认识。

一:hybrid开发的概念

在具体介绍hybrid开发之前,我们先看一下什么是hybrid开发,在这里我们先引用一下百度百科中对hybrid开发的定义:

Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。 
从定义中我们可以看到hybrid开发其实就是在App开发过程中既使用到了web开发技术也使用到了native开发技术,通过这两种技术混合实现的App就是我们通常说的hybrid app,而通过这两种技术混合开发就是hybrid开发。

好吧,我们已经知道hybrid开发的具体含义,那么一个问题就产生了,既然我们已经有了native开发了为何还需要hybrid开发呢?它有什么好处么?答案是肯定的,下面我们就来看一下为何需要hybrid开发方式。

二:为何需要hybrid开发

下面我们简单看一下Native开发中存在的弊端以及使用hybrid开发方式的好处,通过对比你就能知道了hybrid开发的优势,当然了,这里不是推崇使用hybrid开发方式,native也有native开发的优势,hybrid开发也有hybrid开发的劣势,这里只是简单的看一下hybrid相对于native开发的优势。

  • 使用Native开发的方式人员要求高,只是一个简单的功能就需要iOS程序员和Android程序员各自完成;
  • 使用Native开发的方式版本迭代周期慢,每次完成版本升级之后都需要上传到App Store并审核,升级,重新安装等,升级成本高;
  • 使用hybrid开发的方式简单方便,同一套代码既可以在IOS平台使用,也可以在Android平台使用,提高了开发效率与代码的可维护性;
  • 使用hybrid开发的方式升级简单方便,只需要服务器端升级一下就好了,对用户而言完全是透明了,免去了Native升级中的种种不便;

通过对比可以发现hybrid开发方式现对于native实现主要的优势就是更新版本快,代码维护方便,当然了这两个优点也是我们推崇使用hybrid开发app的主要因素。知道了hybrid开发的好处之后,我们如何在Android中实现hybrid开发呢?下面我们就将介绍这个问题。

三:Android中如何实现Bybird开发

其实在Android开发中使用hybrid模式开发app,也是有两种方案的:

  • 使用第三方hybrid框架
  • 自己使用webview加载

通过这两种方案实现hybrid开发各有利弊,具体如下:

使用PhoneGap、AppCan之类的第三方框架,其实现的原理是以WebView作为用户界面层,以JavaScript作为基本逻辑,以及和中间件通讯,再由中间件访问底层API的方式,进行应用开发。相当于为我们封装了webview与相应的native组件;

使用webview控件加载H5网页的内容,其中客户端的webview只是作为一个加载H5页面的壳子,具体的实现效果是由H5实现的,这个需要Native程序员和H5程序员一起合作完成;

使用第三方框架的方式的好处是许多功能已经被集成好了,只需要简单的调用即可,但是这种方式集成度高,不容易定制化处理,而且性能上也是一个打的问题;

使用webview加载H5页面,定制化程度高,问题可控,但是相对与第三方框架集成度不够高,但是其已经可以满足我们日常的开发功能需要了,目前还是比较推荐使用这种方式实现hybrid开发;

下面我们就看一下如何在Android系统中通过webview实现对H5页面的加载操作。

四:hybrid开发简单实现

  • 在AndroidManifest.xml中定义网络请求权限
<uses-permission Android:name="Android.permission.INTERNET"/>
  • 1

注意这个权限是必须的,因为加载webview页面一般而言经常是网络上的H5页面,这时候的网络请求权限就是必须的了,好多时候测试webview加载网络H5页面失败,找了半天不知道是什么原因,最后才发现是网络权限没有添加…

  • 在Layout布局文件中定义Webview控件
<WebView     Android:layout_width="match_parent"    Android:layout_height="match_parent"    Android:id="@+id/webView"    />
  • 1
  • 2
  • 3
  • 4
  • 5

这里的WebView控件就是Android原生的webview控件了,其和普通的Android控件的使用没有什么不同都是在布局文件中定义,然后在Activity代码中获取并执行初始化操作等等。

  • 在代码中获取Webview控件加载本地或者网络H5资源

加载本地H5页面

/** * 加载本地H5资源文件 */webView = (WebView) findViewById(R.id.webView);webView.loadUrl("file:///Android_asset/example.html");
  • 1
  • 2
  • 3
  • 4
  • 5

加载网络H5页面

/** * 加载网络H5资源 */webView = (WebView) findViewById(R.id.webView);webView.loadUrl("http://baidu.com");
  • 1
  • 2
  • 3
  • 4
  • 5

可以发现在获取到webview组件之后直接执行一个loadUrl方法传入一个url地址就可以了,这样在activity页面中就可以展示出webview页面了,契合普通的网页效果没什么不同,这里需要说明的是,webview不但能够加载网页地址,同样的也可以加载html代码,本地html资源等等,相对来说功能还是很强大的。

当然了以上只是最最简单的webview使用的例子,下面我们可以为我们的webview对象设置各种参数:

  • 为Webview控件设置参数
 WebSettings webSettings = h5Fragment.mWebView.getSettings();        webSettings.setJavaScriptEnabled(true);        webSettings.setLoadWithOverviewMode(true);        webSettings.setAllowFileAccess(false);        webSettings.setUseWideViewPort(false);        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);        webSettings.setDatabaseEnabled(false);        webSettings.setAppCacheEnabled(false);        webSettings.setBlockNetworkImage(true);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里的WebSettings就是webview的设置参数对象,我们是通过它为webview设置各种参数值的,见名知意,看见名字我们就知道各个set方法的意思了。比如设置webview中的html页面js代码是否可用,是否可以访问系统文件,H5缓存是否可用,是否立即加载网页图片等等。

  • 为Webview控件设置WebChromeClient

WebChromeClient对象是webview的关于页面效果回调方法的实现对象,主要用于实现webview页面上一些效果的回调,我们可以看一下其中实现的一些回调方法:

/** * 自定义实现WebChromeClient对象 */public class MWebChromeClient extends WebChromeClient{    /**     * 当webview加载进度变化时回调该方法     */    @Override    public void onProgressChanged(WebView view, int newProgress) {        super.onProgressChanged(view, newProgress);    }    /**     * 当加载到H5页面title的时候回调该方法     */    @Override    public void onReceivedTitle(WebView view, String title) {        super.onReceivedTitle(view, title);    }    /**     * 当接收到icon的时候回调该方法     */    @Override    public void onReceivedIcon(WebView view, Bitmap icon) {        super.onReceivedIcon(view, icon);    }    /**     * 当H5页面调用js的Alert方法的时候回调该方法     */    @Override    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {        return super.onJsAlert(view, url, message, result);    }    /**     * 当H5页面调用js的Confirm方法的时候回调该方法     */    @Override    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {        return super.onJsConfirm(view, url, message, result);    }    /**     * 当H5页面调用js的Prompt方法的时候回调该方法     */    @Override    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {        return super.onJsPrompt(view, url, message, defaultValue, result);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

上面的WebChromeClient中我们重写了其中的几个字方法,我们已经在方法中添加了注释标明了各个方法的调用时机,而且通过方法名我们也不难发现各个方法的具体作用,这里就不在具体的介绍了。

  • 为Webview主要设置WebviewClient
/** * 自定义实现WebViewClient类 */public class MWebViewClient extends WebViewClient {    /**     * 在webview加载URL的时候可以截获这个动作, 这里主要说它的返回值的问题:     *  1、返回: return true;  webview处理url是根据程序来执行的。      *  2、返回: return false; webview处理url是在webview内部执行。      */    @Override    public boolean shouldOverrideUrlLoading(WebView view, String url) {    }    /**     * 在webview开始加载页面的时候回调该方法     */    @Override    public void onPageStarted(WebView view, String url, Bitmap favicon) {        super.onPageStarted(view, url, favicon);    }    /**     * 在webview加载页面结束的时候回调该方法     */    @Override    public void onPageFinished(WebView view, String url) {        super.onPageFinished(view, url);    }    /**     * 加载页面失败的时候回调该方法     */    // 该方法为Android23中新添加的API,Android23中会执行该方法    @TargetApi(21)    @Override    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {    }    /**     * 加载页面失败的时候回调该方法     */    /**     * 在Android23中改方法被onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) 替代     * 因此在Android23中执行替代方法     * 在Android23之前执行该方法     * @param view     * @param errorCode     * @param description     * @param failingUrl     */    @Override    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

这里我们只是暂时看一下WebViewClient中的几个比较重要的方法,shouldOverrideUrlLoading方法,onPageStarted方法,onPageFinished方法,onReceivedError方法等,相关的方法说明已经有注释了,这里就不在做过多的说明了。好了介绍完了相关的API之后我们来看一下我们在产品中关于hybrid开发的实践。

友友用车中关于hybrid开发的实践

hybrid这么高逼格的东西友友用车怎么能不涉及呢?在我们的产品开发中也使用到了Webview,并封装了自己的Webview库,下面我们就看一下友友用车中关于hybrid开发的实践。

  • (1)定义H5Activity类,用于展示H5页面
/** * 自定义实现的H5Activity类,主要用于在页面中展示H5页面,整个Activity只有一个Fragment控件 */public class H5Activity extends BaseActivity {    public H5Fragment h5Fragment = null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_h5);        h5Fragment = new H5Fragment();        getSupportFragmentManager().beginTransaction().replace(R.id.mfl_content_container, h5Fragment).commit();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • (2)在H5Fragment中具体实现对H5页面的加载操作
/** * 具体实现H5页面加载Fragment,只有一个Webview控件 */public class H5Fragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {    @BindView(R.id.sswipeRefreshLayout)    public SwipeRefreshLayout swipeRefreshLayout;    /**     * H5页面 WebView     */    @BindView(R.id.mwebview)    public WebView mWebView = null;    @BindView(R.id.rl)    public RelativeLayout rl;    /**     * 页面title     */    public String title = "";    /**     * 页面当前URL     */    public String currentUrl = "";    /**     * 判断网页是否加载成功     */    public boolean isSuccess = true;    /**     * 判断前一页H5是否需要刷新     */    public boolean isNeedFlushPreH5 = false;    private BasePayFragmentUtils payFragmentUtils;    public static final String KEY_DIALOG_WEB_VIEW = "dialog_webView";    /**     * 是否是弹窗中的WebView     */    private boolean isDialogWebView = false;    View.OnClickListener errorOnClickListener = new View.OnClickListener() {        @Override        public void onClick(View view) {            mProgressLayout.showLoading();            isSuccess = true;            reflush();        }    };    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        payFragmentUtils = new BasePayFragmentUtils(this, BasePayFragmentUtils.ORDER_TYPE_H5);        Bundle bundle = getArguments();        if (bundle != null && bundle.containsKey(KEY_DIALOG_WEB_VIEW)) {            isDialogWebView = bundle.getBoolean(KEY_DIALOG_WEB_VIEW, false);        }    }    @Override    public View setView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View rootView = inflater.inflate(R.layout.fragment_h5, null);        ButterKnife.bind(this, rootView);        if (getActivity() instanceof H5Activity) {            H5Activity h5Activity = (H5Activity) getActivity();            mProgressLayout = h5Activity.mProgressLayout;        }        initView();        initData();        return rootView;    }    @Override    public void onResume() {        super.onResume();        payFragmentUtils.onPayResume();        if (H5Constant.isNeedFlush == true || isNeedFlushPreH5 == true) {            H5Constant.isNeedFlush = false;            isNeedFlushPreH5 = false;            // 加载数据            initData();        }    }    /**     * 执行组件初始化的操作     */    private void initView() {        // 判断下拉刷新组件是否可用        isSwipeEnable();        // 初始化WebView组件        H5FragmentUtils.initH5View(this);        // 设置WebView的Client        mWebView.setWebViewClient(new MWebViewClient(this));        // 设置可现实js的alert弹窗        mWebView.setWebChromeClient(new WebChromeClient());        if (isDialogWebView) {            mProgressLayout.setCornerResId(R.drawable.map_confirm_bg);        }    }    /**     * 执行初始化加载数据的操作     */    private void initData() {        mProgressLayout.showLoading();        // 设置title        H5FragmentUtils.setTitle(this, title);        // 获取请求URL        currentUrl = H5FragmentUtils.getUrl(this, currentUrl);        // 刷新页面        reflush();    }    /**     * 判断下拉刷新组件是否可用     */    private void isSwipeEnable() {        if (getActivity() == null) {            return;        }        if (isDialogWebView) {            getActivity().getIntent().putExtra(H5Constant.CARFLUSH, false);        }        //判断滑动组件是否可用        if (getActivity().getIntent().getBooleanExtra(H5Constant.CARFLUSH, true)) {            swipeRefreshLayout.setEnabled(true);            swipeRefreshLayout.setColorSchemeResources(R.color.c1, R.color.c1, R.color.c1);            swipeRefreshLayout.setOnRefreshListener(this);            mWebView.setOnTouchListener(new View.OnTouchListener() {                @Override                public boolean onTouch(View v, MotionEvent event) {                    if (event.getAction() == MotionEvent.ACTION_DOWN) {                        int downY = (int) event.getY();                        if (downY <= DisplayUtil.screenhightPx / 3) {                            swipeRefreshLayout.setEnabled(true);                        } else {                            swipeRefreshLayout.setEnabled(false);                        }                    }                    return false;                }            });        } else {            swipeRefreshLayout.setEnabled(false);        }    }    /**     * 执行Webview的下拉刷新操作     */    @Override    public void onRefresh() {        //判断是否执行刷新动作        reflush();    }    /**     * 刷新当前页面     */    private void reflush() {        if (Config.isNetworkConnected(mContext)) {            if (!TextUtils.isEmpty(currentUrl)) {                H5Cookie.synCookies(mContext, currentUrl, H5Cookie.getToken());                mWebView.loadUrl(currentUrl);            } else {                swipeRefreshLayout.setRefreshing(false);                mProgressLayout.showError(errorOnClickListener);            }        } else {            swipeRefreshLayout.setRefreshing(false);            mProgressLayout.showError(errorOnClickListener);        }    }    @Override    public void onDestroyView() {        super.onDestroyView();        /*swipeRefreshLayout.removeView(mWebView);        mWebView.removeAllViews();        mWebView.destroy();*/    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • (3)初始化WebView组件
/**     * 初始化组件WebView     *     * @param h5Fragment     */    public static void initH5View(H5Fragment h5Fragment) {        if (h5Fragment == null || h5Fragment.getActivity() == null) {            return;        }        if (h5Fragment.getActivity().getIntent().getBooleanExtra(H5Constant.SOFT_INPUT_IS_CHANGE_LAYOUT, false)) {            h5Fragment.getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);        }        // 设置H5页面默认能够长按复制        /*if (!h5Fragment.getActivity().getIntent().getBooleanExtra(H5Constant.OPENLONGCLICK, false)) {            h5Fragment.mWebView.setOnLongClickListener(new View.OnLongClickListener() {                @Override                public boolean onLongClick(View v) {                    return true;                }            });        }*/        WebSettings webSettings = h5Fragment.mWebView.getSettings();        webSettings.setJavaScriptEnabled(true);        webSettings.setLoadWithOverviewMode(true);        webSettings.setAllowFileAccess(false);        webSettings.setUseWideViewPort(false);        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);        webSettings.setDatabaseEnabled(false);        webSettings.setAppCacheEnabled(false);        webSettings.setBlockNetworkImage(true);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • (4)自定义实现WebviewClient对象
/** * 自定义实现WebviewClient类 */public class MWebViewClient extends WebViewClient {    public H5Fragment h5Fragment = null;    public Activity h5Activity = null;    public MWebViewClient(H5Fragment h5Fragment) {        this.h5Fragment = h5Fragment;        if (h5Fragment.getActivity() == null) {            h5Activity = Config.currentContext;        } else {            h5Activity = h5Fragment.getActivity();        }    }    /**     * 拦截H5页面的a标签跳转,解析scheme协议     * 相当于放弃了a标签的使用,转而使用自定义的scheme协议     */    @Override    public boolean shouldOverrideUrlLoading(WebView view, String url) {        //解析scheme        if (url.indexOf(H5Constant.SCHEME) != -1) {            try {                Uri uri = Uri.parse(url);                String[] urlSplit = url.split("\\?");                Map<String, String> queryMap = new HashMap<String, String>();                String h5Url = null;                if (urlSplit.length == 2) {                    queryMap = H5Constant.parseUriQuery(urlSplit[1]);                    h5Url = queryMap.get(H5Constant.MURL);                }                // 解析SCHEME跳转                {                    // 若设置刷新,则刷新页面                    if (queryMap.containsKey(H5Constant.RELOADPRE) && "1".equals(queryMap.get(H5Constant.RELOADPRE))) {                        h5Fragment.isNeedFlushPreH5 = true;                    }                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);                    h5Activity.startActivityForResult(intent, H5Constant.h5RequestCode);                }            } catch (Exception e) {                e.printStackTrace();            }            return true;        }        // 打电话        else if (url.indexOf("tel://") != -1) {            final String number = url.substring("tel://".length());            Config.callPhoneByNumber(h5Activity, number);            return true;        } else if (url.indexOf("tel:") != -1) {            final String number = url.substring("tel:".length());            Config.callPhoneByNumber(h5Activity, number);            return true;        }        // 其他跳转方式        else {            view.loadUrl(url);            //如果不需要其他对点击链接事件的处理返回true,否则返回false            return false;        }    }    /**     * H5页面刚刚开始被webview加载时回调该方法     */    @Override    public void onPageStarted(WebView view, String url, Bitmap favicon) {        super.onPageStarted(view, url, favicon);    }    /**     * H5页面结束被加载时回调该方法     */    @Override    public void onPageFinished(WebView view, String url) {        super.onPageFinished(view, url);        h5Fragment.swipeRefreshLayout.setRefreshing(false);        if (h5Activity.getTitle().toString().equals("找不到网页")) {            h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener);            return;        }        if (h5Fragment.isSuccess)            h5Fragment.mProgressLayout.showContent();        else            h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener);        h5Fragment.onLoadFinish(h5Fragment.isSuccess);        if (h5Fragment.isSuccess) {            h5Fragment.mWebView.getSettings().setBlockNetworkImage(false);        }    }    // 该方法为Android23中新添加的API,Android23中会执行该方法    @TargetApi(21)    @Override    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {        if (Build.VERSION.SDK_INT >= 21) {            if (request.isForMainFrame()) {                h5Fragment.isSuccess = false;                h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener);            }        }    }    /**     * 在Android23中改方法被onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) 替代     * 因此在Android23中执行替代方法     * 在Android23之前执行该方法     * @param view     * @param errorCode     * @param description     * @param failingUrl     */    @Override    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {        if (Build.VERSION.SDK_INT < 23) {            h5Fragment.isSuccess = false;            h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • (5)打开H5页面
Intent intent = new Intent(context, H5Activity.class);intent.putExtra(H5Constant.MURL, currentUrl);intent.putExtra(H5Constant.TITLE, title);context.startActivity(intent);
  • 1
  • 2
  • 3
  • 4

可以发现在产品实际开发过程中使用webview的页面都是整个的Activity页面,也就是说整个Activity页面只有一个webview控件,所以这时候页面的内容都是通过H5实现的。 
然后当我们需要打开H5页面的时候可以通过服务器下发H5页面的url和title,并作为参数传递给H5Activity,然后打开该url所表示的网页。

同时我们使用Fragment用于实现加载H5页面的所以,所以以后当我们需要在其他地方使用加载H5页面的时候可以很方便的一直。

在MWebviewClient的shouldOverrideUrlLoading方法中我们拦截了所有的a标签跳转,转而实现我们自身的scheme协议,即a标签的跳转链接不再是常规的http链接,而是我们自定义的scheme协议,具体可参考:Android产品研发(十一)–>应用内跳转scheme协议

总结:

本文中我们介绍了hybrid开发的概念,hybrid开发的作用,Android中如何实现hybrid开发,Android实现hybrid的例子,产品中对hybrid开发的实践

在定义webview的时候可以设置WebviewSettings,设置WebviewClient,设置WebChromeClient等参数对象可以在WebviewClient的shouldOverrideUrlLoading方法中拦截a标签的跳转并执行相应的逻辑