馬上加入Android 台灣中文網,立即免費下載應用遊戲。
您需要 登錄 才可以下載或查看,沒有帳號?註冊
x
六、for循環
- # virtual methods
- .method public onClick(Landroid/view/View;)V
- .locals 9
- .parameter 「v」
-
- .prologue
- .line 36
- invoke-virtual {p1}, Landroid/view/View;->getId()I # 非靜態方法參數中隱含的第一個參數p0為this指針, p1為第一個參數, 即View對像
-
- move-result v6 # 把上次的計算結果給第七個寄存器,v6=p1.getId(), v6中為View對象的id
-
- packed-switch v6, :pswitch_data_0 # switch(v6)
-
- # —————– 程式出口開始 ——————
- .line 58
- :goto_0 # for循環出口
- return-void # return;
- # —————– 程式出口結束 ——————
-
- # —————– 獲取控件內容開始 ——————
- .line 39
- :pswitch_0
- iget-object v6, p0, Lcom/sanwho/crackdemo/ForActivity$1;->this$0:Lcom/sanwho/crackdemo/ForActivity; # v6保存this指針
-
- const v7, 0x7f080001 # v7 = txtValue1, 該id保存在public.xml中
-
- invoke-virtual {v6, v7}, Lcom/sanwho/crackdemo/ForActivity;->findViewById(I)Landroid/view/View; # findViewById(txtValue1)
-
- move-result-object v4 # v4為txtValue1對應的View對像
-
- check-cast v4, Landroid/widget/EditText; # 將View對像轉換成EditText, 完成後v4中是txtValue1對像, 失敗會拋出ClassCastException異常
-
- .line 40
- .local v4, txtValue1:Landroid/widget/EditText;
- iget-object v6, p0, Lcom/sanwho/crackdemo/ForActivity$1;->this$0:Lcom/sanwho/crackdemo/ForActivity;
-
- const v7, 0x7f080003 # v7 = txtValue2
-
- invoke-virtual {v6, v7}, Lcom/sanwho/crackdemo/ForActivity;->findViewById(I)Landroid/view/View;
-
- move-result-object v5 # v5為txtValue2對應的View對像
-
- check-cast v5, Landroid/widget/EditText; # 將View對像轉換成EditText, 完成後v5中是txtValue2對像
-
- .line 41
- .local v5, txtValue2:Landroid/widget/EditText;
- invoke-virtual {v4}, Landroid/widget/EditText;->getText()Landroid/text/Editable; # 根據.line 39處可知,v4中為txtValue1對像
-
- move-result-object v6 # v6 = txtValue1.getText();
-
- invoke-interface {v6}, Landroid/text/Editable;->toString()Ljava/lang/String;
-
- move-result-object v6 # v6 = txtValue1.getText().toString();
-
- invoke-static {v6}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
-
- move-result v1 # v1 = Integer.parseInt(v6); 也就是起始數值
-
- .line 42
- .local v1, from:I
- invoke-virtual {v5}, Landroid/widget/EditText;->getText()Landroid/text/Editable; # 根據.line 40處可知,v5中為txtValue2對像
-
- move-result-object v6 # v6 = txtValue2.getText();
-
- invoke-interface {v6}, Landroid/text/Editable;->toString()Ljava/lang/String;
-
- move-result-object v6 # v6 = txtValue2.getText().toString();
-
- invoke-static {v6}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
-
- move-result v0 # v0 = Integer.parseInt(v6); 也就是結束數值
-
- # —————– 獲取控件內容結束 ——————
-
- .line 43
- .local v0, end:I
- if-le v1, v0, :cond_0 # if v1 <= v0, 即起始數值 <= 結束數值, 則跳到cond_0
-
- # —————– 起始數值 > 結束數值時開始 ——————
- .line 45
- iget-object v6, p0, Lcom/sanwho/crackdemo/ForActivity$1;->this$0:Lcom/sanwho/crackdemo/ForActivity;
-
- const-string v7, 「u8d77u59cbu6570u503cu4e0du80fdu5927u4e8eu7ed3u675fu6570u503c!」 # 起始數值不能大於結束數值
-
- #calls: Lcom/sanwho/crackdemo/ForActivity;->MessageBox(Ljava/lang/String;)V
- invoke-static {v6, v7}, Lcom/sanwho/crackdemo/ForActivity;->access$0(Lcom/sanwho/crackdemo/ForActivity;Ljava/lang/String;)V
-
- goto :goto_0
-
- # —————– 起始數值 > 結束數值時結束 ——————
-
- # —————– 起始數值 <= 結束數值時開始 —————–
- .line 49
- :cond_0
- const/4 v3, 0x0 # v3 = 0, 即int sum = 0;
-
- .line 50
- .local v3, sum:I
- move v2, v1 # v2 = v1, v2即源碼中的i變量
-
- .local v2, i:I
- :goto_1 # for循環主要入口
- if-le v2, v0, :cond_1 # if 目前數值 <= 結束數值, 跳到cond_1; 否則循環結束, 顯示累加結果
-
- .line 54
- iget-object v6, p0, Lcom/sanwho/crackdemo/ForActivity$1;->this$0:Lcom/sanwho/crackdemo/ForActivity; # v6指向MessageBox方法
-
- new-instance v7, Ljava/lang/StringBuilder; # v7為StringBuilder對像
-
- const-string v8, 「u7d2fu52a0u7ed3u679cuff1a」 # v8 = 「累加結果:」
-
- invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V # 以v8為參數調用StringBuilder構造函數
-
- invoke-static {v3}, Ljava/lang/Integer;->toString(I)Ljava/lang/String; # 把int型的sum值轉成字符串
-
- move-result-object v8 # v8 = Integer.toString(v3); 此時v8中為sum的值
-
- invoke-virtual {v7, v8}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; # 把累加結果和sum的值進行追加
-
- move-result-object v7 # v7 為 「累加結果:」 + Integer.toString(sum)的StringBuilder對像;
-
- invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; # 將v7轉為字符串對像
-
- move-result-object v7 # v7 = 「累加結果:」 + Integer.toString(sum);
-
- #calls: Lcom/sanwho/crackdemo/ForActivity;->MessageBox(Ljava/lang/String;)V
- invoke-static {v6, v7}, Lcom/sanwho/crackdemo/ForActivity;->access$0(Lcom/sanwho/crackdemo/ForActivity;Ljava/lang/String;)V # 調用MessageBox顯示字符串
-
- goto :goto_0 # 跳到goto_0
- # —————– 起始數值 <= 結束數值時結束 —————–
-
- .line 52
- :cond_1 # 加1操作入口
- add-int/2addr v3, v2 # v3 = v3 + v2, 即sum += i
-
- .line 50
- add-int/lit8 v2, v2, 0x1 # v2 = v2 + 1, , 即i = i + 1
-
- goto :goto_1 # 跳到for循環入口繼續比對
-
- .line 36
- nop
-
- :pswitch_data_0
- .packed-switch 0x7f080004
- :pswitch_0
- .end packed-switch
- .end method
複製代碼 源碼解釋
- Button.OnClickListener onClickListener = new Button.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- switch (v.getId())
- {
- case R.id.btnSubmit:
- EditText txtValue1 = (EditText) findViewById(R.id.txtValue1);
- EditText txtValue2 = (EditText) findViewById(R.id.txtValue2);
- int from = Integer.parseInt(txtValue1.getText().toString());
- int end = Integer.parseInt(txtValue2.getText().toString());
- if (from > end){
- MessageBox("起始數值不能大於結束數值!");
- }
- else
- {
- int sum = 0;
- for (int i = from; i <= end; i++){
-
- sum += i;
- }
- MessageBox("累加結果:" + Integer.toString(sum));
- }
- break;
- }
- }
- };
-
- private void MessageBox(String str)
- {
- Toast.makeText(this, str, Toast.LENGTH_LONG).show();
- }
複製代碼
八、內部類
Java 語言允許在一個類的內部定義另一個類,這種在類中定義的類被稱為內部類(Inner Class)。內部類可分為成員內部類、靜態嵌套類、方法內部類、匿名內部類。前面我們曾經說過,baksmali 在反編譯dex 檔案的時候,會為每個類單獨生成了一個 smali 檔案,內部類作為一個獨立的類,它也擁有自己獨立的smali 檔案,只是內部類的檔案名形式為「[外部類]$[內部類].smali 」,例如下面的類。
class Outer {
class Inner{}
}
baksmali 反編譯上述代碼後會在同一目錄生成兩個檔案:Outer.smali 與Outer$Inner.smali。
- public class MainActivity extends Activity {
- private Button btnAnno;
- private Button btnCheckSN;
- private EditText edtSN;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btnAnno = (Button) findViewById(R.id.btn_annotation);
- btnCheckSN = (Button) findViewById(R.id.btn_checksn);
- edtSN = (EditText) findViewById(R.id.edt_sn);
- btnAnno.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- getAnnotations();
- }
- });
-
- btnCheckSN.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- SNChecker checker = new SNChecker(edtSN.getText().toString());
- String str = checker.isRegistered() ? "註冊碼正確" : "註冊碼錯誤";
- Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- private void getAnnotations() {
- try {
- Class<?> anno = Class.forName("com.droider.anno.MyAnno");
- if (anno.isAnnotationPresent(MyAnnoClass.class)) {
- MyAnnoClass myAnno = anno.getAnnotation(MyAnnoClass.class);
- Toast.makeText(this, myAnno.value(), Toast.LENGTH_SHORT).show();
- }
- Method method = anno.getMethod("outputInfo", (Class[])null);
- if (method.isAnnotationPresent(MyAnnoMethod.class)) {
- MyAnnoMethod myMethod = method.getAnnotation(MyAnnoMethod.class);
- String str = myMethod.name() + " is " + myMethod.age() + " years old.";
- Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
- }
- Field field = anno.getField("sayWhat");
- if (field.isAnnotationPresent(MyAnnoField.class)) {
- MyAnnoField myField = field.getAnnotation(MyAnnoField.class);
- Toast.makeText(this, myField.info(), Toast.LENGTH_SHORT).show();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.activity_main, menu);
- return true;
- }
-
- public class SNChecker {
- private String sn;
- public SNChecker(String sn) {
- this.sn = sn;
- }
-
- public boolean isRegistered() {
- boolean result = false;
- char ch = "\0";
- int sum = 0;
- if (sn == null || (sn.length() < 8)) return result;
- int len = sn.length();
- if (len == 8) {
- ch = sn.charAt(0);
- switch (ch) {
- case "a":
- case "f":
- result = true;
- break;
- default:
- result = false;
- break;
- }
- if (result) {
- ch = sn.charAt(3);
- switch (ch) {
- case "1":
- case "2":
- case "3":
- case "4":
- case "5":
- result = true;
- break;
- default:
- result = false;
- break;
- }
- }
- } else if (len == 16) {
- for (int i = 0; i < len; i++) {
- char chPlus = sn.charAt(i);
- sum += (int) chPlus;
- }
- result = ((sum % 6) == 0) ? true : false;
- }
- return result;
- }
- }
- }
複製代碼
MainActivity$ SNChecker.smali 檔案,這個SNChecker 就是MainActivity的一個內部類。開啟這個檔案,代碼結構如下。
- .class public Lcom/droider/crackme0502/MainActivity$SNChecker;
- .super Ljava/lang/Object;
- .source "MainActivity.java"
-
-
- # annotations
- .annotation system Ldalvik/annotation/EnclosingClass;
- value = Lcom/droider/crackme0502/MainActivity;
- .end annotation
-
- .annotation system Ldalvik/annotation/InnerClass;
- accessFlags = 0x1
- name = "SNChecker"
- .end annotation
-
-
- # instance fields
- .field private sn:Ljava/lang/String;
-
- .field final synthetic this$0:Lcom/droider/crackme0502/MainActivity;
-
-
- # direct methods
- .method public constructor <init>(Lcom/droider/crackme0502/MainActivity;Ljava/lang/String;)V
- .locals 0
- .parameter
- .parameter "sn"
-
- .prologue
- .line 83
- iput-object p1, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->this$0:Lcom/droider/crackme0502/MainActivity;
-
- invoke-direct {p0}, Ljava/lang/Object;-><init>()V
-
- .line 84
- iput-object p2, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- .line 85
- return-void
- .end method
-
-
- # virtual methods
- .method public isRegistered()Z
- .locals 10
-
- .prologue
- const/16 v9, 0X8
-
- const/4 v7, 0x0
-
- .line 88
- const/4 v4, 0x0
-
- .line 89
- .local v4, result:Z
- const/4 v0, 0x0
-
- .line 90
- .local v0, ch:C
- const/4 v6, 0x0
-
- .line 91
- .local v6, sum:I
- iget-object v8, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- if-eqz v8, :cond_0
-
- iget-object v8, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- invoke-virtual {v8}, Ljava/lang/String;->length()I
-
- move-result v8
-
- if-ge v8, v9, :cond_1
-
- :cond_0
- move v5, v4
-
- .line 126
- .end local v4 #result:Z
- .local v5, result:I
- :goto_0
- return v5
-
- .line 92
- .end local v5 #result:I
- .restart local v4 #result:Z
- :cond_1
- iget-object v8, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- invoke-virtual {v8}, Ljava/lang/String;->length()I
-
- move-result v3
-
- .line 93
- .local v3, len:I
- if-ne v3, v9, :cond_3
-
- .line 94
- iget-object v8, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- invoke-virtual {v8, v7}, Ljava/lang/String;->charAt(I)C
-
- move-result v0
-
- .line 95
- sparse-switch v0, :sswitch_data_0
-
- .line 101
- const/4 v4, 0x0
-
- .line 104
- :goto_1
- if-eqz v4, :cond_2
-
- .line 105
- iget-object v7, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- const/4 v8, 0x3
-
- invoke-virtual {v7, v8}, Ljava/lang/String;->charAt(I)C
-
- move-result v0
-
- .line 106
- packed-switch v0, :pswitch_data_0
-
- .line 115
- const/4 v4, 0x0
-
- :cond_2
- :goto_2
- move v5, v4
-
- .line 126
- .restart local v5 #result:I
- goto :goto_0
-
- .line 98
- .end local v5 #result:I
- :sswitch_0
- const/4 v4, 0x1
-
- .line 99
- goto :goto_1
-
- .line 112
- :pswitch_0
- const/4 v4, 0x1
-
- .line 113
- goto :goto_2
-
- .line 119
- :cond_3
- const/16 v8, 0x10
-
- if-ne v3, v8, :cond_2
-
- .line 120
- const/4 v2, 0x0
-
- .local v2, i:I
- :goto_3
- if-lt v2, v3, :cond_4
-
- .line 124
- rem-int/lit8 v8, v6, 0x6
-
- if-nez v8, :cond_5
-
- const/4 v4, 0x1
-
- :goto_4
- goto :goto_2
-
- .line 121
- :cond_4
- iget-object v8, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- invoke-virtual {v8, v2}, Ljava/lang/String;->charAt(I)C
-
- move-result v1
-
- .line 122
- .local v1, chPlus:C
- add-int/2addr v6, v1
-
- .line 120
- add-int/lit8 v2, v2, 0x1
-
- goto :goto_3
-
- .end local v1 #chPlus:C
- :cond_5
- move v4, v7
-
- .line 124
- goto :goto_4
-
- .line 95
- :sswitch_data_0
- .sparse-switch
- 0x61 -> :sswitch_0
- 0x66 -> :sswitch_0
- .end sparse-switch
-
- .line 106
- :pswitch_data_0
- .packed-switch 0x31
- :pswitch_0
- :pswitch_0
- :pswitch_0
- :pswitch_0
- :pswitch_0
- .end packed-switch
- .end method
複製代碼
發現它有兩個註解定義塊「Ldalvik/annotation/EnclosingClass;」與「Ldalvik/annotation/ InnerClass; 」、兩個實例字段sn 與this$0 、一個直接方法 init()、一個虛方法isRegistered() 。註解定義塊我們稍後進行講解。先看它的實例字段,sn 是字符串類型,this$0 是MainActivity類型,synthetic 關鍵字表明它是「合成」的,那 this$0 到底是個什麼東西呢?
其實this$0 是內部類自動保留的一個指向所在外部類的引用。左邊的 this 表示為父類的引用,右邊的數值0 表示引用的層數。我們看下面的類。
- public class Outer { <span style="white-space:pre"> </span> //this$0
- public class FirstInner { <span style="white-space:pre"> </span>//this$1
- public class SecondInner { //this$2
- public class ThirdInner {
- }
- }
- }
- }
複製代碼 每往裡一層右邊的數值就加一,如 ThirdInner類存取 FirstInner 類的引用為this$1 。在生成的反彙編代碼中,this$X 型字段都被指定了synthetic 屬性,表明它們是被編譯器合成的、虛構的,代碼的作者並沒有聲明該字段。
我們再看看MainActivity$SNChecker的構造函數,看它是如何初始化的。代碼如下。
- # direct methods
- .method public constructor <init>(Lcom/droider/crackme0502/MainActivity;Ljava/lang/String;)V
- .locals 0
- .parameter #第一個參數MainActivity引用
- .parameter "sn" #第二個參數字符串sn
-
- .prologue
- .line 83
- #將MainActivity引用賦值給this$0
- iput-object p1, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->this$0:Lcom/droider/crackme0502/MainActivity;
-
- #調用預設的構造函數
- invoke-direct {p0}, Ljava/lang/Object;-><init>()V
-
- .line 84
- #將sn字符串的值賦給sn字段
- iput-object p2, p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
-
- .line 85
- return-void
- .end method
複製代碼
對於一個非靜態的方法而言,會隱含的使用p0寄存器當作類的this 引用。因此,這裡的確是使用了3 個寄存器:p0表示MainActivity$SNChecker自身的引用,p1表示MainActivity的引用,p2表示sn 字符串。另外,從 MainActivity$SNChecker的構造函數可以看出,內部類的初始化共有以下 3 個步驟:首先是保存外部類的引用到本類的一個 synthetic字段中,以便內部類的其它方法使用,然後是調用內部類的父類的構造函數來初始化父類,最後是對內部類自身進行初始化。
一個方法中指定的寄存器個
在一個方法(method)中有兩中方式指定有多少個可用的寄存器。指令.registers指令指定了在這個方法中有多少個可用的寄存器,指令.locals指明了在這個方法中非參(non-parameter)寄存器的數量。然而寄存器的總數也包括保存方法參數的寄存器。
參數是如何傳遞的?
當一個方法被調用時,該方法的參數被保存在最後N個寄存器中。如果一個方法有2個參數和5個寄存器(V0-V4),參數將被保存在最後的2個寄存器內V3和V4.
非靜態方法的第一個參數,總是被方法調用的對象。
例如,你寫了一個非靜態方法LMyObject;->callMe(II)V。這個方法有2個int參數,但在這兩個整型參數前面還有一個隱藏的參數LMyObject;所以這個方法總共有3個參數。
比如說,在方法中指定有5個寄存器(V0-V4),只用.register指令指定5個,或者使用.locals指令指定2個(2個local寄存器+3個參數寄存器)。該方法被調用的時候,調用方法的對象(即this引用)會保存在V2中,第一個參數在V3中,第二個參數在v4中。
除了不包含this隱藏參數,對於靜態方法都是相同的。
寄存器名稱
有兩種寄存器的命名方式,對於參數寄存器有普通的V命名方式和P命名方式。在方法(method)中第一個參數寄存器,是使用P方式命名的第一個寄存器,讓我們回到前面的例子中,有三個參數和5個寄存器,下面的這個表顯示了對每個寄存器的普通V命名方式,後面是P方式命名的參數寄存器。
|