foreword
Recently, I helped a colleague solve a difficult problem, and the process of digging along the way was quite interesting. Document it here. (PS: The main reason is that the project is relatively large, and we only have Android
the development authority for part of the business-side code of the entire project. Therefore, some conventional means of solving problems cannot be used.)
question
Requirements: Web
page H5
and native
interaction, save base64图片
.
Problem: Using the existing encapsulated Hybrid
protocol, it was found in the final integration test that some mobile phones could not be saved successfully.
- During debugging, it was found
H5
that the original protocol format was used to call the new protocol, andnative
the logs and breakpoints of the new protocol registered on the business side could not be triggered.
- Suspected the problem of the original protocol format, when H5 uses the original protocol format to call the new protocol that already exists on the line, it is found that the
native
call can be successful, and this problem is ruled out; - Suspecting the parameter problem in the new protocol, H5 removes the parameter in the new protocol and finds that it can be called to the
native
new protocol implementation. It is guessed that the problem may be caused by the protocol parameters;- Find the place where the protocol call is triggered through the breakpoint, that is,
H5
thenative
place where the data is communicated. It is found that the currentHybrid
protocol uses thenative
end-to-end replicationonJsPrompt
method and the interceptionJavaScript
methodprompt()
. - Change the new protocol parameters back and call again. The breakpoint found that the transmitted data was truncated in the
native
end-to-end replication method, and the data parsing failed and could not be forwarded to the next service test.onJsPrompt
- Find the place where the protocol call is triggered through the breakpoint, that is,
Problems
The original communication bridge Hybrid协议
used in the project is now encountering **a major flood (larger data)** and there is an obstacle problem.复写prompt方法
solution selection
-
Change the picture in the format to
H5
the picture in the format;base64
http
The picture is originally
H5
drawn, and the interactive experience of the client downloading after uploading is too poor; -
Our business side implements our own set of
Hybrid
protocols; -
Let the project's infrastructure department modify the existing
Hybrid
agreement;The bug discovered at night will require closed testing tomorrow. It is difficult to complete the change of cross-departmental infrastructure within 24 hours.
In the end, we chose the second option, implementing a set of Hybrid
protocols ourselves.
solution realization
- Get
WebView
callsaddJavascriptInterface
the method toH5
addJS
objects to the environment. - The development
JS
tool allows it to call the newJS
communication method according to the old protocol format. - Parse the obtained data and throw it to
onJsPrompt
the wrapper class object that originally processed the data in the method.
If we can change all the project code, then there is no difficulty in this solution. The difficulty is that we only have H5
the outermost shell of this page Activity
, and the encapsulation WebView
does not expose the method we want to the outside world. So the implementation of the third step of the program has problems.
We have two solutions to this problem:
- This time, two sets of logic are made by injecting the communication protocol
JS
of the objectHybrid
and the originalHybrid
protocol of the project; - Call methods
hook
in other original classes through multiple black technology;dispatch
If that was the case, there would be no article for this article.
Well, after so much rambling, I finally started to get to the point. I don't know how many students plan to continue reading.
We can take all AAR
the files in the project, think about whether we can modify the source code to provide what we want API
, and then solve the problem by upgrading AAR
the version . Well, the focus of this article has come out to modify the class file in AAR .
Modify the class file in AAR
Option One
First delete
AAR
the ones you want to modifyclass
, and repackage them as new onesAAR
. The project depends on the new versionAAR
, and then create an identical class under the corresponding package of the project.
- Decompile the content of the original
class
file and copy it to the newly created class and run it directly.- Decompile the original
class
file content and copy it to the newly created class. Finally, recompile the generated oneclass
and add itAAR
to repackage to generate a new oneAAR
.
If the class is obfuscated, then this solution is basically abolished. Because there are a large number of packages with the same class name in the decompiled class
content, it is impossible to confirm that the class used in this call is still a package during this recompilation.
As an example, the following structure is often seen after obfuscation.
com.xx.a
com.xx.a.a
When writing the following code, it will prompt that a
there is no class under the class a
, instead of a
looking for the class under the package a
.
a ma = new com.xx.a.a();
Option II
According to the above figure, it can be seen that this scheme is for aspect programming. We only need to add a method or two to a class to solve the problem of limited access. Considering the difficulty of the current problem we choose Javassist
. Because the actual bytecode manipulation in Javassist
the source code level API
is easier to use, it can be used without deep knowledge of the specification.ASM
JVM
Javassist
official document
jar
Package download address Github:javassist
//需要添加的方法
//public void executeJSCmd(String var1) {
// if (this.mActionDispatcher != null) {
// Message var2 = this.mActionHandler.obtainMessage(0);
// var2.obj = var1;
// this.mActionHandler.sendMessage(var2);
// }
//}
//需要操作的class的类名:com.xxx.android.web.webview.BaseWebChromeClient
public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool = ClassPool.getDefault();
// 必须将class文件放在这个工程编译后的class文件中,路径也对应起来
CtClass ctClass = classPool.get("com.xxx.android.web.webview.BaseWebChromeClient");
CtMethod newmethod = CtNewMethod.make("public void executeJSCmd(String message) { if (this.mActionDispatcher != null) { android.os.Message msg = this.mActionHandler.obtainMessage(0); msg.obj = message; this.mActionHandler.sendMessage(msg); } }",ctClass);
ctClass.addMethod(newmethod);
ctClass.writeFile();
}
}
It should be noted that, for example, the method we added involves other classes that need to write the full path android.os.Message
, and the package related to this class jar
must also be added to the running environment (you can also put the class file of this class in the compiled project class file directory), otherwise an error will be reported during execution.
Exception in thread "main" javassist.CannotCompileException: [source error] no such class: android.os.Message
at javassist.CtNewMethod.make(CtNewMethod.java:78)
at javassist.CtNewMethod.make(CtNewMethod.java:44)
at com.test.pattern.Test.main(Test.java:13)
important point
When replacing or deleting, jar
it class
is best not to decompress and then use the named package. When I Max
use the command jar
to package there will be a .DS_Store
file. BetterZip
The compression & decompression tool I use is very convenient to add and delete in jar
the package without decompression .class
The article is all told here, if you have other needs to communicate, you can leave a message ! !
If you want to read more articles by the author, you can check out my personal blog and public account: