开发Hybrid App的技术选型

简介: 开发Hybrid App的技术选型

一、前言

如果我们把Hybrid App理解为运行在android或者ios以及其他移动终端设备上的应用,也可以叫做H5 APP,这种开发应用的模式结合web开发技术与Native开发的部分技术,通常也被称为混合开发模式。

二、移动应用开发的三种方式

  • Native App:原生应用,在android端通常使用Java或Kotlin开发,ios端使用OC或者Swift开发
  • Hybrid App:混合应用,结合Web与Native技术开发
  • Web App:web应用,网页三剑客html+css+js

Native App开发依旧是移动应用的主导,但如今的Native App或多或少会嵌入一些web页面,诸如淘宝、京东等APP,所以如今真正意义上的原生应用又该如何去定义呢?Hybrid App受到越来越多开发者的追捧与其开发周期短,开发难度小,跨平台离不开,当然APP的效果也成为大家诟病的话题,如首屏打开缓慢,动画效果不够流畅等。

三种方式的技术比较(图片来自网络)

三、Hybrid App开发的核心

毫无疑问,webview是Hybrid App开发的核心。webview可以简单的理解为一个浏览器。webview 使用的是手机自带的浏览器内核,一般来说,手机厂家在内置浏览器的时候都会对其内核做一定的修改,所以在webview渲染的内容可能或有些差异,但是这基本上不影响APP的开发。绝大部分手机都使用的是WebKit作为webview的渲染引擎。关于WebKit以及其他的浏览器内核知识可以查看这里

关于webview的知识点非常多,如在android上常用的属性:WebSettings、WebViewClient,与JavaScript的交互,js注入漏洞,jsBridge等等,在此以Kotlin为例实现一个简单的例子,项目源码点击这里

MAinActivity.kt

package com.example.wxqdoit.kotlintest
import android.os.Build
import android.os.Bundle
import android.support.annotation.RequiresApi
import android.support.design.widget.BottomNavigationView
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bottom_navigation.setOnNavigationItemSelectedListener(mBottomNavigationView)
        val fragments: MutableList<Fragment> = ArrayList<Fragment>()
        fragments.add(Fragment1("https://www.baidu.com"))
        fragments.add(Fragment1("https://www.jianshu.com"))
        fragments.add(Fragment1("https://aidn.jp/mikutap/"))
        vp_main.adapter = ViewPagerAdapter(supportFragmentManager,fragments)
    }
    private val mBottomNavigationView = BottomNavigationView.OnNavigationItemSelectedListener { item ->
        when (item.itemId) {
            R.id.word -> {
                //vp_main.currentItem = 0
                Toast.makeText(this,"点击了",Toast.LENGTH_SHORT).show()
                return@OnNavigationItemSelectedListener true
            }
            R.id.pic -> {
                //vp_main.currentItem = 1
                return@OnNavigationItemSelectedListener true
            }
            R.id.me -> {
                //vp_main.currentItem = 2
                return@OnNavigationItemSelectedListener true
            }
        }
        false
    }
}

ViewPagerAdapter.kt

package com.example.wxqdoit.kotlintest
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
//继承 FragmentPagerAdapter 创建适配器
class ViewPagerAdapter(fm: FragmentManager?, var list: List<Fragment>) : FragmentPagerAdapter(fm) {
    override fun getItem(position: Int): Fragment {
        return list.get(position)
    }
    override fun getCount(): Int {
        return list.size
    }
}

Fragment1.kt

package com.example.wxqdoit.kotlintest
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.support.annotation.RequiresApi
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import kotlinx.android.synthetic.main.fragment1.view.*
@SuppressLint("ValidFragment")
class Fragment1(private val url: String):Fragment() {
    @SuppressLint("SetJavaScriptEnabled")
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment1, container, false)
        val webView = view.testWebView
        webView!!.webViewClient = webClient
        webView.settings.javaScriptEnabled = true
        webView.settings.allowContentAccess = true
        webView.settings.layoutAlgorithm
        webView.loadUrl(url)
        return view
    }
    private val webClient = object : WebViewClient() {
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun shouldOverrideUrlLoading(view: WebView, request:WebResourceRequest ): Boolean {
            view.loadUrl(request.url.toString())
            return true
        }
    }
}

效果如下

