Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the Technology of Android burying

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article shows you how to carry out Android buried technical analysis, the content is concise and easy to understand, can definitely brighten your eyes, through the detailed introduction of this article, I hope you can get something.

First, burying a site is a method of data collection for applications such as websites, App or background programs. Through embedding, we can collect users' generating behavior in the application, which can be used to analyze and optimize the subsequent experience of the product, and can also provide data support for the operation of the product. The common indicators are PV, UV, page length, button clicks and so on.

When collecting behavior data, you usually need to add some code in the Web page / App. When the user's behavior reaches a certain condition, the user's behavior will be reported to the server. In fact, the process of adding this code can be called "burying point", and this technique has been around for a long time. With the development of technology and the continuous improvement of people's requirements for data acquisition, I think the technical solution of the buried site has gone through the following stages:

Code embedding: code embedding means that developers add behavior reporting code to the source code of the Web page / App according to the requirements of the product / operation. When the user's behavior meets a certain condition, the code will be executed and the behavior data will be reported to the server. This scheme is the most basic solution, each time the conditions for data reporting are added or modified, developers are required to participate, and the results can only be seen after the next version is online. Many companies provide this kind of data reporting SDK, which encapsulates the background server interface of behavior reporting into a simple client-side SDK interface. Developers can report behavior data by embedding this type of SDK and calling a small amount of code where it is buried.

Full burying point: full buried point refers to the reporting of all behaviors generated in Web pages / App that meet certain conditions to the backend server. For example, all the button clicks in App are reported, and then the product / operator goes to the background to filter the required behavior data. The advantage of this solution is that you don't have to ask the developer to modify the buried code when you add / modify behavior reporting conditions. However, its disadvantages are as obvious as its advantages, that is, the amount of data reported is much larger than that of the code, and there may be a lot of worthless data. In addition, this scheme tends to look at the user's behavior independently, without paying attention to the context of the behavior, which brings some difficulties to the data analysis. Many companies also provide SDK with this kind of function. Through static or dynamic way, "Hook" realizes the monitoring of behavior by using the original App code. When reporting data, it usually uses the scheme of accumulating multiple messages and then reporting to merge requests.

Hook literal translation means hook. I heard it on windows when I studied information security before. It generally means to change a behavior of the system API by some means, bypass a method of the system, or change the workflow of the system. In this case, it actually refers to replacing the object that is supposed to execute a method with another, usually using reflection or proxy, you need to find the code location of the hook, and you can even implement the replacement at compile time.

Visual embedding point: visual embedding point means that the product / operator circles the Web page / App interface. The configuration needs to monitor which element on the interface, and then save the configuration. When the App starts, it will get the pre-selected configuration of the product / operation from the backend server, and then monitor the elements on the App interface according to this configuration. When a certain element meets the conditions, the behavior data will be reported to the backend server. With the full burying point technical solution, it is easy to think of burying points on demand from the point of view of experience optimization. Visual burying points is a scheme to configure burying points on demand. At present, some companies also provide this kind of SDK. When selecting monitoring elements, they generally provide a Web management interface. After installing and initializing the SDK, the phone can connect with the management interface, allowing users to configure the elements to be monitored on the Web management interface.

A number of SDK in the industry support one or all of the three burial points described above, such as Mixpanel, Sensorsdata, TalkingData, GrowingIO, Zhuge IO, Heap Analytics, MTA, Umeng Analytics, Baidu, but the last two burial points are not exactly the same, some are called no burial points or codeless burial points. Because Mixpanel (support code embedding point, visual embedding point) and Sensorsdata (all support) have opened up all their own SDK, the technical solutions are similar, so take their Android SDK as an example to briefly analyze the technical implementation of the three embedding solutions. About the SDK technology implementation of JS, you can take a look at my other blog-JS burying SDK technology analysis.

Second, code burying point

Most SDK, including Mixpanel SDK, encapsulate this embedding solution into a relatively simple interface. In this case, it is track (String eventName, JSONObject properties). When calling this API, developers can pass an event name and attributes of the event, and then report it to the backend.

