咖啡伴侶

          呆在上海
          posts - 163, comments - 156, trackbacks - 0, articles - 2
           
          英文: /android-sdk/docs/guide/guide_toc.html
           
           

           

          編寫高效的Android代碼

           

          毫無(wú)疑問(wèn),基于Android平臺(tái)的設(shè)備一定是嵌入式設(shè)備。現(xiàn)代的手持設(shè)備不僅僅是一部電話那么簡(jiǎn)單,它還是一個(gè)小型的手持電腦,但是,即使是最快的最高端的手持設(shè)備也遠(yuǎn)遠(yuǎn)比不上一個(gè)中等性能的桌面機(jī)。

          這就是為什么在編寫Android程序時(shí)要時(shí)刻考慮執(zhí)行的效率,這些系統(tǒng)不是想象中的那么快,并且你還要考慮它電池的續(xù)航能力。這就意味著沒有多少剩余空間給你去浪費(fèi)了,因此,在你寫Android程序的時(shí)候,要盡可能的使你的代碼優(yōu)化而提高效率。

          本頁(yè)介紹了幾種可以讓開發(fā)者的Android程序運(yùn)行的更加有效率的方法。通過(guò)下面的一些跳轉(zhuǎn)的連接,你可以學(xué)會(huì)怎么讓你的程序更加有效運(yùn)行

           

          內(nèi)容

           

          介紹

          盡可能避免創(chuàng)建對(duì)象(Object

          使用自身方法(Use Native Methods

          使用虛擬優(yōu)于使用接口

          使用靜態(tài)優(yōu)于使用虛擬

          盡可能避免使用內(nèi)在的GetSet方法

          緩沖屬性調(diào)用Cache Field Lookups

          聲明Final常量

          慎重使用增強(qiáng)型For循環(huán)語(yǔ)句

          避免列舉類型Avoid Enums

          通過(guò)內(nèi)聯(lián)類使用包空間

          避免浮點(diǎn)類型的使用

          一些標(biāo)準(zhǔn)操作的時(shí)間比較

          結(jié)束語(yǔ)

          介紹

          對(duì)于如何判斷一個(gè)系統(tǒng)的不合理,這里有兩個(gè)基本的原則:

          ·           不要做不必要做的事情。

          ·           盡可能的節(jié)省內(nèi)存的使用。

          下面的所有方法都是基于這兩項(xiàng)的。

          有人會(huì)認(rèn)為本頁(yè)花了大量的篇幅去講如何進(jìn)行初步優(yōu)化 premature optimization)。雖然有時(shí)候微觀優(yōu)化對(duì)開發(fā)高效的數(shù)據(jù)結(jié)構(gòu)和算法很困難,但是在嵌入式手持設(shè)備上面你毫無(wú)選擇。例如,如果把桌面電腦的虛擬機(jī)移植到你的Android系統(tǒng)中,你會(huì)發(fā)現(xiàn)你寫的程序會(huì)耗盡你的內(nèi)存。這就會(huì)導(dǎo)致程序運(yùn)行起來(lái)極度緩慢,即使不考慮它對(duì)系統(tǒng)上其他的運(yùn)行程序的影響。

          這就是為什么上面兩條原則這么重要。Android的 成功在于開發(fā)程序提供給用戶的體驗(yàn),然而用戶體驗(yàn)的好壞又決定于你的代碼是否能及時(shí)的響應(yīng)而不至于慢的讓人崩潰。因?yàn)槲覀兯械某绦蚨紩?huì)在同一個(gè)設(shè)備上面 運(yùn)行,所以我們把它們作為一個(gè)整體來(lái)考慮。本文就像你考駕照需要學(xué)習(xí)的交通規(guī)則一樣:如果所有人遵守,事情就會(huì)很流暢;但當(dāng)你不遵守時(shí),你就會(huì)撞車。

          在我們討論實(shí)質(zhì)問(wèn)題之前,有一個(gè)簡(jiǎn)要的說(shuō)明:無(wú)論虛擬機(jī)是否是Java編譯器的一個(gè)特點(diǎn),下面介紹的所有觀點(diǎn)都是正確的。如果我們有兩種方法完成同樣的事情,但是foo()的解釋執(zhí)行要快于bar(),那么foo()的編譯速度一定不會(huì)比bar()慢,僅僅靠編譯器使你的代碼運(yùn)行速度提升是不明智的做法。

          盡可能避免創(chuàng)建對(duì)象(Object

          對(duì)象的創(chuàng)建并不是沒有代價(jià)的。一個(gè)帶有線程分配池的generational的內(nèi)存管理機(jī)制會(huì)使創(chuàng)建臨時(shí)對(duì)象的代價(jià)減少,不是分配內(nèi)存總比不上不分配內(nèi)存好。

          如果你在一個(gè)用戶界面的循環(huán)中分配一個(gè)對(duì)象,你不得不強(qiáng)制的進(jìn)行內(nèi)存回收,那么就會(huì)使用戶體驗(yàn)出現(xiàn)稍微打嗝的現(xiàn)象。

          因此,如果沒有必要你就不應(yīng)該創(chuàng)建對(duì)象實(shí)例。下面是一件有幫助的例子:

          ·           當(dāng)從原始的輸入數(shù)據(jù)中提取字符串時(shí),試著從原始字符串返回一個(gè)子字符串,而不是創(chuàng)建一份拷貝。你將會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象,但是它和你的原始數(shù)據(jù)共享數(shù)據(jù)空間。

          ·           如果你有一個(gè)返回字符串地方法,你應(yīng)該知道無(wú)論如何返回的結(jié)果是StringBuffer,改變你的函數(shù)的定義和執(zhí)行,讓函數(shù)直接返回而不是通過(guò)創(chuàng)建一個(gè)臨時(shí)的對(duì)象。

          一個(gè)比較激進(jìn)的方法就是把一個(gè)多維數(shù)組分割成幾個(gè)平行的一維數(shù)組:

          ·           一個(gè)Int類型的數(shù)組要比一個(gè)Integer類型的數(shù)組要好,但著同樣也可以歸納于這樣一個(gè)原則,兩個(gè)Int類型的數(shù)組要比一個(gè)(intint)對(duì)象數(shù)組的效率要高的多。對(duì)于其他原始數(shù)據(jù)類型,這個(gè)原則同樣適用。

          ·           m 如果你需要?jiǎng)?chuàng)建一個(gè)包含一系列FooBar對(duì)象的容器(container)時(shí),記住:兩個(gè)平行的Foo[]Bar[]要比一個(gè)(Foo,Bar)對(duì)象數(shù)組的效率高得多。(這個(gè)例子也有一個(gè)例外,當(dāng)你設(shè)計(jì)其他代碼的接口API時(shí);在這種情況下,速度上的一點(diǎn)損失就不用考慮了。但是,在你的代碼里面,你應(yīng)該盡可能的編寫高效代碼。)

          一般來(lái)說(shuō),盡可能的避免創(chuàng)建短期的臨時(shí)對(duì)象。越少的對(duì)象創(chuàng)建意味著越少的垃圾回收,這會(huì)提高你程序的用戶體驗(yàn)質(zhì)量。

          使用自身方法(Use Native Methods

          當(dāng)處理字符串的時(shí)候,不要猶豫,盡可能多的使用諸如String.indexOf()String.lastIndexOf()這樣對(duì)象自身帶有的方法。因?yàn)檫@些方法使用C/C++來(lái)實(shí)現(xiàn)的,要比在一個(gè)java循環(huán)中做同樣的事情快10-100倍。

          還有一點(diǎn)要補(bǔ)充說(shuō)明的是,這些自身方法使用的代價(jià)要比那些解釋過(guò)的方法高很多,因而,對(duì)于細(xì)微的運(yùn)算,盡量不用這類方法。

          使用虛擬優(yōu)于使用接口

          假設(shè)你有一個(gè)HashMap對(duì)象,你可以聲明它是一個(gè)HashMap或則只是一個(gè)Map

          Map myMap1 = new HashMap();

          HashMap myMap2 = new HashMap();

          哪一個(gè)更好呢?

          一般來(lái)說(shuō)明智的做法是使用Map,因?yàn)樗軌蛟试S你改變Map接口執(zhí)行上面的任何東西,但是這種明智的方法只是適用于常規(guī)的編程,對(duì)于嵌入式系統(tǒng)并不適合。通過(guò)接口引用來(lái)調(diào)用會(huì)花費(fèi)2倍以上的時(shí)間,相對(duì)于通過(guò)具體的引用進(jìn)行虛擬函數(shù)的調(diào)用。

          如果你選擇使用一個(gè)HashMap,因?yàn)樗m合于你的編程,那么使用Map會(huì)毫無(wú)價(jià)值。假定你有一個(gè)能重構(gòu)你代碼的集成編碼環(huán)境,那么調(diào)用Map沒有什么用處,即使你不確定你的程序從哪開頭。(同樣,publicAPI是一個(gè)例外,一個(gè)好的API的價(jià)值往往大于執(zhí)行效率上的那點(diǎn)損失)

          使用靜態(tài)優(yōu)于使用虛擬

          如果你沒有必要去訪問(wèn)對(duì)象的外部,那么使你的方法成為靜態(tài)方法。它會(huì)被更快的調(diào)用,因?yàn)樗恍枰粋€(gè)虛擬函數(shù)導(dǎo)向表。這同時(shí)也是一個(gè)很好的實(shí)踐,因?yàn)樗嬖V你如何區(qū)分方法的性質(zhì)(signature),調(diào)用這個(gè)方法不會(huì)改變對(duì)象的狀態(tài)。

          盡可能避免使用內(nèi)在的GetSet方法

          C++編程語(yǔ)言,通常會(huì)使用Get方法(例如 i = getCount())去取代直接訪問(wèn)這個(gè)屬性(i=mCount)。 這在C++編程里面是一個(gè)很好的習(xí)慣,因?yàn)榫幾g器會(huì)把訪問(wèn)方式設(shè)置為Inline,并且如果想約束或調(diào)試屬性訪問(wèn),你只需要在任何時(shí)候添加一些代碼。

          Android編程中,這不是一個(gè)很不好的主意。虛方法的調(diào)用會(huì)產(chǎn)生很多代價(jià),比實(shí)例屬性查詢的代價(jià)還要多。我們應(yīng)該在外部調(diào)用時(shí)使用GetSet函數(shù),但是在內(nèi)部調(diào)用時(shí),我們應(yīng)該直接調(diào)用。

          緩沖屬性調(diào)用Cache Field Lookups

          訪問(wèn)對(duì)象屬性要比訪問(wèn)本地變量慢得多。你不應(yīng)該這樣寫你的代碼:

          for (int i = 0; i < this.mCount; i++)

                dumpItem(this.mItems[i]);

          而是應(yīng)該這樣寫:

           int count = this.mCount;

           Item[] items = this.mItems;

           

           for (int i = 0; i < count; i++)

                dumpItems(items[i]);

          (我們直接使用“this”表明這些是它的成員變量)

          一個(gè)相似的原則就是:決不在一個(gè)For語(yǔ)句中第二次調(diào)用一個(gè)類的方法。例如,下面的代碼就會(huì)一次又一次地執(zhí)行getCount()方法,這是一個(gè)極大地浪費(fèi)相比你把它直接隱藏到一個(gè)Int變量中。

          for (int i = 0; i < this.getCount(); i++)

              dumpItems(this.getItem(i));

          這是一個(gè)比較好的辦法,當(dāng)你不止一次的調(diào)用某個(gè)實(shí)例時(shí),直接本地化這個(gè)實(shí)例,把這個(gè)實(shí)例中的某些值賦給一個(gè)本地變量。例如:

              protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {

                  if (isHorizontalScrollBarEnabled()) {

                      int size = mScrollBar.getSize(false);

                      if (size <= 0) {

                          size = mScrollBarSize;

                      }

                      mScrollBar.setBounds(0, height - size, width, height);

                      mScrollBar.setParams(

                              computeHorizontalScrollRange(),

                              computeHorizontalScrollOffset(),

                              computeHorizontalScrollExtent(), false);

                      mScrollBar.draw(canvas);

                  }

              }

          這里有四次mScrollBar的屬性調(diào)用,把mScrollBar緩沖到一個(gè)堆棧變量之中,四次成員屬性的調(diào)用就會(huì)變成四次堆棧的訪問(wèn),這樣就會(huì)提高效率。

          附帶說(shuō)一下,對(duì)于方法同樣也可以像本地變量一樣具有相同的特點(diǎn)。

          聲明Final常量

          我們可以看看下面一個(gè)類頂部的聲明:

          static int intVal = 42;

          static String strVal = "Hello, world!";

          當(dāng)一個(gè)類第一次使用時(shí),編譯器會(huì)調(diào)用一個(gè)類初始化方法——<clinit>,這個(gè)方法將42存入變量intVal,并且為strVal在類文件字符串常量表中提取一個(gè)引用,當(dāng)這些值在后面引用時(shí),就會(huì)直接屬性調(diào)用。

          我們可以用關(guān)鍵字“final”來(lái)改進(jìn)代碼:

          static final int intVal = 42;

          static final String strVal = "Hello, world!";

          這個(gè)類將不會(huì)調(diào)用es a <clinit>方法,因?yàn)檫@些常量直接寫入了類文件靜態(tài)屬性初始化中,這個(gè)初始化直接由虛擬機(jī)來(lái)處理。代碼訪問(wèn)intVal將會(huì)使用Integer類型的42,訪問(wèn)strVal將使用相對(duì)節(jié)省的字符串常量來(lái)替代一個(gè)屬性調(diào)用。

          將一個(gè)類或者方法聲明為“final”并不會(huì)帶來(lái)任何的執(zhí)行上的好處,它能夠進(jìn)行一定的最優(yōu)化處理。例如,如果編譯器知道一個(gè)Get方法不能被子類重載,那么它就把該函數(shù)設(shè)置成Inline

          同時(shí),你也可以把本地變量聲明為final變量。但是,這毫無(wú)意義。作為一個(gè)本地變量,使用final只能使代碼更加清晰(或者你不得不用,在匿名訪問(wèn)內(nèi)聯(lián)類時(shí))。

          慎重使用增強(qiáng)型For循環(huán)語(yǔ)句

          增強(qiáng)型For循環(huán)(也就是常說(shuō)的“For-each循環(huán))經(jīng)常用于Iterable接口的繼承收集接口上面。在這些對(duì)象里面,一個(gè)iterator被分配給對(duì)象去調(diào)用它的hasNext()和next()方法。在一個(gè)數(shù)組列表里面,你可以自己接的敷衍它,在其他的收集器里面,增強(qiáng)型的for循環(huán)將相當(dāng)于iterator的使用。

          盡管如此,下面的源代碼給出了一個(gè)可以接受的增強(qiáng)型for循環(huán)的例子:

          public class Foo {

              int mSplat;

              static Foo mArray[] = new Foo[27];

           

              public static void zero() {

                  int sum = 0;

                  for (int i = 0; i < mArray.length; i++) {

                      sum += mArray[i].mSplat;

                  }

              }

           

              public static void one() {

                  int sum = 0;

                  Foo[] localArray = mArray;

                  int len = localArray.length;

           

                  for (int i = 0; i < len; i++) {

                      sum += localArray[i].mSplat;

                  }

              }

           

              public static void two() {

                  int sum = 0;

                  for (Foo a: mArray) {

                      sum += a.mSplat;

                  }

              }

          }

          zero() 函數(shù)在每一次的循環(huán)中重新得到靜態(tài)屬性兩次,獲得數(shù)組長(zhǎng)度一次。

          one() 函數(shù)把所有的東西都變?yōu)楸镜刈兞浚苊忸惒檎覍傩哉{(diào)用

          two() 函數(shù)使用Java語(yǔ)言的1.5版本中的for循環(huán)語(yǔ)句,編輯者產(chǎn)生的源代碼考慮到了拷貝數(shù)組的引用和數(shù)組的長(zhǎng)度到本地變量,是例遍數(shù)組比較好的方法,它在主循環(huán)中確實(shí)產(chǎn)生了一個(gè)額外的載入和儲(chǔ)存過(guò)程(顯然保存了“a”),相比函數(shù)one()來(lái)說(shuō),它有一點(diǎn)比特上的減慢和4字節(jié)的增長(zhǎng)。

          總結(jié)之后,我們可以得到:增強(qiáng)的for循環(huán)在數(shù)組里面表現(xiàn)很好,但是當(dāng)和Iterable對(duì)象一起使用時(shí)要謹(jǐn)慎,因?yàn)檫@里多了一個(gè)對(duì)象的創(chuàng)建。

          避免列舉類型Avoid Enums

          列舉類型非常好用,當(dāng)考慮到尺寸和速度的時(shí)候,就會(huì)顯得代價(jià)很高,例如:

          public class Foo {

             public enum Shrubbery { GROUND, CRAWLING, HANGING }

          }

          這會(huì)轉(zhuǎn)變成為一個(gè)900字節(jié)的class文件(Foo$Shrubbery.class)。第一次使用時(shí),類的初始化要在獨(dú)享上面調(diào)用方法去描述列舉的每一項(xiàng),每一個(gè)對(duì)象都要有它自身的靜態(tài)空間,整個(gè)被儲(chǔ)存在一個(gè)數(shù)組里面(一個(gè)叫做“$VALUE”的靜態(tài)數(shù)組)。那是一大堆的代碼和數(shù)據(jù),僅僅是為了三個(gè)整數(shù)值。

          Shrubbery shrub = Shrubbery.GROUND;

          這會(huì)引起一個(gè)靜態(tài)屬性的調(diào)用,如果GROUND是一個(gè)靜態(tài)的Final變量,編譯器會(huì)把它當(dāng)做一個(gè)常數(shù)嵌套在代碼里面。

          還有一點(diǎn)要說(shuō)的,通過(guò)列舉,你可以得到更好地API和一些編譯時(shí)間上的檢查。因此,一種比較平衡的做法就是:你應(yīng)該盡一切方法在你的公用API中使用列舉型變量,當(dāng)處理問(wèn)題時(shí)就盡量的避免。

          在一些環(huán)境下面,通過(guò)ordinal()方法獲取一個(gè)列舉變量的整數(shù)值是很有用的,例如:把下面代碼

          for (int n = 0; n < list.size(); n++) {

              if (list.items[n].e == MyEnum.VAL_X)

                 // do stuff 1

              else if (list.items[n].e == MyEnum.VAL_Y)

                 // do stuff 2

          }

          替換為:

             int valX = MyEnum.VAL_X.ordinal();

             int valY = MyEnum.VAL_Y.ordinal();

             int count = list.size();

             MyItem items = list.items();

           

             for (int n = 0; n < count; n++)

             {

                  int valItem = items[n].e.ordinal();

           

                  if (valItem == valX)

                    // do stuff 1

                  else if (valItem == valY)

                    // do stuff 2

             }

          在一些條件下,這會(huì)執(zhí)行的更快,雖然沒有保障。

          通過(guò)內(nèi)聯(lián)類使用包空間

          我們看下面的類聲明

          public class Foo {

              private int mValue;

           

              public void run() {

                  Inner in = new Inner();

                  mValue = 27;

                  in.stuff();

              }

           

              private void doStuff(int value) {

                  System.out.println("Value is " + value);

              }

           

              private class Inner {

                  void stuff() {

                      Foo.this.doStuff(Foo.this.mValue);

                  }

              }

          }

          這里我們要注意的是我們定義了一個(gè)內(nèi)聯(lián)類,它調(diào)用了外部類的私有方法和私有屬性。這是合法的調(diào)用,代碼應(yīng)該會(huì)顯示"Value is 27"

          問(wèn)題是Foo$Inner在理論上(后臺(tái)運(yùn)行上)是應(yīng)該是一個(gè)完全獨(dú)立的類,它違規(guī)的調(diào)用了Foo的私有成員。為了彌補(bǔ)這個(gè)缺陷,編譯器產(chǎn)生了一對(duì)合成的方法:

          /*package*/ static int Foo.access$100(Foo foo) {

              return foo.mValue;

          }

          /*package*/ static void Foo.access$200(Foo foo, int value) {

              foo.doStuff(value);

          }

          當(dāng)內(nèi)聯(lián)類需要從外部訪問(wèn)“mValue”和調(diào)用“doStuff”時(shí),內(nèi)聯(lián)類就會(huì)調(diào)用這些靜態(tài)的方法,這就意味著你不是直接訪問(wèn)類成員,而是通過(guò)公共的方法來(lái)訪問(wèn)的。前面我們談過(guò)間接訪問(wèn)要比直接訪問(wèn)慢,因此這是一個(gè)按語(yǔ)言習(xí)慣無(wú)形執(zhí)行的例子。

          讓 擁有包空間的內(nèi)聯(lián)類直接聲明需要訪問(wèn)的屬性和方法,我們就可以避免這個(gè)問(wèn)題,哲理詩(shī)是包空間而不是私有空間。這運(yùn)行的更快并且去除了生成函數(shù)前面東西。 (不幸的是,它同時(shí)也意味著該屬性也能夠被相同包下面的其他的類直接訪問(wèn),這違反了標(biāo)準(zhǔn)的面向?qū)ο蟮氖顾袑傩运接械脑瓌t。同樣,如果是設(shè)計(jì)公共的API你就要仔細(xì)的考慮這種優(yōu)化的用法)

          避免浮點(diǎn)類型的使用

          在奔騰CPU發(fā)布之前,游戲作者盡可能的使用Integer類型的數(shù)學(xué)函數(shù)是很正常的。在奔騰處理器里面,浮點(diǎn)數(shù)的處理變?yōu)樗粋€(gè)突出的特點(diǎn),并且浮點(diǎn)數(shù)與整數(shù)的交互使用相比單獨(dú)使用整數(shù)來(lái)說(shuō),前者會(huì)使你的游戲運(yùn)行的更快,一般的在桌面電腦上面我們可以自由的使用浮點(diǎn)數(shù)。

          不幸的是,嵌入式的處理器通常并不支持浮點(diǎn)數(shù)的處理,陰齒所有的“float”“double”操作都是通過(guò)軟件進(jìn)行的,一些基本的浮點(diǎn)數(shù)的操作就需要花費(fèi)毫秒級(jí)的時(shí)間。

          同事,即使是整數(shù),一些芯片也只有乘法而沒有除法。在這些情況下,整數(shù)的除法和取模操作都是通過(guò)軟件實(shí)現(xiàn)。當(dāng)你創(chuàng)建一個(gè)Hash表或者進(jìn)行大量的數(shù)學(xué)運(yùn)算時(shí),這都是你要考慮的。

          一些標(biāo)準(zhǔn)操作的時(shí)間比較

          為了距離說(shuō)明我們的觀點(diǎn),下面有一張表,包括一些基本操作所使用的大概時(shí)間。注意這些時(shí)間并不是絕對(duì)的時(shí)間,絕對(duì)時(shí)間要考慮到CPU和時(shí)鐘頻率。系統(tǒng)不同,時(shí)間的大小也會(huì)有所差別。當(dāng)然,這也是一種有意義的比較方法,我們可以比叫不同操作花費(fèi)的相對(duì)時(shí)間。例如,添加一個(gè)成員變量的時(shí)間是添加一個(gè)本地變量的四倍。

          Action

          Time

          Add a local variable

          1

          Add a member variable

          4

          Call String.length()

          5

          Call empty static native method

          5

          Call empty static method

          12

          Call empty virtual method

          12.5

          Call empty interface method

          15

          Call Iterator:next() on a HashMap

          165

          Call put() on a HashMap

          600

          Inflate 1 View from XML

          22,000

          Inflate 1 LinearLayout containing 1 TextView

          25,000

          Inflate 1 LinearLayout containing 6 View objects

          100,000

          Inflate 1 LinearLayout containing 6 TextView objects

          135,000

          Launch an empty activity

          3,000,000

          結(jié)束語(yǔ)

          寫高效的嵌入式程序的最好方法就是要搞清楚你寫的程序究竟做了些什么。如果你真的想分配一個(gè)iterator類,進(jìn)一切方法的在一個(gè)List中使用增強(qiáng)型的for循環(huán),使它成為一個(gè)有意而為之的做法,而不是一個(gè)無(wú)意的疏漏而產(chǎn)生負(fù)面影響。

          有備無(wú)患,搞清楚你在做什么!你可以假如你自己的一些行為準(zhǔn)則,但是一定要注意你的代碼正在做什么,然后開始尋找方法去優(yōu)化它。

           

          主站蜘蛛池模板: 利辛县| 房产| 甘洛县| 扎兰屯市| 南开区| 随州市| 长顺县| 阿巴嘎旗| 什邡市| 会理县| 河南省| 驻马店市| 赫章县| 鄂州市| 定远县| 财经| 泸溪县| 公主岭市| 二连浩特市| 水富县| 桑日县| 利辛县| 汶川县| 哈巴河县| 苗栗市| 双峰县| 红桥区| 获嘉县| 连山| 康保县| 汤阴县| 十堰市| 汝城县| 宜城市| 绥宁县| 垦利县| 长沙市| 九江县| 尚志市| 卓资县| 彩票|