前章節(jié)我們講了部門部門管理,項(xiàng)目管理,這節(jié)我們繼續(xù)實(shí)現(xiàn)添加腳本。
那么關(guān)聯(lián)關(guān)系就是,在部門對應(yīng)的項(xiàng)目中添加多個(gè)腳本。實(shí)現(xiàn)這個(gè)模塊我們需要完成三步操作。
1:完成數(shù)據(jù)表的創(chuàng)建及與部門表和項(xiàng)目表之間的關(guān)聯(lián)關(guān)系
2:完成前端界面布局
3:完成對應(yīng)后端接口的開發(fā)
首先完成第一步,數(shù)據(jù)表的創(chuàng)建,既然是添加腳本,那么我們能夠想到的字段就有:
腳本名稱,上傳的圖片名稱,操作的類型(點(diǎn)擊、滑動(dòng)、校驗(yàn)...),執(zhí)行的步驟,操作類型的次數(shù),生成腳本的類型。
對應(yīng)如下:
ui自動(dòng)化平臺(tái)添加腳本管理模塊:
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `script_images`;
CREATE TABLE `script_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`script_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`image_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`action_type` enum('click','assert','exist') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`step_number` int(11) NULL DEFAULT NULL,
`ftp_path` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`department_id` int(11) NULL DEFAULT NULL,
`project_id` int(11) NULL DEFAULT NULL,
`repeatCount` int(11) NULL DEFAULT NULL,
`select_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `department_id`(`department_id`) USING BTREE,
INDEX `project_id`(`project_id`) USING BTREE,
CONSTRAINT `script_images_ibfk_1` FOREIGN KEY (`department_id`) REFERENCES `department` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `script_images_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB AUTO_INCREMENT=24 CHARACTER SET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=Compact;
SET FOREIGN_KEY_CHECKS=1;
各個(gè)字段看名字對應(yīng)即可。里面定義了部門表和項(xiàng)目表的主外鍵關(guān)聯(lián)關(guān)系,之前章節(jié)詳細(xì)講過,這里不在啰嗦。
接下來我們實(shí)現(xiàn)前端部分,直接看下前端需要的功能界面。
完整邏輯為:
在腳本的實(shí)現(xiàn)中使用了Ajax進(jìn)行異步通信,主要進(jìn)行了以下操作:
在獲取部門信息和項(xiàng)目信息時(shí),使用GET方法接收服務(wù)器返回的數(shù)據(jù),并更新選擇部門和項(xiàng)目的下拉列表;在保存圖片數(shù)據(jù)、上傳圖片和生成測試腳本時(shí),使用POST方法將數(shù)據(jù)發(fā)送到服務(wù)器。
完整實(shí)現(xiàn)為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試腳本生成</title>
<!-- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>-->
<style>
/*body {*/
/* font-family: Arial, sans-serif;*/
/* margin: 20px;*/
/*}*/
h1 {
text-align: center;
color: #3c3c44;
font-weight: bold;
}
label {
margin-top: 10px;
display: block;
color: #1c1c1d;
}
select,
input[type=text],
input[type=file] {
width: 100%;
padding: 12px;
margin-top: 4px;
display: inline-block;
border: 1px solid #ccc;
}
input[type=file] {
padding: 3px;
}
button {
border: none;
color: white;
padding: 14px 28px;
font-size: 16px;
cursor: pointer;
border-radius: 4px;
margin-top: 16px;
}
#click_btn {
background-color: #4CAF50;
}
#assert_btn {
background-color: #FF9800;
}
#generate_script_btn {
background-color: #2196F3;
}
</style>
</head>
<body>
<h1>測試腳本生成</h1>
<div id="whole-body">
<label for="select-department">選擇部門:</label>
<select id="select-department">
<option value=""> -- 請選擇部門 -- </option>
</select>
<label for="select-project">選擇項(xiàng)目:</label>
<select id="select-project">
<option value=""> -- 請選擇項(xiàng)目 -- </option>
</select>
<label for="select_type">腳本類型:</label>
<select id="select_type">
<option value=""> -- 請選擇類型 -- </option>
<option value="1">windows</option>
<option value="2">unity</option>
<option value="3">android</option>
</select>
<label for="script_name">腳本名稱:</label>
<input type="text" id="script_name">
<input type="file" id="image_upload">
<br>
<button id="click_btn">點(diǎn)擊</button>
<button id="assert_btn">校驗(yàn)</button>
<button id="exist_btn" style="background-color: #9C27B0; color: white; padding: 14px 28px; font-size: 16px; cursor: pointer; border-radius: 4px; margin-top: 16px;">
是否存在
</button>
<button id="generate_script_btn">生成測試腳本</button>
<p id="action_prompt" style="color: red; font-weight: bold; margin-top: 10px;"></p>
<div id="uploaded_images"></div>
<script>
var step_number=0;
var selected_action='';
// 添加 isImageUploaded 和 isActionPerformed 變量
var isImageUploaded=false;
var isActionPerformed=false;
function save_image_data(image_name, action_type, step_number, script_name,ftp_path, department_id, project_id,repeatCount,select_type) {
$.ajax({
url: "/save-image-data", // 您的后端接口
type: "POST",
contentType: "application/json",
data: JSON.stringify({
image_name: image_name,
action_type: action_type,
step_number: step_number,
script_name: script_name,
// ftp_path: `/bignoxData/bignoxData/software/qa/Mobile/uitest/${script_name}/${image_name}`, // 添加新字段
department_id: department_id,
project_id: project_id,
repeatCount:repeatCount,
select_type:select_type,
ftp_path: ftp_path, // 添加新字段
}),
success: function (res) {
console.log("Image data saved successfully");
console.log(repeatCount);
console.log(select_type);
console.log(res); // 添加這一行以查看響應(yīng)內(nèi)容
},
error: function (err) {
console.error("Image data save failed");
console.error(err); // 添加這一行以查看錯(cuò)誤內(nèi)容
},
});
}
function disableActionButtons() {
$("#click_btn").attr("disabled", true);
$("#assert_btn").attr("disabled", true);
}
function enableActionButtons() {
$("#click_btn").attr("disabled", false);
$("#assert_btn").attr("disabled", false);
}
disableActionButtons(); // 最初禁用按鈕,直到上傳第一張圖片
$("#click_btn").on("click", function () {
selected_action='click';
var repeatCount=prompt("請輸入點(diǎn)擊次數(shù)", "1");
repeatCount=parseInt(repeatCount);
while(isNaN(repeatCount) || repeatCount<1) {
alert("無效的輸入!至少點(diǎn)擊一次。")
repeatCount=prompt("請輸入點(diǎn)擊次數(shù)", "1");
repeatCount=parseInt(repeatCount);
}
$("#action_prompt").text("已選擇點(diǎn)擊操作");
if (step_number > 0) {
var imgCaption=`點(diǎn)擊第${step_number}步。`;
$("#uploaded_images div:last-child p").text(imgCaption);
}
disableActionButtons(); // 選擇操作后重新禁用按鈕
// 修改并添加以下兩行代碼到 #click_btn 的事件處理程序中
const department_id=$('#select-department').val();
const project_Id=$('#select-project').val();
const imageUrl=$(this).attr("data-url");
const select_type=$('#select_type').val();
save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), imageUrl,department_id, project_Id,repeatCount,select_type);
isActionPerformed=true; // 將 isActionPerformed 設(shè)置為 true
//save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), `/uploads${res.image_url}`); // 傳遞 ftp_path 參數(shù)
});
//新函數(shù)
$("#image_upload").on("click", function () {
this.value=null;
});
//分割線
$("#assert_btn").on("click", function () {
selected_action='assert';
var repeatCount=1; // 為assert操作設(shè)置repeatCount為1
$("#action_prompt").text("已選擇校驗(yàn)操作");
if (step_number > 0) {
var imgCaption=`校驗(yàn)第${step_number}步。`;
$("#uploaded_images div:last-child p").text(imgCaption);
}
disableActionButtons(); // 選擇操作后重新禁用按鈕
// 修改并添加以下兩行代碼到 #click_btn 的事件處理程序中
const department_id=$('#select-department').val();
const project_Id=$('#select-project').val();
const imageUrl=$(this).attr("data-url");
save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), imageUrl, department_id, project_Id,select_type,repeatCount);
isActionPerformed=true; // 將 isActionPerformed 設(shè)置為 true
//save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), `/uploads${res.image_url}`); // 傳遞 ftp_path 參數(shù)
});
$("#exist_btn").on("click", function () {
selected_action='exist';
var repeatCount=prompt("請輸入循環(huán)點(diǎn)擊次數(shù)", "1");
repeatCount=parseInt(repeatCount);
while(isNaN(repeatCount) || repeatCount<1) {
alert("無效的輸入!次數(shù)至少為一。")
repeatCount=prompt("請輸入循環(huán)點(diǎn)擊次數(shù)", "1");
repeatCount=parseInt(repeatCount);
}
$("#action_prompt").text("已選擇是否存在操作");
if (step_number > 0) {
var imgCaption=`是否存在第${step_number}步。`;
$("#uploaded_images div:last-child p").text(imgCaption);
}
disableActionButtons();
const department_id=$('#select-department').val();
const project_Id=$('#select-project').val();
const imageUrl=$(this).attr("data-url");
save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), imageUrl, department_id, project_Id,select_type, repeatCount);
isActionPerformed=true;
});
var imgFile; // 將 imgFile 變量移到外面
$("#image_upload").on("change", function () {
var fileInput=this;
imgFile=fileInput.files[0];
var imgName=imgFile.name;
var formData=new FormData();
formData.append("image", imgFile);
formData.append("script_name", $("#script_name").val());
formData.append("department_id", $("#select-department").val()); // 新增加這行
formData.append("project_id", $("#select-project").val()); // 新增加這行
formData.append("select_type", $("#select_type").val()); // 新增加這行
step_number++;
$.ajax({
url: "/upload-image", // 您的后端接口
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function (res) {
var newImg=document.createElement("img");
newImg.src=res.image_url; // 響應(yīng)中的圖片 URL
newImg.style.maxWidth="200px";
newImg.alt=imgName;
var imageCaption=document.createElement("p");
var imageContainer=document.createElement("div");
imageContainer.appendChild(newImg);
imageContainer.appendChild(imageCaption);
$("#uploaded_images").append(imageContainer);
enableActionButtons(); // 上傳新圖片后啟用點(diǎn)擊和校驗(yàn)按鈕
$("#click_btn").attr("data-url", res.image_url);
$("#assert_btn").attr("data-url", res.image_url);
$("#exist_btn").attr("data-url", res.image_url);
enableActionButtons(); // 添加這一行代碼
// 在這里設(shè)置 isImageUploaded 為 true
isImageUploaded=true;
},
error: function (err) {
console.error("圖片上傳失敗", err);
}
});
});
$("#generate_script_btn").on("click", function () {
if (!isImageUploaded) {
alert("未上傳任何文件!");
return;
}
if (!isActionPerformed) {
alert("請先選擇當(dāng)前圖片要執(zhí)行的動(dòng)作!");
return;
}
// 添加的部分: 點(diǎn)擊生成后隱藏主頁面,顯示加載動(dòng)畫和提示
$('#whole-body').hide();
$('#loading').show();
// 更新此部分,使用正確的 ID
console.log("項(xiàng)目ID: " + $("#select-project").val());
console.log("部門ID: " + $("#select-department").val());
var postData=JSON.stringify({
script_name: $("#script_name").val(),
department_id: $("#select-department").val(),
project_id: $("#select-project").val(),
select_type: $("#select_type").val()
});
$.ajax({
url: "/generate-test-script",
type: "POST",
contentType: "application/json",
data: postData,
success: function (res) {
//接收到返回?cái)?shù)據(jù)后先延時(shí)3秒
setTimeout(function () {
//隱藏加載動(dòng)畫和提示,顯示生成成功提示,并顯示主頁面
$('#loading').hide();
$('#script_gen_prompt').show(); //顯示生成成功提示
alert('測試腳本已生成!');
setTimeout(function(){
$('#script_gen_prompt').hide();
$('#whole-body').show(); //重新顯示主頁面
}, 2000); //2000ms后隱藏成功提示,并重新顯示主界面
}, 3000); // 延時(shí) 3000 ms 顯示生成完成提示
},
error: function (err) {
setTimeout(function() {
$('#loading').hide();
alert("生成測試腳本失敗");
$('#whole-body').show(); //重新顯示主頁面
}, 3000); // 延時(shí) 3000 ms 顯示失敗提示
console.error(err)
}
});
});
<!-- 在現(xiàn)有的 <script> 標(biāo)簽內(nèi)添加以下代碼 -->
$(document).ready(function () {
// 獲取部門信息并填充到下拉框
function loadDepartments() {
$.get("/get-all-departments", function (data) {
var departments=data.departments;
$("#select-department").empty();
$("#select-department").append("<option value=''> -- 請選擇部門 -- </option>")
for (var i=0; i < departments.length; i++) {
var option=$("<option>").val(departments[i].id).text(departments[i].name);
$("#select-department").append(option);
}
});
}
loadDepartments();
$("#select-department, #select-project").on("change", function() {
if ($("#select-department").val() && $("#select-project").val()) {
$("#script_name").prop("disabled", false);
} else {
$("#script_name").prop("disabled", true);
}
});
$("#select-department").on("change", function () {
var departmentId=$(this).val();
if (departmentId) {
// 發(fā)送請求獲取部門對應(yīng)項(xiàng)目
$.get("/get-projects?department_id=" + departmentId, function (data) {
var projects=data.projects;
$("#select-project").empty();
$("#select-project").append("<option value=''> -- 請選擇項(xiàng)目 -- </option>");
for (var i=0; i < projects.length; i++) {
var option=$("<option>").val(projects[i].id).text(projects[i].name);
$("#select-project").append(option);
}
});
} else {
$("#select-project").empty();
$("#select-project").append("<option value=''> -- 請先選擇部門 -- </option>");
}
});
})
</script>
</div>
</body>
<div id="loading" style="display: none; text-align: center;">
<img src="/uploads/static/Picture/gif/xz.gif" style="width: 200px; height: auto; margin: 0 auto;">
<p style="margin-top: 20px; font-size: 20px; font-weight: bold;">正在生成腳本...</p>
</div>
</html>
對應(yīng)后端生成腳本的邏輯為:
@app.route('/generate-test-script', methods=['POST'])
def generate_test_script():
try:
script_name=request.json.get('script_name')
script_nameair=request.json.get('script_name') + '.air'
department_id=int(request.json.get('department_id'))
select_type=request.json.get('select_type')
print(select_type)
repeatCount=request.json.get('repeatCount')
if request.json:
project_id=int(request.json.get('project_id'))
department_id=int(department_id)
project_id=int(project_id)
start_package='com.noxgroup.game.android.townsurvivor'
# mycursor.execute("SELECT image_name, action_type, step_number, ftp_path FROM script_images WHERE script_name=%s ORDER BY step_number", (script_name, ))
# images_data=mycursor.fetchall()
# mycursor.execute("SELECT name FROM department WHERE id=%s", (department_id, ))
# department_name=mycursor.fetchone()[0]
# mycursor.execute("SELECT name FROM project WHERE id=%s and status=1", (project_id, ))
# project_name=mycursor.fetchone()[0]
sql="SELECT image_name, action_type, step_number, ftp_path,repeatCount FROM script_images WHERE script_name='{}' ORDER BY step_number".format(
script_name, )
print(sql+'lx')
images_data=db.select_data(sql)
print('這里出現(xiàn)imagesdata數(shù)據(jù)')
print(images_data)
sql="SELECT name FROM department WHERE id={}".format(department_id, )
department_name=db.select_one_data(sql)[0]
sql="SELECT name FROM project WHERE id={} and status=1".format(project_id, )
project_name=db.select_one_data(sql)[0]
script_folder=os.path.join(os.getcwd(), 'uploads', department_name, project_name, script_nameair)
if not os.path.exists(script_folder):
os.makedirs(script_folder)
with open(os.path.join(script_folder, f"{script_name}.py"), "w") as f:
# startapkpath=os.path.join(os.getcwd(), 'uploads', department_name, project_name, "uploadfiles")
# try:
# apk_files=glob.glob(f'{startapkpath}/*.apk')
# if not apk_files:
# print(f"No apk files in {startapkpath}")
# return jsonify({"result": "error", "error_message": f"No apk files in {startapkpath}"})
# else:
# apk_file=apk_files[0]
# except Exception as e:
# print(e)
# a=APK(apk_file)
# package=a.get_package()
# activity=a.get_main_activity()
if project_name=='TownSurvivor':
ipconnect='127.0.0.1:{}'.format(config.tsphoneip)
print(123)
elif project_name=='忍者貓':
ipconnect='127.0.0.1:{}'.format(config.maophoneip)
elif project_name=='Player':
ipconnect='127.0.0.1:{}'.format(config.maophoneip)
else:
return 'adb erro'
print(ipconnect)
f.write("from airtest.core.api import *\n")
f.write("from airtest.core.settings import Settings as ST \n")
f.write("ST.THRESHOLD_STRICT=0.7\n")
f.write("ST.THRESHOLD=0.7\n")
f.write("auto_setup(__file__)\n")
# f.write("import subprocess\n")
# # 先構(gòu)造要寫入的子進(jìn)程調(diào)用命令字符串
# subprocess_cmd=(
# f"output=subprocess.check_output('adb -s {ipconnect} shell am start -n {package}/{activity}', "
# "shell=True)"
# )
#
# # 然后寫入文件
# f.write(subprocess_cmd + "\n")
# # f.write("output=subprocess.check_output(f'adb -s {ipconnect} shell am start -n {package}/{activity}', shell=True)\n")
# f.write("time.sleep(8)\n")
# f.write("print(output.decode())\n")
for image_data in images_data:
print(image_data)
image_name, action_type, step_number, _, repeatCount=image_data # 接收重復(fù)次數(shù)
if action_type=='click':
if int(repeatCount) > 1:
f.write(f"for _ in range({repeatCount}):") # 添加循環(huán)語句用于重復(fù)點(diǎn)擊
f.write(f"\n if exists(Template(r'{image_name}')):\n") # 添加循環(huán)語句用于重復(fù)點(diǎn)擊
f.write(f" sleep(1.0)\n") # 添加循環(huán)語句用于重復(fù)點(diǎn)擊
f.write(f" touch(Template(r'{image_name}'))\n")
f.write(f" else:\n")
f.write(f" break\n")
else:
f.write(f"touch(Template(r'{image_name}'))\n")
f.write(f"time.sleep(1)\n")
elif action_type=='assert':
f.write(f"assert_exists(Template(r'{image_name}'))\n")
f.write(f"time.sleep(1)\n")
elif action_type=='exist': # 處理新的action
f.write(f"for i in range({repeatCount}):\n")
f.write(f" if exists(Template(r'{image_name}')):\n")
f.write(f" touch(Template(r'{image_name}'))\n")
f.write(f" else:\n")
f.write(f" break\n")
# 添加保存腳本邏輯
save_generated_script_to_DB(department_name, project_name, script_name,select_type)
return jsonify({'result': 'success'}, script_folder)
else:
return jsonify({'error': 'No script name found.'}), 400
except Exception as e:
print('Error:', e)
traceback.print_exc()
return jsonify({"result": "error", "error_message": str(e)})
綜上:添加腳本的功能邏輯開發(fā)完畢。
下一節(jié):執(zhí)行腳本邏輯開發(fā)。敬請期待
in10手機(jī)版怎么運(yùn)行安卓APP應(yīng)用?微軟官方并沒有給出正規(guī)的方法,但民間破解組織已經(jīng)成功讓W(xué)in10手機(jī)版安裝安卓APP應(yīng)用。據(jù)外媒報(bào)道,一西班牙開發(fā)者Antonio de la Iglesia寫出了一個(gè)在電腦上使用的神器,只要連接Win10手機(jī),就可以一鍵將安卓APP應(yīng)用裝到Win10手機(jī)上。
軟件名稱: | Win10 Mobile APK Installer(Win10手機(jī)版裝安卓應(yīng)用神器) |
軟件版本: | 1.1 |
軟件大小: | 10 |
能手機(jī)的應(yīng)用開發(fā)也隨著手機(jī)的發(fā)展成為一個(gè)火熱的行業(yè),有著不錯(cuò)的收入。為了方便而有效的進(jìn)行開發(fā)選擇一套合適的開發(fā)模式是十分重要的。智能手機(jī)APP的開發(fā)模式可以分為三類分別是Native APPWeb APP和Hybrid APP開發(fā)模式。
1. Native APP
NativeAPP開發(fā)模式,即本地開發(fā)模式,又稱為傳統(tǒng)型開發(fā)模式。這種開發(fā)模式基于移動(dòng)終端的操作系統(tǒng)進(jìn)行開發(fā),可以良好的利用系統(tǒng)的硬件資源。其缺點(diǎn)也是顯而易見的,其應(yīng)用只能在一種移動(dòng)終端的系統(tǒng)中安裝使用,而且由于APP對硬件的依賴,應(yīng)用升級(jí)會(huì)比較麻煩。
1.2 Web APP
Web APP開發(fā)模式基本依靠網(wǎng)絡(luò)技術(shù)實(shí)現(xiàn)。其APP 是一個(gè)針對手機(jī)優(yōu)化后的 Web站點(diǎn),優(yōu)點(diǎn)是實(shí)現(xiàn)了跨平臺(tái),而且對硬件幾乎沒有依賴,開發(fā)周期短。缺點(diǎn)也很明顯,APP 對網(wǎng)絡(luò)的依賴很大,數(shù)據(jù)基本都來自服務(wù)器,因此網(wǎng)絡(luò)狀況會(huì)直接影響用戶體驗(yàn)。在沒有網(wǎng)絡(luò)的情況下,APP的功能基本不能使用。而且APP無法調(diào)用手機(jī)的硬件API功能受到一定的限制。
1.3 Hybrid APP
HybridAPP開發(fā)模式即混合開發(fā)模式。這種開發(fā)模式使用第三方的跨平臺(tái)開發(fā)框架,將一種語言開發(fā)出應(yīng)用兼容到不同的移動(dòng)設(shè)備上。這樣開發(fā)者就可以用Java Web技術(shù)或另一種不針對某具體系統(tǒng)的第三方的開發(fā)技術(shù),實(shí)現(xiàn)應(yīng)用的功能。APP同時(shí)具有了跨平臺(tái)性和不錯(cuò)的硬件資源調(diào)用能力。
2.1 Hybrid APP的PhoneGap開發(fā)框架
Nitobi公司(現(xiàn)在已被Adobe公司收購)推出了基于Web技術(shù)的移動(dòng)解決方案PhoneGap,這一方案在其官網(wǎng)上的定義是“可以使用Web技術(shù)編寫手機(jī)本地應(yīng)用程序的Htm15應(yīng)用程序平臺(tái)”。簡單來說PhoneGap是一套基于Htm15的移動(dòng)應(yīng)用開發(fā)框架6PhoneGap開發(fā)框架成功將Java Web開發(fā)技術(shù)應(yīng)用到了移動(dòng)設(shè)備的開發(fā)上。已經(jīng)具備 Java Web開發(fā)能力的開發(fā)者們可以使用熟知的HTML、CSS和JavaScript來開發(fā)手機(jī)APP。
PhoneGap 開發(fā)框架支持包括IOS、Android、Windows Phone在內(nèi)的多種手機(jī)平臺(tái)。開發(fā)者通過PhoneGap 提供的插件可以調(diào)用API使用攝像頭聯(lián)系人、地理定位等功能。PhoneGap 的使用是免費(fèi)的它的兼容性強(qiáng)而且開發(fā)成本低。但PhoneGap本身也有不少缺點(diǎn),它的運(yùn)行需要依靠移動(dòng)設(shè)備具有內(nèi)置的瀏覽器引擎WebKit,PhoneGap的APP運(yùn)行速度較慢,硬件調(diào)用能力也不如Native APP。安卓手機(jī)使用 PhoneGap 框架開發(fā)的Hy-bridAPP的基本結(jié)構(gòu)。
2.2PhoneGapAPP開發(fā)環(huán)境搭建
開發(fā)PhoneGap APP可以使用Dreamweaver工具來制作Java Web頁面,并使用 PhoneGap Build(PhoneGap的在線編譯云服務(wù))生成安裝包。但如果想測試某一平臺(tái)的硬件功能,就必須配置對應(yīng)的開發(fā)環(huán)境,再進(jìn)行編譯和測試。例如安卓開發(fā)的SDK工具包提供了手機(jī)模擬器,可以在PC機(jī)上進(jìn)行APP測試。
下面介紹在配置好安卓開發(fā)環(huán)境后,如何使用PhoneGap框架開發(fā)應(yīng)用。首先,下載好 PhoneGap的工具包,打開 Eclipse后,按照創(chuàng)建步驟,新建一個(gè)安卓應(yīng)用項(xiàng)目。復(fù)制cordova-xxxjar 文件到項(xiàng)目中的lib 文件夾下,右擊該jar 文件添加引用。然后在項(xiàng)目的assets 目錄下新建文件,復(fù)制 PhoneGap 工具包內(nèi)的cordovajs 文件到個(gè)文件夾下,并創(chuàng)建Ja-va Web運(yùn)行的級(jí)聯(lián)樣式表,Html文件和其他is 文件。創(chuàng)建完成后的文件結(jié)構(gòu)。
另外,安卓的應(yīng)用需要依靠Activity啟動(dòng)。要讓Activity啟動(dòng)后,加載html頁面并運(yùn)行JavaScript代碼,需要對Activity進(jìn)行修改。改動(dòng)包括兩步,要將Activity 繼承的類改為 DroidGap,導(dǎo)人需要的cor-dova包。然后將 setContentView(R.layout.activi-ty-main)改為 super.loadUrl(“file://android-asset/www/indexhtml”)其中www是新建的文件夾的名字index.html是該文件夾下的html文件
編寫好文件夾下的html和is代碼生成apk安裝到安卓手機(jī)上就可以運(yùn)行了。
2.3NativeAPP的開發(fā)環(huán)境配置及APP運(yùn)行
對于Native APP發(fā)模式,不同的手機(jī)系統(tǒng)都有套成熟的開發(fā)框架與對應(yīng)的開發(fā)語言”。例如,蘋果手機(jī)的APP需要用Object-C 編寫,AndroidAPP主要用Java開發(fā),而屬于微軟公司的Win-dows Phone應(yīng)用主要使用C#語言開發(fā)。如圖4 所示是安卓手機(jī)的Native APP的基本結(jié)構(gòu)。其中Activity可以理解為活動(dòng)窗口或者是一個(gè)單獨(dú)的頁面,它是與用戶交互的最基本的成像單元。在XMI文件中記錄了手機(jī)頁面的組件信息,Activity啟動(dòng)后加載XML文件顯示頁面。開發(fā)者在 Activity里編寫Java代碼綁定組件,給綁定的組件添加監(jiān)聽事件,在監(jiān)聽事件中實(shí)現(xiàn)功能和API的調(diào)用。
Android手機(jī)的Native APP開發(fā)環(huán)境配置主要包括兩個(gè)方面,一方面是配置程序設(shè)計(jì)語言的運(yùn)行環(huán)境,需要下載JDK工具包并安裝。另一方面是開發(fā)平臺(tái)的搭建,需要將安卓開發(fā)的ADT插件安裝到Eclipse 開發(fā)平臺(tái)上,并用SDK 工具配置好安卓虛擬機(jī)JDK工具包只需要點(diǎn)擊按照提示即可安裝,配置完環(huán)境變量成后在dos下輸人命令java - version,若顯示 java 的版本則安裝成功。ADT的安裝需要打開 Eclipse,選擇工具欄的安裝新軟件,輸入地址https://dl-ssl.google.com/android/eclipse/,進(jìn)行在線安裝。最后是虛擬機(jī)的創(chuàng)建,要將下載好的SDK包解壓并將路徑配置到 Eclipse 上,隨后在 eclipse 的工具欄打開AVD Manager下載適合自己的手機(jī)系統(tǒng)鏡像并更新到對應(yīng)的平臺(tái)工具。然后打開SDKManager 創(chuàng)建一臺(tái)虛擬機(jī),環(huán)境配置工作就基本完成了。
2.4WebAPP的環(huán)境配置及APP運(yùn)行
Web APP開發(fā)模式的環(huán)境搭建主要是服務(wù)器端的環(huán)境搭建,Web APP的使用一般是用手機(jī)自帶的瀏覽器訪問站點(diǎn),不需要下載安裝。這里選擇Java Web的應(yīng)用。Java Web的開發(fā)環(huán)境配置,需要下載Java EE 版的 Eclipse,安裝Tomcat服務(wù)器并配置Java運(yùn)行環(huán)境。僅僅靠 Java Web 技術(shù)開發(fā)出的頁面并不適合手機(jī)顯示,界面往往太大,操作起來也不方便。開發(fā)者可以使用成熟的移動(dòng) Web 開發(fā)框架來解決這個(gè)問題。現(xiàn)在已經(jīng)有了很多優(yōu)秀的移動(dòng) Web開發(fā)框架,例如Sencha TouchjQuery Mobile、DHTMLXTouch等。這些框架對HTML5和CSS3都有不錯(cuò)的支持。用JQuery Mobile界面奈材創(chuàng)建的 Web APP在安卓模擬器上運(yùn)行的效果。
出處 長春理工大學(xué)學(xué)報(bào)(自然科學(xué)版)
原標(biāo)題 移動(dòng)APP開發(fā)模式研究
作者 李莉 張超然 劉丹 李紀(jì)成
*請認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。