MoreRSS

site iconobaby@mars修改

定居青岛,女,闺蜜圈开发者。AI界的小学生,逆向分析工程师,非专业APP开发者。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

obaby@mars的 RSS 预览

一键资源地址替换工具 — UniApp发布小程序体积精简

2025-12-14 09:22:43

闺蜜圈 小程序版本一直落后很多,之所以没更新主要的问题在于 uni 打包小程序之后体积太大了,体积大一个原因是组件压缩到了 vendor.js 中,一个文件就到了 1.1m(主包限制大小2048kb)。

 

图片文件也有1m 左右,再加上其他的一些组件,主包的体积到了 4 m 左右。

虽然已经启用了分包,但是没啥效果,包括代码压缩组,所以最后发版的小程序靠的是压缩图片文件。

让 cursor 尝试写了个优化代码出了各种错误,最后决定采用将图片资源直接网络加载的方式来缩减体积,这样1m 的图片资源就不需要打包在本地资源中了。

本地资源文件都是通过 127 的地址来加载的,将资源移动到服务器之后,修改小程序资源地址之后:

此时加载的图片可以看到是从 cdn 加载了,

并且资源包大小已经基本可以忽略不计了

要实现上看的效果也简单,将static 目录上传到服务器,执行修改工具,修改资源路径删除本地资源。对于 tabbar 的图片不能通过网络加载,需要添加到排除列表,hbuilder 发行小程序后执行修改工具。此时基本就 ok 了,2.06mb,缺的那一点稍微弄一下也就解决了。

工具代码:

/**
 * 将打包后产物里的 /static/** 路径替换为 CDN 前缀。
 * 目前是替换为 https://cdn.guimiquan.cn/ 前缀。
 * 使用方法:
 *   1) 先发行构建微信小程序,生成 unpackage/dist/build/mp-weixin 或 unpackage/dist/dev/mp-weixin
 *   2) 执行:node cdn-rewrite.js [--mode=dist|dev] [--remove-static]
 *   3) 在 dist 内搜索或用开发者工具 Network 确认已变成 CDN 域名
 * 
 * 参数说明:
 *   --mode=dist   : 处理生产构建目录 (默认)
 *   --mode=dev    : 处理开发构建目录
 *   --remove-static : 删除本地 static 目录(排除配置的目录和文件)
 * By: obaby
 * Date: 2025-12-12
 * Version: 1.0.0
 * https://oba.by
 * https://h4ck.org.cn
 * ------------------------------------------------------------
 */
const fs = require('fs');
const path = require('path');

// CDN 根路径,末尾带 /
const CDN = 'https://cdn.guimiquan.cn/';

// 路径配置
const DIST_ROOT = path.resolve(__dirname, 'unpackage/dist/build/mp-weixin');
const DEV_ROOT = path.resolve(__dirname, 'unpackage/dist/dev/mp-weixin');

// 处理的文件类型
const ALLOWED_EXTS = new Set(['.js', '.json', '.wxss', '.css', '.wxml', '.html']);
// 跳过的文件(app.json 里的 tabBar iconPath 不允许 http/https)
const SKIP_FILES = new Set(['app.json']);

// 排除删除的目录(相对于 static 目录)
const EXCLUDE_DIRS = [
  'tabbar_icons',  // tab栏图标必须使用本地文件
  // 可以在这里添加更多需要排除的目录
];

// 排除删除的文件(相对于 static 目录,支持 glob 模式或完整路径)
const EXCLUDE_FILES = [
  // 可以在这里添加需要排除的文件,例如:
  // 'tabbar_icons/**/*',
  // 'custom-icon.png',
  'icons/record_love_add.png',
  'icons/calendar_icon_project_start.png',
  'icons/calendar_icon_project_end.png',
  'icons/calendar_icon_project_start_invalid.png',
  'apk_emotion_2.png',
  'apk_emotion_1.png',
  'apk_emotion_38.png',
  'apk_emotion_9.png',
  'apk_emotion_28.png',
];

// 解析命令行参数
function parseArgs() {
  const args = {
    mode: 'dist',  // 默认使用 dist
    removeStatic: false
  };

  process.argv.slice(2).forEach(arg => {
    if (arg.startsWith('--mode=')) {
      const mode = arg.split('=')[1];
      if (mode === 'dist' || mode === 'dev') {
        args.mode = mode;
      } else {
        console.warn(`警告: 未知的模式 "${mode}", 使用默认模式 "dist"`);
      }
    } else if (arg === '--remove-static') {
      args.removeStatic = true;
    }
  });

  return args;
}

// 获取目标根目录
function getTargetRoot(mode) {
  return mode === 'dev' ? DEV_ROOT : DIST_ROOT;
}

function replaceInFile(file, targetRoot) {
  const source = fs.readFileSync(file, 'utf8');
  let output = source;

  // 处理 JS/JSON 中的字符串形式 "static/xxx" 或 "/static/xxx"
  output = output.replace(/(["'])\/?static\//g, `$1${CDN}static/`);

  // 处理样式中的 url(static/xxx) 或 url('/static/xxx')
  output = output.replace(/url\(\s*(['"]?)\/?static\//g, `url($1${CDN}static/`);

  if (output !== source) {
    fs.writeFileSync(file, output, 'utf8');
    console.log('rewrote', path.relative(targetRoot, file));
  }
}

function walk(dir, targetRoot) {
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      walk(full, targetRoot);
    } else if (ALLOWED_EXTS.has(path.extname(entry.name)) && !SKIP_FILES.has(entry.name)) {
      replaceInFile(full, targetRoot);
    }
  }
}

// 检查路径是否应该被排除
function shouldExclude(filePath, staticRoot) {
  const relativePath = path.relative(staticRoot, filePath);
  const normalizedPath = relativePath.replace(/\\/g, '/'); // 统一使用 / 分隔符

  // 检查是否在排除目录中
  for (const excludeDir of EXCLUDE_DIRS) {
    if (normalizedPath.startsWith(excludeDir + '/') || normalizedPath === excludeDir) {
      return true;
    }
  }

  // 检查是否匹配排除文件模式
  for (const excludeFile of EXCLUDE_FILES) {
    // 简单的 glob 匹配(支持 * 和 **)
    const pattern = excludeFile.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*');
    const regex = new RegExp('^' + pattern + '$');
    if (regex.test(normalizedPath)) {
      return true;
    }
    // 精确匹配
    if (normalizedPath === excludeFile) {
      return true;
    }
  }

  return false;
}

// 删除本地 static 目录(排除指定目录和文件)
function removeLocalStatic(targetRoot) {
  const staticDir = path.join(targetRoot, 'static');

  if (!fs.existsSync(staticDir)) {
    console.log('static 目录不存在:', staticDir);
    return;
  }

  let deletedCount = 0;
  let skippedCount = 0;

  function removeRecursive(dir) {
    const entries = fs.readdirSync(dir, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);

      if (shouldExclude(fullPath, staticDir)) {
        skippedCount++;
        console.log('跳过(排除):', path.relative(staticDir, fullPath));
        continue;
      }

      if (entry.isDirectory()) {
        removeRecursive(fullPath);
        // 目录为空时才删除
        try {
          fs.rmdirSync(fullPath);
          deletedCount++;
        } catch (err) {
          // 目录不为空,忽略错误
        }
      } else {
        fs.unlinkSync(fullPath);
        deletedCount++;
      }
    }
  }

  removeRecursive(staticDir);

  // 如果 static 目录为空,尝试删除它
  try {
    const remaining = fs.readdirSync(staticDir);
    if (remaining.length === 0) {
      fs.rmdirSync(staticDir);
      console.log('已删除空的 static 目录');
    } else {
      console.log(`static 目录保留,包含 ${remaining.length} 个排除项`);
    }
  } catch (err) {
    // static 目录已被删除或无法访问
  }

  console.log(`删除完成: 已删除 ${deletedCount} 项, 跳过 ${skippedCount} 项`);
}

// 主函数
function main() {
  const args = parseArgs();
  const targetRoot = getTargetRoot(args.mode);

  console.log(`模式: ${args.mode}`);
  console.log(`目标目录: ${targetRoot}`);

  if (!fs.existsSync(targetRoot)) {
    console.error('目标目录不存在:', targetRoot);
    process.exit(1);
  }

  walk(targetRoot, targetRoot);
  console.log('路径替换完成');

  if (args.removeStatic) {
    console.log('\n开始删除本地 static 目录...');
    removeLocalStatic(targetRoot);
  } else {
    console.log('\n提示: 使用 --remove-static 参数可删除本地 static 目录');
  }

  console.log('\n完成');
}

main();

使用方法,放到项目根目录下,打包之后执行:

node cdn-rewrite.js [--mode=dist|dev] [--remove-static]

 

道阻且长

2025-12-10 13:44:31

都道:“一步错,步步错”。虽然有时候是无心之失,却也在不经意间制造了太多的问题。就像撒了个谎,后面需要无数的谎言来解释当初这个谎言。最开始开发 app 的时候,对于所谓的前段并没那么清晰深入的理解,毕竟之前做 ios 和安卓的开发还是基于 oc 和 java 来搞。

之前虽然也看过不少 uni 的项目,但是真正开始的时候。当时还是选择了 vue2 的框架,虽然那时候3 也依然有些规模。鉴于当时找到的很多组件都是 vue2 的不支持 3,所以开始的时候就入了这个坑,直接基于 2 的各种组件搭起来了现在的这个闺蜜圈的项目。

当然,后续再开发过程中也发现了很多问题,虽然是 vue2 的各种组件,然而很多满足不了自己的需求,在不断的开发过程中也在不断的修改这些组件,甚至很多组件到现在已经改的完全不是原来的样子了。重写了大量的代码,也修复了很多组件的问题。所谓的生态,国内的生态环境并不是看到的那么好。各种插件市场非常活跃,插件也非常多。但是实际上出了问题大概率得不到作者的解答,只能自己去读代码,理解逻辑之后重写有问题的部分。

再后来,鸿蒙 next 横空出世,uni 也承诺会支持鸿蒙系统。当然后来也确实是支持了,不过仅支持 vue3 的项目的鸿蒙系统编译。于是这原来的 vue2 的项目就面临着一次大版本的升级和兼容处理。在开发了一年之后,如果重新搭架子从 vue3 重写,成本固然是太高了,不在可接受的范围之内。最终的方案是直接将 vue2 的项目迁移到 vue3。

在去年uni 开放鸿蒙的体验版本兼容编译之后,就尝试将项目迁移到 vue3,不过那时候仅限于能编译运行。可以登录到首页,而其他的页面全部都没处理。在这次鸿蒙的技术支持介入之后,也是下定决心将项目迁移到 vue3 进一步迁移到原生鸿蒙。到原生鸿蒙才是最终目的。

不过在迁移过程中也发现一些问题:

1.导航栏 button 无效:

HarmonyOS 基于uniapp的方式开发HarmonyOS应用时候,通过app-plus:titleNView 配置页面右上角按钮未生效(API12+)
【问题描述】:HarmonyOs基于uniapp的方式开发HarmonyOS应用的时候,通过app-plus:titleNview配置页面右上角按钮不生效

【问题现象】:使用app-plus:titleNview配置导航栏右上角button不显示

【版本信息】:hbuilder 版本4.8.5、DevEco Studio 6.0.1 Release

【复现代码】:{

  "path": "pages/besties",

  "style": {

    "navigationBarTitleText": "XXX",

    "navigationBarBackgroundColor": "#ff4f87",

    "app-plus": {

      "bounce": "none",

      "titleNView": {

        "buttons": [{

          "fontSize": "18px",

          "text": "+",          // 直接写加号字符

          "color": "#FFFFFF"

        }]

      }

    }

  }

}

官方给的解决方案:

在基于 UniApp 开发 HarmonyOS 应用时,app-plus:titleNView配置未生效的核心原因是:app-plus是 UniApp 针对 Android/iOS 端设计的原生导航栏配置,HarmonyOS 平台的导航栏实现逻辑与 Android/iOS 不同,因此该配置暂未适配 HarmonyOS 平台。

解决方案:使用「自定义导航栏」替代(推荐,跨平台兼容)
由于 HarmonyOS 不支持 UniApp 的titleNView配置,建议通过隐藏原生导航栏 + 自定义导航栏组件的方式实现右上角按钮,同时兼容跨平台场景。

步骤 1:隐藏原生导航栏
在pages.json中,给目标页面的style添加navigationStyle: "custom",隐藏原生导航栏:

json

{
  "path": "pages/besties",
  "style": {
    "navigationBarTitleText": "XXX",
    "navigationBarBackgroundColor": "#ff4f87",
    "navigationStyle": "custom", // 隐藏原生导航栏
    "app-plus": {
      "bounce": "none"
    }
  }
}
步骤 2:自定义导航栏组件(实现右上角按钮)
在页面中手动编写导航栏布局,包含标题和右上角按钮,示例代码(Vue3 语法):

vue

<template>
  <!-- 自定义导航栏 -->
  <view class="custom-nav-bar" :style="{backgroundColor: '#ff4f87'}">
    <!-- 标题 -->
    <view class="nav-title">XXX</view>
    <!-- 右上角按钮 -->
    <view class="nav-right-btn" @click="handleRightBtnClick">
      <text :style="{fontSize: '18px', color: '#FFFFFF'}">+</text>
    </view>
  </view>

  <!-- 页面内容 -->
  <view class="page-content">
    <!-- 你的页面内容 -->
  </view>
</template>

<script setup>
// 右上角按钮点击事件
const handleRightBtnClick = () => {
  console.log("右上角按钮被点击");
  // 这里写按钮的业务逻辑
};
</script>

<style scoped>
/* 自定义导航栏样式(适配HarmonyOS状态栏高度) */
.custom-nav-bar {
  width: 100%;
  height: var(--status-bar-height) + 44px; /* 状态栏高度 + 导航栏高度 */
  padding-top: var(--status-bar-height); /* 适配状态栏 */
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 16px;
  box-sizing: border-box;
}

.nav-title {
  color: #FFFFFF;
  font-size: 18px;
  font-weight: bold;
}

.nav-right-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
}

/* 页面内容区,避免被导航栏遮挡 */
.page-content {
  padding-top: calc(var(--status-bar-height) + 44px);
}
</style>
补充说明(针对 UniApp X 项目)
如果你的项目是UniApp X(专门适配 HarmonyOS 的 UniApp 模式),可以直接使用 HarmonyOS 原生的Navigation组件配置右上角按钮(需在uts页面中实现):

ts

// pages/besties/besties.uts
@Entry
@Component
struct BestiesPage {
  build() {
    Navigation() {
      // 页面内容
      Column() {
        Text("页面内容")
      }
    }
    .title("XXX") // 导航栏标题
    .titleMode(NavigationTitleMode.Free)
    .backgroundColor("#ff4f87")
    // 配置右上角按钮
    .toolBar({
      items: [
        {
          value: "+",
          textColor: "#FFFFFF",
          fontSize: 18,
          action: () => {
            console.log("右上角按钮被点击");
            // 业务逻辑
          }
        }
      ]
    })
  }
}
关键注意事项
app-plus配置的局限性:app-plus是 UniApp 对 Android/iOS 端的扩展配置,HarmonyOS 平台的导航栏、原生组件等逻辑与 Android/iOS 不同,因此大部分app-plus配置在 HarmonyOS 上不生效。
状态栏高度适配:自定义导航栏时,需通过var(--status-bar-height)获取 HarmonyOS 设备的状态栏高度,避免导航栏被状态栏遮挡。
UniApp X 的适配:若使用 UniApp X 开发 HarmonyOS 应用,建议直接使用 HarmonyOS 原生组件(如Navigation),兼容性和体验更好。
通过自定义导航栏或 UniApp X 的原生Navigation组件,即可实现 HarmonyOS 应用中页面右上角按钮的需求,同时保证跨平台兼容性或原生体验。

官方答复地址:https://developer.huawei.com/consumer/cn/forum/topic/0203200053870018576?fid=0109140870620153026&pid=0314200135195141595

当然,这个问题解决最终也是通过自绘导航栏解决的,不过目前还有个问题,就是与 uni 自带的原生导航栏样式稍微有些区别,主要问题在导航栏高度计算,以及导航栏字体渲染问题。这个如果要想彻底解决可能就得全部重写几个页面的导航栏了。这个不影响使用,稍微往后放一点。

另外一个问题,那就是 ai 助理迁移问题,迁移到鸿蒙系统之后,由于部分属性不支持,导致页面直接白屏了。目前虽然就解决了大多数的问题,单机 markdown 渲染问题,以及聊天窗口自动滚动到最新消息依然有问题,在没有全屏的时候是没问题的,消息列表全屏之后,滚动总是少一块。

而至于上篇文章提到的,google play 测试的问题,目前也有好多宝子参与进来了。也反馈了很多问题,当然,很多东西自己觉得习以为常,但是很多人第一次使用的时候却并没有看到相应的效果,或者对于操作步骤有疑惑。

记录:
1.增加异常状态种类,支持多选
2.优化日历样式
3.增加用户徽章
4.针对不同设备处理流式数据
5.优化生日选择的日期选择器
6.优化爱爱记录操作方式
7.优化日历操作方式,更简单明了 删除记录 结束记录逻辑调整
todo

todo:
个人信息–手机号,随便在那里输点东西再去修改昵称, 修改昵称那个输入框变成修改手机号的输入框了。✓
修改完手机号点一下邮箱–取消,在点击昵称 ,输入框变成修改邮箱输入框了✓
我的–高级设置–安全设置 点击密码没反应,改不了密码 ✓
青少年模式不能记录新的了,但是已经记录的💊在日历上还是显示的。
数据分析页面:
年没上限
月只有60天。而且英文界面出错提示是中文。
体重和体温也没有边界值,甚至可以输入负数。 ✓
please拼错账 pleace ✓
中文用户名长度判断问题,中文允许两个字符 ✓

目前已经更新发布了一版新的,并且 ios 和安卓终于版本号同步了。之前由于种种问题,ios 的版本号稍微落后了一点。

当然,正式的测试还得找个时间一起处理。多人长测的确是个问题,需要投入那么点精力。

然而,由于 google 原来的 appid 用不了了,只能换个新的 id,然而新的 id 就会出现 pushid 和 unicloud 不一致的问题,最终只能在 google 包开了 1.0 的 push 临时解决了这个问题。最终没有再搞个分支,不同的分支维护真的太麻烦了。

本来想着一套代码搞定,现在就已经拆分成了鸿蒙的 vue3 和通用版本的 vue2。修复一点问题,现在就得维护两套代码。这个也是属实是无奈之举了。

之前上架华为应用市场的版本是没有 ai 助理的,也就是一个屏蔽了 ai 助理的版本。对于这种不完美事情,总是觉得有些欠缺。继续咨询了一下相关的问题,https://developer.huawei.com/consumer/cn/forum/topic/0203200246757472747?fid=0109140870620153026 给出的答案倒是和之前的区别不大,意思是可以上,但是有些限制。

除了生成的内容要有明显的标识:https://developer.huawei.com/consumer/cn/doc/app/50111-10

另外一个就是使用这种功能需要进行实名认证:

目前看来对于这种认证最简单的侵入性最小的还是手机号认证。为了进行手机号认证,也看了几种方案,一种是基于 uni 自带的证号认证功能,

https://doc.dcloud.net.cn/uniCloud/uni-rpia/mobile-verify/intro.html

相关费用报价其实略贵一些:

真题来说两毛多钱一次,这价格属实是贵了点。随便认证个几百人,这几十块钱就没了。虽然不多,但是毕竟作为一个不挣钱的项目该省的还是要省的。那么,除此之外,那就是直接短信验证码了,uni的短信验证码费用为 0.0355,这个价格比上面的便宜了很多。

然而,这个资源包太大了,有效期一年:

看了下阿里的,虽然大家贵点,但是相对来说资源包可以买的小一些,用完再买,也不会存在浪费的问题:

至于切换,那等短信需求量大了再说吧,毕竟现在只有上架华为应用市场的需要实名,并且不知道这种实名认证是不是满足需求。如果满足不了,那就得再换别的方式了。

既然,已经到了这里,也不在乎多折腾一个应用商店了,登录三星的开发者后台,不得不说,三星还是简单,除了提示改密码,别的什么都没问题。添加应用,补充资料。上传 app 提交审核,然而,晚上收到一封邮件,说审核被拒了,而至于为什么被拒,简单粗暴给了个 apk 扫描没通过的链接。

按照文档的提示,给 google 发送扫描误报请求。结果添加到 google 邮件里的附件又被扫描了,禁止下载。这尼玛真是陷入死循环了,

只好压缩之后重新给 google 发送文件,这尼玛是属于套娃了吧。真是服了。

zhongling@MacBookPro Pycharm_projects % cd dayima-vue3
zhongling@MacBookPro dayima-vue3 % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      49
zhongling@MacBookPro dayima-vue3 % cd ..
zhongling@MacBookPro Pycharm_projects % cd dayima_backend
zhongling@MacBookPro dayima_backend % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      50
zhongling@MacBookPro dayima_backend % cd ../dayima_uniapp
zhongling@MacBookPro dayima_uniapp % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      89

 

最近为了处理这些问题,最近这段时间确实比较忙,白天公司的事情,晚上自己的事情,差不多每天都到十一点多,偶尔一天十一点上床了,对象问了一句:“今天咋这么早?!”

再次感谢最近参与测试,提出 bug 的宝子们。

大致小新笔记坊云无心天天向上vind似水流年夏末~、乖、lgin、jason、jungle

杜郎
以及其他最新入群的宝子,后面几个我对不上号,或者有遗漏的也补充下哈可以给我留一下地址哦,测试结束后给每人发红包,嘻嘻。

尽管道阻且长,

但是行则将至,不是吗?

蜀道难 — 有偿招募 Google Play 测试用户

2025-12-03 13:35:57

懒惰,有时候带来的负面效果,后期想要修复的时候,要付出的代驾比当时处理要复杂的多。更恐怖的是,哪怕付出了这么多的代码,依然无法达到最开始的效果。

当 google playstore 开发者给我发邮件提示账号快过期的时候,并没有太留意,后来就给忘了。当然,gmail 的邮箱已经收到了数次提醒,但是由于那段时间不怎么翻墙,导致并没有看到这些邮件,等看到邮件的时候账号已经被用了。

看到这个停用的原因,真的是让人崩溃,这 tmd,当时但凡翻墙了,也就是点几下鼠标的事情。现在好了,重新注册账号依然面临一系列的问题。appid 被占用,旧应用无法下架,无法转让。现在连上线发布都需要面临另外一个问题,那就是需要开启封闭测试,只有封闭测试通过之后才能有发布正式版的权限。

google play开发者给出的答案是:

针对新创建的个人账号的测试要求简介
测试是应用开发流程中不可或缺的一环。通过持续对应用运行测试,您可以在公开发布应用之前验证其正确性、功能行为和易用性。这能让您及时解决发现的技术问题或用户体验问题,最大限度地降低这些问题对用户的影响,从而确保您在 Google Play 中发布的是应用的最佳版本。如果开发者在发布应用之前经常使用 Play 管理中心的测试工具进行测试,他们的应用将能够带给用户更优质的使用体验,从而在 Google Play 上赢得更高的评分,取得更大的成就。

为了帮助所有开发者确保提供高品质的应用,我们提出了新的测试要求。如果开发者使用的是 2023 年 11 月 13 日之后创建的个人账号,则其应用需要先经过测试,然后才能在 Google Play 上发布和分发。若应用未经过测试,系统会停用 Play 管理中心内的部分功能,例如正式版(测试和发布 > 正式版)和预注册(测试和发布 > 测试 > 预注册),直到开发者满足相关要求为止。

测试要求概览
如果您使用的是新创建的个人开发者账号,则必须对您的应用运行封闭式测试,且至少有 12 名测试人员在过去至少 14 天内选择持续参与测试。满足上述条件后,您便可以在 Play 管理中心的信息中心申请正式版发布权限,以便最终在 Google Play 上分发您的应用。申请时,您需要回答一些问题,帮助我们了解您的应用、测试流程及正式版发布准备情况。

下文详细介绍了不同类型的测试轨道和相关要求,以及关于申请正式版发布权限的更多详情。

了解不同的测试轨道和相关要求
Play 管理中心提供不同类型的测试轨道,以便您逐步扩大测试范围并改进应用,力求达到适合面向数十亿 Google Play 用户发布的水平。

内部测试:在完成应用设置之前,您可以自行将 build 快速分发给少量可信测试员。这有助于您排查问题并收集早期反馈。通常,build 被添加到 Play 管理中心几秒钟后,就会向测试人员开放。虽然内部测试并非强制性要求,但我们建议您从这里开始。
封闭式测试:利用封闭式测试,您可以与由您控管的众多用户分享应用。这样一来,您可以在发布前修复问题,并确保应用符合 Google Play 政策的要求。您必须先运行封闭式测试,然后才能申请发布正式版应用。当您申请正式版发布权限时,必须至少有 12 名测试人员选择参与您的封闭式测试。他们必须在过去 14 天内选择持续参与。完成应用设置后,您即可启动封闭式测试。
开放式测试:让您可在 Google Play 中发布测试版应用。进行开放式测试时,任何人都可以加入您的测试计划并向您提交非公开反馈。请先确保您的应用和商品详情已经准备好在 Google Play 上架,然后再选择该选项。如果您拥有正式版发布权限,则可以使用开放式测试。
正式版:让您可通过 Google Play 面向数十亿用户发布应用。您需要先运行符合我们条件的封闭式测试,然后才能申请将应用发布为正式版。在申请时,您还需要回答一些与封闭式测试相关的问题。当您申请正式版发布权限时,必须至少有 12 名测试人员选择参与您的封闭式测试。他们必须在过去 14 天内选择持续参与。

如果开发者使用的是 2023 年 11 月 13 日之后创建的个人账号,则其应用需要先经过测试,然后才能在 Google Play 上发布和分发。若应用未经过测试,系统会停用 Play 管理中心内的部分功能

测试要求概览

如果您使用的是新创建的个人开发者账号,则必须对您的应用运行封闭式测试,且至少有 12 名测试人员在过去至少 14 天内选择持续参与测试。

重要提示:向测试人员强调,他们需要选择持续参与至少为期 14 天的封闭式测试。

说实话就是现在google 的做法是越来越看不懂了,关键是很多操作也不知道后续该如何操作。这就很蛋疼了,这种事情当然更好的办法是去咸鱼之类的找个专门做测试的。

测试资格需要加入这个 google 群组(需要加入群组之后才能参与测试):[email protected]

https://groups.google.com/g/guimiquan

我也没咋用过这个东西,不清楚怎么去处理这个破玩意儿。有时候感觉老外设计的东西,的确是不怎么符合自己的使用习惯。

ap测试地址:

https://play.google.com/apps/testing/gma.dayi.app

app商店地址:

https://play.google.com/store/apps/details?id=gma.dayi.app

网上依然能找到这种测评服务了,价格大约 200+。

现在的问题在于重新注册开发者账号话费了 30 美元,现在不上架恶心,上架也恶心。既然如此那还是折腾一下吧,不过鉴于自己手上没那么多的设备,想请各位宝子帮帮忙测试一下,想参与的加一下 google 的群组。需要 12 名测试人员,根据提供的测试账号数量,在测试完成后给于相应的报酬,如果有时间,或者闲着没事的希望可以板帮我哦。

目前我自己还在修改一些问题,希望大家除了加下 google group,顺便加下群哦,QQ群:777692924obaby@mars

需要大家开始帮忙测试了会统一通知一下的。

都说蜀道难,现在发版比蜀道还难,蜀道难都是看得到的难,而这发版,完全是不知道的陷阱。

最后说下测试报酬哈,鉴于价格大约都是 200+,12 个账号,按照每个账号 20 元红包来计算(提供几个测试账号,会获得相应账号数量的红包)。如果不想要红包的,可以寄写真照片(3 张),如果喜欢的话。

如果种种原因,到时最后测试没通过,红包依然会发放,不过金额就变成 10 元了哈。毕竟不想让大家白费精力,这个钱属实不多。聊表心意,主要是这个应用也没啥盈利能力,全靠用爱发电,小女子先行写过啦。

完美主义

2025-12-01 10:53:14

小时候跟姐姐学的用扑克牌算命,洗牌就那么一张一张的抽出来排上去,从 1 开始一直排到 13,不同的花色对应着好或者不好,每个数字代表不用的意义,事业啊、婚姻啊、学历啊等等。有时候,为了抽到一排红心,自己会将扑克牌在洗牌之后重新排号,按照一个特定的顺序,抽几张扔几张。最后总是能拿到一排红心。

慢慢长大之后发现,不要说一排红心,哪怕能拿到一颗红心已经实属不易。为了这颗所谓的红心,要付出的实在是太多太多。

反常的气温,忽然又升高到了十七度。给人一种暖春的感觉,周五的时候,对象说给宝子约了周六的牙医。宝子的这个牙,已经成了一种非常严重的问题。由于之前一直吃手,后来用这种手段干预,戴手套,贴嘴等等,虽然不吃了,但是舌头还是不自觉的就往前顶,现在牙齿已经有些变形了,甚至连骨头都开始过度生长。

去妇幼、齐鲁去问诊,给的建议都是等牙齿换完再处理,但是现在似乎依然等不到那个时候了,再不干预,以后要该起来就更难了。原本想着,简单的咨询一下,但是在经过一系列的检查之后,觉得还算靠谱,给的医疗建议也在接受范围之内。决定不再折腾了直接在这里处理,费用六千五。为这些所谓的坏习惯的付出依然不止这些。

有时候懒惰真的会付出代驾,小的时候想下狠心纠正这种坏习惯,但是宝子的姥姥各种觉得残忍,阻挠。最后的结果就是,虽然当时是痛快了。后患却没那么容易消除。虽然现在自己依然不是一个完美主义者,但是,这种过失,现在想起来却也时常后悔。

周末,有时候感觉时间是真的少,各种乱七八糟的事情就占据掉了大半。剩下一点点的时间,来处理下那些乱七八糟微不足道的事情。之前 google play store 的账号,因为长时间没登录被停用了,导致原有的闺蜜圈 app 也被下架。在重新注册开发者账号,想要重新发布应用的时候,提示 appid 被占用了,给 google 发邮件申请转移,给的答案是账号可以解除封禁,可以登录,但是转移却是一直失败的,根本没有转移权限。

既然如此,那暂时也就不再尝试使用原来的 appid 了,毕竟,这个流程一直持续下去,也不知道会到猴年马月能结束,就酱紫吧。完美主义,自己坚持有个 p 用,还得条件允许才能完美。

除了 google play,其实还有一个平台是自己之前也想上的,那就是鸿蒙,uniapp 刚支持打包鸿蒙 应用的时候,自己就尝试过向鸿蒙的迁移。然而,由于项目框架较老,需要做的工作不止一点点,需要先将 vue2 升级到 vue3,然后在将 vue3 版本打包成鸿蒙的 app。

升级这一步就不是很顺利,作为一个初学前端(vue 框架)的菜鸟,最开始项目建立的时候,代码结构设计的并不是非常好,并且硬编码了很多 vue 2 only的一些代码。升级到 vue3 之后,勉强编译通过,运行到了鸿蒙系统上。

再后来,这件事情也确实没什么动力,就不了了之了。然而,就在上周又接到了一个广东深圳的电话,接起来之后说是鸿蒙开发者中心的。问有没有app 升级或者开发计划,说看到在应用商店上架的闺蜜圈 app了,并且说帮忙给建立技术支持服务群,协助将项目从 vue2 到鸿蒙系统 app 的发版。 

在接到这一通电话之后,总觉得不做点什么真的对不起鸿蒙生态的付出。(这个电话打了很多次了,一直没接)之前,自己的那种完美主义追求,想要在国内的各大应用市场上架。然而在多年以后,所有的手机应用市场都关上了针对个人开发者的大门,除了华为。国内的个人开发者,真的连狗都不如。

在自己开发第一款 app 的时候,国内应用商店华为、小米、锤子、魅族还是针对个人开发者开放的,只是现在小米把个人开发者推出了门外,魅族也关闭了那扇大门,锤子死了。只剩下华为还算是对个人开发者开放,更何况现在,人家都找上门了,自己又有什么理由不做出点努力呢?

而至于完美主义,现在依然不可能了,如果要做,也只能部分完美。让那些自己有能力去完美的地方,能稍微完美一点吧。

为了能升级到 vue3 和支持最新版的鸿蒙开发工具,将 hbuilder 升级到了最新版,切换到原来 vue3 的分支,不得不说,最新版的 hbuilder 在鸿蒙的支持上友好了很多。

配置好一系列工具和插件之后,甚至应不需要在使用鸿蒙开发者工具打包就可以直接运行到模拟器了,虽然提示只支持 arm 架构的模拟器,但是运行是完全没有问题的。

当然,现在升级最大的优势在于,通过 cursor 可以帮忙解决大部分的 vue代码升级问题。

的确减少了自己的大部分工作量,只需要关注那些 ai 解决不了的问题就 ok 了。两天陆陆续续的修复,最终还是在鸿蒙系统上运行起来了,也修复了大部分的错误,当然,这个升级之后的功能,还需要进一步的细致测试。

实际运行效果:

有的事情,开始固然是艰难了一些,甚至,很长时间都看不到方向,然而,做了也就那样,没什么做不了。也没什么做不到。

网上总是说 hbuiler 这不好,那不好,性能太差,不如原生。有哪有啊完美无瑕的工具或者框架,如果通过这个工具或者框架实现了自己的目的,那么这个框架或者工具就是足够优秀的,哪怕不完美。国外的东西不见得就是好的,国内的东西也不见得就是不好。很多程序员为了争论 emacs 和 vi 到底哪个更好,能口诛笔伐。甚至连 vi 党和 vim 党都能同室操戈,我作为一个实用主义党是在不明白这种争论的意义和价值。

当然,其实这些年我说 hbuiler 好不是一次了,我也是目光短浅,没用过 flutter 之类的其他的跨平台语言。仅仅局限于自己的鼠目寸光,与我而言,这解决了我的问题,就足够了。通过自学,两个月的时间,能让我通过这门语言或者工具来做一款产品,这就够了。

只是,现在我站在了自己写的屎山代码上,有太多的东西需要优化,有太多的结构需要调整。

或许,是时候放弃完美了,对于用户来说,你的代码是不是屎并没那么重要,只要给用户呈现的不是💩就完了。

绽放

2025-11-25 14:36:07

晚上下班之后,沿着那条跑过无数次的公路奔跑

路边的树上依然没了绿意

一阵北风吹来

树上的黄叶控制不住的在空中飞舞

身不由己的在空中划出一条不规则的曲线

落在地面上

一阵更猛烈的风吹来

地上的树叶又被裹挟着飞到了空中

耳机里伴随着风声传来的是张韶涵的《阿刁》

想大口呼吸

却不禁让狂风灌满了整个胸腔

虽然已经解开了薄羽绒服的扣子

微微出汗的身体依然想要抛弃这沉重的枷锁

只是啊

或许这被嫌弃的枷锁

是多少孩子的爱而不得

或许

我们生不逢时

或许

我们一路荆棘

但是

孩子啊,我们还是要努力的绽放

意义

2025-11-24 16:52:28

中午吃完饭,打开电脑下载了点乱七八糟的东西,期间传来了 iphone 的两次短信提示音。平时垃圾短信、垃圾电话一大堆所以也没去看到底是什么东西。等到要出门上班的时候,看了下 p70 手机,推送栏有一条消息:“……驾驶 中型以上……汽车…… 超速 ……不超过 20% ……”,都不用仔细看就知道是超速了。

距离上次收到超速短信已经过了数年,最近几年除了路边停车收到的罚单,以及对象在快速路上超速吃了一张罚单。从来没再收到过超速罚单,倒不是因为没超速,只是山东高速的容错率还是蛮高的。

看了下拍的现场图片,应该是在原来的济潍高速被拍的。区间限速路段 22km 长度,限速 120km/h,实际的区间速度为 134km/h。之前跑 G20的时候,基本区间限速都是压着 140 左右跑,但是却没被拍过。只有回老家的时候才会跑济潍高速,跑的时候一般是压着 130 跑,然而这次稍微快了点。这一下,两百块没了,说实话就是这两百干啥不好呢,为什么要超速呢。

带着家人跑高速的时候,对象总是提示说,不要超速,被罚款太不值了。有时候被说,心里也感觉不爽,但是真的压着 120 跑,总是觉得有些憋屈。然而,等真被拍了之后,交了两百块钱,现在也会想着 200 花的有意义么?平均时速降 2km/h 就完全可以省掉这 200 快了。也常在网上看到有人说,限速 120 超速 20% 不要紧,然而,这个超速 20% 以内不要紧,不是所有的高速都适用。如果要超速,还是贴着 10% 之内超吧,毕竟,交的罚款都赶上来回的油费了。

周日带着宝子去上网球课,终于有重新能达到隔网接球了。跟着前一个教练学的内容,扔了一年多的钱,学了个不标准的动作。现在每节课都在纠正之前的错误,也在承担之前这错误的姿势带来的影响。目前已经又消耗掉 20 节课时费了。好在现在纠正的还算可以,最起码就是球能打实了,能听到打球的声音了。总说及时止损,有时候却因为自己的懒惰和松懈,浪费了太多的时间在那些错误的地方,然而这些错误最终的买单人还是自己。

有时候也不得不思考究竟什么是有意义的,学习?工作?玩耍?还是什么其他的东西?

然而,有时候想想这些都没什么意义,或许真正有意义的事情是做自己喜欢的事情,让自己快乐。然而,这种快乐又从何而来呢?是兴趣爱好还是什么其他的东西?亦或者说是一种无所谓的坚持?

坚持有意义吗?答案就是我也不知道有没有意义,只是从某种意义上在坚持一件事情。最近晚上跳绳的时候,有时候会一边看电视一边跳,这么跳的好处是看到入迷的时候,跳绳反而觉得没那么累了,甚至很容易就跳到 4000+。

昨晚跳的时候,看的第一集电视剧已经过半,在第一集结束之后,继续后面一集。既然开始跳了,自然是要坚持下去的。就这样,刚好差不多一个小时多一点的时间,完成了一万个。

虽然最开始的 4000 还能有点感觉,但是到了 6000+以后反而没什么太大的感觉了,除了有点出汗,并没有太吃力。或许这就是坚持的意义吧,只有牛马有个一个好身体,才能赚更多的牛马费不是吗?也只有更多的牛马费,才能给宝子营造更好的生活环境。

总说年轻人不愿意结婚生子,之前忘了在谁那里看到的一个专家的言论说,那年轻人不愿意生孩子,并不是年轻人没有责任心,而是现在的年轻人责任心比上一辈高太多了,知道为自己的孩子负责,知道为他们创造更好的生活条件。自己没有能力的时候,不想生而不养。

而这个好的条件,有需要付出多少的努力?然而,这些所谓的努力又有多少事有意义的?对于多数人来说,从来不会希望你过得比他们好。上周回去奔丧的时候,在殡仪馆,大姐夫跟二姑家的堂哥在那里聊天,自己去厕所的时候路过。堂哥玩笑了一句:“发财了,不认识亲戚了?”

我幽怨的回了一句:“嗐,发个屁才啊,不单工资没涨,还降工资了……”

简单的聊了几句就去洗手间了。努力或许从来都没什么意义,别人看到的都是你现在的表象,至于是不是他们希望的,大概率并不是。而之所以说这句话,大概率是因为这次回去自己没有开大白而是开粉皮回去的。毕竟这个时候在一堆车牌号鲁 V 和鲁 G 的车里,找到一辆鲁 B 的车还是蛮容易的,自己下车的时候他们大概率也是看到了。虽然不是什么豪车,但是也能戳痛某些人吧。不管承认不承认,有时候看到别人开着跑车出现,自己的内心也会触动,那种内心的由衷的羡慕,因为这些东西大概率自己这辈子无法体会了。而这些东西,不是自己努力就能得到的,如果要按这个来平评判,自己的努力就没有任何的意义。

穷,固然会被人看不起,但是你过得比他好,大概率他会记恨你。

于是,多数时候,我总是习惯展示给他们自己想看的那个自己。更像一个虚伪的骗子,哪怕是自己的家人,有时候竟然也会带着一份伪装,只是不知道这份伪装如果真的去掉,会变成什么样子?大概率不是自己想要的样子。

自己不断追求各种所谓的意义,以前却很少追求快乐。在某些时候,甚至为了单纯的快乐竟然有一些负罪的感觉。就想那个拉磨的驴,偶然从磨盘上下来之后,也会怀疑自己存在的意义和价值。内心的不自信,太多的时候需要所谓的外界的认可来证明自己的价值。诚然,这种病态从来没有真正消除过。

跟大多数人一样,人生的轨迹似乎也没什么太多的变化。上学、工作、结婚、生子,在父辈的言语中,听到的最多的就是多生几个孩子。这一辈子什么都不会留下,能留下的只有自己的孩子。然而,留下那么多孩子干嘛呢?如果给不了好的生活,宁可不要孩子。自己摸爬滚打从按个小村子里爬出来,难道要让自己的孩子继续重蹈覆辙吗?

四叔家的姐姐得了脑瘤,在鬼门关走了一遭之后,心态变了很多。自己很喜欢这个小姐姐,小时候经常在一起,长大了之后见的少了,但是感觉依然很亲切。然而也只有过年的时候才有时间去看看她,身体瘦削,这几年气色好了很多。她说:“有时候读书太多也不好,你看你跑那么远都见不到你。”

“这哪里算远,一点都不远”我答道,我握着她的手,能感到那只手冰冰凉凉。我想努力的给她暖一下,就这么紧紧的握着。

也许从来没什么所谓的感同身受,哪怕经历过同样的事情,每个人依然有每个人的感觉。而自己能做的,也仅仅是尽量去体会他人的内心的感觉。

人生,或许从来没什么意义。也许只有自己能给自己的人生赋予意义,快乐活着,痛快生活。

只是,说起来从来都简单,然而,追寻却从来不易。