Android開發減少方法數的建議

     現在的Android應用越來越龐大,開發者越來越能感受到方法數限制帶來的影響。這裏簡單講幾個減少方法數的小建議。

 

1.什麼是方法數?爲什麼它這麼重要?

    這要從dex的文件格式說起,在把源碼編譯、轉化爲dex文件格式時,dex文件中會有一個區域包含了所有源碼中定義或引用的方法列表。這個區域中所有方法項的總數就是方法數。

很遺憾,Android在剛開始被設計的時候,這一區域的方法數量不能超過65536個,也就是2個字節表示的範圍。當源碼定義或引用的方法數量超過了這個限制的話,就會導致編譯不成功,你說重要不重要呢? 

 

2.如何查看這些方法?

    可以使用修改過的dexdump。標準的dexdump可以解析方法列表,但無法打印出來,修改版的dexdump可以打印這些信息

如下是一個簡單類LogicActivity中使用的方法

Class: Lcom/xxx/activity/LogicActivity; 18

Method: <init> ()V

Method: access$000 (Lcom/xxx/activity/LogicActivity;)Lcom/xxx/app/AppInterface;

Method: access$100 (Lcom/xxx/activity/LogicActivity;)Lcom/xxx/app/AppInterface;

Method: addFriend (Ljava/lang/String;ILjava/lang/String;)V

Method: addObserver (Lcom/xxx/app/BusinessObserver;)V

Method: finish ()V

Method: getIntent ()Landroid/content/Intent;

Method: getString (I)Ljava/lang/String;

Method: getTitleBarHeight ()I

Method: joinTroop ()V

Method: onActivityResult (IILandroid/content/Intent;)V

Method: onCreate (Landroid/os/Bundle;)V

Method: onDestroy ()V

Method: removeObserver (Lcom/xxx/app/BusinessObserver;)V

Method: setLastActivityName ()Ljava/lang/String;

Method: setResult (ILandroid/content/Intent;)V

Method: startActivity (Landroid/content/Intent;)V

Method: startActivityForResult (Landroid/content/Intent;I)V

可以看出,這裏面的方法是包含代碼中引用的方法的,如finish(),getIntent()這些方法。

 

3.減少方法數的辦法

    以下所介紹的方法都可以在修改後,用dexdump –j來觀察、比較所修改的方法以及驗證減少的效果。

方法1 避免在內部類中訪問外部類的私有方法/變量

當在Java內部類(包括內部匿名類)中訪問外部類的私有方法/變量時,編譯器會生成額外的方法,這也會增加方法數,建議編碼時儘量避免。

 

具體原因:

考慮如下的代碼

publicclassFoo{
    privateclassInner{
        void stuff(){
            Foo.this.doStuff(Foo.this.mValue);
        }
    }

    privateint mValue;

    publicvoid run(){
        Innerin=newInner();
        mValue =27;
        in.stuff();
    }

    privatevoid doStuff(int value){
        System.out.println("Value is "+ value);
    }
}

 

雖然Java語言允許內部類直接訪問外部類的方法,但是虛擬機卻認爲Foo和Foo$Inner是兩個不同的類,爲了支持Foo$Inner訪問Foo的private成員,編譯器會生成兩個額外的方法,而生成的這些方法也算在方法總數裏面

 

/*package*/staticintFoo.access$100(Foo foo){
    return foo.mValue;
}
/*package*/staticvoidFoo.access$200(Foo foo,int value){
    foo.doStuff(value);
}
 

 

具體可以參考:http://developer.android.com/training/articles/perf-tips.html#PackageInner

 

解決辦法:

很簡單,把mValue和doStuff()的private修飾符去掉就好了,這樣它的默認訪問域爲包級,編譯器就不需要生成額外的代碼。

 

方法2 避免調用派生類中的未被覆蓋(override)的方法

考慮下面的代碼

 

publicclass DemoActivity extends Activity {
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        if (intent.getAction().equals("add")) {
            finish();
        }
        else {
            setContentView(R.id.background);
        }
    }
}
 

 

實際上它會生成5個方法,除了定義的onCreate和構造函數之外,還有setContentView、getIntent()和finish()。因爲按照java的語義,如果有覆蓋父類的方法,則會直接調用覆蓋的方法。

Class: Lcom/xxx/activity/DemoActivity; 5

Method: <init> ()V

Method: finish ()V

Method: getIntent ()Landroid/content/Intent;

Method: onCreate (Landroid/os/Bundle;)V

Method: setContentView (I)V

 

解決辦法:

對於不需要被override的方法,顯式的改成調用父類的方法,如下所示

 

publicclass DemoActivity extends Activity {
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = super.getIntent();
        if (intent.getAction().equals("add")) {
            super.finish();
        }
        else {
            super.setContentView(R.id.background);
        }
    }
}
 

 

則實際在方法數列表中它只佔2個方法

Class: Lcom/tencent/mobileqq/activity/DemoActivity; 2

Method: <init> ()V

Method: onCreate (Landroid/os/Bundle;)V

 

其實減少方法數還有很多辦法,比如插件化之類的,後面有空再介紹。

 

另外,手機QQ空間求Android開發,有意者可以直接發送簡歷到郵箱 [email protected],或者聯繫QQ 115818782,註明來意。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章