Android優化指南

 

Android系統中GC內存泄漏的原因

 

主動回收內存System.gc();、getruntime.runtime.gc

導致內存泄漏主要的原因是,申請了內存空間而忘記了釋放。如果程序中存在對無用對象的引用,那麼這些對象就會駐留內存,消耗內存,因爲無法讓垃圾回收器GC驗證這些對象是否不再需要。如果存在對象的引用,這個對象就被定義爲"有效的活動",同時不會被釋放。要確定對象所佔內存將被回收,我們就要務必確認該對象不再會被使用。典型的做法就是把對象數據成員設爲null或者從集合中移除該對象。但當局部變量不需要時,不需明顯的設爲null,因爲一個方法執行完畢時,這些引用會自動被清理。

 

 什麼是GC

GC垃圾收集器,它讓創建的對象不需要像c/c++那樣delete、free掉,GC的時間系統自身決定,時間不可預測或者說調用System.gc()的時候。 對超出作用域的對象或引用置爲空的對象進行清理,刪除不使用的對象,騰出內存空間。

 

Java帶垃圾回收的機制,爲什麼還會內存泄露呢?

舉個例子 當你堆裏某個對象沒有被引用時,然後再過一段時間,垃圾回收機制纔會回收,那麼
while(true){
String str=new String("ni hao ni hao ");
}
一直循環創建 String對象。。。你覺得堆不會溢出嘛。。。
垃圾回收 要有2個條件
1 該對象沒有被引用
2 過了一段時間

Java 內存泄露的根本原因就是 保存了不可能再被訪問的變量類型的引用,回收不確定性

 

 

內存溢出和內存泄漏

 

內存泄露 memory leak,是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。memory leak會最終會導致out of memory!

 

內存溢出就是你要求分配的內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。 

 

從用戶使用程序的角度來看,內存泄漏本身不會產生什麼危害,作爲一般的用戶,根本感覺不到內存泄漏的存在。真正有危害的是內存泄漏的堆積,這會最終消耗盡系統所有的內存。從這個角度來說,一次性內存泄漏並沒有什麼危害,因爲它不會堆積,而隱式內存泄漏危害性則非常大,因爲較之於常發性和偶發性內存泄漏它更難被檢測到。

 

 

 

 

 

 Java中有內存泄露嗎?舉一些例子?

 

1.查詢數據庫沒有關閉遊標 2. 構造Adapter時,沒有使用緩存的 convertView 3. Bitmap對象不再使用時調用recycle()釋放內存 4. 無用時沒有釋放對象的引用 5. 在Activity中使用非靜態的內部類,並開啓一個長時間運行的線程,因爲內部類持有Activity的引用,會導致Activity本來可以被gc時卻長期得不到回收 6.使用Handler處理消息前,Activity通過例如finish()退出,導致內存泄漏 7.動態註冊廣播在Activity銷燬前沒有unregisterReceive

 

 

 

內存的優化

 

  • 回收已經使用的資源,比如遊標cursor 、I/O、Bitmap(close並且引用置爲null)
  • 合理的使用緩存,比如圖片是很耗內存的,使用lru緩存圖片和壓縮
  • 合理設置變量的作用範圍
  • 節制的使用服務,後臺任務運行完,即使它不執行任何操作,服務也會一直運行,這些是十分消耗內存的,可以用intentservice
  • 當界面不可見時釋放內存,在activity的onTrimMemory方法裏與ui的相關資源,在onstop裏釋放與組件相關的資源
  • 合理的使用多進程,如果後臺任務和前臺界面是相互獨立在,可以在組件標籤下寫process,這樣這個組建就在另一個進程裏了。而服務的話更傾向於開啓自己所依賴的進程,而那個進程可能很多東西都不需要,比如ui
  • 使用線程池、對象池

  • Bitmap對象在不使用時,應該先調用recycle()釋放內存,然後才它設置爲null。

 

 

說說線程池

好處

避免線程的創建和銷燬所帶來的性能得開銷

能有效控制線程池的最大併發數,避免了大量線程間搶佔資源而導致的阻塞現象

能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能

由於不需要每次處理複雜邏輯耗時操作,比如加載網絡並不需要都開啓一個新的線程,可以用線程池處理,把線程存起來,用的時候在取出來,

在onDestory裏去銷燬線程,這樣就會節省內存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//AsyncTask就是Handler和線程池的封裝
//自定義線程池
public class ThreadManager {
    private ThreadManager() {
    }
    private static ThreadManager instance = new ThreadManager();
    private ThreadPoolProxy longPool;
    private ThreadPoolProxy shortPool;
    public static ThreadManager getInstance() {
        return instance;
    }
    // 聯網比較耗時
    // 開啓線程數一般是cpu的核數*2+1
    public synchronized ThreadPoolProxy createLongPool() {
        if (longPool == null) {
            longPool = new ThreadPoolProxy(5, 5, 5000L);
        }
        return longPool;
    }
    // 操作本地文件
    public synchronized ThreadPoolProxy createShortPool() {
        if(shortPool==null){
            shortPool = new ThreadPoolProxy(3, 3, 5000L);
        }
        return shortPool;
    }
    public class ThreadPoolProxy {
        private ThreadPoolExecutor pool;
        private int corePoolSize;
        private int maximumPoolSize;
        private long time;
        public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long time) {
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.time = time;
        }
        /**
         * 執行任務
         * @param runnable
         */
        public void execute(Runnable runnable) {
            if (pool == null) {
                // 創建線程池
                /*
                 * 1. 線程池裏面管理多少個線程2. 如果排隊滿了, 額外的開的線程數3. 如果線程池沒有要執行的任務 存活多久4.
                 * 時間的單位 5 如果 線程池裏管理的線程都已經用了,剩下的任務 臨時存到LinkedBlockingQueue對象中 排隊
                 */
                pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                        time, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>(10));
            }
            pool.execute(runnable); // 調用線程池 執行異步任務
        }
        /**
         * 取消任務
         * @param runnable
         */
        public void cancel(Runnable runnable) {
            if (pool != null && !pool.isShutdown() && !pool.isTerminated()) {
                pool.remove(runnable); // 取消異步任務
            }
        }
    }
}

  

 

三種靜態

 

  • 靜態內部類:儘量不要用一個生命週期長於Activity的對象來持有Activity的引用。聲明handler爲static類,這樣內部類就不再持有外部類的引用了,就不會阻塞Activity的釋放。在Activity中儘量避免使用生命週期不受控制的非靜態類型的內部類,可以使用靜態類型的內部類加上弱引用的方式實現。

  • 靜態變量:不要直接或者間接引用Activity、Service等。這會使用Activity以及它所引用的所有對象無法釋放,然後,用戶操作時間一長,內存就會狂升。  

  • 靜態引用:應該避免 static 成員變量引用資源耗費過多的實例,比如 Context。儘量使用 getApplicationContext:如果爲了滿足需求下必須使用 Context 的話:Context 儘量使用 Application Context,因爲 Application 的 Context 的生命週期比較長,引用它不會出現內存泄露的問題,而不是activity的context,單例。可以通過調用 Context.getApplicationContext() or Activity.getApplication()來獲得

 

 

Handler內存泄漏
Handler作爲內部類存在於Activity中,但是Handler生命週期與Activity生命週期往往並不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。
解決辦法:聲明handler爲static類,這樣內部類就不再持有外部類的引用了,就不會阻塞Activity的釋放

 

 

System.gc()

 

我們可以調用System.gc方法,建議虛擬機進行垃圾回收工作(注意,是建議,但虛擬機會不會這樣幹,我們也無法預知!)

 

下面來看一個例子來了解finalize()System.gc()的使用:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestGC { 
    public TestGC() {} 
       
    //當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。 
    protected void finalize() { 
        System.out.println("我已經被垃圾回收器回收了..."); 
    
       
    public static void main(String [] args) { 
        TestGC gc = new TestGC(); 
        gc = null;   
        // 建議虛擬機進行垃圾回收工作 
        System.gc(); 
    
}

 

如上面的例子所示,大家可以猜猜重寫的finalize方法會不會執行?

 

答案是:不一定

 

因爲無論是設置gc的引用爲null還是調用System.gc()方法都只是"建議"垃圾回收器進行垃圾回收,但是最終所有權還在垃圾回收器手中,它會不會進行回收我們無法預知!

 

 

 

AsynTask爲什麼要設計爲只能夠一次任務?

 

最核心的還是線程安全問題,多個子線程同時運行,會產生狀態不一致的問題。所以要務必保證只能夠執行一次

 

 

 

java中的soft reference

 

StrongReference 是 Java 的默認引用實現, 它會儘可能長時間的存活於 JVM 內, 當沒有任何對象指向它時 GC 執行後將會被回收,SoftReference 會儘可能長的保留引用直到 JVM 內存不足時纔會被回收(虛擬機保證), 這一特性使得 SoftReference 非常適合緩存

 

 

 

內存溢出OOM解決方案?(解決方法)

 

內存緩存的時候可能內存溢出,因爲Android默認給每個app只分配16M的內存,,每個手機不一樣,我的手機是3G內存,分配的內存是29m,通過這樣可以獲得

 

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  

 

解決方法1:java中的引用(使用軟引用)

 

    - 強引用 垃圾回收器不會回收, java默認引用都是強引用
    - 軟引用 SoftReference   在內存不夠時,垃圾回收器會考慮回收
    - 弱引用 WeakReference  在內存不夠時,垃圾回收器會優先回收
    - 虛引用 PhantomReference  在內存不夠時,垃圾回收器最優先回收

注意: Android2.3+, 系統會優先將SoftReference的對象提前回收掉, 即使內存夠用

 

內存中使用LRUCache是最合適的。如果用HashMap來實現,不是不可以,但需要注意在合適的時候釋放緩存。至於具體怎麼釋放,我沒考慮過,但用軟引用的問題在於,你很難控制緩存的大小,也就是說,只有等到你的內存快要撐爆,你的圖片緩存纔會被回收。是不是感覺傻傻的?

 

 解決方法2:LruCache 

 


    least recentlly use 最少最近使用算法

    會將內存控制在一定的大小內, 超出最大值時會自動回收, 這個最大值開發者自己定。他內部是是一個linkedhashmap以強引用的方式存儲外界的緩存對象,提供了get,put方法來操作,當緩存滿了,lru會移除較早使用的緩存對象,把新的添加進來。也可以自己remove

 解決方法3:圖片壓縮

 

三級緩存

 

  • 先讀取內存緩存, 因爲優先加載, 速度最快,內存緩存沒有再讀取本地緩存, 次優先加載, 速度也快,本地沒有再加載網絡緩存, 速度慢,浪費流量在網絡緩存中從網絡下載圖片,並且保存在本地和內存中,在下載的時候可以對圖片進行壓縮
  • 服務器端下載的圖片是使用 Http的緩存機制,每次執行將本地圖片的時間發送給服務器,如果倆次訪問的時間間隔短,返回碼是 304,會讀取網絡緩存(說明服務端的圖片和本地的圖片是相同的,直接使用本地保存的圖片),如果返回碼是 200,則開始下載新的圖片並實現緩存。在從服務器獲取到圖片後,需要再在本地和內存中分別存一份,這樣下次直接就可以從內存中直接獲取了,這樣就加快了顯示的速度,提高了用戶的體驗。
  • 大量圖片加載,當用戶不停的滑動時,由於ui在主線程操作的,會出現卡頓,可以在滑動的時候停止加載(setOnscrollerListener),在getView方法裏只有靜止才加載圖片

 

1
2
3
4
5
6
7
8
9
10
InputStream inputStream = conn.getInputStream();
                 
                //圖片壓縮處理
                BitmapFactory.Options option = new BitmapFactory.Options();
                option.inSampleSize = 2;//寬高都壓縮爲原來的二分之一, 此參數需要根據圖片要展示的大小來確定
                option.inPreferredConfig = Bitmap.Config.RGB_565;//設置圖片格式
                 
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, option);
                return bitmap;
....

 

本地保存時可以將名字用MD5加密保存

 

1
2
3
4
5
6
7
8
9
10
11
12
// 將圖片保存在本地,
            bitmap.compress(CompressFormat.JPEG, 100,
                    new FileOutputStream(file));//100是質量
//取
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                        file));//decodeStream放的是輸入輸出流
                return bitmap;
 
 
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                        file));//
                return bitmap;

 

