整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

企业级UI自动化平台—添加脚本

企业级UI自动化平台—添加脚本

前章节我们讲了部门部门管理,项目管理,这节我们继续实现添加脚本。

那么关联关系就是,在部门对应的项目中添加多个脚本。实现这个模块我们需要完成三步操作。

1:完成数据表的创建及与部门表和项目表之间的关联关系

2:完成前端界面布局

3:完成对应后端接口的开发

首先完成第一步,数据表的创建,既然是添加脚本,那么我们能够想到的字段就有:

脚本名称,上传的图片名称,操作的类型(点击、滑动、校验...),执行的步骤,操作类型的次数,生成脚本的类型。

对应如下:

ui自动化平台添加脚本管理模块:

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;

各个字段看名字对应即可。里面定义了部门表和项目表的主外键关联关系,之前章节详细讲过,这里不在啰嗦。

接下来我们实现前端部分,直接看下前端需要的功能界面。


完整逻辑为:

  1. 加载用户界面,界面中包含选择部门、选择项目、脚本类型和脚本名称的输入项,输入项选择后,用户可以上传图片。
  2. 上传图片后,根据这张图片,可以选择执行的动作:点击、校验或是否存在。这三种动作分别对应到按钮:“点击”,“校验”,“是否存在”。
  3. 选择动作后,图片数据被保存,并且动作按钮会被禁用,直到上传下一张图片。
  4. 当所有图片上传并选择动作完成后,用户可以点击“生成测试脚本”按钮生成脚本。
  5. 在生成脚本的过程中,显示加载动画和提示,完成后隐藏加载动画,并提示测试脚本已经生成。

在脚本的实现中使用了Ajax进行异步通信,主要进行了以下操作:

  • 保存图片数据
  • 上传图片
  • 获取部门信息和对应的项目信息
  • 生成测试脚本

在获取部门信息和项目信息时,使用GET方法接收服务器返回的数据,并更新选择部门和项目的下拉列表;在保存图片数据、上传图片和生成测试脚本时,使用POST方法将数据发送到服务器。

完整实现为:

<!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">选择项目:</label>
<select id="select-project">
  <option value=""> -- 请选择项目 -- </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">点击</button>