四、移动APP适配

做过原生开发的都知道美术需要出几套图以适配不同的分辨率,720×1280、750×1334、1080×1920、1242×2208,更大或者更小的屏在市面上也常见,android上字体大小通常以dp、sp作为单位。Hybrid App在只有一套美术UI的情况下应当如何处理以适配不同的机型呢?

媒体查询、百分比,或是直接使用web端常用的单位px、em、rem以及vh、vw,都是常用的适配方案。在设计稿给到固定宽度的情况下,相对而言,使用rem作为单位是比较合理的选择,至于移动设备适配,屏幕宽度,逻辑像素、物理像素、dpi等知识就不在此处赘述,分享优秀的博客:

移动设备适配基础知识速成:weibo.com/p/1001603933391216084991

五、flexible.js

flexible是淘宝使用的移动端适配方案,原理即根据机型分辨率进行适配,设置根font-size,使用相对单位rem。其核心功能如下:

  • 判断meta标签,动态改写标签
  • 给html标签添加data-dpr属性
  • 给html标签添加font-size属性

根font-size(即html标签上的)属性值即为一个单位的rem,在chrome浏览器上,默认的font-size值为16px:即16px=1rem

getComputedStyle(document.getElementsByTagName("html")[0])["font-size"];
//16px

flexible.js核心代码:

function refreshRem(){
        var width = docEl.getBoundingClientRect().width;
        if (width / dpr > 540) {
            width = 540 * dpr;
        }
        var rem = width / 10;
        docEl.style.fontSize = rem + 'px';
        docEl.setAttribute("font-size",rem + 'px');
        flexible.rem = win.rem = rem;
    }
    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 200);
    }, false);
    win.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 200);
        }
    }, false);

将设备宽度分为10份,而每一份视作一个单位,再将html的font-size设置为这个单位即:

font-size = 750/10 = 75px;

1rem = 75px;

如果在宽度为750px的设备上,完全符合上述换算。如果在iphone6/7/8上,宽度为375,那么:

font-size = 375/10 = 37.5px;

1rem = 37.5px;

现在以宽为750的设计稿为例,有一个款750px高75px的按钮 ;在iphone6/7/8上(实际宽375px)我们实际看到的宽高是多少呢?

width = 750/75 = 10rem ---> 10rem(在iphone6/7/8上) = 37.5px*10 = 375px;

height = 75/75 = 1rem --->1rem(在iphone6/7/8上) = 37.5px;

不难看出,只需要将这个单位作为除数即可计算出所需要的值;

六、打包工具

开发完成之后使用hbuilder或cordova打包成为android APK或者ios IPA。我更偏向于cordova,插件相对更多,社区更加活跃,稳定的更新维护。当然,孰优孰劣各自体会。下文会具体介绍cordova的相关知识。

Hbuild:http://www.dcloud.io/

cordova:https://cordova.apache.org/

七、UI框架

开发框架常用的有ionic,mui,jQuery Mobile,weui等等。就事实而论,当设计稿给到开发者时,或者这些都用不到,全套UI自己写也是常见的。

八、angular、react还是vue?

angular、react相对而言比较重,vue显得轻量一些,当开发大型SPA应用时,前两者是不错的选择,而vue完整的工具链以及活跃的社区也适应绝大部分的开发场景。

九、jQuery还用吗?

对于事件的封装,DOM操作的实现都是毫无疑问需要使用的,同时集成的ajax封装也必不可少,不过不论是Web APP的开发还是 Hybrid App的开发,jq都是不二之选。但当我们在对请求过滤的处理时,这些get,post方法基本上不能满足我们的需求,所以需要对请求进行二次封装。

/**
     * 通用请求数据接口
     * @param reqUrl
     * @param reqType
     * @param data
     * @param fn
     */
    commonRequest: function commonRequest(reqUrl, reqType, data, fn) {
        $.ajax({
            url: reqUrl,
            type: reqType,
            data: data,
            async: false,
            success: function success(data) {
                fn(data);
            },
            error: function error(data) {
                $.lightTip.error("网络请求错误-" + data.status, 2000);
            }
        });
    },
    /**
     * 二次封装get请求
     * @param reqUrl
     * @param data
     * @param fn
     */
    get: function get(reqUrl, data, fn) {
        $.ajax({
            url: reqUrl,
            type: 'GET',
            data: data,
            async: false,
            success: function success(data) {
                if (data.meta.code === 200) {
                    fn(data);
                } else {
                    alert(data.meta.message);
                    throw new Error(data.meta.message);
                }
            },
            error: function error(data) {
                $.lightTip.error("网络请求错误-" + data.status, 2000);
            }
        });
    },
    /**
     * 二次封装post请求
     * @param reqUrl
     * @param data
     * @param fn
     */
    post: function post(reqUrl, data, fn) {
        $.ajax({
            url: reqUrl,
            type: 'POST',
            data: data,
            async: false,
            success: function success(data) {
                if (data.meta.code === 200) {
                    fn(data);
                } else {
                    alert(data.meta.message);
                    throw new Error(data.meta.message);
                }
            },
            error: function error(data) {
                $.lightTip.error("网络请求错误-" + data.status, 2000);
            }
        });
    },
    /**
     * formData上传数据
     * @param reqUrl
     * @param reqType
     * @param data
     * @param fn
     */
    formDataReq: function formDataReq(reqUrl, reqType, data, fn) {
        $.ajax({
            url: reqUrl,
            data: data,
            type: reqType,
            processData: false,
            contentType: false,
            success: function success(data) {
                if (data.meta.code === 200) {
                    fn(data);
                } else {
                    alert(data.meta.message);
                    throw new Error(data.meta.message);
                }
            },
            error: function error(data) {
                $.lightTip.error("网络请求错误-" + data.status, 2000);
            }
        });
    },

这样封装一次,当用户没有登录时,可以根据返回的数据进行过滤处理。

当然如果你不用jq也可以选择其他的类库如封装ajax请求的axios

十、swiper是个好东西

swiper常用于移动端网站的内容触摸滑动,是纯javascript打造的滑动特效插件,面向手机、平板电脑等移动终端,Swiper能实现触屏焦点图、触屏Tab切换、触屏多图切换等常用效。这个插件功能确实强大,官网惊艳,api文档走心,性能不错。用作APP开发的主容器亦有一战之力。

十一、cordova开发详解


1、cordova开发环境搭建,以android为例

  1、安装jdk(建议jdk8+),配置环境变量;

  2、安装android sdk(至少到27,也可以安装android studio依赖安装),配置环境变量;

  3、安装node(npm,也可以选择使用yarn)(node建议8+),如果不是默认安装,请配置环境变量;

  4、安装bower,用以下载各种前端类库;

  5、使用npm install -g cordova全局安装cordova,如果安装过慢或失败请科学上网;


2、创建一个app并运行起来

   1、cordova create [文件夹名] [包名] [app名]

$ cordova create hello com.example.hello HelloWorld

   2、添加平台(以android为例)

$ cd hello
$ cordova platform add android

   3、build项目

cordova build android
......
.....
...
.
BUILD SUCCESSFUL in 7s
47 actionable tasks: 1 executed, 46 up-to-date
Built the following apk(s):
        D:\dev\****\****\****\platforms\android\app\build\outputs\apk\debug\app-debug.apk

在路径:\platforms\android\app\build\outputs\apk\debug\app-debug.apk下可以找到apk文件。


3、常用命令

Global Commands
    create ............................. 创建项目
    help ............................... 获取帮助
    telemetry .......................... 开启或关闭遥测采集
    config ............................. 全局配置
Project Commands
    info ............................... 项目基本信息
    requirements ....................... 检查并打印出指定平台的所有要求
    platform ........................... 管理项目平台
    plugin ............................. 管理插件
    compile ............................ 编译项目
    clean .............................. 清除项目

更多命令可以到官网查看。


4、项目结构

如果项目成功运行,您看到的项目结构应该如下:

www文件夹作为开发主文件夹;

res文件夹存放app的闪屏图片和icon;

plugins文件夹存放插件;

plaatforms文件夹存放诸如android、ios等各端的文件;

node-modules文件夹自然是依赖的各个模块

config.xml是项目的配置文件,你添加的插件将会在里面显示,如状态栏插件,你可以添加更多插件。

<plugin name="cordova-plugin-statusbar" spec="^2.4.2" />


5、常用插件收集:

phonegap-plugin-barcodescanner : 二维码扫描

