标签: android

  • 解决 React Native + Expo 面对 Google Play 的 16KB memory page 问题

    解决 React Native + Expo 面对 Google Play 的 16KB memory page 问题

    最近开始尝试开发 App,倒不是什么复杂的大项目,只是把朋友网站上的功能移植到移动端。技术栈仍然是 React Native + Expo,不过以前只做过 iOS,这次连 Android 一起做。

    那么自然的,这次就要踩 React Native Android 的坑。以后会分享所有相关的知识体验和坑,今天先分享最近两天花了不少时间解决的 Google Play 16KB memory page 问题。

    我们的应用提交到 Google Play 后,原本一切正常,前两天突然收到 Google 的政策通知:

    为确保您的应用能在最新版 Android 上正常运行,Google Play 要求以 Android 15 及更高版本为目标平台的应用支持 16 KB 内存页面大小。

    自 2026年5月30日起,如果您的应用更新不支持 16 KB 内存页面大小,您将无法发布相应更新。

    您的最新正式版应用不支持 16 KB 内存页面大小。

    嗯,必须承认,看到这个问题我一头雾水。不过好在我也不需要把它理解透彻,只要知道该怎么改就好。可惜的是,Gemini 对这个问题没什么了解,我只好去阅读 Google 的文档,得到的结论是:

    我需要修改 /android/app/build.gradle 其中的配置 useLegacyPackaging 将其改成 true

    android {
        packagingOptions {
            jniLibs {
                useLegacyPackaging true
            }
        }

    不过我使用的是最新版 expo prebuild 生成的 Android 项目,所以这个配置本身依赖 app.json 的配置,那么理论上,我只需要添加下面这行:

    {
      "expo": {
        "android": {
          "useLegacyPackaging": true
        }
      }
    }

    于是我改好配置重新打包上传,结果还是不行。认真阅读 app bundle 详细信息,发现错误位于 base/lib/arm64-v8a/librnskia.sobase/lib/x86_64/librnskia.so ,很明显,这是 @shopify/react-native-skia 包,也就是我们的绘图依赖。

    因为我的项目里用到 Expo,所以我一般用 Expo 安装依赖,安装的版本也由 Expo 决定。目前版本的 Expo 要求的 @shopify/react-native-skia 版本是 v2.0.0-next.4,在 GitHub issues 里搜索一下,发现这个版本果然不支持 16KB Memory page,而修复的版本是 v2.0.6。

    按照我的习惯,有新不用旧。于是直接升级到 2.2.9,然后应用就挂了……于是降级到 v2.0.7(2.0 版本的最高版本),测试没问题。打包上传,终于解决了 16KB 警告。

    简单总结一下:

    1. 不同平台有不同要求,不过大多可能和 RN 无关,通过项目配置就能解决
    2. React Native 由于跨平台,跨运行时,依赖之间的关系很复杂,不能乱升级,尽量控制小版本,只升补丁版本
  • 2024 中国大陆搭建 React Native 开发环境

    2024 中国大陆搭建 React Native 开发环境

    我从 Web 前端做起,后来发展到全栈,至今十几年。我觉得大陆的网络环境对 Web 开发还算比较友好,除去 Google 之外,大部分网站都能轻易访问,大部分网络产品都能自由使用。比如 GitHub,NPM,直接访问都没什么问题。只有少数几个软件包比较麻烦,也多半跟 Google 有关,比如关联到 chromium 的 Electron、Puppeteer 等。

    上周开始准备做 React Native 开发,真的踩了不少坑,感觉大陆网络对移动开发相当不友好……Vincent 问我:搭环境需要这么久么?问一下 GPT 不是一两个小时就搞定了?我微笑着告诉他,他把这件事情想简单了。于是他也的确花了一天时间才把 demo 跑起来。这里我就分享一下最新的知识吧。

    Expo

    Expor 之于 React Native,就像 Next.js 之于 React。它是一个上层的框架,提供很多常用的组件和工具函数。比如基于文件目录结构的路由管理系统,以及从之衍生出的全局 URL、页面间跳转功能等。还有一些页面组件,比如 Stack 等。

    我建议使用 Expo,我相信能给我们节省很多自己码代码的时间。学别人规划好的框架,很多时候比自己慢慢手搓要快很多。

    不需要 Expo Go

    Expo 的网站会推荐我们安装 Expo Go 作为开发调试工具。不过实测之后我发现,App Store 的 iOS 版本无法手动输入 URL,而且我们构建项目的时候,就会包含 Expo Go 的功能,所以这个东西在不需要 EAS(即 Expo 提供的云编译服务)时完全没用,可以不用考虑。

    iOS

    Ruby & Gem

    使用 React Native 需要用到 CocosPods,这是基于 Ruby 写的一些东西,所以必须通过 Gem 下载。国内访问 Gem 源非常的慢,几乎不可用。

    不过解决方案很简单,换国内镜像即可。

    升级 Xcode 及 SDK

    虽然可能不开发原生应用,但我相信所有开发者 macOS 上都有安装 Xcode。第一次安装 Xcode 的时候,可能会随手装一些 SDK 并创建虚拟机,那么当你准备尝试 React Native 开发,配置开发环境的时候,旧的 SDK 和虚拟机就可能带来问题。

    解决方案也不复杂:删掉旧的虚拟机,安装新版本 SDK,即可。

    项目目录不能有空格

    2024 年了,居然还有这种问题……总之吧,从根目录算起,一直到我们的 React Native 项目目录,目录名都不能有空格。我建议尽量只使用英文字母和数字作为目录名,几乎不会遇到问题。

    Android

    Android 所需的网络环境之恶劣超出我的想象。可能跟它比较依赖 Google 服务有关,大量服务要么完全无法访问,要么速度非常慢。而且跟梯子没什么关系,很多东西可以直连下载,但就是慢的无法使用。

    不要使用太新的 SDK

    比如以目前这个时间点,Android 35 和 NDK 27 都不行,最高只可以用 Android 34 + NDK 26。我甚至建议大家用旧一些的,反正我们搞 React Native,没必要追新。

    手动处理 gradle-{VERSION}-all.zip

    我的 Android 环境 第一关卡在下载 gradle-N-N-all.zip 上。其实我本地网络直连下载也没问题,但就是太慢。200+MB 的包每次都超时。解决方案是先手动下载,慢慢下不着急,普通网络也几乎不会失败。

    然后把文件放到 android/gradle/wrapper/ 目录下面。接着修改 android/gradle/wrapper/gradle-wrapper.properties 文件,把其中 distributionUrl 指向对应的相对路径,即可。

    再次执行 expo run:android,就从本地处理,速度飞快。

    手动处理
    react-android-{VERSION}-debug.aar

    第二个卡住我的是 react-android-0.74.5-debug.aar,问题表现跟上面的 gradle 一样,不是不能下载,就是慢。以至于我抱着试一试的想法坚持了两天……

    最终找到解决方案,手动下载文件,然后找到本地目录 $HOME/.gradle/caches/modules-2/files-2.1/com.facebook.react/react-android ,在里面找到对应的版本号,然后在里面可以看到几个目录,找到里面有 .pom 文件的目录,把刚才下载好的文件放进去,即可。

    其它文件

    其它文件下载也需要很久,但是不至于因为超时失败,我就不多说了,耐心等待即可。我也试过换源,但是不知为何,都不成功,可能跟 React Native 有关?国内镜像源都只针对了一般 Android 仓库?

    总结

    希望以后网络环境会愈来越好。希望这篇博客里的经验对大家有用。

    以后要好好钻研 React Native 开发了,相信会有更多的内容分享给大家。敬请期待吧。如果各位读者有什么想看的,也不妨留言告诉我。

  • 在 macOS 上安装配置 Flutter SDK

    在 macOS 上安装配置 Flutter SDK

    前天 Flutter 官宣开始支持 Windows App,所以我想新的一年,也学一学 Flutter 开发,填补大前端的空白。这两天抽空把 Flutter SDK 装好了,过程还有点费劲,所以记一下。

    我的系统是 macOS 12.1。

    0. Flutter SDK

    直接在官网下载压缩包:Install | Flutter

    解压后放到一个不太深的文件夹里,我的是 ~/flutter

    添加路径到 .zshrc

    export PATH="`pwd`/flutter/bin:$PATH"

    最后运行 flutter -v 能正常返回就算成功。

    1. Android Studio

    接下来运行 flutter doctor 检查环境,发现不少问题。先安装 Android Studio 解决 Android 开发问题吧。

    下载并安装:Download Android Studio and SDK tools  |  Android Developers

    我对 Android 生态不熟悉,用 Android Studio 的目的就是用 GUI 工具配置环境。所以在里面找到 SDK Manager,安装

    • Android API 32
    • Android SDK Command-line Tools
    • Android Emulator
    • Android SDK Platform-Tools

    1.1 安装 OpenSDK

    Android 平台需要安装 JDK。按说 macOS 系统本身就集成在内,不过我希望用 OpenSDK 替换之。

    # 安装 opensdk
    brew install openjdk
    
    # 链接库
    sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
    
    # 添加 PATH
    export PATH="/opt/homebrew/opt/openjdk/bin:`pwd`/flutter/bin:`pwd`/Library/Android/sdk/tools/bin:$PATH"
    
    # 验证
    java -version

    1.2 验证

    最后运行 flutter doctor --android-licenses,接受所有 Android 协议,这篇儿也就通过了。

    2. Xcode

    直接使用 App Store 安装 Xcode。装好后启动,安装其余组件。然后执行下面命令,完成安装:

    sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
    sudo xcodebuild -runFirstLaunch

    接下来还要装 CocoaPods,以便将来使用插件。我也不知道插件是干嘛的,不过让装就装吧。

    brew install cocoapods

    3. Chrome

    Flutter 需要 Chrome 作为 web 开发平台,所以我们也要安装。不过它并不严格要求 Chrome,Edge 也可以。我现在主要用 Edge,配置一下就可以:

    export CHROME_EXECUTABLE="/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"

    4. 配置开源镜像源

    Flutter 也要使用类似 NPM 的包管理工具,这些工具都放在 Google 服务器上,所以难免受到牵连。所以要配置一下国内的镜像源,方便使用。

    同样编辑 .zshrc

    export PUB_HOSTED_URL=https://pub.flutter-io.cn
    export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

    还有一些其它源可用,参考:在中国网络环境下使用 Flutter | Flutter 中文文档 | Flutter 中文开发者网站

    (更多…)
  • Safari 下 Date 不支持”2018-01-01 00:00:00“

    Safari 下 Date 不支持”2018-01-01 00:00:00“

    前两天发现一个小程序的问题,Android 正常,iPhone 出错。我们都知道,Debug 的关键在定位,如果是某些特殊环节,不常见的错误,就会浪费很多时间。

    这个 Bug 也是如此,反复拉锯之后终于发现,问题出在下面这句:

    let a = new Date(`${date} 00:00:00`);
    

    date 是服务器端返回的值,我是把它和后面的 00:00:00 连起来,记作某天零点零刻,和今天的零点零刻做减法,计算日期差,并按照日期差来决定接下来的逻辑。这段代码在开发工具(包括 Mac)、Android 手机上运行都正常,只有在 iPhone 上不正常,于是我打开 Safari——苹果这点做得不错,桌面版 Safari 环境和 iOS 几乎没有差别,该出的问题一定会出——果然复现了这个问题。

    按照规范,中国的日期格式是:“2018/01/01”,Safari 只支持这个格式。而 2018-01-012018-01-01T00:00:00 ISO 格式,Safari 也支持,但是会以格林威治时间为准,和我们有8小时的时差。Chrome 和 Android 内嵌的 WebView(基于 Blink 或者 Webkit)则都支持,所以在本地和 Android 手机上没有问题。

  • 未命名文章 1978

    React Native开始支持Android了,看来近期有必要去学学看了。做个什么好呢?

  • Android Hybrid App四大坑

    Android Hybrid App四大坑

    首先解释下题目,Hybrid App,混合应用,代表平台Phonegap,一般指使用原生包装Web页面开发的应用。与原生应用相比,主要用户界面和业务逻辑都是用Web技术也就是HTML+CSS+Javascript实现的;与Web应用相比,Web部分打包在应用内部,使用时不需要网络。

    顺便说一句,很多解决方案其实不算Hybrid,比如Adobe AIRTitaniumMono,这些都是使用某一特定技术开发跨平台应用的工具,最终产品都是编译成原生来跑的。

    我们没有选择phoengap为技术基础(我对此并不满意,我认为以phonegap为基础可以少走一些弯路,少花一些精力,还能产出很多有价值的副产品),而是自行开发原生框架,主要目标平台是Android——嗯,就是那个从系统版本到模块组合都巨分散的Android,可以这么说,坎坷从立项的那一刻起就已经注定了……接下来,便请听我一一讲述:Android Hybrid App四大坑。(此文主要针对Android 4.3-的webview,部分浏览器比如Chrome已经改善了具体实现,所以Web App其实环境不错。)

    游戏泡泡v0.2首页截图
    游戏泡泡v0.2首页截图

    前端代码开源就好,https://github.com/Dianjoy/gamepop,要跑起来需要修改config.js,把if (debug) {}的内容删掉。

    (更多…)

  • 的pathname在不同浏览器中表现不同

    <a href="download://gamename/游戏名称">Download</a>

    在桌面版Chrome 32里,这样的<a>,其pathname会被解析成“//gamename/游戏名称”。

    在Android 4.2的WebView里,会被解析成“/游戏名称”。

  • Phonegap 2.6在Android上的Icon设置

    这个问题Google了半天也没找到答案,难道只有我碰到了?

    我的测试平台:Windows 8 + Eclipse 4.2 + ADT 21.1 + 小米 2 + Phonegap 2.6。

    开发测试一切正常,只是编译输出的应用装到手机上只能用默认图标,怎么都改不过来。我把所有res/drawable-xxxx里面的icon都换了也不行。然后修改AndroidManifest > Application > Application Attributes里的icon和logo设置,也不行。怎么试都不行,Google也找不到。安装时提示的图标正常,查看运行过/运行中的图标也正常,就那个桌面上的图标不对。

    刚才吃过饭,来回点,突然发现在Application Nodes里有个Activity,还可以设置icon和logo,想着反正也不亏,就也给填上了,居然OK了。

    哎,记一下,省得以后忘掉。

  • 原来早期Android的WebView真的很奇葩

    以前没在Android下做过原生HTML开发,所以一直觉得大家的吐槽很莫名其妙——不同于桌面错综复杂的环境,高度统一的webkit内核浏览器能整出什么幺蛾子来?结果这次遇到了,才发现原来早期Android系统的WebView真的很奇葩。

    先是有一块文字,可能比较长,我就在作容器的div上写样式为overflow:scroll,以为这样就能用手指触摸滚动了,拿来小米1S一试,通过。结果后来同事找来,说他的手机不行。研究半天,在StackOverflow上看到Android 3.0之后才开始支持触摸滚动,想在2.x系统上实现还得自己写JS实现。一看他的手机,2.3.5。OK,写JS嘛,好说。

    事先我已经知道触摸事件的触发机制,所以很自然的就去侦听touchstarttouchendtouchmove。结果,不行,滚不起来。我以为自己记错了,去MDN上查了查,没错;又猜是事件用错了,尝试touchleavetouchcancel,也不行;2.x的系统不能装Chrome,测试也不好搞。后来终于通过输出发现,touchmove只触发一次,然后Google之,原来Android设计的机制就是只触发一次,想要自主控制事件结束的时机只能event.preventDefault()。这……难道touchmove不是对应mousemove么?

    只这两点就花了我不少时间,不知道还有没有其它的坑。反正做完这个需求我不禁感慨,早期Android的WebView真的很奇葩啊。

    PS:这篇博文应该早于上一篇,换言之就是这个时候我为了在本地调试打开了“Emulate Touch Events”开关的。

  • Nexus S很诡异的单击变双击现象

    被报告了一个很诡异的Bug,在且只在三星Nexus S上出现,系统版本4.0.4和4.1.1都有:

    一次点击,会触发两次点击事件。两次事件的 target 和 currentTarget 都相同。

    因为只在三星Nexus S上出现,调试相当困难,反复无果。后来想起来我用zepto类库作为底层库,而且编译时把touch部分也编译进去了,所以尝试着将 click 替换为 tap ,居然解决了……

    具体问题症结,以后再研究吧。