Android开发升级AGP7.0后的一些适配方法技巧

目录
  • 升级
  • Java11配置
  • AndroidComponentsExtension
    • onVariants
    • Component Artifacts
  • registerJavaGenerateTask 没了
  • 其他
    • 启动configuration cache
  • 结尾

升级

年初了,我们打算升级下apg,这样之后就拥抱下jetpack compose了!!

想用comopse有两个必选项agp7.0和kotlin版本1.5.31.

Java11配置

因为apg7.0需要把所有的module编译环境切换到java11版本上,然而这个地方很容易出错。

首先是命令行的配置,对于mac用户来说,可能会写死默认的java环境到1.8. 这个时候我们需要做的就是删除bashprofile内的java配置。

同时最好用命令行java --version尝试下输出的版本是不是java11。

as的配置则相对来说比较简单了。

按照这两个配置完成之后重新同步下工程应该就行了。

AndroidComponentsExtension

之前在现在准备好告别Transform了吗?的文章就简单展开了下AndroidComponentsExtension,新版本更换了extensions。这次给项目升级适配之前写的哪些插件,想了想用新不用久吗,尝试下新东西了。

onVariants

以前在写android插件的时候很多时候都需要在gralde的afterEvaluate方法执行之后才能获取到很多安卓对应的属性。

这次在v2的api中,则提供了非常多不同的节点,让我们在不同的阶段做一些不同的事情。

比如说onVariants,beforeVariants,finalizeDsl这三个不同的阶段,正常情况下我们选择onVariants就足够了。

Component Artifacts

demo 地址

Gralde 内的一部分核心是Task,但是要想写好一个Task其实并没有想象中的那么容易。特别是一个CacheableTask,他更多的关注与他们的输入和输出。

构建缓存(build cache)的工作原理是:在缓存中存储已编译的类、测试输出和其他构建构件,同时考虑所有的任务输入,包括输入文件内容、相关类路径和任务配置。

所以在AGP 7.0中,提供了这部分新的api让我们简化对于task,输入输出这些参数的优化,让我们可以更专注到我们想要变更的东西上。

比如说APk,MANIFEST,MAPPING_FILE,BUNDLE,AAR或者其他的一些编译产物,当前agp给我们提供的也是相对来说比较少的一部分功能。

另外一点就是,我们如果想要知道一个task的输入其实如果不去阅读源码,之后获取对应的路径或者源代码,其实是一个非常繁琐的过程。之后还要通过变更dependon或者finalizedBy等等手段将任务插入到编译流程内。

所以就有了我们这次的其中一个主角Artifacts,他主要负责帮助我们将我们的task,插入到编译流程内,让我们尽量少的关注到输入和输出。

          // 生成TaskProvider
          val taskProvider = project.tasks.register(
                  "manifestCopy${variant.name}Task",
                  ManifestSampleTask::class.java
          )
          // 获取variant的artifacts之后将Task转化成我们所想要的
          variant.artifacts.use(taskProvider).wiredWithFiles(
                  ManifestSampleTask::mergedManifest,
                  ManifestSampleTask::outputManifest
          ).toTransform(SingleArtifact.MERGED_MANIFEST)

这个就是官方的一个Artifacts的简单的使用了,通过变化我们可以轻松的完成一个有获取合并后的Manifest作为输入,之后以另外一个Manifest文件作为输出的一个task。而且会被直接添加编译流程内,就不需要我们关心他们的前置和后置任务是啥了。

registerJavaGenerateTask 没了

原先在v1的api上吧,有registerJavaGenerateTask这个方法,能加一些简单的代码生成的操作,比如j神以前生成的R2就是通过挂载这个方法。

这次v2版本我在AndroidComponentsExtension中没找到的对应的api,所以只能无中生友,自己搞一个出来咯。

@Override
 public void registerJavaGeneratingTask(@NonNull Task task, @NonNull File... sourceFolders) {
     getVariantData().registerJavaGeneratingTask(task, sourceFolders);
 }
 open fun registerJavaGeneratingTask(
    task: Task,
    generatedSourceFolders: Collection<File>
) {
    @Suppress("DEPRECATION")
    taskContainer.sourceGenTask.dependsOn(task)
    val fileTrees = extraGeneratedSourceFileTrees ?: mutableListOf<ConfigurableFileTree>().also {
        extraGeneratedSourceFileTrees = it
    }
    for (f in generatedSourceFolders) {
        val fileTree = services.fileTree(f).builtBy(task)
        fileTrees.add(fileTree)
    }
    addJavaSourceFoldersToModel(generatedSourceFolders)
}

