总结android有多少种方式做逻辑区分适配。
-
- res/values/xxx.xml,res/values-xxx/xxx.xml
这种方式类似代码适配,在不同的values文件夹放入相同的名称的字段,配上不同的值;通过屏幕尺寸、方向、语言等方式,不再赘述;
-
- SystemProperties反射
这种方案常见于apk服役于自制的机器上,属于类似系统开发。通过系统集成的值,或者其他模块或者framework动态调整它,端上逻辑调用来区分代码逻辑。当然普通apk也可以。
比如SystemUI上有一个开关,动态切换ro.xxx.data=1或者0,端上遇到某个代码执行的时候,就可以反射SystemProperties来区分;
try { Class<?> c = Class.forName("android.os.SystemProperties"); Method get = c.getMethod("get", String.class, String.class); String value = (String) (get.invoke(c, key, defValue)); return value; } catch (Throwable e) { e.printStackTrace(); } //类似的getBoolean,getInt有很多自行百度。
备注:
针对非 SDK 接口的限制 | Android 开发者 | Android Developers
android9开始每个版本追加一些限制反射的名单。至少SystemProperties是可以使用的。
查看了android12(别问我怎么就到12了?)的名单。
屏蔽名单 (blacklist) 无论应用的目标 API 级别是什么,您都无法使用的非 SDK 接口。 如果您的应用尝试访问其中任何一个接口,系统就会抛出错误。 有条件屏蔽 (greylist-max-x) 从 Android 9(API 级别 28)开始,当有应用以该 API 级别为目标平台时,我们会在每个 API 级别分别限制某些非 SDK 接口。 这些名单会以应用无法再访问该名单中的非 SDK 接口之前可以作为目标平台的最高 API 级别 (max-target-x) 进行标记。例如,在 Android Pie 中未被屏蔽、但现在已被 Android 10 屏蔽的非 SDK 接口会列入 max-target-p (greylist-max-p) 名单,其中的“p”表示 Pie 或 Android 9(API 级别 28)。 如果您的应用尝试访问受目标 API 级别限制的接口,系统就会将此 API 视为已列入屏蔽名单。 不支持 (greylist) 当前不受限制且您的应用可以使用的非 SDK 接口。 但请注意,这些接口不受支持,可能会在未发出通知的情况下随时发生更改。预计这些接口在未来的 Android 版本中会被有条件地屏蔽,并列在 max-target-x 名单中。 SDK (whitelist) 已在 Android 框架软件包索引中正式记录、受支持并且可以自由使用的接口。
Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String; sdk system-api test-api Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; sdk system-api test-api Landroid/os/SystemProperties;->getBoolean(Ljava/lang/String;Z)Z sdk system-api test-api Landroid/os/SystemProperties;->getInt(Ljava/lang/String;I)I sdk system-api test-api Landroid/os/SystemProperties;->getLong(Ljava/lang/String;J)J sdk system-api test-api
说明我们可以放心的使用。
-
- productFlavor+productDimensions
参考 https://blog.csdn.net/jzlhll123/article/details/116700023
-
- Compiler参数
在Perfercences->搜索Compiler,添加编译参数,-Dxxxx。
任意模块下的build.gradle都可以获取得到,通过build.gradle,android下defaultConfig下面,System.getProperty获取。也可以自定一个函数。在设置属性之前初始化一个环境参数,再行获取设置BuildConfigField即可。
最终该模块下面的BuildConfig.java
就能得到这些参数。一定程度上,解决了第3种方式导致编译productflavor+dimens组合太多的尴尬境地。
// Fields from default config.
public static final String Balabala = "false";
public static final boolean USE_SN_MODE = true;
各大公司基本发布都有自带编译脚本,所以只需要如下类似的编译命令即可:
./gradlew assembleDimen1Dimen2Release -Dorg.gradle.java.home=/usr/xxx/jdk1.8.0_25 -Dmyid=10102 -DAPP_ENV=0 -DXXXX=jjj
- gradle Task copy
还有一种,就是建立不同的代码目录,通过拷贝的方式覆盖代码。
task myCustomCopyTask(type: Copy) {
from 'src/customProduct/main'
// 显式使用任务的 outputs 属性复制任务的输出文件
into 'src/main'
}
实在迫不得已才使用,实在是不建议这种方式。
有如下几个弊端:
-
别人或者自己,都容易忘记某个项目需要先执行这个脚本才能再编译;
-
由于有多份代码,修改共性问题,容易忘记修改copy的那份代码;
-
提交代码,容易把copy的代码传到主代码中去;
-
search code的时候,太多相同的,改了半天发现改到另外一个里面。
我还是推荐通过编译属性或者flavor在代码中做硬性判断。备注:
有多少种task:/数据源目录,多个目录 public AbstractCopyTask from(Object... sourcePaths) //目标目录,单一 public AbstractCopyTask into(Object destDir) //过滤文件 包含 public AbstractCopyTask include(String... includes) //过滤文件 排除 public AbstractCopyTask exclude(String... excludes) //重新命名,老名字 新名字 public AbstractCopyTask rename(String sourceRegEx, String replaceWith) //删除文件 Project 接口 boolean delete(Object... paths);