前言
随着2021年即将结束,因技术迭代的升级,不少公司开始使用AndroidX代替原有的各种support包,升级势在必行
AndroidX升级
-
升级所需环境
1.androidx的最低使用条件
AndroidStudio 3.2.0+
gradle:gradle-4.6+
compileSdkVersion 28
2.本次升级配置
AndroidStudio 3.5.2、AndroidStudio 2020.3.1 Patch 4 ...etc
gradle-5.4.1
compileSdkVersion 28
targetSdkVersion 26 -
操作步骤
1.点击Refactor > Migrate to AndroidX
image.png
2.点击Migrate
image.png
此处的Backup project as Zip file
是转换前的本地备份可以不勾选,省去本地备份时间直接进入转换环节
3.点击 DO Refactor
image.png
貌似可选择转换哪些文件,默认是为全部文件,本次升级是全部故直接点
4.转换后可能会有转换错误,使用项目组给出的.py扫描工具也可使用快捷键ctrl+shift+r
全局搜索并替换,搜索内容见如下错误转换对照表
(转换后一旦有错误内容,此时build项目无法通过,有文章反馈编译无报错,个人猜测使用了4.0的AS版本,3.6以上AS进行过"核"的升级产生了BUG,以前遇过一次再未升3.6+)
5.更改完毕本地验证无明显crash后,源码提交git分支 -
组件升级
由于当前组件暂未发现可直接独立编译,故需依附主工程进行置换
1.找leader添加权限,使用git管理工具clone下相关分支代码
2.在工程中添加组件依赖,添加完成后将对应在线版本注释掉
3.重复上述【操作步骤】的操作
4.本地跑测无问题后点击右侧gradle->选中对应组件->点击uploadArchives
将包打包进maven库
9009142-db98c3fcf62e7c02.png
5.maven打包配置,在gradle.properties配置权限账号,POM_NAME
,POM_VERSION
;据悉以-SNAPSHOT
为后缀名的可无限覆盖打包否则需将版本号增加,具体详情咨询项目组
6.向git仓库提交组件代码,确保组件与主工程代码同时无问题后可提交代码,此时可能会遇到git的问题,可查看下文【本次升级所遇问题】
x64升级
x64升级主要涉及cpu abi这块知识,关于这块知识可以百度搜索查看一番,本次升级主要对原有32位的内容进行适配,涉及C及C++代码编译,本次采用cmake方式进行编译打出so包
-
环境:
pc:win10
ndk:21.3.6528147
cmake:3.10.2.4988404
AndroidStudio:3.5.2
另有ndk-build方式未使用,cmake方式需注意ndk库问题,cmake工具与ndk库貌似有着版本匹配问题,曾遇到过cmake遇到高版本库包ndk路径找不到错误。 - 操作步骤:
-
AS中新建C++模板的工程应用
image.png
-
将需要编译源码文件复制进工程中
调用的jni方法在头文件中已有声明,相关代码亦无需改写,打出的包名前缀“lib”关键字不可删除,否则在System.loadLirary()方法中会报错,本次按如下配置直接编译打包即可 -
在Module的build文件defaultConfig{}方法中添加ndk配置
android {
...
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64','armeabi','mips','mips64'
}
}
abiFilters主要配置so包目标架构版本,当前主流cpu架构如配置所列,本次目标配置为打出64位包故只配置arm64-v8a,需提醒apk包瘦身一般大头就是so包,故兼容与体积
需斟酌
- CmakeLists分别配置打包
CMakeLists.txt文件中分别配置如下代码,当前打包方式是配置一次打一个so包,故若打多个so包需配置多次,打的包有两种一种是debug包,可输出调试日志,一种是release包较为安全,release的so包直接对工程进行签名打包即可得到,编译过程中最好清除.cxx文件夹与build文件夹否则可能输出错误,本次目标打出两份源码的.so包文件,故其CmakeLists配置分别如下:
//OfflineXXXCode.so包配置
cmake_minimum_required(VERSION 3.4.1)
add_library(
cnXXXCode
SHARED
cnXXXCode.cpp
com_xxx_mobile_apa_staffcode_jni_OfflineXXXCodeJNI.cpp
)
find_library(log-lib log)
target_link_libraries(cnXXXCode android ${log-lib})
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -s")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -s")
//scancode_encode_GeneratorCode .so包配置
cmake_minimum_required(VERSION 3.4.1)
add_library(
XXXCode
SHARED
aes.c
encryption.c
com_xxx_mobile_apa_scancode_encode_GeneratorCode.c
)
# 头文件目录
include_directories( ${PROJECT_SOURCE_DIR})
find_library(log-lib log)
target_link_libraries(XXXCode android ${log-lib})
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -s")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -s")
本次升级所遇问题
- 转换后依旧错误问题
本次升级使用Migrate to AndoridX以后部分控件转换名称异常,需手动处理,下方有一份对照表可参考 - 启动黑屏时长过长问题
本次升级过程中发现冷启动达到4.7s左右,从application建立到开屏页聚焦差不多1.5s~1.7s+之间,符合冷启动2s标准,但查日志中有3秒停滞有点诡异,未明原因,因见到打包日志中有xposed怀疑有编译时字节码插入等故未深查
2021-12-21 19:35:13.223 19798-19798/com.xxx.mobile.apa I/ning.mobile.ep: The ClassLoaderContext is a special shared library.
2021-12-21 19:35:16.105 19798-19798/com.xxx.mobile.apa I/Perf: Connecting to perf service.
- 环境选择页面窗口泄露问题
发现测试包中有泄露问题,推断测试包无法准确进行性能测试 - Glide库问题
12-27 14:43:02.250 30049 30049 E AndroidRuntime: FATAL EXCEPTION: main
12-27 14:43:02.250 30049 30049 E AndroidRuntime: Process: com.xxx.mobile.apa, PID: 30049
12-27 14:43:02.250 30049 30049 E AndroidRuntime: java.lang.NoSuchMethodError: No virtual method placeholder(I)Lcom/bumptech/glide/request/RequestOptions; in class Lcom/bumptech/glide/request/RequestOptions; or its super classes (declaration of 'com.bumptech.glide.request.RequestOptions' appears in /data/app/com.xxx.mobile.apa-gzUOAXBojHInZ80NNkkQTQ==/base.apk!classes2.dex)
12-27 14:43:02.250 30049 30049 E AndroidRuntime: at com.xxx.mobile.apa.creditcard.utils.GlideUtils.loadImage(GlideUtils.java:35)
报错如上,在升级过程中遇到glide4.3.0版本与X兼容问题,网上有很多blog说4.9版本即有AndroidX经本人查看自4.10版本才开始有,4.9是没有的,本次为与融合项目一致采用版本为4.11.0
- httpClient
由于AndroidX的编译版本是api28故开始时设置targetSdkVersion 28
与其保持一致,但由于项目中使用了HttpClient这个网络包,报了找不到类的错误,经查是谷歌与Apache关于此包使用的历史争端,在api28时谷歌移除内置包由开发者自行选择,故报此错误,又处理时搜索公司Maven库中无此包,遂降低至api27(27又发生问题) -
git同步问题
主工程添加依赖后发现无法同步分支,File->Setting->Version Control
image.png
无效可尝试使用命令git branch --set-upstream-to [分支名]再无效则分别用AS打开对应组件工程直接提交
- Activity透明主题与其设置方向冲突问题
发生条件:
1.运行机Android系统为8.0
2.targetSdkVersion > 26
3.Activity设置透明背景或设置float + 设置android:screenOrientation="portrait"
需同时满足三者条件,此bug仅限于8.0系统,后续版本系统源码删除了问题代码
说明:将targetSdkVersion设置为27后遇到一个系统问题,在8.0手机上Crash并报错Only fullscreen opaque activities can request orientation
,这是谷歌开发人员因为一个理念产生的一个愚蠢至极的bug,观源码其理念就是有悬浮或者透明的Activity都不得固定方向,据说Dialog形式Activity的方向被希望由父容器控制
,经过搜索并亲自对8.0系统源码查看确认为仅8.0系统的手机有此bug,8.1时已经移除此段代码
E7D10D031563814009152D3AE4ED8E2E.png
3种处理方式:
1.反射处理设置方向
a、封装方法1
b、封装方法2:
/**
* 在oncrate()里super()之前调用
* Only fullscreen opaque activities can request orientation
*/
protected void fitTranslucent(){
if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O
&&Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
changeTranslucentOrient();
}
}
private void changeTranslucentOrient(){
try {
Field field = Activity.class.getDeclaredField("mActivityInfo");
field.setAccessible(true);
ActivityInfo activityInfo = (ActivityInfo)field.get(this);
activityInfo.screenOrientation = -1;
field.setAccessible(false);
} catch (Exception e) {
e.printStackTrace();
}
}
2.在onCreate()里super()与setContentView()之间重设非透明主题
//setContentView之前调用
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.O)){
setTheme(R.style.xxxx)
}
3.设置targetSdkVersion 26,本次升级采用此法,因本次组件与主工程有很多Activity未继承自定义的基类Activity又组件与主工程未将公共基础部分抽取成单独组件,改动量太大,故采取降低版本的方法,节约时间
- 科大讯飞问题
科大讯飞主要是因为升级64无对应包问题,咨询项目组得知原开发项目组解散,账号无法找回,又工单咨询官方,其sdk下载与账号及其平台创建的应用有关,所以本次科大讯飞功能隐藏
Crash
glide版本兼容发生崩溃
jni方法调用未找到方法崩溃
xml引用已剔除组件控件崩溃
8.0手机透明Activity与方向设置冲突导致的崩溃
记录整理
-
错误转换对照表
如下为本次发现异常转换记录,应该不全,可提供上述【组件升级】操作查询使用
错误转换 | 正确转换 |
---|---|
android.support.annotation. | androidx.annotation. |
androidx.appcompat.widget. | androidx.recyclerview.widget. |
android.support.v7.widget. | androidx.recyclerview.widget. |
androidx.core.view.ViewPager | androidx.viewpager.widget.ViewPager |
androidx.core.view.PagerAdapter | androidx.viewpager.widget.PagerAdapter |
androidx.core.app.FragmentManager | androidx.fragment.app.FragmentManager |
androidx.core.app.Fragment | androidx.fragment.app.Fragment |
androidx.core.app.DialogFragment | androidx.fragment.app.DialogFragment |
androidx.core.app.FragmentActivity | androidx.fragment.app.FragmentActivity |
androidx.core.app.FragmentTransaction | androidx.fragment.app.FragmentTransaction |
androidx.core.content.LocalBroadcastManager | androidx.localbroadcastmanager.content.LocalBroadcastManager |
android.arch.core | |
android.arch.lifecycle | |
android.arch.paging | |
android.arch.persistence |
- AndroidX旧包对照表
https://www.jianshu.com/p/1466ebefe4d0 - 项目组给予的Python脚本(py2.7)
import os
DEBUG=True
allSearchKeywords=[]
#根据文件扩展名判断文件类型
def endWith(s,*endstring):
array = map(s.endswith,endstring)
if True in array:
return True
else:
return False
#将全部已搜索到的关键字列表中的内容保存到result.log文件中
def writeResultLog(allExistsKeywords):
#行分隔符
ls = os.linesep
#结果日志文件名
logfilename = "C:\\Users\\dell\\Desktop\\AndroidX_Improve_Version\\result.log" #相对路径,文件在.py文件所在的目录中
try:
fobj = open(logfilename,'w')
except IOError as e:
print("*** file open error:",e)
else:
fobj.writelines(['%s%s' % (keyword,ls) for keyword in allExistsKeywords])
fobj.close()
#搜索指定关键字是否在指定的文件中存在
def searchFilesContent(dirname):
for keyword in allSearchKeywords:
print("***keyword",keyword)
allExistsKeywords=[]
for (root,dirs,files) in os.walk(dirname):
for file in files:
#只在扩展名为.java/.xml文件中搜索
if endWith(file,'.java','.xml'):
#打开文件
filename = root + os.sep + file
filename = filename.replace("\\","\\\\") #将路径中的单反斜杠替换为双反斜杠,因为单反斜杠可能会导致将路径中的内容进行转义了,replace函数中"\\"表示单反斜杠,"\\\\"表示双反斜杠
try:
fobj = open(filename,'r', encoding='utf-8');
print("***search",filename)
except IOError as e:
print("*** file open error:",e)
else:
#遍历文件的每一行
for fileLine in fobj:
#判断当前行是否包含所有搜索关键字
for keyword in allSearchKeywords:
if keyword in fileLine:
print("***hit",filename)
allExistsKeywords.append("***hit "+keyword+" path: "+filename)
fobj.close()
#全部文件遍历结束
writeResultLog(allExistsKeywords)
print ("OVER!")
#仅当本python模块直接执行时,才执行如下语句,若被别的python模块引入,则不执行
if __name__ == '__main__':
allSearchKeywords=["androidx.appcompat.widget.RecyclerView","androidx.core.app.Fragment","androidx.core.app.FragmentActivity","android.support.v4"
,"androidx.core.view.ViewPager","android.arch.core","android.arch.lifecycle","android.arch.paging","android.arch.persistence"]
#搜索的project路径
searchFilesContent(r"G:\CNJRWorkSpace")
# searchFilesContent(r"D:\dev\mainProject\CNMain_android")
#searchFilesContent(r"D:\dev\mainProject\CNFCommonKits_android\src\main\res\layout")
网友评论