In implementation, Mixpanel SDK defaults to a HandlerThread thread to handle events. When the developer calls the track (String eventName, JSONObject properties) method, the main thread switches to HandlerThread and stores the events in the database first. Then check whether there are 40 events accumulated in the SDK, and if so, merge them and report them to the background.

When the developer sets the debug mode or calls the flush API manually, all cumulative events can be reported immediately. However, since there is only one thread, if the previous events have not been processed at the time of flush, SDK will deal with the subsequent events again at an interval of 1 minute.

Developers can set a threshold for the number of events to be reported accumulatively, and the interval at which they try to report again when the event is blocked. This kind of solution is relatively basic. I believe most developers have come into contact with it and do not need too much analysis.

3. Full burial point

3.1Basics of AOP

Mixpanel's current Android SDK does not provide this feature, but Shenze Android SDK does, and it is implemented by relying on AOP. So what is AOP?

In the software industry, AOP is the abbreviation of Aspect Oriented Programming, which means: a technology for aspect-oriented programming, which realizes the unified maintenance of program functions through precompilation and runtime dynamic agents. AOP is the continuation of OOP, is a hot spot in software development, is also an important content of Spring framework, and is a derivative paradigm of functional programming. Each part of the business logic can be isolated by using AOP, which reduces the coupling between the various parts of the business logic, improves the reusability of the program, and improves the efficiency of development at the same time. (from baidu baike)

In short, AOP is a technology that can add functions to programs dynamically and uniformly without modifying the source code through precompilation and runtime dynamic proxies.

The implementation of the full burying point of Sensors Analytics AndroidSDK is to find the location in the source code that needs to be reported in the source code during the code compilation phase, and insert the event reporting code of SDK. The framework it uses is AspectJ.

At this point, it is necessary to have a brief understanding of AspectJ and some of its concepts. It is the leader of AOP, and we can see it in many places, such as an annotation log contributed by JakeWartson and the performance tuning framework Hugo, which is also widely used in the Spring framework. I understand that the main concepts in AspectJ are:

JPoint: code pointcut (where we want to insert the code)

Aspect: description of code pointcut

Pointcut: describe what the pointcut is, such as the place where the function is called (Call (MethodSignature)), the interior of the function execution (execution (MethodSignature))

Advice: describes where to insert code at the pointcut, such as before (@ Before) or after (@ After) the Pointcut, or around the entire Pointcut (@ Around)

Thus, when implementing the AOP function, you need to do the following:

Define an Aspect, which must have Pointcut and Advice attributes in the Aspect

Code that needs to be injected when it matches the Pointcut and Advice descriptions

When the code is compiled, through a special java compiler (Aspect's ajc compiler), find the code that meets our definition of Aspect, and insert the code that needs to be injected into the location specified by Advice.

If you have an understanding of AspectJ, you can already guess how the internal SDK is fully embedded; if there is no contact, I think there is no need to rush to learn AspectJ comprehensively, because the internal SDK only uses a small part of the functions of AspectJ, you can directly see the following analysis.

3.2 full burying point-technical implementation

How do you monitor View clicks in Shenze SDK? I'll simplify the SDK code for analysis, and there are a few steps:

3.2.1 define an Aspect

Import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut @ Aspect public class ViewOnClickListenerAspectj {/ * * android.view.View.OnClickListener.onClick (android.view.View) * * @ paramjoinPoint JoinPoint * @ throwsThrowable Exception * / @ After ("execution (* android.view.View.OnClickListener.onClick (android.view.View)") public void onViewClickAOP (final JoinPoint joinPoint) throwsThrowable {AopUtil.sendTrackEventToSDK (joinPoint, "onViewOnClick");}}

The code for this Aspect defines that AopUtil.sendTrackEventToSDK (joinPoint, "onViewOnClick") needs to be inserted after the original implementation of the android.view.View.OnClickListener.onClick (android.view.View) method.

AopUtil.sendTrackEventToSDK (joinPoint, "onViewOnClick"); all this code does is report the click event. Because Shenze SDK separates the full burying point function and the main SDK package into two jar packages, the real event reporting code is called through the AopUtil tool. The implementation is not detailed here. Let's take a look at the real reporting implementation behind this code.

SensorsDataAPI.sharedInstance () .track (AopConstants.APP_CLICK_EVENT_NAME properties)

You can see the click monitoring implemented by AOP, and * also uses the track method to report.

3.2.2 use the ajc compiler to insert Aspect code into the source code

If you want to inject the code of the original project into the code written in the AspectJ framework, you need to reference the ajc compiler in / app/build.gradle. The script is as follows:

... Import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main buildscript {repositories {mavenCentral ()} dependencies {classpath 'org.aspectj:aspectjtools:1.8.10'}} repositories {mavenCentral ()} android {...} dependencies {... Compile 'org.aspectj:aspectjrt:1.8.10'} final def log = project.logger final def variants = project.android.applicationVariants variants.all {variant-> if (! variant.buildType.isDebuggable ()) {log.debug ("Skipping non-debuggable build type' ${variant.buildType.name}'.") Return } JavaCompile javaCompile = variant.javaCompile javaCompile.doLast {String [] args = ["- showWeaveInfo", "- 1.5", "- inpath", javaCompile.destinationDir.toString (), "- aspectpath", javaCompile.classpath.asPath, "- d", javaCompile.destinationDir.toString () -classpath, javaCompile.classpath.asPath, "- bootclasspath", project.android.bootClasspath.join (File.pathSeparator)] log.debug "ajc args:" + Arrays.toString (args) MessageHandler handler = new MessageHandler (true) New Main () .run (args, handler); for (IMessage message: handler.getMessages (null, true)) {switch (message.getKind ()) {case IMessage.ABORT: case IMessage.ERROR: case IMessage.FAIL: log.error message.message, message.thrown break Case IMessage.WARNING: log.warn message.message, message.thrown break; case IMessage.INFO: log.info message.message, message.thrown break Case IMessage.DEBUG: log.debug message.message, message.thrown break;}

In SensorsAndroidSDK, the above script is written as a gradle plug-in, and the developer only needs to reference the plug-in in app/build.gradle.

Apply plugin: 'com.sensorsdata.analytics.android'

3.2.3 complete code insertion and view the effect after insertion

By completing the above two steps, you can insert our data reporting code into the android.view.View.OnClickListener.onClick (android.view.View) method. We add a Button to the demo code, and give it an OnClickListener, compile the code, check the class file in / build/intermediates/classes/debug/, after ajc compilation, insert the Aspect code into the original code, and call the onViewClickAOP method in ViewOnClickListenerAspectj.

Public class MainActivityextends Activity {public MainActivity () {} protected void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); this.setContentView (2130968603); Button btnTst = (Button) this.findViewById (2131427422); btnTst.setOnClickListener (new OnClickListener () {public void onClick (View v) {JoinPoint var2 = Factory.makeJP (ajc$tjp_0, this, this, v) Try {Log.i ("MainActivity", "button clicked");} catch (Throwable var5) {ViewOnClickListenerAspectj.aspectOf () .onViewClickAOP (var2); throw var5;} ViewOnClickListenerAspectj.aspectOf () .onViewClickAOP (var2) } static {ajc$preClinit ();});}}

This is the basic use of AspectJ. SensorsAndroidSDK inserts Aspect code with AspectJ, which is a static way. The static full burying point scheme essentially modifies the bytecode and inserts the code for event reporting.

To modify bytecode, in addition to this scheme, there are trasform api (version 1.5.0 and above), ASM, and Javassist provided by the Android Gradle plug-in. The practice of these technologies can be seen in NetEase Lade's burying project and the Nuwa thermal restoration project.

3.3Information about AspectJ

Aspect Oriented Programming in Android: https://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/

A comprehensive analysis of AOP's AspectJ in Android: http://www.jianshu.com/p/f90e04bcb326

Hujiang has opened up a plug-in called AspectJX, which extends AspectJ. In addition to AOP src code, it also supports kotlin, jar and aar referenced in the project to AOP: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx.

Everything you should know about Spring AOP (AspectJ): http://blog.csdn.net/javazejian/article/details/56267036

3.4 other ideas

The above is the "static Hook" implementation represented by AspectJ, is there any other way not to modify the source code, but to deal with the click behavior of "dynamic Hook" while App is running? The answer is yes, in the world of Java, there is also reflection Dafa, let's take a look at how to achieve the replacement of click events.

In the source code of android.view.View.java (API > = 14), there are several key methods:

/ / getListenerInfo method: return all listener information mListenerInfo ListenerInfogetListenerInfo () {if (mListenerInfo! = null) {return mListenerInfo;} mListenerInfo = new ListenerInfo (); return mListenerInfo;} / / listener information static class ListenerInfo {. / / various xxxListener / * Listener used to dispatch click events are omitted here. * This field should be made private, so it is hidden from the SDK. * {@ hide} * / public OnClickListener mOnClickListener; / * Listener used to dispatch long click events. * This field should be made private, so it is hidden from the SDK. * {@ hide} * / protected OnLongClickListener mOnLongClickListener;...} ListenerInfo mListenerInfo; / / We are very familiar with the method. Inside, the mOnClickListener of mListenerInfo is actually set to the OnclickListner object public void setOnClickListener (@ Nullable OnClickListener l) {if (! isClickable ()) {setClickable (true);} getListenerInfo (). MOnClickListener = l } / * determine whether the View has a click listener * Return whether this view has an attached OnClickListener. Returns * true if there is a listener, false if there is none. * / public boolean hasOnClickListeners () {ListenerInfo li = mListenerInfo; return (li! = null & & li.mOnClickListener! = null);}

As you can see from the above methods, the click listener is actually saved in mListenerInfo.mOnClickListener. So when implementing the Hook click listener, just replace the mOnClickListener with our packaged click listener proxy object. Take a brief look at the implementation ideas:

1. Create a proxy class for click listeners

/ / proxy class of click listener, with the function of reporting click behavior class OnClickListenerWrapperimplements View.OnClickListener {/ / original click listener object private View.OnClickListener onClickListener; public OnClickListenerWrapper (View.OnClickListener onClickListener) {this.onClickListener = onClickListener } @ Override public void onClick (View view) {/ / make the original click listener work properly if (onClickListener! = null) {onClickListener.onClick (view);} / / Click event report, you can get some properties of the clicked view track (APP_CLICK_EVENT_NAME, getSomeProperties (view));}}

two。 Reflection gets a mListenerInfo.mOnClickListener of View and replaces it with the agent's click listener

/ / hook public void hookView (View view) {/ / 1 to a View click listener. Reflection calls the getListenerInfo method of View (API > = 14) to get the mListenerInfo object Class viewClazz = Class.forName ("android.view.View"); Method getListenerInfoMethod = viewClazz.getDeclaredMethod ("getListenerInfo"); if (! getListenerInfoMethod.isAccessible ()) {getListenerInfoMethod.setAccessible (true);} Object mListenerInfo = listenerInfoMethod.invoke (view); / / 2. Then reflect from mListenerInfo to get mOnClickListener object Class listenerInfoClazz = Class.forName ("android.view.View$ListenerInfo"); Field onClickListenerField = listenerInfoClazz.getDeclaredField ("mOnClickListener"); if (! onClickListenerField.isAccessible ()) {onClickListenerField.setAccessible (true);} View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get (mListenerInfo); / / 3. Create the click listener object View.OnClickListener mOnClickListenerWrapper = new OnClickListenerWrapper (mOnClickListener) of the proxy; / / 4. Set the mOnClickListener of mListenerInfo to the new onClickListenerWrapper onClickListenerField.set (mListenerInfo, mOnClickListenerWrapper); / / it seems possible to use this: view.setOnClickListener (mOnClickListenerWrapper);}

Note that if it is API

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 301

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report