「个人app」2024年的第19周(做了个大决定💪🏻 💪🏻 💪🏻 )

285 阅读8分钟

「个人app」2024年的第19周(做了个大决定💪🏻 💪🏻 💪🏻 )

前言

距离上次更新已经很久了为啥鸽了这么久?以前个人APP采用原生+java springboot方式实现的, 这就带来一个弊端要维护两套代码 + 一套网页端。 所以在经过深思熟虑后【看见了小伙伴 Requable以后 坚定了信心换 Flutter 】。
是的你没有看错 Flutter + java springboot,所以就有了现在的模式,重大转变以后有啥改观呢?

  • apple store 能够发布了, 这样用户接触面上就大了一倍。
  • 手机网页端完全和原生APP功能+交互同步了。
    截止发文章Android、iOS 均已已经审核通过并上线了。

直接放图,先看成果

3351715416205_.pic.jpg

和大家分享一下这个过程,如果有打算上架或者操作的有个参考。

原生切Flutter

首先还是得找个学习网站 中文版 | 英文版, 快速学习浏览一遍后就是照猫画虎了, 从github 找个开源的Flutter项目参考学习下。 开始搭建应用:dart语法学习、状态管理、网络通信、插件、图片加载、数据存储、路由管理... 和原生APP构造大差不差,然后就是UI构建。 于是就有了接下来的页面:

首页打卡学校列表我的学校
cec74592-13a0-49c6-becf-2b4ef18476a5.pnga9e53aed-149d-4b51-9a14-64ddee47679a.pngd161a129-3d41-4581-a6e2-a25e63e9f981.pnga9345c6d-ca5f-4c41-878a-6816775fdac2.pngf528aa30-7ebb-42d9-8aa1-010e34590f01.png

市场上架

APP首版完成开发以后最重要的就是上架了,应用市场大致分几类:Android、鸿蒙、iOS、海外。
要提前准备的物料:市场图、各种资质、授权、隐私政策。

应用市场

Android端几个大的渠道:小米、荣耀、华为、百度、应用宝、360、阿里、OPPO、vivo
iOS、鸿蒙、GoogleStore: 官方独家

Android
隐私合规

先说大概说一下厂商的审核机制: 机审 + 人工
机审:安卓端的主要是史宾格【百度旗下】、小米隐私合规扫描等各种厂商自己开发的,先说下小米比较特殊他的机审是多引擎监测,就是自己的隐私合规+ 三方的比如百度的,这个就比较开明了,其他的几家都是只会用自己的。

机审的几个方向:

  • 首次安装必须以弹框形式告知用户隐私协议、用户协议等,且在此之前不能调用任何涉及隐私的方法,比如传感器 【这块后面会重点说下】
  • 隐私政策文件中描述的敏感权限是否和实际运行时获取的匹配,这里要求APP中声明的敏感权限在隐私合规中都要体现关键字。
  • 三方插件、SDK这种必须要在隐私政策中体现,比如: 友盟、极光、融云im等
  • 各种权限及服务调用的频次不能过于频繁

人工审核需要注意的:

  • 华为:用户注册、登录、注销 要严格按照审核要求来, 页面涉及个性化推荐、定向推送、精准营销的 一定要有开关以及在隐私政策中有体现。 APP不能有明显bug,测试会走一遍基本的账号流程。

机审关于隐私政策:

  • 弹框形式告知用户隐私协议,且在此之前不得申请任何权限、传感器、隐私数据收集 Android 纯原生的处理方式:
  • SDK初始化延迟、 用户同意后再初始化

在提审百度、小米的时候都被拒了,提示

image.png

