任务
- 解决调试热修复框架生成补丁失败的问题
- 还是解决项目的ANR问题
- WebView嵌套ScrollView滑动冲突
热修复框架
热修复生成补丁失败,报错如下:
"org.jf.util.ExceptionWithContext: Exception occurred while writing code_item for method Activity路径;->init()V"
"\tat org.jf.dexlib2.writer.DexWriter.writeDebugAndCodeItems(DexWriter.java:825)"
"\tat org.jf.dexlib2.writer.DexWriter.writeTo(DexWriter.java:268)"
"\tat org.jf.dexlib2.writer.DexWriter.writeTo(DexWriter.java:246)"
"\tat org.jf.smali.Smali.assemble(Smali.java:130)"
"\tat com.taobao.sophix.impl.PatchCommand.start(PatchCommand.java:373)"
"\tat com.taobao.sophix.Main.main(Main.java:34)"
"Caused by: org.jf.util.ExceptionWithContext: Error while writing instruction at code offset 0x12"
"\tat org.jf.dexlib2.writer.DexWriter.writeCodeItem(DexWriter.java:1098)"
"\tat org.jf.dexlib2.writer.DexWriter.writeDebugAndCodeItems(DexWriter.java:821)"
"\t... 5 more"
"Caused by: org.jf.util.ExceptionWithContext: Unsigned short value out of range: 143796"
"\tat org.jf.dexlib2.writer.DexDataWriter.writeUshort(DexDataWriter.java:116)"
"\tat org.jf.dexlib2.writer.InstructionWriter.write(InstructionWriter.java:236)"
"\tat org.jf.dexlib2.writer.DexWriter.writeCodeItem(DexWriter.java:1025)"
"\t... 6 more"
花了三天时间,总算把这个问题解决了,自己总结了大概的流程:
首先公司有两个项目:项目A和项目B,在项目A上可以正常生成补丁,但是项目B就会生成补丁失败,对比了两个项目的配制文件大部分都是一样的,自己网上搜了一下,都没有遇到过类似的问题,后面就请教阿里的工程师了,记录如下:
阿里给的回复可能是插件住代码中插入不同的代码了,导致两次编译出来的apk差别很大,项目是用到AspectJ插入代码,但是两个项目都有用到了,也排除了不是这个问题引用的
这里注意通过使用Android Studio自带的工具来对比两个apk的差别是不准的,后面使用apktool反编译apk,然后再对比两个apk的区别(这一步是关键,后面一步步分析最终找到问题所在)
反编译的apk对比如下:
这里看出虽然我只改一项目的一点代码,但是再次打包的时候资源文件也会不一样,搜了一下资源文件,发现不同的模块都用引用到相同的资源名称,所以想到就是不同的模块在合并相同资源文件的时候会导致每次合并的结果会不一样
对比每次打包出来的依赖列表,发现每次打包出来的依赖顺序都会不一样,项目是使用自己写的一个插件进行模块化管理的,代码是使用HashSet
来保存的,而HashSet是无序的,就导致读取出来的依赖列表也是无序的,改用LinkedHashSet
就解决了
反思: 自己在解决这个问题上效率不太高,主要原因还是方法不对,生成补丁不成功,第一时间想着是项目配制或者是代码的问题引起的,但是当一个项目很大的时候,很难快速定位到问题,这里浪费了很多的时间,后面还是要从结束出来,生成补丁失败了,直接反编译apk看具体的差别,这个就能很快定位问题了
ANR问题
从bugly上日记分析太难定位到问题,只知道哪个模块会有问题,但是具体是哪一块引起的就不清楚了,联系了测试的小姐姐,看后面出现了ANR的问题,第一时间保留现场,拿过来分析解决
WebView嵌套ScrollView滑动冲突
项目中出现个滑动冲突的问题,布局大概如下:
<ScrollView
<LinearLayout
<LinearLayout
<WebView
<LinearLayout
这里ScrollView处理了滑动事件,导致滑动的时候WebView不能加载下一页的内容
思路如下:
- 当ScrollView整体向上滑动的时候,监听WebView在屏幕中的位置,如果超出屏幕的时候,不让ScrollView处理滑动事件,而交给WebView处理,这样WebView就可以正常加载下页内容
- 当WebView滑动到最下面的时候,并且所以页数都加载完了之后就把事件又还给ScrollView处理,这样整体又可以正常滑动了
ps:考虑到性能的问题,还需要固定一下WebView的高度,不能一直往上加
遇到的问题
- apktool反编译报错
W: Could not decode attr value, using undecoded value instead: ns=android, name=paddingLeft, value=0x00000f01
W: Could not decode attr value, using undecoded value instead: ns=android, name=layout_width, value=0xffffffff
W: Could not decode attr value, using undecoded value instead: ns=android, name=layout_height, value=0xfffffffe
W: Could not decode attr value, using undecoded value instead: ns=android, name=textSize, value=0x00001002
W: Could not decode attr value, using undecoded value instead: ns=android, name=textColor, value=0xff171717
W: Could not decode attr value, using undecoded value instead: ns=android, name=layout_gravity, value=0x00000010
W: Could not decode attr value, using undecoded value instead: ns=android, name=id, value=0x7f09172b
W: Could not decode attr value, using undecoded value instead: ns=android, name=paddingTop, value=0x00001001
W: Could not decode attr value, using undecoded value instead: ns=android, name=paddingBottom, value=0x00001001
安装了不同版本的apktool引起的
解决:
rm -rf ~/.local/share/apktool/framework
- sophix
addPatch forbid loading local patch for secure reason in release mode
sophix本场调试没有打开
解决:
final SophixManager instance = SophixManager.getInstance();
instance.setEnableDebug(true)
- 打Release包报错
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at org.aspectj.bridge.MessageHandler.hasAnyMessage(MessageHandler.java:160)
at org.aspectj.weaver.Dump.shouldDumpOnExit(Dump.java:179)
at org.aspectj.weaver.Dump.dumpOnExit(Dump.java:147)
at org.aspectj.weaver.Dump.dumpOnExit(Dump.java:135)
at org.aspectj.ajdt.ajc.AjdtCommand.doCommand(AjdtCommand.java:116)
at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:60)
at org.aspectj.tools.ajc.Main.run(Main.java:371)
at sun.reflect.GeneratedMethodAccessor272.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:43)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:190)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:55)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:135)
at com.hujiang.gradle.plugin.android.aspectjx.internal.AJXTask.call(AJXTask.groovy:99)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
还没想到怎么解决...
总结
- 使用ack快速搜索文件内容
$ sudo apt-get install ack
$ ack "<style name=\"AppTheme\""
- 查找当前项目依赖:
- 方法一
./gradlew 项目名称:androidDependencies
- 方法二
a. View > Tool Windows > Gradle
b. AppName > Tasks > android, 双击androidDependencies
- Android Studio ClassPath库引用路径
~/.gradle/caches/modules-2/files-2.1/库路径/库名称/1.2.5/a447d311d11f043c3ee783af90077b34e05b5263
- Android Studio 使用Alt+箭头可以在方法之间跳转
- 使用AnimationDrawable加载帧动画(可能会引起OOM和ANR)
- MANIFEST.MF CERT.RSA作用
- CERT.RSA包含了公钥信息和发布机构信息
- MANIFEST.MF包含所有文件进行SHA-1(这是一个Hash算法)之后再base64编码之后的值
-
Apk打包编译过程
Android Overlay是一种资源替换机制,它能在不重新打包apk的情况下,实现资源文件的替换(res目录非assert目录),Overlay又分为静态Overlay(Static Resource Overlay)与运行时Overlay(Runtime Resource Overlay)。
获取View在屏幕中的位置
final int[] decorViewLocationInScreen=new int[2];
decorView.getLocationOnScreen(decorViewLocationInScreen);
- Android 9.0系统开始不允许Http协议访问网络