preface
Recently, I helped my colleague solve a thorny problem. The process of pit mining was quite interesting. Record it here. (PS: The main reason is that the project is relatively large, and we only have the development authority for part of the business code of the entire Android project. So some of the usual methods of problem solving are not available.)
The problem
Requirements: Web page H5 and native interaction, save base64 pictures.
Problem: Using the existing encapsulated Hybrid protocol, it was found in the final integration test that some mobile phones could not be saved successfully.
- Debugging found
H5
The original protocol format is used to invoke the new protocol, but the service side cannot be triggerednative
Log and breakpoints for the registered new protocol.
- H5 uses the original protocol format to call a new protocol that already exists online
native
Can end call success, this problem is eliminated; - H5 removed the parameters in the new protocol and found that it could be called
native
The new protocol is implemented. Guess that protocol parameters may cause the problem;- The breakpoint is used to find where the protocol call is triggered, i.e
H5
andnative
Where data is communicated. Found that the currentHybrid
The protocol usesnative
The copyonJsPrompt
Method, interceptJavaScript
theprompt()
Methods. - Change the new protocol parameters back and call again. Breakpoints in
native
The copyonJsPrompt
In the method, it is found that the transmitted data is truncated and the data fails to be parsed and forwarded to the next service test.
- The breakpoint is used to find where the protocol call is triggered, i.e
Trouble spots
The clone Prompt method used by the original Hybrid protocol in the project serves as the communication bridge, but now it encounters the problem of ** big flood (large data) ** obstacles.
Solution selection
-
Let H5 change base64 image to HTTP image;
The picture is originally drawn by H5, and the interactive experience is poor when the client downloads again after uploading.
-
Our business side implements a set of Hybrid protocol.
-
Have the infrastructure team modify the existing Hybrid protocol;
Bugs discovered overnight will need to be closed beta tomorrow. It’s hard to make cross-departmental infrastructure changes in 24 hours.
Finally, we choose the second option, which is to implement a Hybrid protocol by ourselves.
Solution implementation
- get
WebView
calladdJavascriptInterface
Approach toH5
Environment AddJS
Object. - The development of
JS
The tool allows it to be called to the new protocol in the old protocol formatJS
Communication methods. - Parse the data and throw it back to the original
onJsPrompt
Method that handles data.
If we could change all the project code, there would be no difficulty in this solution. The difficulty is that we now only have the outermost shell of the H5 page for the Activity, and the wrapped WebView does not expose the methods we want. So the third step of the scheme is problematic.
We have two solutions to this problem:
- This time through injection
JS
The object’sHybrid
Communication protocol and original projectHybrid
Protocol does two sets of logic; - Through many times
hook
Black tech call to the original other classesdispatch
Methods;
If that were all there would be no article.
Okay, after all this talk we’re finally getting down to business. I don’t know how many students are going to continue to watch.
We can take all the AAR files in the project, see if we can modify the source code to provide the API we want, and then fix the problem by upgrading the AAR version. Well, the focus of this article is on modifying the AAR class file.
Modify the AAR class file
Plan a
Delete the class you want to modify in the AAR and repackage it as a new AAR. The project relies on the new version of the AAR and then creates an identical class under the corresponding package for the project.
- Will the original
class
The content of the file is decompiled and copied to the newly created class to run directly.- Will the original
class
The contents of the file are decompiled and copied to the newly created class. Last recompile generatedclass
To add to theAAR
Repackage to generate a newAAR
.
If the class is confused, the scheme is basically invalid. Because a large number of packages and class names are the same in the contents of the decompilated class, there is no way to determine whether the class or package was used during the call during the recompilation.
For example, the following structure is often seen after confusion.
com.xx.a
com.xx.a.a
Copy the code
Instead of looking for class A under package A, write the following code saying there is no class A under class A.
a ma = new com.xx.a.a();
Copy the code
Scheme 2
As can be seen from the figure above, this scheme is faceted programming. We just need to add a method or two to a class to solve the problem of restricted access. Given the difficulty of the problem at hand, we chose Javassist. Because the Javassist source-level API is easier to use than the actual bytecode operations in ASM, you don’t need a deep understanding of the JVM specification to use it.
Javassist official documentation
Jar package download address: Github: JavAssist
// The method to be added
//public void executeJSCmd(String var1) {
// if (this.mActionDispatcher ! = null) {
// Message var2 = this.mActionHandler.obtainMessage(0);
// var2.obj = var1;
// this.mActionHandler.sendMessage(var2);
/ /}
/ /}
/ / need to manipulate the class name of the class: com. XXX. Android. Web. Webview. BaseWebChromeClient
public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool = ClassPool.getDefault();
// The class file must be placed in the compiled class file of the project, and the path is corresponding
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(); }}Copy the code
Note that, for example, the method we add involves other classes that need to write the full path android.os.Message, and the jar package associated with this class must also be added to the runtime environment (you can also put the class file in the class file directory compiled by the project). 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.java44) :at com.test.pattern.Test.main(Test.java13) :Copy the code
Pay attention to the point
When replacing or deleting a class in a JAR, it is best not to unzip it and then use the named package. I have one when I use the command to open a jar package on my Max computer. DS_Store file. The BetterZip compression & Decompression tool I use makes it very easy to add and remove classes from jar packages without decompressing them.
The article here is all about the end, if there are other need to exchange can leave a message oh ~! ~!