前言
很多人對大廠的印象是;面試造飛機,工作擰螺絲。
用造飛機的能力去擰螺絲,形象的說明了大廠裡的技術標準是遠高於在小公司搬程式碼的。在具備java語言,設計模式,資料結構演算法技能能基礎之上,效能調優是關鍵點
這篇文章希望給大家介紹一下目前那些一線公司Android開發中效能調優最常用的方法,後面會再把那些效能調優的技術解析文章分享給大家,歡迎持續關注~
全方位效能優化方法
1,佈局優化
佈局優化的思想很簡單,就是儘量減少佈局檔案的層級,佈局中的層級少了,這就意味著Android繪製時的工作量少了,那麼程式的效能自然就高了。
那麼如何進行佈局優化呢?有以下兩點:
•首先刪除佈局中無用的看控制元件和層級,其次有選擇地使用效能較低的ViewGroup,比如RelativeLayout。
•可以採用標籤、標籤、ViewStub。標籤主要用於佈局重用,標籤一般配合標籤使用,它可以降低減少佈局的層級,而ViewStub則提供了按需載入的功能。
2,繪製優化
繪製優化是指View的onDraw方法要避免執行大量的操作,主要體現在兩個方面
•onDraw中不要建立新的區域性物件,這是因為onDraw方法可能會被頻繁呼叫,這樣就會在一瞬間產生大量的臨時物件,這不僅佔用了過多的記憶體而且還會導致系統會更頻繁gc,降低程式的執行效率。
•onDraw方法中不要做耗時的任務,也不能執行成千上萬次的迴圈操作,儘管每次迴圈都很輕量級,但是大量的迴圈仍然十分搶佔CPU的時間片,這會造成View的繪製過程很不流暢。
3,記憶體優化
記憶體洩露在開發過程中是一個需要重視的問題,記憶體優化分為兩個方面,一方面是在開發過程中避免寫出有記憶體洩露的程式碼,另一方面是通過一些分析工具比如MAT來找出潛在的記憶體洩露繼而解決。
場景1:靜態變數導致記憶體洩露
比如下面這段程式碼:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static Context sContext;
private static View sView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sContext = this;
sView = new View(this);
}
}
複製程式碼
MainActivity無法正常銷燬,因為靜態變數sContext引用了它。同樣,sView是一個靜態變數,他內部持有了當前Activity,所以Activity仍然無法釋放。
場景2:單例模式導致記憶體洩露
靜態變數導致的記憶體洩露都太過明顯了,但單例模式所帶來的記憶體洩露是我們容易忽視的。比如下面這段程式碼:
public class TestManager {
private List<OnDataArrivedListener> mOnDataArrivedListeners = new ArrayList<OnDataArrivedListener>();
private static class SingletonHolder {
public static final TestManager INSTANCE = new TestManager();
}
private TestManager() {
}
public static TestManager getInstance() {
return SingletonHolder.INSTANCE;
}
public synchronized void registerListener(OnDataArrivedListener listener) {
if (!mOnDataArrivedListeners.contains(listener)) {
mOnDataArrivedListeners.add(listener);
}
}
public synchronized void unregisterListener(OnDataArrivedListener listener) {
mOnDataArrivedListeners.remove(listener);
}
public interface OnDataArrivedListener {
public void onDataArrived(Object data);
}
}
複製程式碼
首先提供一個單例模式的TestManager,TestManager可以接收外部的註冊並將外部的監聽器儲存起來。然後用Activity實現OnDataArrivedListener介面並向TestManager註冊監聽,但是如果缺少解註冊的操作,會引起記憶體洩露。比如下面這段程式碼:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TestManager.getInstance().registerListener(this);
}
複製程式碼
Activity的物件被單例模式的TestManager所持有,而單例模式的特點是其生命週期和Application保持一致,因此Activity物件無法被及時釋放。
場景3:屬性動畫導致的記憶體洩露
從Android3.0開始,Google提供了屬性動畫,屬性動畫中有這麼一類無限迴圈的動畫,如果在Activity中播放此類動畫且沒有在onDestroy中停止動畫,那麼動畫就會一直播放下去,儘管已經無法在介面上看到動畫效果,但這個時候Activity的View會被動畫持有,而View又持有了Activity,最終Activity無法釋放。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "rotation",0, 360).setDuration(2000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.start();
//animator.cancel();
}
複製程式碼
4,響應速度優化和ANR日誌分析
響應速度優化的核心思想是避免在主執行緒中做耗時操作,但是有時候的確有很多耗時操作,怎麼辦呢?可以將這些耗時操作放線上程中去執行,即採用非同步的方式執行耗時操作。響應速度過慢更多地體現在Activity的啟動速度上面,如果在主執行緒中做太多的事情,會導致Activity啟動出現黑屏現象,甚至出現ANR。Android規定,Activity如果5秒鐘之內無法響應螢幕觸控事件或者鍵盤輸入事件就會出現ANR,而BroadcastReceiver如果10秒之內還未執行完操作也會出現ANR,那麼在實際開發過程中遇到ANR,怎麼定位問題呢?其實當一個程式發生ANR了以後,系統會在/data/anr/目錄下建立一個檔案traces.txt,通過分析這個檔案就能定位出ANR的原因。比如下面程式碼在Activity的onCreate中休眠30s,程式執行持續點選螢幕,應用一定會出現ANR:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SystemClock.sleep(30 * 1000);
}
複製程式碼
5,ListView和Bitmap優化
ListView優化三個方面:
•採用ViewHolder並避免在getView中執行耗時操作
•根據列表的滑動狀態來控制任務的執行頻率,比如當列表快速滑動時顯然是不太適合開啟大量非同步任務的。
•嘗試開啟硬體加速來使ListView的滑動更加流暢。
Bitmap優化,主要是通過BitmapFactory.Options來根據需要對圖片進行取樣,取樣過程中主要用到了BitmapFactory.Option的inSampleSize引數。
6,執行緒優化
執行緒優化的思想是採用執行緒池,避免在程式中存在大量的Thread。執行緒池可以重用內部的執行緒,從而避免了現場的建立和銷燬所帶來的效能開銷,同時執行緒池還能有效地控制執行緒池的最大併發數,避免大量的執行緒因互相搶佔系統資源從而導致阻塞現象發生。
7,其他效能優化建議
還有一些其他效能優化的小建議,通過它們可以在一定程度上提高效能:
•避免建立過多的物件
•不要過多使用列舉,列舉佔用的記憶體空間要比整型大
•常量請用static final來修飾
•使用一些Android特有的資料結構,比如SpareArray和Pair等,它們都具有更好的效能
•適當使用軟引用和弱引用
•採用記憶體快取和磁碟快取
•儘量採用靜態內部類,這樣可以避免在的由於內部類而導致的記憶體洩露
提高程式的可維護性
主要是提高程式碼的可維護性和可擴充套件性,而程式的可維護性本質上也包含可擴充套件性。
•命名要規範,要能正確地傳達出變數或者方法的含義,少用縮寫,關於變數的字首可以參考Android原始碼的命名方式,比如私有方式以m開頭,靜態成員以s開頭,常量則全部用大寫字母表示,等等。
•程式碼的排版上需要留出合理的空白區分不同的程式碼塊,其中同類變數的宣告要放在一組,兩類變數之間要留出一行空白作為區分。
•僅為非常關鍵的程式碼新增註釋,其他地方不寫註釋,這就對變數和方法的命名風格提出了很高的要求。
•程式碼的層次性指程式碼要有分層的概念,對於一段業務邏輯,不要試圖在一個方法或者一個類中去全部實現,它可以分成幾個子邏輯,然後每個子邏輯做自己的事情。單一職責是和層次性相關聯,程式碼分成以後,每一層僅僅關注少量的邏輯,這樣就做到了單一職責。
效能調優技術大綱;
效能優化系列文章;
BAT主流Android高階架構技術大綱+學習路線+資料分享
架構技術詳解,學習路線與資料分享都在部落格這篇文章裡《BATJ一線大廠最主流的Android高階架構技術;體系詳解+學習路線》
(包括自定義控制元件、NDK、架構設計、混合式開發工程師(React native,Weex)、效能優化、完整商業專案開發等)
- 阿里P8級Android架構師技術腦圖
- 全套體系化高階架構視訊;七大主流技術模組,視訊+原始碼+筆記