我仔细观察了下registerJavaGenerateTask的源代码,发现其中只做了两件比较简单的事情。将Task挂载到generateVariantResources任务之后,然后将生成java类的文件夹加入到sourcetSet上去,这样就行了。

sourcetSet就是javac的将java转化成class的编译路径。

所以相对来说就比较简单,我们用新的api模拟出原来的效果就差不多可以了,我们只要找到挂载的任务之后,顺便把代码添加到java和kotlin的sourceset中就行了

fun Project.registerJavaGenerateTask(
    variant: String?,
    task: TaskProvider<out Task>,
    generatedSourceFolders: Collection<File>
) {
    if (variant.isNullOrEmpty().not()) {
        variant?.apply {
            // 因为task生成在配置阶段完成之后
            afterEvaluate {
                findJavaGenerateTask(variant)?.dependsOn(task)
            }
            //  获取最新版本sourceSet
            val application = extensions.findByType(ApplicationExtension::class.java)
            application?.sourceSets {
                findByName(variant)?.apply {
                    generatedSourceFolders.forEach {
                        java.srcDir(it)
                        kotlin.srcDir(it)
                    }
                }
            }
        }
    }
}

这次升级适配主要的代码就是这个了,其实代码量上来说不多。但是把有个坑点,之前因为偷懒就直接用了setSrcDirs这个api,所以文件就被覆盖了。导致了一部分代码没有被编译成class,导致了classnotfound异常。

其他

获取applicationId,我们的插件内有对于应用id的判断,之后进行不同的manifest pleaceholder的调整。逻辑比较简单,只是切换了新版本的api而已。

  if (variant is ApplicationVariant) {
    val applicationId = variant.applicationId.get()
      variant.manifestPlaceholders.put("xxxxx", applicationId)
  }

对resValue插入新的string或者values。也是原来就有的能力,但是要对新版本进行一次小小的适配和更换。

private fun addAPGClassFile(config: Variant, key: String, value: String) {
    val resValue = ResValue(value)
    val reskey = config.makeResValueKey("string", key)
    config.resValues.put(reskey, resValue)
}

启动configuration cache

启用配置缓存的操作,本质上是在项目的 gradle.properties 文件中设置了环境变量 org.gradle.unsafe.configuration-cache=true。

结尾

AGP对我们这个算是每年一更新了,会碰到一些新的有趣的api以及新的写法。另外每次新版本的AGP对于编译上都有变更和优化,更多关于AGP7.0升级适配的资料请关注我们其它相关文章!

时间: 2022-06-21

Android实现跳转到应用市场进行版本更新功能

现在版本更新有两种处理方式: 跳转到App应用市场,通过应用市场下载更新安装.在App内进行Apk下载,下载完成后更新安装. 实现思路: 请求后台数据,根据返回版本号对比判断应用是否需要进行更新操作.若不需要,跳转到登录或应用主界面,若需要进行弹框,让用户选择是否进行更新操作.若用户选择取消更新,则跳转到登录或应用主界面若用户选择更新,则判断手机内是否安装需要进入的应用市场APP.如果已安装,那么根据包名直接进入应用市场的详情页面下载apk.如果未安装,那么根据后台返回的地址,用浏览器打开进行下

Android应用开发的版本更新检测升级功能实现示例

