برنامه نویسی

Harmonyos Next Multi-Environment + چند کانال + خروجی مسیر سفارشی + نام سفارشی برنامه یک کلیک و بسته HAP

مقدمه

هنگام انجام توسعه موبایل ، به ناچار با سناریوهای زیر روبرو خواهید شد

  • تست: یک بسته آزمایشی به من بدهید ، من می خواهم یک عملکرد را آزمایش کنم ، اگر بیشتر مضطرب هستید ، لطفاً این کار را سریع انجام دهید

  • محصول: من یک سوال رسمی را به من داد.

  • رهبر تیم: آیا این بسته آزمایشی یا بسته رسمی در این دستگاه تست نصب شده است؟

  • محصول: آخرین بسته بندی محیط XX را برای من نصب کنید ، می خواهم از آن برای تظاهرات استفاده کنم ، فوری تر است

  • عملیات: شماره نسخه این بسته ای که برای من ارسال کرده اید چیست؟ کی آن را زدی؟ آیا این آخرین است؟ همه چیز فوری است ، لطفاً تأیید کنید

  • تست: چرا این اشکال در این نسخه برطرف نشده است؟
    (در نتیجه ، شما مدت طولانی جستجو کردید و دریافتید که نسخه موجود در تستر حاوی کد تعمیر نشده است)
    من نمی دانم چه زمانی بسته در آزمایش قرار داده شده است ، و شما نمی دانید چه زمانی برنامه در بسته قرار داده شده است.

  • محصول: دلایل تقاضای کسب و کار ، شما باید کیسه های جلیقه XX را روی قفسه ها قرار دهید و نیازی به تغییر تجارت اصلی نیست.
    فقط صفحه UI ، نام برنامه ، نماد برنامه را تغییر دهید

  • شما: برای مقابله با سناریوی فوق ، باید بعد از اتمام ساخت بسته ، نام را به صورت دستی تغییر داده و یک شماره و نشانگر را اضافه کنید و آن را در یک فهرست کپی کنید

سناریوهای فوق در شرکتهای کوچک و متوسط ​​بسیار رایج است.

اول ، ارائه ها

  • قوانین نامگذاری پرونده بسته: شماره نسخه + نام سفارشی + محصول + روز ماه ، ساعت ، دقیقه و دوم.
  • قوانین نامگذاری پرونده بسته بندی HAP: شماره نسخه + نام سفارشی + محصول + هدف + روز ماه + ساعت ، دقیقه و دوم.

کد منبع در پایان مقاله

شرح تصویر

شرح تصویر

در مرحله اولیه پروژه ، ثابت ها به طور کلی در یک کلاس خاص در پروژه تعریف می شوند تا بین محیط های رسمی یا محیط های آزمایش تمایز قائل شوند.
قبل از هر بسته ، مقدار ثابت را به صورت دستی اصلاح کرده و در آخر بسته محیط مربوطه را کامپایل کنید.

در شرایط عادی ، این عمل یک مشکل بزرگ نیست ، صرفه جویی در وقت ساده و آسان است

اما وضعیت واقعی این است که وقتی شما یک بسته رسمی را تهیه می کنید ، اتفاقات زیادی برای انجام آن دارید ، همیشه با موقعیت هایی روبرو خواهید شد که فراموش می کنید محیط را به عقب تغییر دهید ، که منجر به تبدیل آن به یک بسته آزمایش می شود.

یا شاید سایر همکاران به اشکال زدایی در محیط آزمایش تغییر کنند و به کد تجاری توجه نکردند و آن را ارسال کردند.
محیط محلی شما یک محیط رسمی است و پس از تکمیل بسته ، کد از راه دور را دریافت می کنید.

اگر با بسته ها مشکلی دارید ، می توانید در بخش نظرات پیام بگذارید تا ببینید که تجربه آنها جادویی تر است

من ابتدا این کار را می کنم ، من به طور تصادفی یک بسته آزمایشی ارسال کردم و آن را بصورت آنلاین ارسال کردم
در آن زمان ، کارهای زیادی برای انجام دادن وجود داشت.
در طول دوره تدوین ، من مشغول تنظیمات دیگر بودم و چندین تنظیم در محیط های مختلف وجود داشت.
بسته رسمی گردآوری شده شامل برخی از تنظیمات برای محیط آزمایش است

ابتدا اهداف و مشخصات بسته بندی را روشن کنید

  • هر بار که بسته ای از یک محیط متفاوت را تهیه می کنید ، نیازی به تغییر دستی پیکربندی نیست.
  • پس از اتمام تدوین ، بسته برنامه (یا بسته HAP) به صورت خودکار تغییر نام داده می شود ، با شماره نسخه ، محیط مربوطه را می توان با نام و نقطه زمان بسته بندی متمایز کرد
  • پس از اتمام تدوین ، پرونده بسته به طور خودکار در یک فهرست کپی می شود.
  • پارامترهای پیکربندی پروژه به طور یکنواخت در یک پرونده در فهرست root project تنظیم می شوند (برای مثال: local.properties)

اصلاح پویا پارامترها از طریق Hvigor

اولین قدم پیکربندی محصول است

محصولات مختلفی را برای مطابقت با محیط های مختلف تنظیم کنید
ساخت-پروفیل. json5 از فهرست اصلی پروژه را اصلاح کنید
محصول را در زیر ماژول ها اضافه کنید-> اهداف-> گره ApplyToproducts

محصول محیط مربوطه پیکربندی امضای مربوطه
AGC بسته های رسمی برای لیست بازار برنامه پیش فرض
agcdebug_official محیط رسمی بسته اشکال زدایی agc_debug
agcdebug_test محیط تست بسته اشکال زدایی agc_debug
شركت_فيكي محیط توزیع سازمانی شرکت
شركت_يايي شركت محیط آزمایش توزیع شرکت شرکت_xxx
xxx محیط xxx پیکربندی امضای XXX
کانال XXX کیف XXX محیط xxx پیکربندی امضای XXX

محصولات مختلف گاهی اوقات باید با تنظیمات مختلف امضا مطابقت داشته باشند
build-profile.json5

شرح تصویر

شرح تصویر

شرح تصویر

شرح تصویر

شرح تصویر

شرح تصویر

شرح تصویر

شرح تصویر

مرحله دوم پیکربندی hvigorfile.ts در فهرست اصلی پروژه است.

یک پرونده جدید HvigorfileConfig.ts ایجاد کنید
این پرونده عمدتا دو عملکرد زیر را پیاده سازی می کند ، و سپس آنها را در hvigorfile.ts وارد کرده و از آنها استفاده می کند

  • زمان فعلی را دریافت کنید و آن را قالب بندی کنید تا برگردد
  • محتویات پرونده local.properties را در فهرست اصلی پروژه دریافت کنید

پیکربندی hvigorfile.ts عمدتا توابع زیر را پیاده سازی می کند

  • محتوای مربوط به خروجی پیکربندی پرونده های محلی.
  • آدرس رابط محیط مربوطه را با توجه به محصول پیکربندی کنید
  • نام پرونده بسته برنامه را سفارشی کنید
  • زمان بسته بندی را پیکربندی کنید
  • پس از اتمام تدوین ، اطلاعات بسته بندی فعلی خروجی است ، مانند: زمان بسته بندی ، نام بسته ، شماره نسخه ، محصول
  • پس از اتمام تدوین ، به طور خودکار پرونده برنامه را در فهرست سفارشی کپی کنید.

در زیر پیکربندی کامل hvigorfile.ts است

/*导入OhosAppContext和OhosPluginId,用于动态修改app.json5配置中的版本号等信息*/
import { appTasks, OhosAppContext, OhosPluginId } from '@ohos/hvigor-ohos-plugin';

/*导入FileUtil,用于复制打包后的hap包和app包文件和判断文件路径是否存在*/
import { hvigor, FileUtil } from '@ohos/hvigor'

/*导入hvigorfileConfig.ts中的getLocalFileContent方法*/
import { getLocalFileContent } from './hvigorfileConfig.ts';

/*获取配置参数的全局对象*/
let localData = getLocalFileContent()
console.info("===========================config===========================")
/*输出local.properties配置参数*/
console.info(JSON.stringify(localData,null,1))

/*记录打包编译时,当前的product*/
let _productName = "def"
/*记录打包编译时,当前的包名*/
let _bundleName = "def"
/*记录当前打包编译时的版本号*/
let _versionCode = "def"
/*记录当前打包编译时的版本名称*/
let _versionName = "def"
/*记录当前打包编译时的app名称*/
let appName = ""

/*获取根项目的节点*/
let rootNode = hvigor.getRootNode()

/*为根节点添加一个afterNodeEvaluate hook 在hook中修改app.json5的内容并生效*/
hvigor.getRootNode().afterNodeEvaluate(rootNode => {
  /*获取app插件的上下文对象*/
  const appContext = rootNode.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;

  /*获取当前product*/
  const productName = appContext.getCurrentProduct().getProductName() ?? ""

  /*通过上下文对象获取从app.json5文件中读出来的obj对象*/
  const appJsonOpt = appContext.getAppJsonOpt();

  /*通过上下文对象获取从根目录build-profile.json5文件中读出来的obj对象,可用于修改app中的signingConfigs*/
  const buildProfileOpt = appContext.getBuildProfileOpt();

  /*修改AppScope/app.json5中的版本号-数据来源于local.properties中的参数配置*/
  if (productName.toLowerCase().indexOf("agc") >= 0) {
    /*上架应用市场的版本号*/
    appJsonOpt['app']['versionCode'] = localData["versionCode"]
    appJsonOpt['app']['versionName'] = localData["versionName"] + ""
  } else {
    /*企业分发的版本号或者各个马甲包的版本号*/
    appJsonOpt['app']['versionCode'] = localData["companyVersionCode"]
    appJsonOpt['app']['versionName'] = localData["companyVersionName"] + ""
  }

  /*将appJsonOpt对象设置回上下文对象以使能到构建的过程与结果中*/
  appContext.setAppJsonOpt(appJsonOpt);

  /*保存打包的相关信息,用于自定义app包名称*/
  _productName = productName
  _versionCode = appJsonOpt['app']['versionCode']
  _versionName = appJsonOpt['app']['versionName']

  /*获取当前编译时生成的时间*/
  /*getLocalFileContent()方法被调用时生成的时间*/
  let timeStr = localData["buildTime"]

  /*遍历工程目录下的build-profile.json5文件中app-->products节点的数据*/
  const products = buildProfileOpt['app']['products']
  for (let i = 0; i < products.length; i++) {
    const item = products[i]
    /*设置打包时间*/
    /*buildOption,arkOptions,buildProfileFields,buildTime需要在build-profile.json5提前定义出来*/
    item?.["buildOption"]?.["arkOptions"]?.["buildProfileFields"]?.["buildTime"] = timeStr

    let output=item["output"]
    if (output && output["artifactName"]) {
      let tempProductName = item["name"]
      if (tempProductName) {
        /*设置正式环境或者测试环境地址*/
        if(tempProductName.toLowerCase().indexOf("test")){
          /*如果product含有test,则配置为测试环境地址*/
          item?.["buildOption"]?.["arkOptions"]?.["buildProfileFields"]?.["url"] = localData["devUrl"]
        }else{
          item?.["buildOption"]?.["arkOptions"]?.["buildProfileFields"]?.["url"] = localData["OfficialUrl"]
        }

        /*设置打包输出文件的自定义名称*/
        /*格式为:版本号+自定义名字+product+时间.app*/
        const resultName = output["artifactName"] = _versionCode + "Harmony_" + tempProductName + "_" + timeStr

        if (item["name"] == productName) {
          /*如果是当前product,记录打包时的app文件名和bundleName*/
          appName = resultName
          _bundleName = item["bundleName"]
          if(!_bundleName){
            /*如果没有在build-profile.json5设置bundleName,直接获取app.json5中的bundleName*/
            _bundleName=appJsonOpt['app']['bundleName']
          }
        }
      }
    }
  }
  /*将buildProfileOpt对象设置回上下文对象以使能到构建的过程与结果中*/
  appContext.setBuildProfileOpt(buildProfileOpt);

})