<button id="assert_btn">校验</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); // 添加这一行以查看响应内容
      },
      error: function (err) {
        console.error("Image data save failed");
        console.error(err); // 添加这一行以查看错误内容
      },
    });
  }
  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("请输入点击次数", "1");
    repeatCount=parseInt(repeatCount);
    while(isNaN(repeatCount) || repeatCount<1) {
      alert("无效的输入!至少点击一次。")
      repeatCount=prompt("请输入点击次数", "1");
      repeatCount=parseInt(repeatCount);
    }
    $("#action_prompt").text("已选择点击操作");

    if (step_number > 0) {
      var imgCaption=`点击第${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 设置为 true
    //save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), `/uploads${res.image_url}`); // 传递 ftp_path 参数
  });
  //新函数
  $("#image_upload").on("click", function () {
    this.value=null;
  });
  //分割线
  $("#assert_btn").on("click", function () {
    selected_action='assert';
    var repeatCount=1; // 为assert操作设置repeatCount为1
    $("#action_prompt").text("已选择校验操作");

    if (step_number > 0) {
      var imgCaption=`校验第${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 设置为 true
    //save_image_data(imgFile.name, selected_action, step_number, $("#script_name").val(), `/uploads${res.image_url}`); // 传递 ftp_path 参数
  });
  $("#exist_btn").on("click", function () {
    selected_action='exist';
    var repeatCount=prompt("请输入循环点击次数", "1");
    repeatCount=parseInt(repeatCount);
    while(isNaN(repeatCount) || repeatCount<1) {
      alert("无效的输入!次数至少为一。")
      repeatCount=prompt("请输入循环点击次数", "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; // 响应中的图片 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(); // 上传新图片后启用点击和校验按钮
        $("#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(); // 添加这一行代码
        // 在这里设置 isImageUploaded 为 true
        isImageUploaded=true;
      },
      error: function (err) {
        console.error("图片上传失败", err);
      }
    });
  });
  $("#generate_script_btn").on("click", function () {
    if (!isImageUploaded) {
      alert("未上传任何文件!");
      return;
    }
    if (!isActionPerformed) {
      alert("请先选择当前图片要执行的动作!");
      return;
    }

    // 添加的部分: 点击生成后隐藏主页面,显示加载动画和提示
    $('#whole-body').hide();
    $('#loading').show();
    // 更新此部分,使用正确的 ID
    console.log("项目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) {
        //接收到返回数据后先延时3秒
        setTimeout(function () {
          //隐藏加载动画和提示,显示生成成功提示,并显示主页面
          $('#loading').hide();
          $('#script_gen_prompt').show(); //显示生成成功提示
          alert('测试脚本已生成!');
          setTimeout(function(){
            $('#script_gen_prompt').hide();
            $('#whole-body').show();  //重新显示主页面
          }, 2000);  //2000ms后隐藏成功提示,并重新显示主界面
        }, 3000);   // 延时 3000 ms 显示生成完成提示
      },
      error: function (err) {
        setTimeout(function() {
          $('#loading').hide();
          alert("生成测试脚本失败");
          $('#whole-body').show();  //重新显示主页面
        }, 3000);   // 延时 3000 ms 显示失败提示
        console.error(err)
      }
    });
  });
  <!-- 在现有的 <script> 标签内添加以下代码 -->
  $(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) {
        // 发送请求获取部门对应项目
        $.get("/get-projects?department_id=" + departmentId, function (data) {
          var projects=data.projects;
          $("#select-project").empty();
          $("#select-project").append("<option value=''> -- 请选择项目 -- </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>

对应后端生成脚本的逻辑为:

@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('这里出现imagesdata数据')
            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")
                # # 先构造要写入的子进程调用命令字符串
                # 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  # 接收重复次数
                    if action_type=='click':
                        if int(repeatCount) > 1:
                            f.write(f"for _ in range({repeatCount}):")  # 添加循环语句用于重复点击
                            f.write(f"\n    if exists(Template(r'{image_name}')):\n")  # 添加循环语句用于重复点击
                            f.write(f"        sleep(1.0)\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)})


综上:添加脚本的功能逻辑开发完毕。

下一节:执行脚本逻辑开发。敬请期待

in10手机版怎么运行安卓APP应用?微软官方并没有给出正规的方法,但民间破解组织已经成功让Win10手机版安装安卓APP应用。据外媒报道,一西班牙开发者Antonio de la Iglesia写出了一个在电脑上使用的神器,只要连接Win10手机,就可以一键将安卓APP应用装到Win10手机上。

软件名称:Win10 Mobile APK Installer(Win10手机版装安卓应用神器)
软件版本:1.1
软件大小:10

能手机的应用开发也随着手机的发展成为一个火热的行业,有着不错的收入。为了方便而有效的进行开发选择一套合适的开发模式是十分重要的。智能手机APP的开发模式可以分为三类分别是Native APPWeb APP和Hybrid APP开发模式。


1三种移动APP开发模式



1. Native APP


NativeAPP开发模式,即本地开发模式,又称为传统型开发模式。这种开发模式基于移动终端的操作系统进行开发,可以良好的利用系统的硬件资源。其缺点也是显而易见的,其应用只能在一种移动终端的系统中安装使用,而且由于APP对硬件的依赖,应用升级会比较麻烦。


1.2 Web APP

Web APP开发模式基本依靠网络技术实现。其APP 是一个针对手机优化后的 Web站点,优点是实现了跨平台,而且对硬件几乎没有依赖,开发周期短。缺点也很明显,APP 对网络的依赖很大,数据基本都来自服务器,因此网络状况会直接影响用户体验。在没有网络的情况下,APP的功能基本不能使用。而且APP无法调用手机的硬件API功能受到一定的限制。


1.3 Hybrid APP


HybridAPP开发模式即混合开发模式。这种开发模式使用第三方的跨平台开发框架,将一种语言开发出应用兼容到不同的移动设备上。这样开发者就可以用Java Web技术或另一种不针对某具体系统的第三方的开发技术,实现应用的功能。APP同时具有了跨平台性和不错的硬件资源调用能力。


2三种开发模式的环境配置及APP运行



2.1 Hybrid APP的PhoneGap开发框架


Nitobi公司(现在已被Adobe公司收购)推出了基于Web技术的移动解决方案PhoneGap,这一方案在其官网上的定义是“可以使用Web技术编写手机本地应用程序的Htm15应用程序平台”。简单来说PhoneGap是一套基于Htm15的移动应用开发框架6PhoneGap开发框架成功将Java Web开发技术应用到了移动设备的开发上。已经具备 Java Web开发能力的开发者们可以使用熟知的HTML、CSS和JavaScript来开发手机APP。


PhoneGap 开发框架支持包括IOS、Android、Windows Phone在内的多种手机平台。开发者通过PhoneGap 提供的插件可以调用API使用摄像头联系人、地理定位等功能。PhoneGap 的使用是免费的它的兼容性强而且开发成本低。但PhoneGap本身也有不少缺点,它的运行需要依靠移动设备具有内置的浏览器引擎WebKit,PhoneGap的APP运行速度较慢,硬件调用能力也不如Native APP。安卓手机使用 PhoneGap 框架开发的Hy-bridAPP的基本结构。


2.2PhoneGapAPP开发环境搭建


开发PhoneGap APP可以使用Dreamweaver工具来制作Java Web页面,并使用 PhoneGap Build(PhoneGap的在线编译云服务)生成安装包。但如果想测试某一平台的硬件功能,就必须配置对应的开发环境,再进行编译和测试。例如安卓开发的SDK工具包提供了手机模拟器,可以在PC机上进行APP测试。

下面介绍在配置好安卓开发环境后,如何使用PhoneGap框架开发应用。首先,下载好 PhoneGap的工具包,打开 Eclipse后,按照创建步骤,新建一个安卓应用项目。复制cordova-xxxjar 文件到项目中的lib 文件夹下,右击该jar 文件添加引用。然后在项目的assets 目录下新建文件,复制 PhoneGap 工具包内的cordovajs 文件到个文件夹下,并创建Ja-va Web运行的级联样式表,Html文件和其他is 文件。创建完成后的文件结构。


另外,安卓的应用需要依靠Activity启动。要让Activity启动后,加载html页面并运行JavaScript代码,需要对Activity进行修改。改动包括两步,要将Activity 继承的类改为 DroidGap,导人需要的cor-dova包。然后将 setContentView(R.layout.activi-ty-main)改为 super.loadUrl(“file://android-asset/www/indexhtml”)其中www是新建的文件夹的名字index.html是该文件夹下的html文件

编写好文件夹下的html和is代码生成apk安装到安卓手机上就可以运行了。


2.3NativeAPP的开发环境配置及APP运行


对于Native APP发模式,不同的手机系统都有套成熟的开发框架与对应的开发语言”。例如,苹果手机的APP需要用Object-C 编写,AndroidAPP主要用Java开发,而属于微软公司的Win-dows Phone应用主要使用C#语言开发。如图4 所示是安卓手机的Native APP的基本结构。其中Activity可以理解为活动窗口或者是一个单独的页面,它是与用户交互的最基本的成像单元。在XMI文件中记录了手机页面的组件信息,Activity启动后加载XML文件显示页面。开发者在 Activity里编写Java代码绑定组件,给绑定的组件添加监听事件,在监听事件中实现功能和API的调用。


Android手机的Native APP开发环境配置主要包括两个方面,一方面是配置程序设计语言的运行环境,需要下载JDK工具包并安装。另一方面是开发平台的搭建,需要将安卓开发的ADT插件安装到Eclipse 开发平台上,并用SDK 工具配置好安卓虚拟机JDK工具包只需要点击按照提示即可安装,配置完环境变量成后在dos下输人命令java - version,若显示 java 的版本则安装成功。ADT的安装需要打开 Eclipse,选择工具栏的安装新软件,输入地址https://dl-ssl.google.com/android/eclipse/,进行在线安装。最后是虚拟机的创建,要将下载好的SDK包解压并将路径配置到 Eclipse 上,随后在 eclipse 的工具栏打开AVD Manager下载适合自己的手机系统镜像并更新到对应的平台工具。然后打开SDKManager 创建一台虚拟机,环境配置工作就基本完成了。


2.4WebAPP的环境配置及APP运行


Web APP开发模式的环境搭建主要是服务器端的环境搭建,Web APP的使用一般是用手机自带的浏览器访问站点,不需要下载安装。这里选择Java Web的应用。Java Web的开发环境配置,需要下载Java EE 版的 Eclipse,安装Tomcat服务器并配置Java运行环境。仅仅靠 Java Web 技术开发出的页面并不适合手机显示,界面往往太大,操作起来也不方便。开发者可以使用成熟的移动 Web 开发框架来解决这个问题。现在已经有了很多优秀的移动 Web开发框架,例如Sencha TouchjQuery Mobile、DHTMLXTouch等。这些框架对HTML5和CSS3都有不错的支持。用JQuery Mobile界面奈材创建的 Web APP在安卓模拟器上运行的效果。


出处 长春理工大学学报(自然科学版)

原标题 移动APP开发模式研究

作者 李莉 张超然 刘丹 李纪成