最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

淺談App響應時間優(yōu)化

2023-04-21 04:41 作者:xuexiangjys  | 我要投稿

響應時間,它是用來衡量系統(tǒng)運行效率的一個重要指標。評價一個應用的響應時間,可以從用戶感知和系統(tǒng)性能這兩個角度來考量。

響應時間的長短,可能影響用戶對某個功能、某個應用、乃至某個系統(tǒng)的使用。畢竟如果有選擇,沒有哪個人會愿意去使用卡頓的應用,運行慢的手機。

作為一名開發(fā)者,雖然我們平時可能只關注于堆業(yè)務,根本就沒有時間或者機會去優(yōu)化我們程序的響應時間,但是這些內容對我們個人的技術成長是至關重要的。大的不說,這部分也是面試中經(jīng)??疾斓膬热荩懒艘膊恢劣诔蕴?。

那么接下來我們就長話短說,趕緊來瞧瞧,到底如何來優(yōu)化我們應用的響應時間。

1. 核心原則

在算法中,我們經(jīng)常會從時間復雜度空間復雜度這兩個緯度來衡量算法的優(yōu)劣。

很多時候,我們無法做到時間復雜度空間復雜度兩者都最佳,只能在"時間"和"空間"中,取折中的最優(yōu)解。同樣的,如果我們追求最極致的"時間"最佳,就可能需要犧牲一部分的"空間",這就是拿"空間"換"時間"的解法。

響應時間優(yōu)化的核心:空間 -> 時間 (用空間換時間)

那么我們應該怎么做呢?下面是我歸納總結出來的四項基本原則:

  • 1.緩存優(yōu)先:能讀緩存讀緩存。

  • 2.減少新建:能復用絕不新建。

  • 3.減少任務:能不做的盡量不做。

  • 4.具體問題具體分析:針對具體事務本身進行分析,必須做的能提前做就提前做,不必須做的延后做。

2. 優(yōu)化措施

可能我上面說的這些核心和基本原則,對絕大多數(shù)人來說都非常好理解,但是知道了這些,并不代表你懂得如何進行優(yōu)化。 這就好比你高中學數(shù)學,即便告訴了你一堆的公式,但真要讓你來一道相關的應用題,你還真不一定能解得出來,這個時候"例題"就很關鍵了。

同樣的,即便你知道了一些關于應用響應時間優(yōu)化的核心和原則后,當你真正面臨具體的優(yōu)化問題時,你可能也會手足無措。

所以,接下來我就從任務執(zhí)行、資源加載數(shù)據(jù)結構、線程/IO頁面渲染這五個角度,來給出我的優(yōu)化建議。

2.1 任務執(zhí)行

  • 1.業(yè)務/任務梳理:對業(yè)務進行拆分,對任務進行整合。

  • 2.任務轉換:串行 -> 并行, 同步 -> 異步。

  • 3.執(zhí)行順序按優(yōu)先級調整。

  • 4.延遲執(zhí)行、空閑執(zhí)行,如:IdleHandler。

2.1.1 業(yè)務/任務梳理

業(yè)務往往是由一個個任務流組合而成。合理的業(yè)務/任務粒度可以有效提高響應的速度。

對業(yè)務和任務的梳理,正確的方式是先進行業(yè)務的拆分,將業(yè)務拆分為一個個子任務,再根據(jù)需要對子任務進行整合。

(1)對不合理的業(yè)務流進行拆分。

  • 對業(yè)務進行拆分,拆分出主要(必要)業(yè)務和次要(非必要)業(yè)務。

  • 分別對主要業(yè)務和次要業(yè)務進行優(yōu)先級評估,業(yè)務執(zhí)行按優(yōu)先級從高到底依次執(zhí)行。

(2)對任務流進行整合。

  • 多個相關的串行任務,可以整合為統(tǒng)一的業(yè)務整體。

  • 多個不相關的串行任務,可以整合為一個并行的業(yè)務。

2.1.2 任務轉換

1.串行 -> 并行的適用范圍:

  • 多個不相關的串行任務。

  • 多個任務弱相關且耗時,但是耗時接近。例如某個頁面你需要調用多個模塊的接口查詢數(shù)據(jù)進行展示。

2.同步 -> 異步的適用范圍:

  • 非必要(重要性不高)且耗時的任務。

  • 耗時且關聯(lián)性不大的任務。

  • 耗時且存在一定相關性的任務。使用異步線程 + 同步鎖的方式執(zhí)行。

2.1.3 任務優(yōu)先級

類似線程中的優(yōu)先級Priority,當系統(tǒng)資源緊張的時候,優(yōu)先執(zhí)行優(yōu)先級高的線程。

首先我們要對應用內所有需要優(yōu)化的業(yè)務以及其子任務的優(yōu)先級進行定義,然后按優(yōu)先級順序進行排列和執(zhí)行。

那么如何才能保證任務被按優(yōu)先級進行執(zhí)行呢?

1.對于線程,我們可以直接設置其Priority值。(但是一般我們不能直接使用線程,所有這個可以忽略) 2.對于線程池,我們可以從代碼層將任務按優(yōu)先級順序加入到線程池中。注意,這里的線程池最好是阻塞式的,例如:使用PriorityBlockingQueue實現(xiàn)的優(yōu)先級線程池 PriorityThreadPoolExecutor 。 3.使用第三方的任務執(zhí)行框架,這里推薦我開源的 XTask 供大家參考。

2.1.4 延遲執(zhí)行

延遲執(zhí)行,是將一些不必要、重要性不高或者高耗時的任務暫停執(zhí)行,等后面資源充足或者要使用時才執(zhí)行。

常見的延遲執(zhí)行有以下幾種:

  • 延遲某個特定的時間執(zhí)行。例如:某應用啟動后,每隔2分鐘同步一下用戶狀態(tài)。

  • 待某個特定的任務執(zhí)行完成之后再執(zhí)行。例如:導航應用定位獲取成功后,再執(zhí)行目的地推薦獲取的任務。

  • 直接不執(zhí)行,等相關業(yè)務用到的時候再執(zhí)行。

  • 空閑執(zhí)行,等待頁面都完全渲染完畢之后再執(zhí)行。例如:使用IdleHandler,具體使用如下:

Looper.myQueue().addIdleHandler(new?MessageQueue.IdleHandler()?{
????@Override
????public?boolean?queueIdle()?{
????????//?執(zhí)行你的任務
????????return?false;
????}
});

當然,如果你想在空閑的時候執(zhí)行多個任務,你也可以這樣寫:

public?class?DelayTaskQueue?{

??private?final?Queue<Runnable>?mDelayTasks?=?new?LinkedList<>();

??private?final?MessageQueue.IdleHandler?mIdleHandler?=?()?->?{
????if?(mDelayTasks.size()?>?0)?{
??????Runnable?task?=?mDelayTasks.poll();
??????if?(task?!=?null)?{
????????task.run();
??????}
????}
????//?mDelayTasks非空時返回ture表示下次繼續(xù)執(zhí)行,為空時返回false系統(tǒng)會移除該IdleHandler不再執(zhí)行
????return?!mDelayTasks.isEmpty();
??};

??public?DelayTaskQueue?addTask(Runnable?task)?{
????mDelayTasks.add(task);
????return?this;
??}

??public?void?start()?{
????Looper.myQueue().addIdleHandler(mIdleHandler);
??}
}

2.2 資源加載

  • 1.懶加載

  • 2.分段加載(部分加載)

  • 3.預加載(數(shù)據(jù)、布局頁面等)

2.2.1 懶加載

對于一些不常用或者不重要的數(shù)據(jù)、圖片、控件以及其他一些資源,我們可以在用到時再進行加載。

1.數(shù)據(jù)懶加載

  • kotlin中的lazy標簽:修飾val變量,程序第一次使用到這個變量(或者對象)時再初始化。

  • Map、List和SharedPreferences等大數(shù)據(jù)的延遲初始化。

private?Map?getSystemSettings()?{
????if?(mSettingMap?==?null)?{
????????mSettingMap?=?initSystemSettings();
????}
????return?mSettingMap;
}

2.圖片資源懶加載

  • 對于不常用的圖片,可以使用云端圖片的資源url來替代。

  • 對于非程序預置的圖片(本地圖片文件或者云端圖片),用到時再加載。

3.控件懶加載

  • 使用ViewStub進行布局的延遲加載。

  • 使用ViewPager2+Fragment進行Fragment的懶加載。

  • 使用RecyclerView替代ListView。

2.2.2 分段加載

分段加載常見應用于大數(shù)據(jù)的加載,這里包括大圖和長視頻等多媒體資源的加載。做到用到哪,加載到哪,完全不必要等全部加載完才給用戶使用。

1.大圖的分段加載:對于大圖,我們可以將其按一定尺寸進行切分,分割成一塊一塊的小瓦片,然后設定一個預覽預加載范圍,用戶預覽到哪里我們就加載到哪里。(就類似地圖的加載)

2.長視頻的分段加載:對于長視頻,我們可以將其按時間片進行拆分,并設置一個加載緩存池。這樣用戶瀏覽一個長視頻時,就可以快速打開加載。

3.大文件或者長WebView的分段加載:對于一些閱讀類的app,經(jīng)常會遇到大文件和長WebView的加載,這里我們也可以同理對其進行拆分處理。

2.2.3 預加載

分段加載常和預加載一起組合使用。對于一些加載非常耗時的內容,我們可以將加載時機提前,從而減小用戶感知的加載時間。

預加載的本質是提前加載,這樣這個提前加載的時機就非常的關鍵和重要。因為預加載時機如果太晚,幾乎看不出效果;但是如果預加載的時機過早,有可能搶占其他模塊資源,造成資源緊張。

那么我們何時可以觸發(fā)預加載,預加載的時機是什么呢?下面我舉幾個簡單的例子。

1.用戶操作時。如果用戶點擊了第2章,我們就開始預加載下一章和上一章;用戶上滑到了第3頁,我們預加載第4頁,用戶下滑到第5頁,我們預加載第4頁.

2.應用空閑時。例如之前說的IdleHandler?;蛘咴?code>onUserInteraction中監(jiān)聽用戶的操作,一段時間沒有操作即視為空閑。

3.耗時等待時。對于一些常見的耗時操作,我們可以在其開始時,并行進行一些預加載操作,從而提高時間的利用率。例如Activity的創(chuàng)建比較耗時,我們可以在startActivity前就開始預加載數(shù)據(jù),這樣Activity創(chuàng)建完之后有可能數(shù)據(jù)就已經(jīng)加載好了,直接可以拿來渲染。例如一些有開屏廣告的app,可以在廣告開始時,同步進行一些數(shù)據(jù)資源的預加載。

2.3 數(shù)據(jù)結構

  • 1.數(shù)據(jù)結構優(yōu)化(空間大小、讀取速度、復用性、擴展性)。

  • 2.數(shù)據(jù)緩存(內存緩存、磁盤緩存、網(wǎng)絡緩存),分段緩存。這里可以參考glide.

  • 3.鎖優(yōu)化(減少過度鎖,避免死鎖),悲觀鎖/樂觀鎖。

  • 4.內存優(yōu)化,避免內存抖動,頻繁GC(尤其關注bitmap)

2.3.1 數(shù)據(jù)結構優(yōu)化

不同的數(shù)據(jù)結構有不同的使用場景,選擇適合的數(shù)據(jù)結構能夠事半功倍。

1.ArrayList和LinkedList:

  • ArrayList:底層數(shù)據(jù)結構是數(shù)組,查詢快、增刪慢。

  • LinkedList:底層數(shù)據(jù)結構是鏈表,查詢慢、增刪快。

2.HashMap和SparseArray:

  • HashMap:底層數(shù)據(jù)結構是數(shù)組和鏈表(或紅黑樹)的組合,結合了ArrayList和LinkedList的優(yōu)點,查詢快、增刪也快。但是擴容很耗性能,且空間利用率不高(75%),浪費內存。

  • SparseArray:底層數(shù)據(jù)結構是雙數(shù)組,一個數(shù)組存key,一個數(shù)組存value。使用二分法查詢進行優(yōu)化,在數(shù)據(jù)量?。ㄒ话贄l以下)的情況下,速度和HashMap相當,但是空間利用率大大提升。

  • ArrayMap:底層數(shù)據(jù)結構是雙數(shù)組,一個數(shù)組存key的hash值,一個數(shù)組存value。設計與SparseArray類似,在數(shù)據(jù)量小的情況下,可完全替代HashMap。

3.Set: 保證每個元素都必須是唯一的。

4.TreeSet和TreeMap:有序的集合,保證存放的元素是排過序的,速度慢于HashSet和HashMap。

可以看到,在不考慮空間利用率的情況下,HashMap的性能是不錯的。

但是由于存在初始化大小和擴展因子對其性能有所影響,我們在使用時,盡量根據(jù)實際需要設置合理的初始化大?。罕苊庠O置小了擴容帶來性能消耗,設置大了造成空間浪費。

因為HashMap的默認擴容因子是0.75,如果你實際使用的數(shù)量是8,那你初始化大小就設置16;如果你實際使用的數(shù)量是60,那你初始化大小就設置128。

2.3.2 數(shù)據(jù)緩存

對于一些變化不是很頻繁的數(shù)據(jù)資源,我們可以將其緩存下來。這樣我們下次需要使用它們的時候,就可以直接讀取緩存,這樣極大地減少了加載和渲染所需要的時間。

一般意義上的緩存,按讀取的時間由快到慢,我們可分為內存緩存、磁盤緩存、網(wǎng)絡緩存。

  • 內存緩存,就是存儲在內存中,我們可以直接讀取使用。而如果從界面渲染的角度,我們又可以將內存緩存分為Active(活躍/正在顯示)緩存和InActive(非活躍/不可顯示)緩存。

  • 磁盤緩存,就是存儲在磁盤文件中,每次讀取都需要將磁盤文件內容讀取到內存中,方可使用。

  • 網(wǎng)絡緩存,就是存儲在遠端服務器中,每次讀取需要我們進行一次網(wǎng)絡請求。一般來說,我們也可以將一次網(wǎng)絡緩存請求到的數(shù)據(jù)緩存到磁盤中,將網(wǎng)絡緩存轉化為磁盤緩存,通過減少網(wǎng)絡請求,來提升讀取速度。

某種意義上來說,內存緩存、磁盤緩存和網(wǎng)絡緩存,它們又是可以相互轉化的,一般來說,我們會將網(wǎng)絡緩存->磁盤緩存->內存緩存,進行使用,從而提升讀取速度。

具體我們可以參考glide框架和RecyclerView的實現(xiàn)原理。

2.3.3 鎖優(yōu)化

鎖是我們解決并發(fā)的重要手段,但是如果濫用鎖的話,很可能造成執(zhí)行效率下降,更嚴重的可能造成死鎖等無法挽回的場景。

當我們需要處理高并發(fā)的場景時,同步調用尤其需要考量鎖的性能損耗:

  • 能用無鎖數(shù)據(jù)結構,就不要用鎖。

  • 縮小鎖的范圍。能鎖區(qū)塊,就不要鎖住方法體;能用對象鎖,就不要用類鎖。

那么我們具體應該怎么做呢?下面我簡單講幾個例子。

1.使用樂觀鎖代替悲觀鎖,輕量級鎖代替重量級鎖。

利用CAS機制, 全稱是Compare And Swap,即先比較,然后再替換。就是每次執(zhí)行或者修改某個變量時,我們都會將新舊值進行比較,如果發(fā)生偏移了就更新。這就好比在一些無鎖的數(shù)據(jù)庫中,每次的數(shù)據(jù)庫操作都會攜帶一個唯一的版本號,每次進行數(shù)據(jù)庫修改的時候都會對比一下數(shù)據(jù)庫記錄和操作請求的版本號,如果版本號是最新的版本號,則進行修改,否則丟棄。

需要注意的是,CAS必須借助volatile才能讀取到共享變量的最新值來實現(xiàn)【比較并交換】的效果,因為volatile會保證變量的可見性。

在Java中,JDK給我們默認提供了一些CAS機制實現(xiàn)的原子類,如AtomicInteger、AtomicReference等。

2.縮小同步范圍,避免直接使用synchronized,即使使用也要盡量使用同步塊而不是同步方法。多使用JDK提供給我們的同步工具:CountDownLatch,CyclicBarrier,ConcurrentHashMap。

3.針對不同使用場景,使用不同類型的鎖。

  • 針對并發(fā)讀多,寫少的,我們可以使用讀寫鎖(多個讀鎖不互斥,讀鎖與寫鎖互斥):ReentrantReadWriteLock,CopyOnWriteArrayList,CopyOnWriteArraySet。

  • 針對某一個并發(fā)操作通常由某一特定線程執(zhí)行時,可嘗試使用偏向鎖(偏向于第一個獲得它的線程)。

  • 針對存在大量并發(fā)資源競爭的場景,推薦使用重量級鎖synchronized。

2.3.4 內存優(yōu)化

內存優(yōu)化的核心是避免內存抖動。不合理的內存分配、內存泄漏、對象的頻繁創(chuàng)建和銷毀,都會導致內存發(fā)生抖動,最終導致系統(tǒng)的頻繁GC。

頻繁的GC,必定會導致系統(tǒng)運行效率的下降,嚴重的可能會導致頁面卡頓,造成不好的用戶體驗。那么我們應該著手從哪些地方進行優(yōu)化呢?

  • 解決應用的內存泄漏問題。這里我們可以使用LeakCanary 或者 Android Profile 等工具來檢查我們查詢可能存在的內存泄漏。

  • 平時編碼應當注意避免內存泄漏。如避免全局靜態(tài)變量和常量、單例持有資源對象(Activity,F(xiàn)ragment,View等),資源使用完立即釋放或者recycle(回收)等。

  • 避免創(chuàng)建大內存對象,頻繁創(chuàng)建和釋放對象(尤其是在循環(huán)體內),頻繁創(chuàng)建的對象需要考慮復用或者使用緩存。

  • 加載圖片可以適當降低圖片質量,小圖標盡量使用SVG,大圖/復雜的圖片考慮使用webp。盡量使用圖片加載框架,如glide,這些框架都會幫我們進行加載優(yōu)化。

  • 避免大量bitmap的繪制。

  • 避免在自定義View的onMeasure、onLayoutonDraw中創(chuàng)建對象。

  • 使用SpareArray、ArrayMap替代HashMap。

  • 避免進行大量的字符串操作,特別是序列化和反序列化。不要使用+(加號)進行字符串拼接。

  • 使用線程池(可設置適當?shù)淖畲缶€程池數(shù))執(zhí)行線程任務,避免大量Thread的創(chuàng)建及泄漏。

2.4 線程/IO

  • 1.線程優(yōu)化(統(tǒng)一、優(yōu)先級調度、任務特性)

  • 2.IO優(yōu)化(網(wǎng)絡IO和磁盤IO),核心是減少IO次數(shù)

    • 網(wǎng)絡:請求合并,請求鏈路優(yōu)化,請求體優(yōu)化,系列化和反序列化優(yōu)化,請求復用等。

    • 磁盤:文件隨機讀寫、SharePreference讀寫等(例如對于讀多寫少的,可使用內存緩存)

  • 3.log優(yōu)化(循環(huán)中的log打印,不必要的log打印,log等級)

2.4.1 線程優(yōu)化

當我們創(chuàng)建一個線程時,需要向系統(tǒng)申請資源,分配內存空間,這是一筆不小的開銷,所以我們平時開發(fā)的過程中都不會直接操作線程,而是選擇使用線程池來執(zhí)行任務。所以線程優(yōu)化的本質是對線程池的優(yōu)化。

線程池使用的最大問題就在于如果線程池設置不對的話,很容易被人濫用,引發(fā)內存溢出的問題。而且通常一個應用會有多個線程池,不同功能、不同模塊乃至是不同三方庫都會有自己的線程池,這樣大家各用各的,就很難做到資源的協(xié)調統(tǒng)一,勁不往一處使。

那么我們應該如何進行線程池優(yōu)化呢?

1.建立主線程池+副線程池的組合線程池,由線程池管理者統(tǒng)一協(xié)調管理。主線程池負責優(yōu)先級較高的任務,副線程池負責優(yōu)先級不高以及被主線程池拒絕降級下來的任務。

這里執(zhí)行的任務都需要設置優(yōu)先級,任務優(yōu)先級的調度通過PriorityBlockingQueue隊列實現(xiàn),以下是主副線程池的設置,僅供參考:

  • 主線程池:核心線程數(shù)和最大線程數(shù):2n(n為CPU核心數(shù)),60s keepTime,PriorityBlockingQueue(128)。

  • 副線程池:核心線程數(shù)和最大線程數(shù):n(n為CPU核心數(shù)),60s keepTime,PriorityBlockingQueue(64)。

2.使用Hook的方式,收集應用內所以使用newThread方法的地方,改為由線程池管理者統(tǒng)一協(xié)調管理。

3.將所有提供了設置線程池接口的第三方庫,通過其開放的接口,設置為線程池管理者管理。沒有提供設置接口的,考慮替換庫或者插樁的方式,替換線程池的使用。

2.4.2 IO優(yōu)化

IO優(yōu)化的核心是減少IO次數(shù)。

1.網(wǎng)絡請求優(yōu)化。

  • 避免不必要的網(wǎng)絡請求。對于那些非必要執(zhí)行的網(wǎng)絡請求,可以延時請求或者使用緩存。

  • 對于需要進行多次串行網(wǎng)絡請求的接口進行優(yōu)化整合,控制好請求接口的粒度。比如后臺有獲取用戶信息的接口、獲取用戶推薦信息的接口、獲取用戶賬戶信息的接口。這三個接口都是必要的接口,且存在先后關系。如果依次進行三次請求,那么時間基本上都花在網(wǎng)絡傳輸上,尤其是在網(wǎng)絡不穩(wěn)定的情況下耗時尤為明顯。但如果將這三個接口整合為獲取用戶的啟動(初始化)信息,這樣數(shù)據(jù)在網(wǎng)絡中傳輸?shù)臅r間就會大大節(jié)省,同時也能提高接口的穩(wěn)定性。

2.磁盤IO優(yōu)化

  • 避免不必要的磁盤IO操作。這里的磁盤IO包括:文件讀寫、數(shù)據(jù)庫(sqlite)讀寫和SharePreference等。

  • 對于數(shù)據(jù)加載,選擇合適的數(shù)據(jù)結構??梢赃x擇支持隨機讀寫、延時解析的數(shù)據(jù)存儲結構以替代SharePreference。

  • 避免程序執(zhí)行出現(xiàn)大量的序列化和反序列化(會造成大量的對象創(chuàng)建)。

2.5 頁面渲染

下面是我簡單列舉的幾點加快頁面渲染的方法,相信大家或多或少都用過,這里我就不詳細闡述了:

  • 1.降低布局層級、減少嵌套、避免過度渲染(背景)(merge,ConstraintLayout)

  • 2.頁面復用(include)

  • 3.頁面懶加載

  • 4.布局延遲加載(ViewStub)

  • 5.inflate優(yōu)化(布局預加載+異步加載,動態(tài)new控件/X2C)

  • 6.動畫優(yōu)化(注意動畫的執(zhí)行耗時和內存占用,不可見時暫停動畫,可見時再恢復動畫)

  • 7.自定義view優(yōu)化(減少onDraw、onLayout、onMeasure的對象創(chuàng)建和執(zhí)行耗時)

  • 8.bitmap和canvas優(yōu)化(bitmap大小、質量、壓縮、復用;canvas復用:clipRect,translate)

  • 9.RecycleView優(yōu)化(減少刷新次數(shù),緩存復用)

3. 推薦工具

  • systrace、Perfetto 、Android Profile

  • DoKit

  • LeakCanary

  • performance

最后

還是那句話,百聞不如一見,百見不如一試。寫了這么多,我還是希望大家在平時開發(fā)的過程中,多重視一些應用響應時間優(yōu)化的相關技巧,讓我們開發(fā)出流暢順滑的應用吧。(盡管很多時候,我們所謂的優(yōu)化會被產(chǎn)品或者設計diss)

我是xuexiangjys,一枚熱愛學習,愛好編程,勤于思考,致力于Android架構研究以及開源項目經(jīng)驗分享的技術up主。獲取更多資訊,歡迎微信搜索公眾號:【我的Android開源之旅】


淺談App響應時間優(yōu)化的評論 (共 條)

分享到微博請遵守國家法律
乌拉特前旗| 精河县| 永福县| 托里县| 玉屏| 黄冈市| 收藏| 昭觉县| 北安市| 株洲市| 廉江市| 图们市| 茌平县| 德庆县| 湘潭市| 江城| 聊城市| 灵璧县| 满城县| 收藏| 济南市| 抚远县| 西贡区| 个旧市| 马龙县| 永城市| 抚宁县| 临武县| 攀枝花市| 宁晋县| 徐汇区| 正安县| 黔西县| 右玉县| 武鸣县| 株洲县| 孝义市| 苗栗县| 平山县| 新巴尔虎左旗| 喀喇沁旗|