/*添加一个构建结束的回调函数(打包完成后,复制app文件到新目录+输出相关信息)*/
hvigor.buildFinished(buildResult => {
  const path = require('path');
  const appContext = rootNode.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;
  /*获取.app文件所在目录*/
  let dirPath = appContext.getBuildProductOutputPath()

  /*获取.app文件完整路径*/
  let appPath = path.join(dirPath, appName + ".app")
  // console.info("======appPath=====" + appPath)

  /*如果.app文件存在,就复制到新目录下*/
  if (FileUtil.exist(appPath)) {
    const parentPath = path.join(path.dirname(__filename), "_app")

    /*如果工程的根目录没有_app目录,则创建*/
    FileUtil.ensureDirSync(parentPath)

    /*目标文件路径*/
    const destPath = path.join(parentPath, appName + ".app")
    console.info("======destPath=====" + destPath)

    /*将打包完成的.app文件复制到项目根目录下的_app目录中*/
    FileUtil.copyFileSync(appPath, destPath)
  }
  /*复制完成后,打印当前编译出的包信息*/
  console.info("================================================")
  console.info("buildTime  :" + localData["buildTime"])
  console.info("productName:" + _productName)
  console.info("bundleName :" + _bundleName)
  console.info("versionCode:" + _versionCode)
  console.info("versionName:" + _versionName)
})


export default {
  system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  plugins: []         /* Custom plugin to extend the functionality of Hvigor. */
}

مرحله 3 برای پیکربندی ماژول ورود

اگر بسته های برنامه تولید شده توسط محصولات مختلف را انتخاب کنیم ، می خواهیم نمادهای مختلف برنامه ، مجموعه کد منبع و سایر سبک های منابع را پیاده سازی کنیم ، بنابراین باید به ترتیب با محصولات مختلف Android (شبیه به پیکربندی چند کانال Android) پیکربندی کنیم.

ابتدا ، دو دایرکتوری Main_Company و Main_def را تعریف کنید و به ترتیب در این دو دایرکتوری ، نسخه منبع تنظیمات ETS و منابع فهرست منابع را پیکربندی کنید.

شرح تصویر

hvigorfile.ts ماژول ورود را پیکربندی کنید

پیکربندی hvigorfile.ts در ماژول ورودی عمدتا توابع زیر را پیاده سازی می کند

  • نام پرونده بسته پیکربندی HAP را سفارشی کنید
  • زمان بسته بندی HAP را پیکربندی کنید
  • پس از اتمام تدوین ، به طور خودکار پرونده HAP را در فهرست سفارشی کپی کنید.

در زیر پیکربندی کامل hvigorfile.ts است

import { hapTasks, OhosHapContext, OhosPluginId } from '@ohos/hvigor-ohos-plugin';
import { hvigor, FileUtil, getNode } from '@ohos/hvigor'
import { readFileSync } from 'fs';
import { appTasks, OhosAppContext } from '@ohos/hvigor-ohos-plugin';
/*导入变量为config的对象,获取打包配置参数*/
import { config } from '../hvigorfileConfig'

/*通过当前目录的hvigorfile.ts获取节点*/
const rootNode = getNode(__filename);

/*通过key-value形式记录hap包名称,key=Module Target,value=hap的文件名*/
let hapNameMap = {}

/*记录product,打包输出时用于自定义hap包名称*/
let _productName = "def"

