国外人的思考方式

国外人的思考方式

31 October 2017


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

  • 能不能实现?

  • 能不能解决?

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




问题追溯


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

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

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
当然,可用可不用。




问题探索


String rPackage = processResources.packageForR  


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


总结

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




尝试解决


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


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 方法内:


@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;
                                    }
                                });
    ...                            
}


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


public class ProcessAndroidResources extends IncrementalTask {  
    ...

    private VariantScope variantScope;

    ...
}


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


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
}


然后,之前的:


String rPackage = processResources.packageForR  


可以改为:


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 的大小。只是一个在编译时期的工具。


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