碼哥是國(guó)內(nèi)領(lǐng)先的移動(dòng)應(yīng)用快速開(kāi)發(fā)平臺(tái)。開(kāi)發(fā)者稍作學(xué)習(xí),就可以用xml + JavaScript定義原生的移動(dòng)應(yīng)用,同時(shí)生成iOS和Android上的app。由于這種開(kāi)發(fā)方式的靈活快速,愛(ài)碼哥在國(guó)內(nèi)已經(jīng)有了龐大的用戶(hù)群體。(了解更多愛(ài)碼哥的方案可訪問(wèn)官網(wǎng)https://www.imagjs.com/)
應(yīng)用是有了,很多用戶(hù)接下來(lái)面臨新的挑戰(zhàn),就是針對(duì)眾多不同系統(tǒng)版本、不同尺寸和分辨率的手機(jī),如何驗(yàn)證移動(dòng)應(yīng)用在這些機(jī)型上都適配,例如在不同Android版本上是否所有功能都能正常使用,不會(huì)出現(xiàn)崩潰和未知錯(cuò)誤。在不同的界面上顯示是否正常。
如果這個(gè)兼容性驗(yàn)證工作讓人來(lái)做,眾多的手機(jī)一一測(cè)過(guò)來(lái),耗時(shí)長(zhǎng),效率低,是不現(xiàn)實(shí)的。這個(gè)工作非自動(dòng)化測(cè)試莫屬。
為更好的幫助移動(dòng)應(yīng)用開(kāi)發(fā)者提高應(yīng)用的質(zhì)量和兼容性。愛(ài)碼哥聯(lián)合聆播科技,推出了快速易學(xué)的愛(ài)碼哥應(yīng)用自動(dòng)化解決方案。作為自動(dòng)化測(cè)試的專(zhuān)家,聆播科技有多款自動(dòng)化測(cè)試產(chǎn)品,用戶(hù)可以使用JavaScript / Node.JS 開(kāi)發(fā)針對(duì)移動(dòng)應(yīng)用、Web、API等類(lèi)型的自動(dòng)化腳本。愛(ài)碼哥的用戶(hù)只要掌握J(rèn)avaScript,就可以開(kāi)發(fā)從應(yīng)用到自動(dòng)化測(cè)試腳本全套內(nèi)容。成為真正的全棧工程師。
這里提供的愛(ài)碼哥應(yīng)用測(cè)試方案使用CukeTest作為測(cè)試工具,它可以安裝在多種平臺(tái)上(包括Windows、Mac),能開(kāi)發(fā)行為驅(qū)動(dòng)的自動(dòng)化腳本。編寫(xiě)iOS應(yīng)用自動(dòng)化測(cè)試的腳本需要在Mac平臺(tái)上,而Android自動(dòng)化測(cè)試的腳本根據(jù)用戶(hù)習(xí)慣在任何一個(gè)平臺(tái)上都可以。
下面就針對(duì)愛(ài)碼哥的樣例應(yīng)用,給大家演示如何做Android的自動(dòng)化測(cè)試。
先看下被測(cè)應(yīng)用,這個(gè)是應(yīng)用的首頁(yè):
下面是登陸頁(yè):
1. 環(huán)境準(zhǔn)備
1. 下載CukeTest,下載地址:http://cuketest.com/, Windows版或Mac版都可以。
2. 自備一臺(tái)Android設(shè)備,下載愛(ài)碼哥app并安裝。
3. PC端配置好Androd開(kāi)發(fā)環(huán)境。
2. 設(shè)計(jì)場(chǎng)景
1. 打開(kāi)CukeTest,創(chuàng)建一個(gè)空的項(xiàng)目。文件——新建項(xiàng)目——項(xiàng)目類(lèi)型選擇 Basic。
2. 創(chuàng)建成功之后,在feature文件中編輯場(chǎng)景,即測(cè)試用例的文字描述。這是行為驅(qū)動(dòng)開(kāi)發(fā)(BDD)的特點(diǎn)。BDD以場(chǎng)景為中心,驅(qū)動(dòng)自動(dòng)化測(cè)試腳本。既增強(qiáng)了可讀性,易于腳本的維護(hù)和團(tuán)隊(duì)的溝通,也方便開(kāi)發(fā)者整理測(cè)試腳本的思路。了解更多關(guān)于BDD的概念可以參考“為什么要行為驅(qū)動(dòng)開(kāi)發(fā)(BDD)”(http://cuketest.com/zh-cn/bdd/why_bdd.html)
剛創(chuàng)建項(xiàng)目默認(rèn)視圖為:
針對(duì)我們的app,我們要編寫(xiě)一個(gè)下圖的簡(jiǎn)單操作場(chǎng)景:
我們也可以將CukeTest切換到文本界面,直接將如下代碼復(fù)制進(jìn)去。
# language: zh-CN
功能: app 功能驗(yàn)證
驗(yàn)證app 基本功能:
底部導(dǎo)航跳轉(zhuǎn)
場(chǎng)景: 首頁(yè)分類(lèi)模塊-子模塊驗(yàn)證
假如切換到首頁(yè)
當(dāng)手機(jī)滑動(dòng)到分類(lèi)模塊
那么此模版應(yīng)該有12個(gè)子模塊
場(chǎng)景: 用戶(hù)登錄場(chǎng)景
假如切換到首頁(yè)
當(dāng)點(diǎn)擊登錄/注冊(cè)按鈕
那么應(yīng)該跳轉(zhuǎn)到登錄頁(yè)面
同時(shí)手機(jī)號(hào)碼輸入"170****1097"
同時(shí)密碼欄輸入"123456"
當(dāng)點(diǎn)擊登錄按鈕
那么用戶(hù)應(yīng)該成功登錄,并且首頁(yè)顯示手機(jī)號(hào)碼"170****1097"
場(chǎng)景: 底部導(dǎo)航欄UI驗(yàn)證
假如切換到首頁(yè)
那么底部導(dǎo)航欄應(yīng)該有3個(gè)模板
那么底部導(dǎo)航欄的文字分別是 | 愛(ài)碼哥 | 文檔 | 我的 |
同時(shí)獲取到底部所有導(dǎo)航欄并遍歷點(diǎn)擊
場(chǎng)景: 用戶(hù)退出登錄
假如切換到我的模塊
當(dāng)點(diǎn)擊退出登錄按鈕
那么彈出確認(rèn)退出提示,點(diǎn)擊確認(rèn)按鈕
那么應(yīng)該用戶(hù)退出登錄
3. 安裝項(xiàng)目依賴(lài)
我們使用其它語(yǔ)言來(lái)寫(xiě)手機(jī)自動(dòng)化代碼的時(shí)候,需要用到一些第三方的依賴(lài)庫(kù)。同樣,我們?cè)谶@個(gè)項(xiàng)目中也需要使用到第三方的庫(kù)文件。
此次我們用的依賴(lài)庫(kù)如下:
項(xiàng)目根目錄下執(zhí)行 npm install webdriverio @types/webdriverio --save 即可。安裝了這個(gè)包之后CukeTest會(huì)在你開(kāi)發(fā)的時(shí)候自動(dòng)提供這個(gè)庫(kù)相關(guān)的智能提示。
4. 生成自動(dòng)化代碼樣例
打開(kāi)features/step_definitions/下的definitions1.js文件,逐一點(diǎn)擊我們feature文件中步驟后面的灰色按鈕,CukeTest會(huì)自動(dòng)為我們生成自動(dòng)化樣例代碼到definitions1.js文件中:
生成完成后,CukeTest會(huì)根據(jù)代碼塊自動(dòng)生成對(duì)應(yīng)的代碼示例。
5.利用appium 進(jìn)行元素定位
1. 打開(kāi) appium 桌面版,點(diǎn)擊start server啟動(dòng)appium server:
2. File – New Session Window打開(kāi)新的窗口,配置app啟動(dòng)參數(shù),根據(jù)自己的手機(jī)配置。
主要參數(shù)說(shuō)明
deviceName:手機(jī)序列串號(hào)
appPackage:被測(cè)應(yīng)用的包名
appActivity:被測(cè)應(yīng)用的入口
resetKeyboard:是否重置手機(jī)鍵盤(pán)
noReset:不要重新安裝手機(jī)app
將手機(jī)與PC端通過(guò)數(shù)據(jù)線連接,點(diǎn)擊【Start Session】 啟動(dòng)appium連接手機(jī),此時(shí)appium 應(yīng)該顯示手機(jī)客戶(hù)端打開(kāi)的界面。
根據(jù)appium桌面端提供的ui元素追蹤器,我們可以很方便的定位到每個(gè)元素。通過(guò)webdriverio(http://webdriver.io/api.html) 庫(kù)提供的API,即可完成對(duì)app的自動(dòng)化。
6. 代碼部分
var webdriverio=require('webdriverio')
function createDriver() {
let options={
desiredCapabilities: {
platformName: 'Android',
platformVersion: '5.1',
automationName: 'UiAutomator2',
deviceName: "Y15QKCPH278J4",
appPackage: "com.imagjs.android.mh20170602",
appActivity: "com.imagjs.imag.ImagActivity",
noReset: true,
unicodeKeyboard: true
},
host:"127.0.0.1",
port:4723
};
let client=webdriverio.remote(options);
return client;}exports.driver=createDriver();
2. 設(shè)置運(yùn)行時(shí),新建features/suport/hooks.js
const { After, BeforeAll, Before, AfterAll, setDefaultTimeout }=require('cucumber');
const { driver }=require('./android_driver');
let cuketest=require('cuketest');
// 設(shè)置默認(rèn)的超時(shí)時(shí)間
setDefaultTimeout(60 * 1000);
BeforeAll(async function () {
//所有場(chǎng)景運(yùn)行之前
await driver.init();
await cuketest.delay(5000);
return driver.timeoutsImplicitWait(5*1000)})
Before(function () {
//定義場(chǎng)景運(yùn)行之前
})
After(async function () {
//定義場(chǎng)景運(yùn)行之后
let screenshot=await driver.saveScreenshot();
this.attach(screenshot, 'image/png');
});
AfterAll(function () {
//perform some shared teardown
return driver.end();
})
3. 實(shí)現(xiàn)場(chǎng)景代碼, 編輯 features/step_definitions/definitions1.js
var { Given, When, Then }=require('cucumber')
let { driver }=require('../support/android_driver')
let cuketest=require('cuketest')
let assert=require('assert');
async function getBottomList() {
let bottomList=driver.element('android=new UiSelector().resourceId("com.imagjs.android.mh20170602:id/main_bottom_tablayout").index(1)')
let list=await bottomList.elements('android=new UiSelector().className("android.widget.LinearLayout")')
return list;
}
Given(/^底部導(dǎo)航欄應(yīng)該有(\d+)個(gè)模板$/, async function (num) {
let list=await getBottomList();
return assert.equal(list.value.length, num, "數(shù)值不相等")
});
Given(/^底部導(dǎo)航欄的文字分別是$/, async function (table) {
let text=await driver.getText('/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.TextView')
console.log("text", text)
let asserVal=table.raw()[0]
for (let i=0; i < text.length; i++) {
assert.equal(text[i], asserVal[i]);
}
});
Then(/^切換到首頁(yè)$/, async function () {
await driver.click('/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.LinearLayout[1]')});Given(/^獲取到底部所有導(dǎo)航欄并遍歷點(diǎn)擊$/, async function () {
let list=await getBottomList();
for (let i=1; i < list.value.length + 1; i++) {
await driver.click('/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.LinearLayout[' + i + ']');
await cuketest.delay(1000)
}
});
When(/^手機(jī)滑動(dòng)到分類(lèi)模塊$/, async function () {
await driver.swipe('android=new UiSelector().resourceId("com.imagjs.android.mh20170602:id/contents_view_pager")', 50, -500, 1000);
});
Then(/^此模版應(yīng)該有(\d+)個(gè)子模塊$/, async function (num) {
let eles=await driver.elements('/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[3]/android.support.v7.widget.RecyclerView/android.widget.LinearLayout')
return assert(eles.value.length, num, "子模塊數(shù)量不一致");
});
When(/^點(diǎn)擊登錄\/注冊(cè)按鈕$/, async function () {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.LinearLayout[1]/android.widget.TextView'
await driver.click(selector)
});
Then(/^應(yīng)該跳轉(zhuǎn)到登錄頁(yè)面$/, async function () {
await cuketest.delay(1000);
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.RelativeLayout[2]/android.widget.LinearLayout/android.widget.TextView'
let text=await driver.getText(selector)
return assert.equal(text,"www.imagjs.com")
});
Then(/^手機(jī)號(hào)碼輸入"([^"]*)"$/, async function (phone) {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[1]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.EditText'
await driver.clearElement(selector)
await driver.element(selector).setValue(phone);
});
Then(/^密碼欄輸入"([^"]*)"$/, async function (passwd) {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[3]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.EditText'
await driver.clearElement(selector);
await driver.element(selector).setValue(passwd)
});
When(/^點(diǎn)擊登錄按鈕$/, async function () {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[5]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout[1]/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.Button'
await driver.click(selector)
});
Then(/^用戶(hù)應(yīng)該成功登錄,并且首頁(yè)顯示手機(jī)號(hào)碼"([^"]*)"$/, async function (phone) {
await cuketest.delay(2000);
let phoneSelector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.LinearLayout[1]/android.widget.TextView'
let text=await driver.getText(phoneSelector);
return assert.equal(text,phone)
});
Given(/^切換到我的模塊$/, async function () {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.LinearLayout[3]'
await driver.click(selector);
});
When(/^點(diǎn)擊退出登錄按鈕$/, async function () {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.support.v4.view.ViewPager/android.view.View/android.widget.LinearLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[4]/android.widget.LinearLayout[3]'
await driver.click(selector);
});
When(/^彈出確認(rèn)退出提示,點(diǎn)擊確認(rèn)按鈕$/, async function () {
await cuketest.delay(2000);
await driver.alertAccept();
});
Then(/^應(yīng)該用戶(hù)退出登錄$/, async function () {
let selector='/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.LinearLayout[1]/android.widget.TextView'
let text=await driver.getText(selector)
return assert.equal(text,"登錄/注冊(cè)");
});
JavaScript語(yǔ)法,愛(ài)碼哥的用戶(hù)有沒(méi)有感到很親切?
7. 運(yùn)行
先將appium啟動(dòng), 再點(diǎn)擊【運(yùn)行項(xiàng)目】,可以看到手機(jī)會(huì)自動(dòng)按照我們?cè)O(shè)計(jì)的場(chǎng)景運(yùn)行起來(lái)。
運(yùn)行完成后自動(dòng)生成測(cè)試報(bào)告。
8.總結(jié)
這里使用了Node.js + Cucumber 開(kāi)發(fā)了Appium的客戶(hù)端腳本,使用CukeTest開(kāi)發(fā)環(huán)境,優(yōu)勢(shì):
原文鏈接:https://mp.weixin.qq.com/s/7QIwd7CfG_ZQSUByYT6Lag
作者:快客俠
MAX馬上就要升初一了,之前學(xué)過(guò)一段時(shí)間python語(yǔ)言,對(duì)計(jì)算機(jī)編程有濃厚興趣。可原先在PC上寫(xiě)的代碼,執(zhí)行的結(jié)果也只能在PC上顯示,效果也不夠酷炫。聽(tīng)說(shuō)爸爸有辦法可以讓代碼執(zhí)行后在手機(jī)上秀出來(lái),就很好奇,決定利用這個(gè)暑期好好學(xué)一下。
爸爸的單位是一家專(zhuān)門(mén)開(kāi)發(fā)app軟件的公司,他們給產(chǎn)品取了一個(gè)很好記的名字-愛(ài)碼哥。打開(kāi)網(wǎng)站(imagjs.com)通過(guò)注冊(cè)賬戶(hù)就可以登錄到工作臺(tái)頁(yè)面,然后點(diǎn)擊“創(chuàng)建新應(yīng)用”就開(kāi)始了我開(kāi)發(fā)app的旅程。
我就先創(chuàng)建了一個(gè)排序算法的應(yīng)用吧,剛好可以把原來(lái)在python學(xué)習(xí)過(guò)的在手機(jī)上看看能做成什么樣子。在爸爸同事的指導(dǎo)和幫助下,我設(shè)計(jì)了一個(gè)簡(jiǎn)單的原型圖:
原型圖
接下來(lái)馬上就可以寫(xiě)代碼了,想想有點(diǎn)激動(dòng)了,我有點(diǎn)迫不及待了。然而爸爸卻給我潑了一盆冷水:“MAX小朋友,手機(jī)應(yīng)用和你原來(lái)只需要寫(xiě)代碼執(zhí)行運(yùn)算不大一樣。首先需要通過(guò)布局(layout)的方式把你設(shè)計(jì)圖紙上的各種元素一行行一列列地顯示出來(lái),你還是先看半天的文檔(doc.imagjs.com/)吧,網(wǎng)站上有鏈接...”
我先在手機(jī)上安裝愛(ài)碼哥開(kāi)發(fā)版APP,在手機(jī)上加載了一個(gè)示例“Hello World”,可以在手機(jī)上打開(kāi)這個(gè)應(yīng)用看到實(shí)際效果圖(下圖1)
圖1
同時(shí)可以在PC端工作臺(tái)(以及手機(jī)源碼查看模式下)看到一些有趣的代碼。我學(xué)到了一個(gè)新詞-XML標(biāo)簽,這些標(biāo)簽成對(duì)使用<XML></XML>,下面就是這個(gè)應(yīng)用的代碼。
<?xml version="1.0" encoding="utf-8"?> <imag>//XML文檔的根節(jié)點(diǎn) <page>//頁(yè)面標(biāo)簽 <title style="background:#6495ED">//頁(yè)面的標(biāo)題 <center><label>歡迎使用愛(ài)碼哥</label></center> </title> <content>//頁(yè)面的內(nèi)容 <list type="group">//用列表的方式布局 <item>//屬于列表項(xiàng) <label>Hello World!</label>//用文本標(biāo)簽放內(nèi)容 </item> </list> </content> </page> </imag>
從這些標(biāo)簽名稱(chēng)上就可以知道大概是干什么用的了,比如:Page,Title,Content,List,Label。。。
文檔中還有很多例子,一看就能明白。老爸太低估了我的閱讀理解能力,我看了半個(gè)小時(shí)就開(kāi)始折騰了。(待續(xù))
凌波
錢(qián)是賺到了,但頭發(fā)掉光了,肚子變大了,人變搓了,女朋友也找不到了
——打一職業(yè)
bingo!你猜對(duì)了!就是程序員,其中做原生APP開(kāi)發(fā)的程序員更是傳說(shuō)中注定光棍的那一批人……為什么?因?yàn)樵鶤PP開(kāi)發(fā)難度大、周期長(zhǎng)啊!每天日以繼夜、勤勤懇懇地埋頭在電腦面前研發(fā)原生APP,別說(shuō)找對(duì)象,休息的時(shí)間對(duì)他們來(lái)說(shuō)都十分奢侈。
也許,有一個(gè)移動(dòng)開(kāi)發(fā)平臺(tái)就能拯救程序員于水火之中。“愛(ài)碼哥”(iMAG),物如其名——關(guān)愛(ài)寫(xiě)碼小哥,不僅為原生APP程序員提供了一個(gè)更加便利的開(kāi)發(fā)平臺(tái),甚至連新手程序員也能用其進(jìn)行原生APP的開(kāi)發(fā)。苦逼的程序員終于可以升職加薪,贏取白富美,走上人生巔峰啦!
減少時(shí)間人力成本
讓原生APP開(kāi)發(fā)更加簡(jiǎn)便
純粹的原生 App 開(kāi)發(fā),需要程序員在語(yǔ)言編寫(xiě)方面有很高造詣,而且周期長(zhǎng),開(kāi)發(fā)成本也很高。此外,目前國(guó)內(nèi)許多APP開(kāi)發(fā)產(chǎn)品普遍采用的HTML5(H5)版本在界面呈現(xiàn)、加載速度、應(yīng)用程序編程接口開(kāi)放程度等方面也逐漸不能滿(mǎn)足用戶(hù)的使用體驗(yàn)。
那“愛(ài)碼哥”是如何讓如此高難度的開(kāi)發(fā)變得簡(jiǎn)單便利呢?“愛(ài)碼哥”CEO李青峰告訴我們,平臺(tái)使用的是NativeScript(一款采用JavaScript來(lái)構(gòu)建跨平臺(tái)原生移動(dòng)應(yīng)用的開(kāi)源框架)作為技術(shù)框架,xlm+JavaScript(配置 + 腳本)為開(kāi)發(fā)方式,并在平臺(tái)上掛出地圖、支付、拍照等各式各樣的延展功能組件,幫助大部分掌握軟件開(kāi)發(fā)入門(mén)技術(shù)的程序員在短時(shí)間內(nèi)做出高大上的手機(jī)原生APP。
除此之外,傳統(tǒng)的應(yīng)用開(kāi)發(fā)需要你在完成代碼并打包后才能查看APP效果,而在愛(ài)碼哥的平臺(tái)上,你可以邊開(kāi)發(fā),邊在APP移動(dòng)端實(shí)時(shí)查看APP效果,大大提升了開(kāi)發(fā)的效率。
一般原生APP開(kāi)發(fā)周期至少需要14周(近四個(gè)月)的時(shí)間,這對(duì)跟時(shí)間賽跑的創(chuàng)業(yè)公司來(lái)說(shuō)時(shí)間無(wú)疑過(guò)長(zhǎng),李青峰介紹,如果用“愛(ài)碼哥”進(jìn)行APP的開(kāi)發(fā),可以提高5倍的開(kāi)發(fā)效率,降低50%的開(kāi)發(fā)成本。
跨平臺(tái)開(kāi)發(fā)
一次開(kāi)發(fā),多處運(yùn)行
“愛(ài)碼哥”的特色是提供純粹的跨平臺(tái)原生 APP的制作方式,程序員可以在 JavaScript、Java、.Net、php 或 HTML5 當(dāng)中選擇任何一套開(kāi)發(fā)語(yǔ)言,直接在“愛(ài)碼哥”平臺(tái)上使用現(xiàn)成的原生控件,再用一套代碼生成 Android(安卓系統(tǒng))和 iOS(蘋(píng)果系統(tǒng)) 兩個(gè)系統(tǒng)的原生 APP。
用通俗點(diǎn)的語(yǔ)言來(lái)說(shuō),就是程序員原先在編寫(xiě) APP 時(shí),需要根據(jù)手機(jī)系統(tǒng)的不同,在用戶(hù)界面設(shè)計(jì)具有針對(duì)性地對(duì)菜單、列表、標(biāo)題欄等布局標(biāo)準(zhǔn)需要一條一條編寫(xiě)代碼。但程序員在使用“愛(ài)碼哥”編寫(xiě) APP 時(shí),可以直接使用平臺(tái)上的原生控件,通過(guò)打包實(shí)現(xiàn)適配不同手機(jī)版本的軟件并呈現(xiàn)效果。同時(shí),程序員也可以在平臺(tái)上上傳自己制作的控件,獲得一定的盈利,“愛(ài)碼哥”就是這樣用產(chǎn)品吸引程序員入駐,再以愛(ài)碼哥為源頭產(chǎn)出更多控件,形成一個(gè)良性循環(huán),并逐漸從開(kāi)發(fā)工具轉(zhuǎn)型為開(kāi)發(fā)服務(wù)平臺(tái),愛(ài)碼哥接下來(lái)會(huì)讓每個(gè)人都可以進(jìn)行APP的開(kāi)發(fā)。
目前“愛(ài)碼哥” 去年已經(jīng)完成了天使輪的千萬(wàn)級(jí)投資,并擁有上萬(wàn)的種子用戶(hù),先后服務(wù)包括“萬(wàn)戶(hù)網(wǎng)絡(luò)”、“聯(lián)想集團(tuán)”“清華大學(xué)”“國(guó)家開(kāi)發(fā)銀行”在內(nèi)的1000多家企業(yè)用戶(hù)。