开发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 过程详解

目录
相关文章
|
2月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
742 1
|
2天前
|
供应链 搜索推荐 API
1688APP原数据API接口的开发、应用与收益(一篇文章全明白)
1688作为全球知名的B2B电商平台,通过开放的原数据API接口,为开发者提供了丰富的数据资源,涵盖商品信息、交易数据、店铺信息、物流信息和用户信息等。本文将深入探讨1688 APP原数据API接口的开发、应用及其带来的商业收益,包括提升流量、优化库存管理、增强用户体验等方面。
24 6
|
3天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
22 3
|
9天前
|
移动开发 小程序 PHP
校园圈子论坛系统采取的PHP语音和uni账号开发的小程序APP公众号H5是否只需要4800元?是的,就是只需要4800元
关于校园圈子论坛系统采用PHP语言和uni-app开发的小程序、APP、公众号和H5是否仅需4800元这个问题,实际上很难给出一个确定的答案。这个价格可能受到多种因素的影响
40 8
|
5天前
|
人工智能 小程序 数据处理
uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!
近期,多家康复机构咨询AI运动识别插件是否适用于肢力运动受限患者的康复锻炼。本文介绍该插件在康复锻炼中的应用场景,包括康复运动指导、运动记录、恢复程度记录及过程监测。插件集成了人体检测、姿态识别等功能,支持微信小程序平台,使用便捷,安全可靠,帮助康复治疗更加高效精准。
|
26天前
|
人工智能 小程序 搜索推荐
uni app下开发AI运动小程序解决方案
本文介绍了在小程序中实现AI运动识别的解决方案。该方案依托于UNI平台,通过高效便捷的插件形式,实现包括相机抽帧控制、人体识别、姿态识别等在内的多项功能,无需依赖后台服务器,大幅提高识别效率和用户体验。方案内置多种运动模式,支持自定义扩展,适用于AI健身、云上赛事、AI体测等多场景,适合新开发和存量改造项目。
|
1月前
|
设计模式 Swift iOS开发
探索iOS开发:从基础到高级,打造你的第一款App
【10月更文挑战第40天】在这个数字时代,掌握移动应用开发已成为许多技术爱好者的梦想。本文将带你走进iOS开发的世界,从最基础的概念出发,逐步深入到高级功能实现,最终指导你完成自己的第一款App。无论你是编程新手还是有志于扩展技能的开发者,这篇文章都将为你提供一条清晰的学习路径。让我们一起开始这段旅程吧!
|
1月前
|
小程序 数据挖掘 UED
开发1个上门家政小程序APP系统,都有哪些功能?
在快节奏的现代生活中,家政服务已成为许多家庭的必需品。针对传统家政服务存在的问题,如服务质量不稳定、价格不透明等,我们历时两年开发了一套全新的上门家政系统。该系统通过完善信用体系、提供奖励机制、优化复购体验、多渠道推广和多样化盈利模式,解决了私单、复购、推广和盈利四大痛点,全面提升了服务质量和用户体验,旨在成为家政行业的领导者。
|
1月前
|
机器人
布谷直播App系统源码开发之后台管理功能详解
直播系统开发搭建管理后台功能详解!
|
2月前
|
NoSQL PHP Redis
布谷语音app源码服务器环境配置及技术开发语言
布谷语音app源码服务器环境配置及技术语言研发。。
下一篇
DataWorks