Android AOP之位元組碼插樁
出自:http://www.jianshu.com/p/c202853059b4
背景
本篇文章基於《網易樂得無埋點資料收集SDK》總結而成,關於網易樂得無埋點資料採集SDK的功能介紹以及技術總結後續會有文章進行闡述,本篇單講SDK中用到的Android端AOP的實現。
隨著流量紅利時代過去,精細化運營時代的開始,網易樂得開始構建自己的大資料平臺。其中,客戶端資料採集是第一步。傳統收集資料的方式是埋點,這種方式依賴開發,採集時效慢,資料採集程式碼與業務程式碼不解藕。
為了實現非侵入的,全量的資料採集,AOP成了關鍵,資料收集SDK探索和實現了一種Android上AOP的方式。
目錄
一、Android AOP
1.1 什麼是AOP
面向切向程式設計(Aspect Oriented Programming),相對於面向物件程式設計(ObjectOriented Programming)而言。
OOP的精髓是把功能或問題模組化,每個模組處理自己的家務事。但在現實世界中,並不是所有問題都能完美得劃分到模組中,有些功能是橫跨並嵌入眾多模組裡的,比如下圖所示的例子。
圖1-1 AOP概念說明示例
上圖是一個APP模組結構示例,按照照OOP的思想劃分為“檢視互動”,“業務邏輯”,“網路”等三個模組,而現在假設想要對所有模組的每個方法耗時(效能監控模組)進行統計。這個效能監控模組的功能就是需要橫跨並嵌入眾多模組裡的,這就是典型的AOP的應用場景。
AOP的目標是把這些橫跨並嵌入眾多模組裡的功能(如監控每個方法的效能) 集中起來,放到一個統一的地方來控制和管理。如果說,OOP如果是把問題劃分到單個模組的話,那麼AOP就是把涉及到眾多模組的某一類問題進行統一管理。
我們在開發無埋點資料收集是同樣也遇到了很多需要橫跨並嵌入眾多模組裡的場景,這些場景將在第二章(AOP應用情景)進行介紹。下面我們調研下Android AOP的實現方式。
1.2 Android AOP方式概述
AOP從實現原理上可以分為執行時AOP和編譯時AOP,對於Android來講執行時AOP的實現主要是hook某些關鍵方法,編譯時AOP主要是在Apk打包過程中對class檔案的位元組碼進行掃描更改。Android主流的aop 框架有:
- Dexposed,Xposed等(執行時)
- aspactJ(編譯時)
除此之外,還有一些非框架的但是能幫助我們實現 AOP的工具類庫:
- java的動態代理機制(對java介面有效)
- ASM,javassit等位元組碼操作類庫
- (偏方)DexMaker:Dalvik 虛擬機器上,在編譯期或者執行時生成程式碼的 Java API。
- (偏方)ASMDEX(一個類似 ASM 的位元組碼操作庫,執行在Android平臺,操作Dex位元組碼)
1.3 Android AOP方式對比選擇
Dexposed,Xposed的缺陷很明顯,xposed需要root許可權,Dexposed只對部分系統版本有效。
與之相比aspactJ沒有這些缺點,但是aspactJ作為一個AOP的框架來講對於我們來講太重了,不僅方法數大增,而且還有一堆aspactJ的依賴要引入專案中(這些程式碼定義了aspactJ框架諸如切點等概念)。更重要的是我們的目標僅僅是按照一些簡單的切點(使用者點選等)收集資料,而不是將整個專案開發從OOP過渡到AOP。
AspactJ對於我們想要實現的資料收集需求太重了,但是這種編譯期操作class檔案位元組碼實現AOP的方式對我們來說是合適的。
因此我們實現Android上AOP的方式確定為:
- 採用編譯時的位元組碼操作的做法
- 自己hook Android編譯打包流程並藉助ASM庫對專案位元組碼檔案進行統一掃描,過濾以及修改。
在具體講解實現技術之前,先看一下無埋點資料收集需求遇到的三個需要AOP的場景。
二、AOP應用情景
下面舉出資料收集SDK通過修改位元組碼進行AOP的三個應用情景,其中情景一和二的位元組碼修改是方法級別的,情景三的位元組碼修改是指令級別的。
2.1 Fragment生命週期
說明
收集頁面資料時發現有些fragment是希望當作頁面來看待,並且計算pv的(如首頁用fragmen實現的tab)。而fragment的頁面顯示/隱藏事件需要根據:
onResume()
onPause()
onHiddenChanged(boolean hidden)
setUserVisibleHint(boolean isVisibleToUser)
這四個方法綜合得出。
也就是說當專案中任一一個Fragment發生如上狀態變化,我們都要拿到這個時機,並上報相關頁面事件,也就是對Fragment的這幾個方法進行AOP。
做法是:
- 對專案中所有程式碼進行掃描,篩選出所有Fragment的子類
- 對這些篩選出來的類的的onResumed,onPaused,onHiddenChanged,setFragmentUserVisibleHint這幾個方法的位元組碼進行修改,新增上類似回撥的邏輯
- 這樣在專案中任何一個Fragment的這些回撥觸發的時候我們都可以得到通知,也即對Fragment的這幾個切點進行了AOP。
示例
假設我們有一個Fragment1(空類,內部什麼程式碼也沒有)
public class Fragment1 extends Fragment {}
經過掃描修改位元組碼後變為:
public class Fragment1 extends Fragment {
@TransformedDCSDK
public void onResume() {
super.onResume();
Monitor.onFragmentResumed(this);
}
@TransformedDCSDK
public void onPause() {
super.onPause();
Monitor.onFragmentPaused(this);
}
@TransformedDCSDK
public void onHiddenChanged(boolean var1) {
super.onHiddenChanged(var1);
Monitor.onFragmentHiddenChanged(this, var1);
}
@TransformedDCSDK
public void setUserVisibleHint(boolean var1) {
super.setUserVisibleHint(var1);
Monitor.setFragmentUserVisibleHint(this, var1);
}
}
注:
- Monitor.onFragmentResumed等函式用於上報頁面事件
- @TransformedDCSDK 註解標記方法被資料收集SDK進行了位元組碼修改
2.2 使用者點選事件
說明
點選事件是分析使用者行為的一個重要事件,Android中的點選事件回撥大多是View.OnClickListener的onClick方法(當然還有一部分是DialogInterface.OnClickListener或者重寫OnTouchEvent自己封裝的點選)。
也就是說當專案中任一一個控制元件被點選(觸發了OnClickListener),我們都要拿到這個時機,並上報點選事件。也就是對View.OnClickListener的onClick方法進行AOP。做法是:
- 對專案中所有程式碼進行掃描,篩選出所有實現View.OnClickListener介面的類(匿名or不匿名)
- 對onClick方法的位元組碼進行修改,添加回調。
- 達到的效果就是當APP中任何一個View被點選時,我們都可以在捕捉到這個時機,並且上報相關點選事件。
示例
假設有個實現介面的類
public class MyOnClickListener implements OnClickListener {
public void onClick(View v) {
//此處代表點擊發生時的業務邏輯
}
}
經過掃描修改位元組碼後變為:
public class MyOnClickListener implements OnClickListener {
@TransformedDCSDK
public void onClick(View v) {
if (!Monitor.onViewClick(v)) {
//此處代表點擊發生時的業務邏輯
}
}
}
注:
- Monitor.onViewClick函式裡面包含上報點選事件的邏輯
- 可以通過Monitor.onViewClick的返回值控制原有業務邏輯是否執行,基本都是執行的,只有在特殊模式下(圈選)資料收集SDK才會忽略原有邏輯
2.3 彈窗事件
說明
彈窗顯示/關閉事件,當然彈窗的實現可以是Dialog,PopupWindow,View甚至Activity,這裡僅以Dialog為例。
當專案中任意一個地方彈出/關閉Dialog,我們都要拿到這個時機,即對Dialog.show/dismiss/hide這幾個方法進行AOP。做法是:
- 對專案中所有程式碼進行掃描,篩選出所有位元組碼指令中有呼叫Dialog.show/dismiss/hide的地方
- 位元組碼指令替換,替換成一段回撥邏輯。
- 這樣APP中所有Dialog的顯示/關閉時,我們都可以在這時進行一些收集資料的操作。
示例
假設專案中有一個程式碼(例如方法)塊如下,其中某處呼叫了dialog.show()
某個方法 {
//其他程式碼
dialog.show()
//其他程式碼
}
經過掃描修改位元組碼後變為
某個方法 {
//其他程式碼
Monitor.showDialog(dialog)
//其他程式碼
}
注:Monitor.showDialog除了呼叫dialog.show()還進行一些資料收集邏輯
三、AOP實現概述
第二章 (AOP應用情景)簡單地列舉了AOP在三種應用情景中達到的效果,下面介紹AOP的實現,實現的大致流程如下圖所示:
圖3-1 Android AOP實現流程
關鍵有以下幾點:
A、位元組碼插樁入口(圖3-1 中1,3兩個環節)。
我們知道Android程式從Java原始碼到可執行的Apk包,中間有(但不止有)兩個環節:
- javac:將原始檔編譯成class格式的檔案
- dex:將class格式的檔案彙總到dex格式的檔案中
我們要想對位元組碼進行修改,只需要在javac之後,dex之前對class檔案進行位元組碼掃描,並按照一定規則進行過濾及修改就可以了,這樣修改過後的位元組碼就會在後續的dex打包環節被打到apk中,這就是我們的插樁入口(更具體的後面還會詳述)。
B、bytecode manipulate(上圖3-1 中第二個環節),這個環節主要做:
- 位元組碼掃描,並按照一定規則進行過濾出哪些類的class檔案需要進行位元組碼修改
- 對篩選出來的類進行位元組碼修改操作
最後B步驟修改過位元組碼的class檔案,將連同資原始檔,一起打入Apk中,得到最終可以在Android平臺可以執行的APP。
下面分別就插樁入口和ASM位元組碼操作兩個方面進行詳述。
四、插樁入口
如 第三章(AOP實現概述)所述,我們在Android 打包流程的javac之後,dex之前獲得位元組碼插樁入口。
4.1 Android打包流程說明
完整的Android 打包流程如下圖所示:
圖4-1 Android打包流程
說明:
-
圖4-1中“dex”節點,表示將class檔案打包到dex檔案的過程,其輸入包括1.專案java原始檔經過javac後生成的class檔案以及2.第三方依賴的class檔案兩種,這些class檔案都是我們進行位元組碼掃描以及修改的目標。
-
具體來說,進行圖4-1中dex任務是一個叫dx.jar的jar包,存在於Android SDK的sdk/build-tools/22.0.1/lib/dx.jar目錄中,通過類似 :
java dx.jar com.android.dx.command.Main --dex --num-threads=4 —-output output.jar input.jar
的命令,進行將class檔案打包為dex檔案的步驟。
-
從上面的演示命令可以看出,dex任務是啟動一個java程序,執行dx.jar中com.android.dx.command.Main類(當然對於multidex的專案入口可能不是這個類,這個再說)的main()方法進行dex任務,具體完成class到dex轉化的是這個方法:
private static boolean processClass(String name,byte[] bytes) {
//內容省略
}
方法processClass的第二個引數是一個byte[],這就是class檔案的二進位制資料(class檔案是一種緊湊的8位位元組的二進位制流檔案, 各個資料項按順序緊密的從前向後排列, 相鄰的項[包括位元組碼指令]之間沒有間隙),我們就是通過對這個二進位制資料進行掃描,按照一定規則過濾以及位元組碼修改達到第二部分所描述的AOP情景。
4.2 插樁入口
那麼我們怎麼獲得插樁入口呢?
入口一:transform api
對於Android Gradle Plugin 版本在1.5.0及以上的情況,Google官方提供了transformapi用作位元組碼插樁的入口。此處的Android Gradle Plugin 版本指的是build.gradle dependencies的如下配置:
compile 'com.android.tools.build:gradle:1.5.0'
此處1.5.0即為Android Build Gradle Plugin 版本。
關於transform api如何使用就不詳細介紹了,
-
可自行檢視API,
-
參考熱修復專案Nuwa的gradle插樁外掛(使用transfrom api實現)
入口二:hook dx.jar
那麼對於Android Build Gradle Plugin 版本在1.5.0以下的情況呢?
下面我們介紹一種不依賴transform api而獲得插樁入口的方法,暫且稱為 hook dx.jar吧。
提示:具體使用可以考慮綜合這兩種方式,首先檢查build環境是否支援transform api(反射檢查類com.android.build.gradle.BaseExtension是否有registerTransform這個方法即可)然後決定使用哪種方式的插樁入口。
4.3 hook dx.jar獲得插樁入口
hook dx.jar 即是在圖4-1中的dex步驟進行hook,具體來講就是hook 4.1節介紹的dx.jar中com.android.dx.command.Main.processClass方法,將這個方法的位元組碼更改為:
private static boolean processClass(String name,byte[] bytes) {
bytes=掃描並修改(bytes);// Hook點
//原有邏輯省略
}
注:這種方式獲得插樁入口也可參見部落格《APM之原理篇》
如何在一個標準的java程序(記得麼?dex任務是啟動一個java程序,執行dx.jar中com.android.dx.command.Main類的main()方法進行dex任務)中對特定方法進行位元組碼插樁?
這就需要運用Java1.5引入的Instrumentation機制。
java Instrumentation
java Instrumentation指的是可以用獨立於應用程式之外的代理(agent)程式來監測和協助執行在JVM上的應用程式。這種監測和協助包括但不限於獲取JVM執行時狀態,替換和修改類定義等。
Instrumentation 的最大作用就是類定義的動態改變和操作。
Java Instrumentation兩種使用方式:
-
方式一(java 1.5+):
開發者可以在一個普通 Java 程式(帶有 main 函式的 Java 類)執行時,通過 – javaagent 引數指定一個特定的 jar 檔案(agent.jar)(包含 Instrumentation 代理)來啟動 Instrumentation 的代理程式。例如:java -javaagent agent.jar dex.jar com.android.dx.command.Main --dex …........
如此,則在目標main函式執行之前,執行agent jar包指定類的 premain方法 :
premain(String args, Instrumentation inst)
-
方式二(java 1.6+):
VirtualMachine.loadAgent(agent.jar) VirtualMachine vm = VirtualMachine.attach(pid); vm.loadAgent(jarFilePath, args);
此時,將執行agent jar包指定類的 agentmain方法:
agentmain(String args, Instrumentation inst)
說明:
-
關於上述程式碼中出現的agent.jar?
這裡的agent就是一個包含一些指定資訊的jar包,就像OSGI的外掛jar包一樣,在jar包的META-INF/MANIFEST.MF中新增如下資訊:Manifest-Version: 1.0 Agent-Class: XXXXX Premain-Class: XXXXX Can-Redefine-Classes: true Can-Retransform-Classes: true
這個jar包就成了agent jar包,其中Agent-Class指向具有agentmain(String args, Instrumentation inst)方法的類,Premain-Class指向具有premain(String args, Instrumentation inst)的類。
-
關於premain(String args, Instrumentation inst)?
第二個引數,Instumentation 類有個方法addTransformer(ClassFileTransformer transformer,boolean canRetransform)
而一旦為Instrumentation inst添加了ClassFileTransformer:
ClassFileTransformer c=new ClassFileTransformer() inst.addTransformer(c,true);
那麼以後這個jvm程序中再有任何類的載入定義,都會出發此ClassFileTransformer的transform方法
byte[] transform( ClassLoader loader,String className,Class classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer)throwsIllegalClassFormatException;
其中,引數byte[] classfileBuffer是類的class檔案資料,對它進行修改就可以達到在一個標準的java程序中對特定方法進行位元組碼插樁的目的。
hook dx.jar獲得插樁入口的完整流程
完整流程如下圖所示:
圖4-2 hook dx.jar流程圖
注:apply plugin: 'bytecodeplugin'中的bytecodeplugin是我們用於位元組碼插樁的gradle外掛
A. 通過任意方式(as介面內點選/命令gradle build等)都會啟動圖4-2所描述的build流程。
B. 通過Java Instrumentation機制,為獲得插樁入口,對於apk build過程進行了兩處插樁(即hook),圖4-2中標紅部分:
-
在build程序,對ProcessBuilder.start()方法進行插樁
ProcessBuilder類是J2SE 1.5在java.lang中新新增的一個新類,此類用於建立作業系統程序,它提供一種啟動和管理程序的方法,start方法就是開始建立一個程序,對它進行插樁,使得通過下面方式啟動dx.jar程序執行dex任務時:java dex.jar com.android.dx.command.Main --dex …........
增加引數-javaagent agent.jar,使得dex程序也可以使用Java Instrumentation機制進行位元組碼插樁
-
在dex程序
對我們的目標方法com.android.dx.command.Main.processClasses進行位元組碼插入,從而實現打入apk的每一個專案中的類都按照我們制定的規則進行過濾及位元組碼修改。
C. 圖4-2左側build程序使用Instrumentation的方式時之前敘述過的VirtualMachine.loadAgent方式(方式二),dex程序中的方式則是-javaagent agent.jar方式(方式一)。
由此,我們獲得了進行位元組碼插樁的入口,下面我們就使用ASM庫的API,對專案中的每一個類進行掃描,過濾,及位元組碼修改。
五、bytecode manipulation
在這一部分我們以第二部分描述的情景二的應用場景為例,對View.OnClickListener的onClick方法進行位元組碼修改。在實踐bytecode manipulation時需要一些關於位元組碼以及ASM的基礎知識需要了解。因此本部分組織結構如下:
- 首先介紹一下我們用來操縱位元組碼的類庫ASM
- 然後介紹一些關於位元組碼的基本知識
- 最後實踐對View.OnClickListener的onClick方法進行bytecode manipulation
5.1 ASM庫簡要介紹
簡介
ASM是一個java位元組碼操縱框架,它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進位制 class 檔案,也可以在類被載入入 Java 虛擬機器之前動態改變類行為。類似功能的工具庫還有javassist,BCEL等。
那麼為什麼選擇ASM呢?
ASM與同類工具庫(這裡以javassist為例)相比:
A. 較難使用,API非常底層,貼近位元組碼層面,需要位元組碼知識及虛擬機器相關知識
B. ASM更快更高效,Javassist實現機制中包括了反射,所以更慢。下表是使用不同工具庫生成同一個類的耗時比較
Framework | First time | Later times |
---|---|---|
Javassist | 257 | 5.2 |
BCEL | 473 | 5.5 |
ASM | 62.4 | 1.1 |
C. ASM庫更加強大靈活,比如可以感知細到位元組碼指令層次(第二部分情景三中的場景)
總結起來,ASM雖然不太容易使用,但是功能強大效率高值得挑戰。
關於ASM庫的使用可以參考手冊,下面對其API進行簡要介紹:
ASM API簡介
ASM(core api) 按照visitor模式按照class檔案結構依次訪問class檔案的每一部分,有如下幾個重要的visitor。
ClassVisitor
按照class檔案格式,按次序訪問類檔案每一部分,如下:
public abstract class ClassVisitor {
public ClassVisitor(int api);
public ClassVisitor(int api, ClassVisitor cv);
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces); public void visitSource(String source, String debug);
public void visitOuterClass(String owner, String name, String desc); AnnotationVisitor visitAnnotation(String desc, boolean visible); public void visitAttribute(Attribute attr);
public void visitInnerClass(String name, String outerName,
String innerName, int access);
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value);
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions); void visitEnd();
}
與之對應的class檔案格式為:
圖5-1 class檔案格式
重點看ClassVisitor的如下幾個方法:
- visit:按照圖5-1中描述的 class檔案格式,讀出“class類名”(this_class的指向),“父類名”(super_class的指向),“實現的介面(陣列)”(interfaces的指向)等資訊
- visitField:訪問欄位,即訪問圖5-1 class檔案格式中的“field_info”,訪問字斷的邏輯委託給另外一種visitor(FieldVisitor)
- visitField:訪問方法,即訪問圖5-1 class檔案格式中的“method_info”,訪問方法的邏輯委託給另外一種visitor(MethodVisitor)
其他方法可參考前面推薦的ASM手冊,下面介紹一下負責訪問方法的MethodVisitor。
MethodVisitor
按以下次序訪問一個方法:
visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn | visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd
注:上述出現的“*”表示出現“0+”次,“?”表示出現“0/1”次。 含義可類比正則式元字元。
下面說明幾個比較關鍵的visit方法:
- visitCode():開始訪問方法體內的程式碼
- visitTryCatchBlock:訪問方法的try catch block
- visitLocalVariable:指令,訪問區域性變量表裡面的某個區域性變數(關於區域性變量表後面會有介紹)
- visitXxxInsn:指令,表示class檔案方法體裡面的位元組碼指令(如:IADD,ICONST_0,ARETURN等等位元組碼指令),完整的位元組碼指令表可參考維基百科。
- visitLabel(Label label):如果方法體中有跳轉指令,位元組碼指令中會出現label,所謂label可以近似看成行號的標記(並不是),指示跳轉指令將要跳轉到哪裡
- visitFrame:記錄當前棧幀(棧幀結構將在後面有介紹)狀態,用於Class檔案載入時的校驗
- visitMaxs:指定當前方法的棧幀中,區域性變量表和運算元棧的大小。(java棧大小是javac之後就確定了的)
簡單介紹了asm庫後,由於使用ASM還需要對位元組碼有一定的瞭解,故在實踐之前再介紹一些關於位元組碼的基礎知識:
5.2 位元組碼基礎
概念
關於位元組碼,有以下概念定義比較重要:
- 全限定名(Internal names):
全限定名即為全類名中的“.”,換為“/”,舉例:類android.widget.AdapterView.OnItemClickListener的全限定名為: android/widget/AdapterView$OnItemClickListener
- 描述符(descriptors):
1.型別描述符,如下圖所示:
圖5-2 java型別描述符
如圖5-2所示,在class檔案中型別 boolean用“Z”描述,陣列用“[”描述(多維陣列可疊加),那麼我們最常見的自定義引用型別呢?“L全限定名;”.例如:
Android中的android.view.View類,描述符為“Landroid/view/View;”
2.方法描述符的組織結構為:
(引數型別描述符)返回值描述符
其中無返回值void用“V”代替,舉例:
方法boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) 的描述符如下:
(Landroid/widget/ExpandableListView;Landroid/view/View;IJ)Z
執行引擎
jvm執行引擎用於執行位元組碼,如下圖
圖5-3 位元組碼執行引擎棧幀結構
如圖5-3所示,縱向來看有三個執行緒,其中每一個執行緒內部都有一個棧結構(即通常所說的“堆疊”中的虛擬機器棧),棧中的每一個元素(一幀)稱為一個棧幀(stack frame)。棧幀與我們寫的方法一一對應,每個方法的呼叫/return對應執行緒中的一個棧幀的入棧/出棧。
方法體中各種位元組碼指令的執行都在棧幀中完成,下面介紹下棧幀中兩個比較重要的部分:
- 區域性變量表:
故名思義,儲存當前方法中的區域性變數,包括方法的入參。值得注意的是區域性變量表的第一個槽位存放的是this。還拿方法onGroupClick舉例:
剛進入此方法時,區域性變量表的槽位狀態如下:boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
Slot Number | value |
---|---|
0 | this |
1 | ExpandableListView parent |
2 | View v |
3 | int groupPosition |
4 | long id |
- 運算元棧:
位元組碼指令執行的工作臺。下面用指令iadd(int型別加)執行時運算元棧的變化進行舉例:
圖5-4 執行iadd指令時運算元棧的狀態變化
例如,方法體中有語句如下:
1+1
-
相關推薦
Android AOP之位元組碼插樁
出自:http://www.jianshu.com/p/c202853059b4 背景 本篇文章基於《網易樂得無埋點資料收集SDK》總結而成,關於網易樂得無埋點資料採集SDK的功能介紹以及技術總結後續會有文章進行闡述,本篇單講SDK中用到的Android端AOP
Java位元組碼插樁修改HiBeaver(結合ASM,實現Hook需求、輕量級AOP、無埋點統計上報等)
Java彙編指令- https://segmentfault.com/a/1190000008606277 一次Android位元組碼插樁實戰- https://segmentfault.com/a/1190000008658815 Android位元組碼修改神器HiBeav
java類載入及動態代理之位元組碼插莊技術
本文介紹一下,當下比較基礎但是使用場景卻很多的一種技術,稍微偏底層點,就是位元組碼插莊技術了...,如果之前大家熟悉了asm,cglib以及javassit等技術,那麼下面說的就很簡單了...,因為下面要說的功能就是基於javassit實現的,接下來先從javaagent的原
Python 原始碼分析之位元組碼之基本操作
本文基於 Python 3.6.4 編譯器生成位元組碼,你可通過如下程式碼片段得到 python 原始碼對應的位元組碼 #!/usr/bin/env python # encoding: utf-8 import sys import dis filename=sys.argv
JVM深度學習系列之位元組碼檔案學習(二)
1. 首先javac編譯java檔案 javac aa.java 2. 使用javap 反編譯class檔案得到位元組碼檔案 javap -v aa.class Javap 相關idea外掛 jclasslib
Android開發之付款碼
對於剛完成的付款碼模組程式碼做一個紀錄,以備後用。以前做預支付通過Netty框架搭建通訊,本次通過輪詢任務完成介面重新整理。 效果圖 知識點 帶頭像二維碼生成 輪詢任務 場景動畫 旋轉動畫 生成二維碼依賴zxing核心庫 compile
JVM之位元組碼指令簡介
java虛擬機器的指令由一個位元組長度的、代表著某種特定操作含義的數字以及跟隨其後的零至多個代表此操作所需的引數而構成的。 位元組碼與資料型別 載入和儲存指令: 將資料在棧幀中的區域性變量表和運算元棧
Java虛擬機器之位元組碼執行引擎
1 概述 虛擬機器執行引擎是Java虛擬機器最核心的部分之一,其目的是實現:輸入位元組碼檔案,將位元組碼解析或等效處理後,執行並輸出結果。 其中兩種執行方式:解釋執行和編譯執行。 2 執行時棧幀結構 棧幀(Stack Frame)是用於支援虛擬機器進
python反編譯之位元組碼
如果你曾經寫過或者用過 Python,你可能已經習慣了看到 Python 原始碼檔案;它們的名稱以.Py 結尾。你可能還見過另一種型別的檔案是 .pyc 結尾的,它們就是 Python “位元組碼”檔案。(在 Python3 的時候這個 .pyc 字尾的檔案不太好找了,它在一個名為__pycache__的子目
Django學習系列之captcha 驗證碼插件
應用 自動 sta 前端 流程 bsp msg char erro 安裝部署 安裝captcha pip3.6 install django-simple-captcha==0.4.6 settings.py中引入captcha INSTALLED_APPS = [
談談-Android-PickerView系列之源碼解析(二)
需求 動態 () comm tag 多個 來源 ntc 寬高 前言 WheelView想必大家或多或少都有一定了解, 它是一款3D滾輪控件,效果類似IOS 上面的UIpickerview 。按照國際慣例,先放一張效果圖: 以上是Android-PickerView
擁抱 Android Studio 之五:Gradle 插件開發
變量 -h min gui root artifact direct 抽象 path 實踐出真知 筆者有位朋友。每次新學一門語言,都會用來寫一個貪吃蛇遊戲,以此來檢驗自己學習的成果。筆者也有相似體會。所謂紙上得來終覺淺
Android服務之bindService源代碼分析
font state them 成功 ear pack exc 方法 直接 上一篇分析startService時沒有畫出調用ActivityManagerServi
介紹自己的一個Android插樁熱修復框架項目QuickPatch
pid android版本 通過 fly 特性 put javassist 執行 自動生成 QuickPatch項目地址:https://gitee.com/egg90/QuickPatch 和 https://github.com/eggfly/QuickPatch 同步
Javassist之使用位元組碼在執行時生成新的類 01
介紹 Javassist是一個開源的分析、編輯和建立Java位元組碼的類庫。是由東京工業大學的數學和計算機科學系的 Shigeru Chiba (千葉 滋)所建立的。它已加入了開放原始碼JBoss 應用伺服器專案,通過使用Javassist對位元組碼操作為JBoss實現動態"AOP"框架。
ASM插樁實現Android端無埋點效能監控
Android端無埋點實現頁面效能監控 背景 當我們需要了解頁面載入效能時,可以通過手動埋點的方式記錄頁面階段耗時、網路耗時、資料庫載入耗時以及其他耗時點,配合slardar平臺,能直觀地瞭解到頁面的效能情況。 但隨著業務變動,手動埋點存在易寫錯,難維護的麻煩。業界廣泛
Android Kotlin 學習總結(一) 《KAE 優缺點並且深入位元組碼分析工作原理》
本章會分為以下內容: 1.Kotlin KAE介紹,使用和原始Android findViewById對比優缺點 2.Kotlin KAE所存在的問題 3.通過位元組碼分析他的實現原理 閱讀本章內容大概需要您5分鐘的時間 一、Kotlin KAE介紹,使用和原始
位元組碼實踐 -- 使用 ASM 實現 AOP
ASM 是一個 Java 位元組碼操控框架。它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進位制 class 檔案,也可以在類被載入入 Java 虛擬機器之前動態改變類行為。Java class 被儲存在嚴格格式定義的 .class 檔案裡,這
android AOP實現之AspectJ
AOP 1.1 背景 OOP(面向物件程式設計)的精髓是把功能或問題模組化,每個模組都有自己的職責,理想狀態是隻處理自己職責之內的事務。但在實際中,理想的職責單一往往攜帶了一些其他的、“髒”的邏輯處理。舉個最簡單而又常見的例子:現在想為模組A加上日誌功能
JVM Class位元組碼之三-使用BCEL改變類屬性
使用BCEL動態改變Class內容 之前對Class檔案中的常量池,Method的位元組碼指令進行了說明。JVM Class詳解之一JVM Class詳解之二 Method位元組碼指令 現在我們開始實際動手,使用BCEL改變位元組碼指令,對Class檔案進行功能擴充。