android.hardware.SensorManager.getSensorList(SensorManager.java:436)<---p1.d.c(Unknown Source:3)<---p1.d.a(Unknown Source:6)<---p1.b.c(Unknown Source:7)<---p1.b.<init>(Unknown Source:10)<---p1.a.i(Unknown Source:42)<---io.flutter.embedding.engine.c.h(Unknown Source:113)<---io.flutter.plugins.GeneratedPluginRegistrant.registerWith(Unknown Source:68)<---java.lang.reflect.Method.invoke(Native Method)<---v5.a.a(Unknown Source:21)<---io.flutter.embedding.android.e.q(Unknown Source:9)<---com.xiaoxinbao.android.MainActivity.q(Unknown Source:5)<---io.flutter.embedding.android.f.q(Unknown Source:58)<---io.flutter.embedding.android.e.onCreate(Unknown Source:13)<---com.xiaoxinbao.android.MainActivity.onCreate(Unknown Source:0)<---android.app.Activity.performCreate(Activity.java:7070)<---android.app.Activity.performCreate(Activity.java:7061)<---android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)<---android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3067)<---android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3191)<---android.app.ActivityThread.-wrap11(Unknown Source:0)<---android.app.ActivityThread$H.handleMessage(ActivityThread.java:1921)<---android.os.Handler.dispatchMessage(Handler.java:106)<---android.os.Looper.loop(Looper.java:164)<---android.app.ActivityThread.main(ActivityThread.java:6843)<---java.lang.reflect.Method.invoke(Native Method)<---com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)<---com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

把APK直接拖到Android Studio找到p1.b.c 对应就是

class MainActivity : FlutterActivity() {


    //注册所有plugin插件
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        ...
    }


}

但是在Flutter 项目中就比较麻烦了,在自己项目中可以在弹框后做各种初始化,但是使用yaml引用各种三方SDK插件在Android编译打包的时候是自动统一生成的。Adnrodi/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

image.png

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    val channel = flutterEngine.dartExecutor.binaryMessenger.let {
        MethodChannel(
            it,
            FLUTTER_CALL_REGISTER_PLUGIN
        )
    }

    channel.setMethodCallHandler { call, result ->
        // 处理来自Flutter的方法调用
        when (call.method) {
            "registerPlugin" -> {
                // 实现你的方法逻辑
                GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine)
                result.success(true)
            }

            else -> {
                result.notImplemented()
            }
        }
    }

    try {
        flutterEngine.plugins.add(QuickActionsPlugin())
    } catch (_: Exception) {
    }

    try {
        flutterEngine.plugins.add(SharedPreferencesPlugin())
    } catch (_: Exception) {
    }

    try {
        flutterEngine.getPlugins().add(WebViewFlutterPlugin())
    } catch (_: Exception) {
    }

    val preferences = context.getSharedPreferences(
        SHARED_PREFERENCES_NAME,
        Context.MODE_PRIVATE
    )

    if (preferences.getBoolean(KEY_PRIVACY, false)) {
        super.configureFlutterEngine(flutterEngine)
    }
}

然后就是复现以及修改了,坑又来了。自己小米手机首次安装未点击同意,查看权限使用记录是空的,点击同意后才有设备传感器使用。这怎么办? 无法复现这样就算修改了也不好测试。

  • 1、盲改开始:网上找了一圈都是使用FutureBuilder去异步加载runApp(XiaoXinBaoAPP());
  • 2、开心提审被拒,以为是应用市场缓存包的原因重新提升百度、小米,再次被拒。
  • 3、不应该啊是不是他们有bug 再次提审再次被拒。

image.png

  • 4、略带喜感,会不会是百度检测引擎坏了,换小米试试

3381715568021_.pic.jpg

  • 5、依旧翻车,痛定思痛,应该是真有问题。 image.png

分析后发现:
yaml引用的插件中有获取设备传感器的,并且在初始化的时候就调用。所以只要延迟这些插件的初始化就行了。
但是具体操作起来就麻烦了,APP进入后要先获取是否同意隐私政策就涉及到SP的插件,如果统一延迟就无法获取是否同意隐私政策, 而且弹框上面有网页跳转,而网页的跳转是使用webview插件的就会导致跳转是白屏。综上就得改成初始化部分插件,涉及隐私政策的延迟初始化。

问题:

  • 全部延迟以后会导致部分功能异常

方案:

  • 部分初始化、部分延迟