內存緩存在保存的時候可以給圖片分配內存

 

1
2
3
4
5
6
7
8
9
10
11
private LruCache<String, Bitmap> mMemoryCache;
    public MemoryCacheUtils() {
        long maxMemory = Runtime.getRuntime().maxMemory() / 8;//主流都是分配16m的8/1
        mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                int byteCount = value.getRowBytes() * value.getHeight();// 獲取圖片佔用內存大小
                return byteCount;
            }
        };
    }

  

 

正常一張720x1080的圖片在內存中佔多少空間?怎麼加載大圖片?

看他的渲染方式,RGB888啥的,佔用的字節不同的。

如果是一張的話壓縮處理,大量圖片的話用lru

圖片的總大小 = 圖片的總像素 * 每個像素佔用的大小

加載大圖片

  • 計算機把圖片所有像素信息全部解析出來,保存至內存
  • Android保存圖片像素信息,是用ARGB保存,所以每個像素佔用4個字節,很容易內存溢出

  • 可以對圖片的寬高和質量進行壓縮

    首先對圖片進行縮放

    • 獲取屏幕寬高

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      //設置縮放比例
      opts.inSampleSize = scale;
      //爲圖片申請內存
      opts.inJustDecodeBounds = false;
      Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      iv.setImageBitmap(bm);
       
       
      int scale = 1;
      int scaleX = imageWidth / screenWidth;
      int scaleY = imageHeight / screenHeight;
      if(scaleX >= scaleY && scaleX > 1){
          scale = scaleX;
      }
      else if(scaleY > scaleX && scaleY > 1){
          scale = scaleY;
      }
      Options opts = new Options();
      //請求圖片屬性但不申請內存
      opts.inJustDecodeBounds = true;
      BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      int imageWidth = opts.outWidth;
      int imageHeight = opts.outHeight;
      Display dp = getWindowManager().getDefaultDisplay();
      int screenWidth = dp.getWidth();
      int screenHeight = dp.getHeight();
    • 獲取圖片寬高

       
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      //設置縮放比例
      opts.inSampleSize = scale;
      //爲圖片申請內存
      opts.inJustDecodeBounds = false;
      Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      iv.setImageBitmap(bm);
       
       
      int scale = 1;
      int scaleX = imageWidth / screenWidth;
      int scaleY = imageHeight / screenHeight;
      if(scaleX >= scaleY && scaleX > 1){
          scale = scaleX;
      }
      else if(scaleY > scaleX && scaleY > 1){
          scale = scaleY;
      }
      Options opts = new Options();
      //請求圖片屬性但不申請內存
      opts.inJustDecodeBounds = true;
      BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      int imageWidth = opts.outWidth;
      int imageHeight = opts.outHeight;

       

    • 圖片的寬高除以屏幕寬高,算出寬和高的縮放比例,取較大值作爲圖片的縮放比例,且大於1才縮放

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      //設置縮放比例
      opts.inSampleSize = scale;
      //爲圖片申請內存
      opts.inJustDecodeBounds = false;
      Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      iv.setImageBitmap(bm);
       
       
      int scale = 1;
      int scaleX = imageWidth / screenWidth;
      int scaleY = imageHeight / screenHeight;
      if(scaleX >= scaleY && scaleX > 1){
          scale = scaleX;
      }
      else if(scaleY > scaleX && scaleY > 1){
          scale = scaleY;
      }

        

    2.然後按縮放比例加載圖片

    1
    2
    3
    4
    5
    6
    //設置縮放比例
    opts.inSampleSize = scale;
    //爲圖片申請內存
    opts.inJustDecodeBounds = false;
    Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
    iv.setImageBitmap(bm);

      

如何在不失真的條件下顯示一張超高清的圖片或者長圖
  • 針對這個問題,我自己一般用以下兩種方法解決: 1、使用WebView來加載該圖片; 2、使用MapView或者TileView來顯示圖片(類似地圖的機制);

subsampling-scale-image-view
 
 
縮減APK包大小代碼
  • 保持良好的編程習慣,不要重複或者不用的代碼,謹慎添加libs,移除使用不到的libs。
  • 使用proguard混淆代碼,它會對不用的代碼做優化,並且混淆後也能夠減少安裝包的大小。
  • native code的部分,大多數情況下只需要支持armabi與x86的架構即可。如果非必須,可以考慮拿掉x86的部分。
  • 使用Lint工具查找沒有使用到的資源。去除不使用的圖片,String,XML等等。
  • assets目錄下的資源請確保沒有用不上的文件。
  • 生成APK的時候,aapt工具本身會對png做優化,但是在此之前還可以使用其他工具如tinypng對圖片進行進一步的壓縮預處理。
  • jpeg還是png,根據需要做選擇,在某些時候jpeg可以減少圖片的體積。
  • 對於9.png的圖片,可拉伸區域儘量切小,另外可以通過使用9.png拉伸達到大圖效果的時候儘量不要使用整張大圖。
  • 有選擇性的提供hdpi,xhdpi,xxhdpi的圖片資源。建議優先提供xhdpi的圖片,對於mdpi,ldpi與xxxhdpi根據需要提供有差異的部分即可。
  • 儘可能的重用已有的圖片資源。例如對稱的圖片,只需要提供一張,另外一張圖片可以通過代碼旋轉的方式實現。
  • 能用代碼繪製實現的功能,儘量不要使用大量的圖片。例如減少使用多張圖片組成animate-list的AnimationDrawable,這種方式提供了多張圖片很佔空間

 

ListView的優化

  • 複用convertview , 歷史的view對象
  • 減少子孩子查詢的次數 viewholder
  • 異步加載數據(把圖片緩存)
  • 條目多時分頁加載數據
  • 加載時顯示進度條讓用戶等待

  • Item的佈局層次結構儘量簡單,避免佈局太深或者不必要的重繪

  • 避免在 getView 方法中做耗時的操作:
    例如加載本地 Image 需要載入內存以及解析 Bitmap ,都是比較耗時的操作,如果用戶快速滑動listview,會因爲getview邏輯過於複雜耗時而造成滑動卡頓現象。用戶滑動時候不要加載圖片,待滑動完成再加載,可以使用這個第三方庫glide

  • 應該儘量避免 static 成員變量引用資源耗費過多的實例,比如 Context。
  • 儘量使用 getApplicationContext:如果爲了滿足需求下必須使用 Context 的話:Context 儘量使用 Application Context,因爲Application 的 Context 的生命週期比較長,引用它不會出現內存泄露的問題

  • 在一些場景中,ScollView內會包含多個ListView,可以把listview的高度寫死固定下來。
    由於ScollView在快速滑動過程中需要大量計算每一個listview的高度,阻塞了UI線程導致卡頓現象出現,如果我們每一個item的高度都是均勻的,可以通過計算把listview的高度確定下來,避免卡頓現象出現

  • 使用 RecycleView 代替listview:
    每個item內容的變動,listview都需要去調用notifyDataSetChanged來更新全部的item,太浪費性能了。RecycleView可以實現當個item的局部刷新,並且引入了增加和刪除的動態效果,在性能上和定製上都有很大的改善

  • ListView 中元素避免半透明:
    半透明繪製需要大量乘法計算,在滑動時不停重繪會造成大量的計算,在比較差的機子上會比較卡。 在設計上能不半透明就不不半透明。實在要弄就把在滑動的時候把半透明設置成不透明,滑動完再重新設置成半透明。

  • 儘量開啓硬件加速:
    硬件加速提升巨大,避免使用一些不支持的函數導致含淚關閉某個地方的硬件加速。當然這一條不只是對 ListView。

 

佈局的優化

  • 儘量重用一個佈局文件,使用include標籤,多個相同的佈局可以複用
  • 減少一個佈局的不必要節點
  • 儘量使用view自身的參數,例如:Button,有一個可以把圖繪製在左邊的參數:android:drawableLeft
  • 使用< ViewStub />標籤來加載一些不常用的佈局;使用< merge />標籤減少佈局的嵌套層次

 

ViewPager的優化

  • viewpager會默認加載左右倆個頁面,有時候我們並不想看,會浪費用戶的流量,可以在setOnPageChangeListener的onPageSelected的方法裏選中哪個頁面,初始化哪個頁面
  • 由於viewpager會默認銷燬第三頁面,可以強制讓viewpager加載所有的頁面pagerView.setOffscreenPageLimit(pageCount);,但是如果頁面多的話就不能這樣幹了
  • 可以定義一個集合將頁面緩存起來,在destroyItem的時候保存起來,在instantiateItem讀取集合,有就用,沒有的話再創建,就像listview的convertView似的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class HomeAdapter extends PagerAdapter {
    // 當前viewPager裏面有多少個條目
    LinkedList<ImageView> convertView=new LinkedList<ImageView>();
    @Override
    public int getCount() {
        return  Integer.MAX_VALUE;
    }
    /* 判斷返回的對象和 加載view對象的關係 */
    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        ImageView view=(ImageView) object;
        convertView.add(view);// 把移除的對象 添加到緩存集合中
        container.removeView(view);
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ImageView view;
        if(convertView.size()>0){
            view=convertView.remove(0);
        }else{
            view= new ImageView(UiUtils.getContext());
        }
        bitmapUtils.display(view, HttpHelper.URL + "image?name="
                + datas.get(index));
        container.addView(view); // 加載的view對象
        return view; // 返回的對象
    }
}

  

內存的優化

  •  回收已經使用的資源,比如遊標cursor 、I/O、Bitmap(close並且引用置爲null)
  •   合理的使用緩存,比如圖片是很耗內存的,使用lru緩存圖片和壓縮
  • 合理設置變量的作用範圍
  • 節制的使用服務,後臺任務運行完,即使它不執行任何操作,服務也會一直運行,這些是十分消耗內存的,可以用intentservice
  • 當界面不可見時釋放內存,在activity的onTrimMemory方法裏與ui的相關資源,在onstop裏釋放與組件相關的資源
  • 合理的使用多進程,如果後臺任務和前臺界面是相互獨立在,可以在組件標籤下寫process,這樣這個組建就在另一個進程裏了。而服務的話更傾向於開啓自己所依賴的進城,而那個進程可能很多東西都不需要,比如ui
  • 使用線程池、對象池
  • Bitmap對象在不使用時,應該先調用recycle()釋放內存,然後才它設置爲null。

 

代碼優化

這部分就是是細微的優化,但是細微多了也就內存節約了

任何一個Java類,包括內部類、匿名類,都要佔用大概500字節的內存空間。
任何一個類的實例要消耗12-16字節的內存開支,因此頻繁創建實例也是會一定程序上影響內存的,所以要避免創建不必要的對象

  1. 如果有一個需要拼接的字符串,那麼可以優先考慮使用StringBuffer或者StringBuilder來進行拼接,而不是加號連接符,因爲使用加號連接符會創建多餘的對象,拼接的字符串越長,加號連接符的性能越低
  2. 儘量使用基本數據類來代替封裝數據類型,int比Integer要更加高效,其它數據類型也是一樣

使用靜態

  1. 使用枚舉通常會比使用靜態常量要消耗兩倍以上的內存,在Android開發當中應當儘可能地不使用枚舉。
  2. 如果你並不需要訪問一個對象中的某些字段,只是想調用它的某個方法來去完成一項通用的功能,那麼可以將這個方法設置成靜態方法,這會讓調用的速度提升15%-20%,同時也不用爲了調用這個方法而去專門創建對象了,這樣還滿足了上面的一條原則。另外這也是一種好的編程習慣,因爲我們可以放心地調用靜態方法,而不用擔心調用這個方法後是否會改變對象的狀態(靜態方法內無法訪問非靜態字段)

 對常量使用static final修飾符

使用增強型for循環語法

多使用系統封裝好的API,比如:indexOf(),System.arraycopy()

  性能優化:儘量使用drawable對象保存圖片而不是bitmap

                        drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");

 

發佈了6 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章