目录 一.版本的基础知识 (一)versionCode (二)versionName (三)版本控制小结 (四)版本控制的文件位置 (五)版本信息的获取,代码 (六)版本更新中重要的代码块: 1.获取本程序的版本号和版本名 2.从服务器获取到一串json数据 3.检测是否更新的代码 4.弹出更新提示的对话框的代码 5.下载新版本程序的代码 6.根据Uri网址获得apk文件对象的代码 7.安装apk文件的代码 二.程序更新的简单示例一 (一)MainActivity的设计 (二)布局文件的设计 (

Android仿微信发送语音消息的功能及示例代码

微信的发送语音是有一个向上取消的,我们使用onTouchListener来监听手势,然后做出相应的操作就行了. 直接上代码: //语音操作对象 private MediaPlayer mPlayer = null; private MediaRecorder mRecorder = null; //语音文件保存路径 private String FileName = null; FileName = Environment.getExternalStorageDirectory().getAbs

PHP+Ajax实现的检测用户名功能简单示例

本文实例讲述了PHP+Ajax实现的检测用户名功能.分享给大家供大家参考,具体如下: 一 代码 fun.js: function chkUsername(username){ if(username==''){ //判断用户名是否为空 alert('请输入用户名!'); }else{ var xmlObj; //定义XMLHttpRequest对象 if(window.ActiveXObject){ //如果是浏览器支持ActiveXObjext则创建ActiveXObject对象 xmlObj

Android画板开发之撤销反撤销功能

一.分析 这篇将会讲解撤销反撤销功能的实现,先讨论一下这个原理是怎么样实现的. 每次撤回的内容,内容是怎么定义呢? 其实就是每一笔,每一笔作为撤回的内容,那每一笔怎么算呢,就是算手指从按下-移动-放开这一个过程就是一笔. 我们只需记录这个过程为一笔,然后用一个已画列表list列表来记录这个过程的paint画笔和路径path. 撤销的时候就把后面的一个数据移到另一个撤销列表 反撤销的时候,就把撤销列表的最后面那条数据移动到已画列表. 然后,还有一个重点,就是画笔的保存数量,上面说记录每一笔画笔,这

Android应用开发中Fragment与Activity间通信示例讲解

首先,如果你想在android3.0及以下版本使用fragment,你必须引用android-support-v4.jar这个包 然后你写的activity不能再继承自Activity类了,而是要继承android.support.v4.app.FragmentActivity,一些其他的父类也有相应的变化. 由于在android的实现机制中fragment和activity会被分别实例化为两个不相干的对象,他们之间的联系由activity的一个成员对象fragmentmanager来维护.fr

Android编程实现自定义Dialog的大小自动控制方法示例

本文实例讲述了Android编程实现自定义Dialog的大小自动控制方法.分享给大家供大家参考,具体如下: Android应用开发中,无论是出于功能还是增加用户体验,弹出对话框(Dialog)进行一些操作提示是非常必要的.Android系统有自带的各种样式的对话框,但是根据项目需要可能从效果上满足不了需求,只时我们就要自定义对话框. 我们可以自定义Dialog的样式及展示布局,做出我们想要的对话框,但有的时候,我们做出的对话框要么显示太大,要么显得太小,或者是在不同的页面大小不一样,需要做个统一

Android开发之手势检测及通过手势实现翻页功能的方法

本文实例讲述了Android开发之手势检测及通过手势实现翻页功能的方法.分享给大家供大家参考,具体如下: 手势是指用户手指或触摸笔在触摸屏上的连续触碰的行为,比如在屏幕上从左至右划出的一个动作,就是手势,再比如在屏幕上画出一个圆圈也是手势.手势这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形.Android对两种手势行为都提供了支持: 1. 对于第一种手势行为而言,Android提供了手势检测,并为手势检测提供了相应的监听器. 2. 对于第二种手势行为,Android允许开

基于Android studio3.6的JNI教程之ncnn人脸检测mtcnn功能

代码链接: https://github.com/watersink/mtcnn-linux-as 本代码可以在模拟器下进行跑. 环境: windows10 Android studio 3.6 Sdk:android10 api 29 Ndk:r15c Ncnn:20200226 Linux下的代码测试: cd mtcnn_linux/build cmake .. make ./mtcnn 如果可以跑通,输出正确结果,证明mtcnn代码的准确性. 实际操作的时候,首先基于linux把c++代码

Android Camera开发手电筒功能

这是一个简单的运用Android Camera开发手电筒功能,AndroidManifest.xml文件的入口是startapp,这个文件没上传上来,大家可以自己写. flashlight.java package com.android.app; import android.app.Activity; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.os.Bun

Android开发中应用程序分享功能实例

本文实例讲述了Android开发中应用程序分享功能.分享给大家供大家参考,具体如下: Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); //设置类型 shareIntent.setType("text/plain"); //设置分享的主题 shareIntent.putExtra("android.intent.extra.SUBJECT", "分享&

Android开发实现各种图形绘制功能示例

本文实例讲述了Android开发实现各种图形绘制功能.分享给大家供大家参考,具体如下: 这里结合本人的开发事例,简单介绍一下如何在Android平台下实现各种图形的绘制. 首先自定义一个View类,这个view类里面需要一个Paint对象来控制图形的属性,需要一个Path对象来记录图形绘制的路径,需要一个Canvas类来执行绘图操作,还需要一个Bitmap类来盛放绘画的结果. Paint mPaint = new Paint(); mPaint.setAntiAlias(true); mPain