国外人的思考方式

国外人的思考方式

31 October 2017 Android


很多时候,我们遇到问题,或者为了实现功能。
都回去寻求各种方案,针对这些方案,我们很可能就考虑:

  • 能不能实现?

  • 能不能解决?

其实,当我们 找到了实现方案并且实现了,或者 找到了解决方案并且解决了。都会觉得瞬间喜悦。而又稍微 忽略了一些东西



问题追溯


由于 Android Studio 3.1 Canary 发布,也带来了 Android gradle plugin 3.1.0-alpha01

随之而来的问题就是:R2 插件 运行异常了!

`{r, engine='bash', count_lines} Error:A problem occurred configuring project ':app'.

Could not determine the dependencies of task ':app:processDebugResources'. Could not resolve all dependencies for configuration ':app:debugCompileClasspath'. A problem occurred configuring project ':widget'. Failed to notify project evaluation listener. com.android.build.gradle.tasks.ProcessAndroidResources.getPackageForR()Ljava/lang/String; `



R2 插件


R2 插件 是从 butterknife 项目提取出来的。


作用:在 预编译期间 processDebugResources 任务执行后,执行 自定义任务 复制一份 R class 成为 R2 class

R2 class 与 R class 的区别:在于 R2 classfield 都是 final 常量。R classlibrary module 的情况下 不是常量。这样就能保证任何 module 都可以取到 finalid


为什么finalid

  • switch

  • 自定义注解value


其实这些看似不必要,但是侵入式的,会额外多一份 R2 class
当然,可用可不用。



问题探索


`{r, engine='groovy', count_lines} String rPackage = processResources.packageForR


<br>

这句话引发的异常。原因是没有 `getPackageForR()` 方法。这里是 **kotlin** 语句,会自动调用 `getPackageForR()` 方法。

<br>

**总结**:

看了 **Android gradle plugin 3.1.0-alpha01** 版本 的 `ProcessAndroidResources` 源码,发现没有 `packageForR Field` ,更没有 `getPackageForR()` 方法 。

<br>
<br>

# 尝试解决

<br>

**Android gradle plugin 3.0.0 版本** 的 `ProcessAndroidResources` 源码 中发现一段:

<br>

```{r, engine='java', count_lines}
String packageForR = null;
File srcOut = null;
File symbolOutputDir = null;
File proguardOutputFile = null;
File mainDexListProguardOutputFile = null;
if(generateCode) {
    packageForR = this.originalApplicationId;
    srcOut = this.getSourceOutputDir();
    if(srcOut != null) {
        FileUtils.cleanOutputDir(srcOut);
    }

    symbolOutputDir = (File)this.textSymbolOutputDir.get();
    proguardOutputFile = this.getProguardOutputFile();
    mainDexListProguardOutputFile = this.getMainDexListProguardOutputFile();
}


但是,讲述到了和 originalApplicationId 有关。而且这里的是 局部变量,值得参考,但是不用完全确定。也有可能 if 不走的话,就有问题。

关于成员变量 packageForR 的赋值是在 ConfigAction#execute 方法内:


`{r, engine='java', count_lines} @Override public void execute(@NonNull ProcessAndroidResources processResources) { final BaseVariantData variantData = variantScope.getVariantData();

...

final GradleVariantConfiguration config = variantData.getVariantConfiguration();

...

processResources.packageForR =
                    TaskInputHelper.memoize(
                            () -> {
                                String splitName = config.getSplitFromManifest();
                                if (splitName == null) {
                                    return config.getOriginalApplicationId();
                                } else {
                                    return config.getOriginalApplicationId() + "." + splitName;
                                }
                            });
...                            

}


<br>

可以说,需要获取 `variantScope` 就可以了。     
但是,属于一个 **私有的成员变量**:

<br>

```{r, engine='java', count_lines}
public class ProcessAndroidResources extends IncrementalTask {
    ...

    private VariantScope variantScope;

    ...
}


于是就想到了,用 kotlin 写一个反射获取 VariantScope 的方法。


`{r, engine='groovy', count_lines} private fun ProcessAndroidResources.getVariantScope(): VariantScope { val property = ProcessAndroidResources::class .declaredMemberProperties .find { it.name == "variantScope" } as KProperty1<, > property.isAccessible = true val value = property.getter.call(this) return value as VariantScope }

<br>

然后,之前的:

<br>

```{r, engine='groovy', count_lines}
String rPackage = processResources.packageForR


可以改为:


{r, engine='groovy', count_lines} val variantScope = processResources.getVariantScope() val variantData = variantScope.variantData val config = variantData.variantConfiguration val splitName = config.splitFromManifest val rPackage = if (splitName == null) { config.originalApplicationId } else { config.originalApplicationId + "." + splitName }



思考方式


https://github.com/JakeWharton/butterknife/pull/1128


JakeWharton 的回答:不是一个合适的解决方案,因为在插件的内部增加了更多的依赖关系


这次改动确实增加了 kotlin.reflect 的依赖。

可能国外人认为精简引入过多的依赖比较好。而且此次 Android gradle plugin 3.1.0-alpha01 的改动,导致 ProcessAndroidResources 删了一些 field 所致。

如果为此引入过多依赖,去达到目的、去实现。可能国外人认为得不偿失,我也想到他的解决方案可能是去 google issue 提个 feature。期望下个 alpha 版本的 android gradle plugin 增加这个 field

但是,回过头来想,获取工作紧急上就这么写了。也有理由,因为 gradle plugin 不影响 apk 的大小。只是一个在编译时期的工具。


或许,以后,我应该以更长远的未知领域为目标,去看待,去想想这些角度和问题