实施:

  • 修改自动生成GeneratedPluginRegistrant.java 【不可行,每次又被自动编译覆盖了】
  • 修改生成类的插件,然后按需生成代码 【尝了了下,工程有点大】
  • GeneratedPluginRegistrant的入口处拦截处理 【可行】

flutter pub get以后插件会基于yaml然后生成GeneratedPluginRegistrant.java,所以直接修改产物会被覆盖,接下来就得找到GeneratedPluginRegistrant.java的调用处,经过分析发现 FlutterActrivity.java 在:

/**
 * Hook for subclasses to easily configure a {@code FlutterEngine}.
 *
 * <p>This method is called after {@link #provideFlutterEngine(Context)}.
 *
 * <p>All plugins listed in the app's pubspec are registered in the base implementation of this
 * method unless the FlutterEngine for this activity was externally created. To avoid the
 * automatic plugin registration for implicitly created FlutterEngines, override this method
 * without invoking super(). To keep automatic plugin registration and further configure the
 * FlutterEngine, override this method, invoke super(), and then configure the FlutterEngine as
 * desired.
 */
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
  if (delegate.isFlutterEngineFromHost()) {
    // If the FlutterEngine was explicitly built and injected into this FlutterActivity, the
    // builder should explicitly decide whether to automatically register plugins via the
    // FlutterEngine's construction parameter or via the AndroidManifest metadata.
    return;
  }

  GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
}

有了入口就是大致思路了:

  • 拦截入口,按需加载,同意隐私政策后全部加载。
  • 1、重写configureFlutterEngine 移除super
  • 2、按GeneratedPluginRegistrant的注册方法手动加载需要的插件 SP、webview... 【GeneratedPluginRegistrant是根据类名防止重复的,所以不用担心多次初始化】
  • 3、注册MethodChannel然后在Flutter同意后调用GeneratedPluginRegistrant的全部初始化。

改完以后提审,百度、小米机审一次通过🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

ICP、安全合规备案、软著

ICP备案:个人的话大致几个方向 博客、个人主页,剩下就是按流程都能办下来的,【审核两周左右吧,不一定】
软著 :自己申请按流程填各种表格,需要注意的是源代码60页,大于的话要前30页,后30页、 第30页要断行。【耗时较长】 APP备案:因为网站、服务器备案过以后 APP的备案只是在原有主体新增一个【耗时:ICP备案过以后很快,可以同时进行】 安全合规备案:提供带有社区性质、舆论性质的才需要申请,需要有敏感词、封禁、鉴黄等功能,这个有点麻烦要和所属地网安打交道 【个人建议功能非必要先不加这个功能,不然很难申请】

苹果

账号需要588人民币,如果有拖延症的可以先买账号,然后倒逼开发进度。 因为每年都要续费

内购、订阅机制、资质
  • 5月1日后需要APP备案才能上架,隐私合规+资质比Android市场要少一些。
  • 内购、订阅 需要测试环境审核
  • 市场图需要和APP内容一致,不能差的版本多
打包上架、Deeplink、微信登录
  • 以后再聊
GoogleStore

和苹果一样账号需付费但是是一次性的

总结下

  • Flutter组件学习、dart语法、kotlin、js、java、python 看多了以后相通的很多,有个巨大的后遗症就是混乱了,有时候编译器报错一看提示js写到dart上了,有时候快捷键等半天不响应一看有些语法不具有那些语法糖。
  • 船小好调头,相较于成熟或者公司性质的,单从原生切Flutter做决定就得好久。这也是优势,可以快速迭代试错。
  • 诚邀体验,提bug。 目前除OPPO、vivo手机搜不到。其他的在应用市场都能搜到了,关键字:『校信宝』

接下来方向

  • 更多是产品方面的思考,大方向是不会变的:提供一个供高考学生查询的工具,基于学生自身的优缺点系统性的提供一个科学的报告,基于这份报告匹配合适的学科+学校。 更像是一个多维图,只要构建的维度足够科学、详细,那匹配的学校+专业也就更加精准、合理。