Build one apk with multiple configs and resource files.

huangapple go评论63阅读模式
英文:

Build one apk with multiple configs and resource files

问题

我有多个应用程序,但它是一个应用程序,针对多个领域有多种变体。

对于每个应用程序,我都有一个单独的包,其中包含AppConfig文件和包含每个领域图像的res文件夹。

路径:app/src/eu/java/in/AppConfig;
app/src/com/java/in/AppConfig;
app/src/fr/java/in/AppConfig 等等。

并且为每个构建都有单独的productFlavors。
我为每个领域制作单独的apk。

但现在我需要制作一个应用程序,应用程序本身必须在运行时根据用户的更改更改配置和资源。

例如,用户选择领域的菜单,并且应用程序必须为所有应用程序获取正确的配置。

我如何实现这个解决方案?如何构建一个在运行时更改其资源和配置的应用程序?

object AppConfig {
    const val oauthToken = "url"
    const val oauthClientID = "id"
    ....
}
//build.gradle example
productFlavors {
        dev {
            applicationId "dev.in.app"
            dimension "default"
        }
        eu {
            applicationId "eu.in.app"
            dimension "default"
        }
        ....
    }
英文:

I have multiple applications but it is one application with many flavors for many domains.

For every app I have separate package with AppConfig file and res folder which contains images for every domain.

Paths: app/src/eu/java/in/AppConfig;
app/src/com/java/in/AppConfig;
app/src/fr/java/in/AppConfig and etc.

And separate productFlavors for every build.
I'm making for every domain separate apk.

But now I need to make one app and app itself must on run time change configs and resources then user change it.

For example menu where user choose domain and app must take for all application correct configs.

How I can achieve this solution? How can i build one opp which change on runtime its resources and configurations.

//config example
object AppConfig {
    const val oauthToken = "url"
    const val oauthClientID = "id"
   ....
}
//build.gradle example
productFlavors {
        dev {
            applicationId "dev.in.app"
            dimension "default"
        }
        eu {
            applicationId "eu.in.app"
            dimension "default"
        }
        ....
    }

答案1

得分: 1

总结一下,您目前有一个多种口味的应用程序,需要重新构建以进行更改,而您希望最终用户能够切换?

不幸的是,没有“键入此代码就能运行”的解决方案,您需要重新设计您的应用程序。您需要从Gradle变体切换转移到代码内的变体切换。总体方法将是:

  1. 从多种代码转变为应用程序内的包。这将使您的所有文件/配置都在您的单一口味中可用。例如,/eu/java/...MyFile.kt 需要变成 /java/.../eu/MyFile.kt
  2. 弄清如何在不同的变体之间过渡。这非常依赖于您的情况,但一种方法是使用变体的枚举,然后存储当前活动的变体(例如,内存中、共享偏好设置、Room、从服务器获取)。
  3. 在您当前执行诸如 isFeatureEnabled 之类的操作的地方,您需要确保这些检查关注当前选择的变体,而不是固定的值。

使用您的 AppConfig 示例,您可以拥有一个包含 EuConfigUsConfig 等定义这些值的 /Config/ 目录。然后,有一个 ConfigManager 类允许用户更改活动的配置并查找所需的功能。对于资源(字符串、颜色),您有两种解决方案:

  1. 每个变体都有自己的UI,其资源是硬编码的(例如 R.string.eu_title 而不是 R.string.title)。
  2. 每个资源都是动态加载的,可以通过字符串串联或某种资源获取助手来实现。这将需要大量测试。

最终,这可能需要相当多的工作,如果没有关于您确切设置的更多信息,很难提供详细的答案。幸运的是,当我以前执行类似的迁移时,工作并不一定很“难”,只是需要一些思考和大量测试。

编辑:刚刚意识到我在3年前回答了类似的问题,那里有更多有用的提示!

英文:

To summarise, you currently have a multi-flavour app that requires a rebuild to change, and you instead want end users to be able to switch?

Unfortunately there's no "type this code and it'll work" solution, you're going to need to rearchitect your app. You're going to need to move away from Gradle variant switching to in-code variant switching. The overall approach will be something like:

  1. Transition from multiple flavours of code to packages within your app. This will make all of your files / configs available in your single flavour. For example /eu/java/...MyFile.kt needs to become /java/.../eu/MyFile.kt.
  2. Figure out how to transition between your variants. This is very dependant on your situation, but one way is an enum of variants, then a store of which one is currently active (e.g. in memory, shared prefs, Room, from server).
  3. Everywhere you currently do things like isFeatureEnabled, you need to ensure these checks pay attention to the currently selected variant instead of being fixed values.

Using your AppConfig example, you might have /Config/ directory with EuConfig, UsConfig etc defining those values. Then, a ConfigManager class that lets the user change the active Config and look up which features are required. For resources (strings, colours) you have 2 solutions:

  1. Each variant has their own UI, with their resources hardcoded (e.g. R.string.eu_title instead of R.string.title).
  2. Each resource is dynamically loaded, either by string concatenation or some sort of resource fetching helper. This will need a lot of testing.

Ultimately this is probably going to take a fair bit of work, and it's hard to answer in detail without more information on your exact setup. Luckily when I've performed similar migrations before the work isn't necessarily hard, just takes a bit of thought and lots of testing.

Edit: Just realised I answered a similar question 3 years ago, there's more helpful tips there!

huangapple
  • 本文由 发表于 2023年4月13日 20:49:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76005628.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定