cordova-plugin-statusbar:状态栏

cordova-plugin-inappbrowser: 内置浏览

cordova-plugin-camera:照相机

插件太多可查看这里

官方的插件搜索地址点击这里


6、插件使用,以imagePicker为例子

cordova plugin add cordova-plugin-imagepicker

使用:

document.addEventListener("deviceready",onDeviceReady);
        function onDeviceReady(){
            console.log("onDeviceReady");
            $(document).on("tap","#getPictures",()=>{
                window.imagePicker.getPictures(
                    function(results) {
                        for (let i = 0; i < results.length; i++) {
                            console.log('Image URI: ' + results[i]);
                            alert('Image URI: ' + results[i]);
                        }
                    }, function (error) {
                        console.log('Error: ' + error);
                        alert('Error: ' + error);
                    },{
                        maxImages: 9,
                        width: 500,
                        height: 500,
                        quality:60
                    }
                );
            })
        }

当然要实现QQ登录、微信登录等功能也是完全没有问题的,只需要添加对应的插件使用就可以了。


7、构建release版本以及签名

构建release版本

cordova build android --release

对 APK 签名

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore release-key.keystore platforms/android/build/outputs/apk/android-release-unsigned.apk [秘钥]apk签名的相关知识比较复杂,可以阅读这一篇:Cordova 打包 Android release app 过程详解

目录
相关文章
|
29天前
|
移动开发 小程序
如何让uni-app开发的H5页面顶部原生标题和小程序的顶部标题不一致?
如何让uni-app开发的H5页面顶部原生标题和小程序的顶部标题不一致?
|
2月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
98 3
|
2月前
|
Android开发 开发者 UED
个人开发 App 成功上架手机应用市场的关键步骤
个人开发 App 成功上架手机应用市场的关键步骤
|
2月前
|
开发工具 数据安全/隐私保护 Android开发
【教程】APP 开发后如何上架?
【教程】APP 开发后如何上架?
|
7天前
|
前端开发 Android开发 开发者
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发
【4月更文挑战第30天】本文探讨了使用Flutter开发混合应用的方法。混合应用结合Web技术和原生容器,提供快速开发和低成本维护。Flutter,一款现代前端框架,以其插件系统和高性能渲染引擎支持混合应用开发。通过创建Flutter项目、添加平台代码、使用WebView、处理平台间通信以及发布应用,开发者可构建跨平台混合应用。虽然混合应用有性能和用户体验的局限,但Flutter的跨平台兼容性和丰富的插件生态降低了开发成本。开发者应根据项目需求权衡选择。
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发
|
28天前
|
移动开发 前端开发 JavaScript
移动端 Hybrid 开发:RN、Flutter与Webview的抉择与融合
【4月更文挑战第6天】本文对比了移动端Hybrid开发的三种主流方案——React Native (RN),Flutter和Webview。RN基于JavaScript,适合React熟练的团队,适用于性能要求高、跨平台的中大型应用。Flutter,使用Dart语言,以其高性能和自定义UI适用于追求极致体验的项目。Webview适合快速移植Web应用至移动端,开发成本低但性能受限。选择时要考虑项目规模、性能需求、团队技术栈等因素,实际应用中常采用混合策略,如RN/Flutter+Webview、原生模块集成等,以实现最佳开发效果和长期技术规划。
68 0
|
29天前
|
移动开发 小程序 前端开发
使用uni-app开发(h5、小程序、app)步骤
使用uni-app开发(h5、小程序、app)步骤
|
2月前
|
Java Android开发 开发者
【Uniapp开发】APP的真机调试指南,从开发到上架全过程
【Uniapp开发】APP的真机调试指南,从开发到上架全过程
41 3
游戏直播APP平台开发多少钱成本:定制与成品源码差距这么大
开发一款游戏直播APP平台所需的费用是多少?对于计划投身这一领域的投资者来说,首要关心的问题之一就是。本文将探讨两种主要的开发模式——定制开发与成品源码二次开发的成本差异及其优劣势。
app开发的一些思路
<p><br></p> <p><br></p> <p></p> <h3 style="margin:0px; padding:0px; border:0px; vertical-align:baseline; clear:both; font-weight:normal; list-style:none; color:rgb(102,102,102); font-family:宋体;
2866 0