快應誕生背景
微信的小程序使得很多原來需要調動APP的場景不復存在,正式由於微信小程序的沖擊,3月20日,華為聯手九大手機廠商,共同舉辦了“快應用”標准啟動發布會。“快應用”是幾家手機廠商基於硬件平台共同推出的新型應用生態,用戶不必下載安裝,即點即用,能夠享受到原生應用的性能體驗。“快應用”使用前端技術棧開發與原生渲染,兼具H5頁面和原生應用的雙重優點。
快應用使用場景
進入小米應用商店,搜索“餓了么”:
點擊“秒開”就可以使用快應用了
在華為應用市場搜索“快應用”:
點擊查看更多:
可以看到一些快應用app已經上線了,使用起來體驗也不錯,體積也相當小
微信小程序VS快應用
微信小程序推出后,盡管前期受到了不少質疑,但卻一直發展非常穩健。騰訊3月21日剛剛公布的2017年全年財報中披露,自2017年1月推出小程序以來,截至2018年1月已推出58萬個小程序,日活躍賬戶超過1.7億個。張小龍也曾經表示,未來兩年內,小程序將取代80%的應用市場。如果該目標達成,這意味着微信小程序建立起一個強大的超級生態,極大擠壓了國產手機廠商應用分發和數字廣告業務的成長空間。因此,國產手機廠商的“快應用”主要針對微信小程序,與后者爭搶用戶和流量。
那么快應用要和微信小程序去競爭,它們各自又有什么優缺點,誰又會更占優勢呢?我個人總結之后列出了如下幾點:
微信小程序
優點:
- 微信小程序已經在市場上取得了一定的規模效益,搶占了市場先機
- 微信自帶用戶流量,用戶黏度較高,通過微信打開小程序非常方便
- 微信小程序支持iOS和安卓兩大操作系統,覆蓋了所有的用戶群體,不同操作系統用戶之間數據共享方便,比如iOS用戶可以通過一個微信鏈接響安卓用戶發送一個微信小程序鏈接
缺點:
- 微信小程序基於系統上層進行的封裝,在性能上和原生app差距較大
快應用
優點:
- 基於系統層開發,將腳本轉化為原生組件,運行效率更接近原聲app,用戶體驗會更加好
缺點:
- 只支持安卓系統,在用戶群體上受到限制,也無法在安卓和iOS系統之間做到數據共享
- 幾大廠商合作,可能會產生分歧,如何去協調各大廠商,以及后期的利益分配等是一個大問題
- 缺乏用戶粘性和使用場景,如果每次打開其他應用都要去應用商店搜索快應用app的話顯然不如微信小程序方便
總結:
從快應用的誕生,我們能夠看到國內手機硬件廠商開始反思自己在安卓生態中的地位,尋求轉型和突破以爭取更大的話語權和利益。這個方向顯然是對的,但是對比微信小程序,較好的性能幾乎成了快應用的唯一優勢,但是隨着現在手機性能不斷增強以及微信小程序的不斷優化,這個問題將變得忽略不計,況且現在很多微信小程序用戶流暢度意境做的相當不錯了,而在用戶群體、使用方便性等幾大方面,微信小程序占據着絕對優勢,而且快應用由於自生的缺陷無法彌補這幾方面的劣勢,所以說快應用幾乎無法撼動微信小程序的地位,更不用說打敗小程序,不過如果快應用能夠在應用分發市場上對微信形成一定威脅並且從中分得一杯羹的話,那也能夠證明快應用取得了成功。
環境配置
6.0版本以上NodeJS,官方推薦 v6.11.3 LTS
安裝hap-toolkit:
npm install -g hap-toolkit(幫助開發者通過命令行工具來完成工程的創建等工作),在命令行中執行hap -V
會輸出版本信息表示hap-toolkit
安裝成功,如下命令所示:
hap -V
創建項目
建好環境后,開發者就可以利用全局hap
命令創建一個項目模板,如下所示,其中<ProjectName>
為自定義的項目名稱
hap init <ProjectName>
命令執行后,會在當前目錄下創建<ProjectName>
文件夾,並作為項目根目錄
這個項目已經包含了項目配置與簡單頁面的初始代碼,項目根目錄結構如下:
├── node_modules
├── sign rpk包簽名模塊
│ └── debug 調試環境
│ ├── certificate.pem 證書文件
│ └── private.pem 私鑰文件
├── src
│ ├── Common 公用的資源文件和組件文件
│ │ └── logo.png manifest.json中配置的icon
│ ├── Demo 頁面目錄
│ | └── index.ux 頁面文件,文件名不必與父文件夾相同
│ ├── app.ux APP文件(用於包括公用資源)
│ └── manifest.json 項目配置文件(如:應用描述、接口申明、頁面路由等)
└── package.json 定義項目需要的各種模塊及配置信息,npm install根據這個配置文件,自動下載所需的運行和開發環境
目錄的簡要說明如下:
- src:項目源文件夾
- node_modules:項目的依賴類庫
- sign:簽名模塊,當前僅有
debug
簽名,如果內測上線,請添加release
文件夾,增加線上簽名;簽名生成方法請參考文檔:編譯工具
的openssl
編譯項目
安裝npm依賴
在項目根目錄下,運行如下命令安裝依賴包(webpack,babel等)
npm install
編譯項目
在項目的根目錄下,運行如下命令進行編譯打包,生成rpk包
npm run build
編譯打包成功后,項目根目錄下會生成文件夾:build、dist
- build:臨時產出,包含編譯后的頁面js,圖片等
- dist:最終產出,包含rpk文件。其實是將build目錄下的資源打包壓縮為一個文件,后綴名為
rpk
,這個rpk
文件就是項目編譯后的最終產出
自動重新編譯
如果希望每次修改源代碼文件后,都自動重新編譯項目,請使用如下命令:
npm run watch
手機安裝調試器
調試器APK是一個Android應用程序,請從站點地址下載
說明如下:
- 掃碼安裝:配置HTTP服務器地址,下載rpk包,並喚起平台運行rpk包
- 本地安裝:選擇手機文件系統中的rpk包,並喚起平台運行rpk包
- 在線更新:重新發送HTTP請求,更新rpk包,並喚起平台運行rpk包
- 開始調試:喚起平台運行rpk包,並啟動遠程調試工具
注意:若無法正常使用調試器,請升級手機系統到最新版本或安裝平台預覽版
手機安裝平台預覽版
較新的系統版本中內置平台正式版,即真實的運行環境。然而,更新平台正式版的時間周期較長,開發調試平台新功能可使用平台預覽版
平台預覽版存在以下優缺點:
- 優點:迭代速度快,可立即體驗平台新功能
- 缺點:實現與真實的運行環境存在差異,對廠商服務和第三方服務的支持存在缺陷
平台預覽版APK是一個Android應用程序,請從站點地址下載
下載安裝成功后,在調試器中點擊切換運行平台至...mockup
即可在平台預覽版上運行rpk包
在平台上運行rpk包
在調試器中喚起平台打開rpk包有多種途徑,以下兩者選其一即可,推薦第一種途徑:
- HTTP請求:開發者啟動HTTP服務器,打開調試器,點擊
掃碼安裝
配置HTTP服務器地址,下載rpk包,並喚起平台運行rpk包 - 本地安裝:開發者將rpk包拷貝到手機文件系統,打開調試器,點擊
本地安裝
選擇rpk包,並喚起平台運行rpk包
1. HTTP請求
啟動HTTP服務器
在終端中新建一個窗口,進入項目的根目錄運行如下命令,啟動本地服務器(默認端口為12306)
npm run server
自定義端口(如:8080)
npm run server -- --port 8080
在手機上預覽運行效果
配置HTTP服務器地址有兩種方式,以下兩者選其一即可:
打開調試器 --> 點擊"掃碼安裝"
,掃描終端窗口中的二維碼即可完成配置(若掃描不成功,可在瀏覽器中打開頁面:http://localhost:<your port>
,掃描頁面中的二維碼)打開調試器 --> 點擊右上角menu --> 設置
,輸入終端窗口中提示的HTTP服務器地址
配置完成后,若沒有自動喚起平台運行rpk包,點擊在線更新
喚起平台運行rpk包
若提示安裝失敗
,請檢查執行npm run server的終端窗口是否正常運行
2. 本地安裝
復制rpk包到手機中
將<ProjectName>/dist
目錄下編譯產出的rpk
包通過USB數據線或其他方式,復制到手機文件系統中
本地安裝rpk包
打開調試器 --> 點擊"本地安裝"
,選擇手機文件系統中的rpk包,並自動喚起平台運行rpk包,查看效果
配置應用基本信息
每個應用都要有專屬的名稱,圖標等,這些信息都需要在manifest.json
文件中配置;詳細信息請參考文檔:manifest文件
應用包名(package)
應用包名,是區別於其他應用的唯一標識
推薦采用com.company.module的格式,示例如下:
{
"package": "com.example.demo" }
應用名稱(name)
應用名稱,6個漢字以內,與應用商店保存的名稱一致;框架提供保存到桌面的功能,桌面上顯示的應用名即為此屬性
示例如下:
{
"name": "發票小助手" }
應用圖標(icon)
規則為正方形(不能是圓角),且務必無白邊
{
"icon": "/Common/logo.png" }
注意:
請使用絕對路徑,其中/
對應於路徑<ProjectName>/src/
應用版本名稱、版本號(versionName、versionCode)
應用版本名稱、版本號為開發者的應用包維護的版本信息
應用版本名稱為主版本.次版本
格式
應用版本號為整數,從1
開始,每次更新上架請自增1
示例如下:
{
"versionName": "1.0", "versionCode": 1 }
支持的最小平台版本號(minPlatformVersion)
支持的最小平台版本號為必填項,默認值為1000,標識開發者的rpk包兼容支持的最小運行平台版本
當使用了1000以上的平台版本新增特性時,就必須確保minPlatformVersion
最低為該平台版本號,避免上線后在更低版本平台上運行出錯
示例如下:
{
"minPlatformVersion": "1000" }
配置接口列表(features)
在使用接口時,需要先在manifest中聲明接口。在每個接口文檔的頂部,都附有聲明接口的配置代碼
以fetch網絡請求為例,示例如下:
{
"features": [ { "name": "system.fetch" } ] }
配置頁面路由(router)
路由,用於定義頁面的實際地址、跳轉地址。如果ux頁面沒有配置路由,則不參與項目編譯。一個目錄下最多只能存在一個主頁面文件(不包括組件文件)
首頁名稱(router.entry)
首頁,即應用平台啟動時默認打開的頁面。首頁需配置為應用中某頁面的名稱,即在<ProjectName>/src
目錄下,頁面目錄的相對路徑
示例如下:
假設工程根目錄如下所示
└── src
└── Demo 頁面目錄,存放各自頁面私有的資源文件和組件文件
└── index.ux 頁面文件,文件名不必與父文件夾相同(推薦index.ux)
假設首頁為Demo目錄下的index.ux文件,則首頁對應的頁面名稱為Demo
{
"router": { "entry": "Demo" } }
頁面路由對象(router.pages)
頁面路由對象,key為頁面名稱(<ProjectName>/src
目錄下,頁面目錄的相對路徑),value為頁面具體路由配置,key不要重復
頁面具體路由配置(router.pages的value)包括以下屬性:
- component:頁面對應的ux文件名
- path:頁面路徑,不填則默認為頁面名稱(
<ProjectName>/src
目錄下,頁面目錄的相對路徑)
示例如下:
假設工程根目錄如下所示
└── src
|── Demo 頁面目錄,存放各自頁面私有的資源文件和組件文件
| └── index.ux 頁面文件,文件名不必與父文件夾相同(推薦index.ux)
└── Doc
└── Layout 頁面目錄,存放各自頁面私有的資源文件和組件文件
└── index.ux 頁面文件,文件名不必與父文件夾相同(推薦index.ux)
當頁面名稱(router.pages的key)為Demo
時,對應的頁面配置(router.pages的value)包括:
- component:頁面對應的ux文件名
index
- path:頁面路徑,默認為頁面名稱
Demo
{
"router": { "pages": { "Demo": { "component": "index" }, "Doc/Layout": { "component": "index" } } } }
現在,開發者就可以通過/Demo
訪問到Demo目錄下的index.ux頁面了
配置頁面UI顯示(display)
UI顯示,用於定義與UI顯示相關的配置。支持定義:頁面公用的默認UI顯示、頁面私有的UI顯示
頁面公用的默認UI顯示
頁面公用的默認UI顯示,即被所有頁面共享
以標題欄文字的配置為例:
{
"display": { "titleBarText": "頁面公用的默認標題" } }
未配置私有標題的頁面,標題欄文字均將顯示為頁面公用的默認標題
頁面私有的UI顯示
頁面私有的UI顯示,在display.pages
對象下配置:key為頁面名稱(與路由中的頁面名稱保持一致),value為頁面私有的UI顯示
以標題欄文字的配置為例:
{
"display": { "pages": { "Demo": { "titleBarText": "Demo頁面的標題" } } } }
manifest文件
manifest.json文件中包含了應用描述、接口聲明、頁面路由信息
manifest
屬性
|
類型
|
默認值
|
必填
|
描述
|
---|---|---|---|---|
package | String | - | 是 | 應用包名,確認與原生應用的包名不一致,推薦采用com.company.module的格式,如:com.example.demo |
name | String | - | 是 | 應用名稱,6個漢字以內,與應用商店保存的名稱一致,用於在桌面圖標、彈窗等處顯示應用名稱 |
icon | String | - | 是 | 應用圖標,提供192x192大小的即可 |
versionName | String | - | 否 | 應用版本名稱,如:"1.0" |
versionCode | Integer | - | 是 | 應用版本號,從1 自增,推薦每次重新上傳包時versionCode +1 |
minPlatformVersion | Integer | 1000 | 是 | 支持的最小平台版本號,原理同Android API Level,兼容性檢查,避免上線后在低版本平台運行並導致不兼容 |
features | Array | - | 否 | 接口列表,絕大部分接口都需要在這里聲明,否則不能調用,詳見每個接口的文檔說明 |
config | Object | - | 是 | 系統配置信息,詳見下面說明 |
router | Object | - | 是 | 路由信息,詳見下面說明 |
display | Object | - | 否 | UI顯示相關配置,詳見下面說明 |
config
用於定義系統配置和全局數據。
屬性
|
類型
|
默認值
|
描述
|
---|---|---|---|
logLevel | String | log | 打印日志等級,分為off,error,warn,info,log,debug |
designWidth | Integer | 750 | 頁面設計基准寬度,根據實際設備寬度來縮放元素大小 |
data | Object | - | 全局數據對象,屬性名不能以$或_開頭,在頁面中可通過this進行訪問;如果全局數據屬性與頁面中data屬性重名,則頁面初始化時,全局數據會覆蓋頁面中對應的屬性值 |
router
用於定義頁面的組成和相關配置信息,如果頁面沒有配置路由信息,則在編譯打包時跳過。
屬性
|
類型
|
默認值
|
描述
|
---|---|---|---|
entry | String | - | 首頁名稱 |
pages | Object | - | 頁面配置列表,key值為頁面名稱(對應頁面目錄名,例如Hello對應'Hello'目錄),value為頁面詳細配置page,詳見下面說明 |
router.page
用於定義單個頁面路由信息。
屬性
|
類型
|
默認值
|
必填
|
描述
|
---|---|---|---|---|
component | String | - | 是 | 頁面對應的組件名,與ux文件名保持一致,例如'hello' 對應 'hello.ux' |
path | String | /<頁面名稱> | 否 | 頁面路徑,例如“/user”,不填則默認為/<頁面名稱>。 path必須唯一,不能和其他page的path相同。 下面page的path因為缺失,會被設置為“/Index”: "Index": {"component": "index"} |
filter | Object | - | 否 | 聲明頁面可以處理某種請求 |
router.page.filter
聲明頁面可以處理某種請求,頁面可以從$page獲取打開頁面的參數,參見script腳本。filter的結構如下:
"filter": { "<action>": { "uri": "<pattern>" } }
屬性
|
類型
|
默認值
|
必填
|
描述
|
---|---|---|---|---|
action | String | - | 是 | 請求的動作,目前僅支持view這一種 |
uri | Pattern | - | 是 | 請求的數據的匹配規則。必須是正則表達式。如https?://.* 可以匹配所有http和https類型的網址 |
可以處理所有http和https請求的filter定義如下:
"filter": { "view": { "uri": "https?://.*" } }
display
用於定義與UI顯示相關的配置。
屬性
|
類型
|
默認值
|
描述
|
---|---|---|---|
backgroundColor | String | #ffffff | 窗口背景顏色 |
fullScreen | Boolean | false | 是否是全屏模式,默認不會同時作用於titleBar,titleBar需要繼續通過titleBar控制 |
titleBar | Boolean | true | 是否顯示titleBar |
titleBarBackgroundColor | String | - | 標題欄背景色 |
titleBarTextColor | String | - | 標題欄文字顏色 |
titleBarText | String | - | 標題欄文字(也可通過頁面跳轉傳遞參數(titleBarText)設置) |
menu | Boolean | false | 是否顯示標題欄右上角菜單按鈕 |
pages | Object | - | 各個頁面的顯示樣式,key為頁面名(與路由中的頁面名保持一致),value為窗口顯示樣式,頁面樣式覆蓋default樣式。 |
示例:
{
"package": "com.company.unit", "name": "appName", "icon": "/Common/icon.png", "versionName": "1.0", "versionCode": 1, "minPlatformVersion": 1000, "features": [ { "name": "system.network" } ], "permissions": [ { "origin": "*" } ], "config": { "logLevel": "off" }, "router": { "entry": "Hello", "pages": { "Hello": { "component": "hello", "path": "/", "filter": { "view": { "uri": "https?://.*" } } } } }, "display": { "backgroundColor": "#ffffff", "fullScreen": false, "titleBar": true, "titleBarBackgroundColor": "#000000", "titleBarTextColor": "#fffff", "pages": { "Hello": { "backgroundColor": "#eeeeee", "fullScreen": true, "titleBarBackgroundColor": "#0000ff", "titleBarText": "Hello" } } } }
源碼文件
APP,頁面和自定義組件均通過ux文件編寫,ux文件由template模板、style樣式和script腳本3個部分組成
app.ux
當前app.ux
編譯后會包含manifest配置信息
(可以在npm run build
之后查看文件內容),所以請不要刪除/**manifest**/
的注釋內容標識。
您可以在<script>
中引入一些公共的腳本,並暴露在當前app的對象上,如下所示,然后就可以在頁面ux文件的ViewModel中,通過this.$app.util
訪問
<script> import util from './util.js'
module.exports = { /**manifest**/, util: util }
</script>
頁面路由
導入模塊 import router from '@system.router' 或 var router = require("@system.router")
接口定義
router.push(OBJECT)
跳轉到應用內的某個頁面
參數:
參數
|
類型
|
必填
|
說明
|
---|---|---|---|
uri | String | 是 | 要跳轉到的uri,可以是下面的格式:
|
params | Object | 否 | 跳轉時需要傳遞的數據,參數可以在頁面中通過this.param1 的方式使用,param1為json中的參數名,param1對應的值會統一轉換為String類型 |
示例:
// launch phone app router.push({ uri: 'tel:10086' }); // open page by path router.push({ uri: '/about', params: {testId:'1'} }); // open page by name router.push({ uri: 'About', params: {testId:'1'} }); // open web page router.push({ uri: 'http://www.example.com' }); // install apk router.push({ uri: 'internal://cache/example.apk' });
router.replace(OBJECT)
跳轉到應用內的某個頁面,當前頁面無法返回
參數:
參數
|
類型
|
必填
|
說明
|
---|---|---|---|
uri | String | 是 | 要跳轉到的uri,可以是下面的格式:
|
params | Object | 否 | 跳轉時需要傳遞的數據,參數可以在頁面中通過this.param1 的方式使用,param1為json中的參數名,param1對應的值會統一轉換為String類型 |
示例:
router.replace({
uri: '/test' params: {testId:'1'} })
router.back()
返回上一頁面
參數:
無
示例:
// A頁面 router.push({ uri: 'B' }) // B頁面 router.push({ uri: 'C' }) // C頁面通過back,將返回B頁面 router.back(); // B頁面通過back,將返回A頁面 router.back();
router.clear()
清空所有歷史頁面記錄,僅保留當前頁面
參數:
無
示例:
router.clear()
router.getLength()
獲取當前頁面棧的頁面數量
返回值:
類型
|
說明
|
---|---|
Number | 頁面數量 |
示例:
var length= router.getLength() console.log("pages' length = "length);
router.getState()
獲取當前頁面狀態
返回參數:
參數名
|
類型
|
說明
|
---|---|---|
index | Number | 當前頁面在頁面棧中的位置 |
name | String | 當前頁面的名稱 |
path | String | 當前頁面的路徑 |
示例:
var page = router.getState() console.log("page index = "+page.index); console.log("page name = "+page.name); console.log("page path = "+page.path);
快應用技術架構
快應用通過腳本來編寫組件,安卓內部嵌入一個腳本解析引擎,將腳本轉化為原生控件,通過編譯生成rpk文件,應用調試器聯系起腳本及安卓系統進行調試工作,具體流程圖如下:
參考資料
- 開應用開發文檔:https://doc.quickapp.cn