/*为节点添加一个afterNodeEvaluate hook 在hook中修改该目录下的build-profile.json5的内容并使能*/
rootNode.afterNodeEvaluate(node => {
  const path = require('path');
  const parentDir = path.dirname(path.dirname(__filename));

  /*通过项目根目录的hvigorfile.ts脚本文件路径获取对应的节点对象*/
  const appNode = getNode(path.join(parentDir, "hvigorfile.ts"))

  const appContext = appNode.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;
  const appJsonOpt = appContext.getAppJsonOpt();

  /*获取当前product,用于自定义hap文件名*/
  _productName = appContext.getCurrentProduct().getProductName() ?? ""

  /*获取此节点使用插件的上下文对象 此时为hap插件 获取hap插件上下文对象*/
  const hapContext = node.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext;

  /*通过上下文对象从entry目录build-profile.json5文件中读出来的obj对象*/
  const buildProfileOpt = hapContext.getBuildProfileOpt();
  const targets = buildProfileOpt['targets']

  /*获取编译时生成的时间,[在hvigorfileConfig.ts文件中的getLocalFileContent()方法中生成]*/
  /*因为工程根目录下hvigor先于module目录下hvigor执行,所以这里的buildTime直接通过变量config获取*/
  /*记得导入import { config } from '../hvigorfileConfig'*/
  let timeStr = config["buildTime"]

  /*因为工程根目录下hvigor先于module目录下hvigor执行,同理可得:此时获取的versionCode是被动态修改之后的值*/
  let versionCode = appJsonOpt["app"]["versionCode"]

  /*遍历build-profile.json5中targets节点下的内容*/
  for (let i = 0; i < targets.length; i++) {
    let output = targets[i]["output"]
    if (output && output["artifactName"]) {
      /*获取target name,自定义hap名称需要*/
      let tempName = targets[i]["name"]
      if (tempName) {

        /*设置自定义hap名称,版本号+自定义名+product+target+打包时间.hap*/
        const resultName =output["artifactName"] = versionCode + "Harmony" + "_"+ _productName + "_" + tempName + "_" + timeStr

        /*key-value形式保存hap文件名,key=target name*/
        hapNameMap[tempName] = resultName + ".hap"
        // console.info("===========" + resultName)
      }
    }
  }
  //console.info("===hapNameMap========" + JSON.stringify(hapNameMap))
  /*将buildProfileOpt对象设置回上下文对象以使能到构建的过程与结果中*/
  hapContext.setBuildProfileOpt(buildProfileOpt);
})


/*添加一个构建结束的回调函数(打包完成后,复制hap文件到新目录)*/
hvigor.buildFinished(buildResult => {

  const hapContext = rootNode.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext;
  hapContext?.targets((target: Target) => {

    /*通过target name获取对应的hap文件*/
    let hapName = hapNameMap[target.getTargetName()]

    //console.info(target.getTargetName()+"======target=====" +hapName)
    if (!hapName) {
      return
    }
    const path = require('path');

    /*获取编译完成后的hap包所在路径*/
    const dirPath = target.getBuildTargetOutputPath();

    /*得到hap包完整路径*/
    let hapPath = path.join(dirPath, hapName)

    //console.info("======hapPath=====" +hapPath)
    if (FileUtil.exist(hapPath)) {
      /*复制到目标目录*/
      const parentPath = path.join(path.dirname(path.dirname(__filename)), "_hap")

      /*如果目标目录不存在就创建*/
      FileUtil.ensureDirSync(parentPath)

      /*定义目标文件路径*/
      const destPath = path.join(parentPath, hapName)

      /*复制hap文件到目标路径*/
      FileUtil.copyFileSync(hapPath, destPath)
    }

  })
})


export default {
  system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  plugins: []         /* Custom plugin to extend the functionality of Hvigor. */
}

در بالا پیکربندی کامل است.

کاملاً پیچیده به نظر می رسد ، اما تا زمانی که اسناد رسمی را با دقت بخوانید و چند بار دیگر آنها را تمرین کنید ، بسیار ساده خواهد شد.

اثر عملکرد واقعی

شرح تصویر

مشکلات احتمالی

برخی از توسعه دهندگان ممکن است با پیکربندی یک محصول برای مطابقت با چندین هدف ماژول ، چندین کانال یا محیط های مختلف را پیاده سازی کنند (DeviceType یا DistributionFilter/Distrofilter یکسان هستند).
اگر مستقیماً در حال اجرا باشد ، مشکلی وجود ندارد ، اما پس از وارد کردن بسته برنامه ، خطایی را گزارش خواهید کرد

همانطور که در شکل زیر نشان داده شده است:

شرح تصویر

توضیح رسمی:

شرح تصویر

بنابراین ، توصیه می شود که آن را از طریق چندین محصول پیکربندی کنید ، یا devicetypes های مختلف را در همان ماژول پیکربندی کنید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا