第8章 Android異常與性能優化相關面試問題


1、anr異常面試問題講解

  a)  什么是anr?

    應用程序無響應對話框

  b)  造成anr的原因?

    主線程中做了耗時操作

  c)  android中那些操作是在主線程呢?

    activity的所有生命周期回調都是執行在主線程的

    Service默認是執行在主線程的

    BroadcastReceiver的onReceiver回調是執行在主線程的

    沒有使用子線程的Looper的Handler的handlerMessage,post(Runnable)是執行在主線程的

    AsyncTask的回調中除了doInBackground,其它都是執行在主線程

  d)  如何解決anr

    使用AsyncTask處理耗時IO操作

    使用Thread或者HandlerThread提高優先級

    使用handler來處理工作線程的耗時任務

    activity的onCreate和onResume回調中盡量避免耗時的代碼

 

2、oom異常面試問題講解

  a)  什么是oom ?

    當前占用的內存加上我們申請的內存資源超過了Dalvik虛擬機的最大內存限制就會拋出的Out  of  memory異常 

  b)  一些容易混淆的概念

   內存溢出 / 內存抖動 / 內存泄漏

   內存溢出:就是oom

   內存抖動:短時間內大量對象被創建然后馬上被釋放

   內存泄漏:當程序不再使用到的內存時,釋放內存失敗而產生了無用的內存消耗

  d) 如何解決oom

   1.有關bitmap優化

    圖片顯示(比如監聽listview滑動,停止的時候加載大圖)

    及時釋放內存

      bitmap的夠着方法都是私有的,通過BitmapFactory生成Bitmap到內存中都是通過jni實現的,簡單點說會有倆部分區域,一部分是java區,一部分是C區,但是Bitmap是通過java分配的,不用的時候也是由java的gc機制回收的。對應C區域不能及時回收的,所以這里說的釋放內存就是釋放的c的那塊區域(通過

recycle方法,該方法內部調用了jni的方法)。要是不釋放只能等進程死了以后就會被釋放
   圖片壓縮
   inBitmap屬性(圖片復用)
   捕獲異常   

   2.其它方法

    listview:convertview / lru(三級緩存)

    避免在onDraw方法里面執行對象的創建

    謹慎使用多進程

 

3、 bitmap面試問題講解

  a)  recycle :釋放bitmap內存的時候會釋放所對應的native的內存,但是不會立即釋放,但是調用完就不能使用該bitmap了,是不可逆的。官方不建議主動調用。垃圾回收器主動會清理。

  b)  LRU:三級緩存,內部是通過map實現的,里面提供了put、get方法來完成緩存的添加和獲取操作。當緩存滿的時候,lru算法會提供trimToSize刪除最久或者使用最少的緩存對象,添加新的緩存對象

  c)  計算inSampleSize:

  d)  縮略圖

    

 /**
     * 獲取縮略圖
     * @param imagePath:文件路徑
     * @param width:縮略圖寬度
     * @param height:縮略圖高度
     * @return
     */
    public static Bitmap getImageThumbnail(String imagePath, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; //關於inJustDecodeBounds的作用將在下文敘述
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
        int h = options.outHeight;//獲取圖片高度
        int w = options.outWidth;//獲取圖片寬度
        int scaleWidth = w / width; //計算寬度縮放比
        int scaleHeight = h / height; //計算高度縮放比
        int scale = 1;//初始縮放比
        if (scaleWidth < scaleHeight) {//選擇合適的縮放比
            scale = scaleWidth;
        } else {
            scale = scaleHeight;
        }
        if (scale <= 0) {//判斷縮放比是否符合條件
            be = 1;
        }
        options.inSampleSize = scale;
        // 重新讀入圖片,讀取縮放后的bitmap,注意這次要把inJustDecodeBounds 設為 false
                options.inJustDecodeBounds = false;
                bitmap = BitmapFactory.decodeFile(imagePath, options);
        // 利用ThumbnailUtils來創建縮略圖,這里要指定要縮放哪個Bitmap對象
        bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
        return bitmap;
    }

 

  e)  三級緩存

    網絡、本地、內存三級緩存,減少流量的使用

 

4、 ui卡頓面試問題講解

  a)  UI卡頓的原理

    60fps -> 16ms    

    overdraw過度繪制 

  b)  UI卡頓的原因分析

    1.人為在UI線程中做輕微耗時操作,導致UI線程卡頓

    2.布局Layout過於復雜,無法在16ms內完成渲染

    3.同一時間動畫執行的次數過多,導致CPU、GPU的負載過重

    4.View的過度繪制,導致某些像素在同一幀內被繪制多次,從而使CPU、GPU的負載過重

    5.View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染

    6.內存頻繁觸發gc過多,導致暫時阻塞渲染操作

    7.冗余資源及邏輯等導致加載和執行的緩慢

    8.ANR

  c)  UI卡頓總結

    1.布局優化

    2.列表及adapter優化

    3.背景和圖片等內存分配優化

    4.避免ANR

 

4、 內存泄漏

  a)  java內存泄漏基礎知識

    1.  java內存的分配策略

      靜態存儲區(方法區):主要存放靜態數據、全局 static 數據和常量。這塊內存在程序編譯時就已經分配好,並且在程序整個運行期間都存在。

      棧區:當方法被執行時,方法體內的局部變量(其中包括基礎數據類型、對象的引用)都在棧上創建,並在方法執行結束時這些局部變量所持有的內存將會自動被釋放。因為棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

      堆區 : 又稱動態內存分配,通常就是指在程序運行時直接 new 出來的內存,也就是對象的實例。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收。

    2.  java是如何管理內存的

          

 

    3.  java中的內存泄漏

      內存泄漏是指無用對象(不再使用的對象)持續占有內存或無用對象的內存得不到及時釋放,從而造成的內存空間的浪費稱為內存泄漏

  b)  android內存泄漏

    1. 單例

      

public class AppManager {

    private Context mContext;
    private static AppManager instance;

    private AppManager(Context context){
        //有內存泄漏的問題:傳入的是actiivty的context,導致activity沒法釋放
        //this.mContext = context;
        //所以這里應該傳入Application全局的context
        this.mContext = context.getApplicationContext();
    }

    public static AppManager getInstance(Context context){
        if(instance ==null){
            instance = new AppManager(context);
        }
        return instance;
    }

}

    2.匿名內部類 

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    //這樣的寫法就會造成內存泄漏
    //原因是這樣寫法雖然避免了重復創建,但是非靜態內部類持有外部類的引用,
    //這時候我們又創建了一個靜態實例TAG的話就會和應用的生命周期一樣長,所以就會使外部的activity沒法釋放
    class TestResource{
        private static final String TAG = "";
    }
    
    //正常的寫法,這樣的話就不會持有外部類的引用。
    static class TestResource1{
        private static final String TAG = "";
    }

}

    3.handler

public class MainActivity extends AppCompatActivity {


    private TextView mTv;
    private MyHandler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myHandler = new MyHandler(this);
    }



    //這樣的寫法會造成內存泄漏
    //mHandler是MainActivity的非靜態內部類的實例,它持有外部類的引用,我們知道handler的消息是在一個loop
    //中不斷的輪詢處理消息,那么當MainActivity退出時,消息隊列中還有沒處理的消息或正在處理的消息,所以會造成內存泄漏
   @SuppressLint("HandlerLeak")
   private Handler mHandler = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
       }
   };

    //這樣寫是正確的寫法
    static class MyHandler extends Handler{

        //創建一個軟引用
        private WeakReference<Context> reference;

        public MyHandler(Context context){
            reference = new WeakReference<Context>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = (MainActivity) reference.get();
            if(mainActivity != null){
                //TODO------
                mainActivity.mTv.setText("11");
            }
        }
    }
  @Override
  protected void onDestroy() {
  super.onDestroy();
  myHandler.removeCallbacksAndMessages(null);
  }
}

    4.避免使用static靜態變量

      如果聲明成靜態變量,那么它的生命周期就會和app的生命周期一樣長,假如你的app是常駐后台的,即使app退到后台,這部分也不會釋放

    5.資源未關閉造成的內存泄漏

    6.AsyncTask造成的內存泄漏

      在onDestory調用cancel()方法

      添加弱引用實現

 

5、內存管理面試問題

  a)內存管理機制概述

    1.分配機制

    2.回收機制

  b)Android內存管理機制

    1.分配機制

      彈性的分配機制,當發現內存不夠用的時候回分配額外的內存。但是額外的內存也不是無限量的。(讓更多的進程存活在系統中)

    2.回收機制

      前台進程   可見進程   服務進程    后台進程     空進程

  c)內存管理機制的特點(目標)  

    1.更少占用的內存

    2.在合適的時候,合理的釋放系統資源

    3.在系統內存緊張的情況下,能釋放掉不部分不重要的資源,來為android系統提供可用的內存

    4.能夠很合理的在特殊的生命周期中,保存或者還原重要數據,以至於系統能夠正確的重新恢復該應用      

  d)內存優化方法

    1.當service完成任務后,盡量停止它

    2.在UI不可見的時候,釋放掉一些只有UI使用的資源

    3.在系統資源緊張的時候,盡可能多的釋放掉一些非重要資源

    4.避免濫用Bitmap導致的內存浪費

    5.使用針對內存優化過的數據容器

    6.避免使用依賴注入的框架

    7.使用ZIP對齊的APK

    8.使用多進程

  d)內存溢出VS內存泄漏

 

6、冷啟動優化面試問題講解

  a)什么是冷啟動?

    1.冷啟動的定義

       冷啟動就是在啟動應用前,系統沒有該應用的任何進程信息

    2.冷啟動 / 熱啟動的區別

       熱啟動:用戶使用返回鍵退出應用,然后馬上又重新啟動應用

       區別:

          1). 定義

          2). 啟動特點

              冷啟動:Application  ->  MainActivity ->UI的繪制

              熱啟動:MainActivity ->UI的繪制

    3.冷啟動時間的計算

       這個時間值從應用啟動(創建進程)開始計算,到完成視圖的第一次繪制(即Activity內容對用戶可見)為止

  b)冷啟動流程

    Zygote進程中fork創建一個新的進程

    創建和初始化Application類,創建MainActivity類

    inflate布局,當onCreate / onStart / onResume方法都走完

    contentView的measure / layout /draw  顯示在界面上

  c)如何對冷啟動的時間進行優化

    1.減少onCreate的工作量

    2.不要讓Application參與業務的操作

    3.不要在Application進行耗時操作

    4.不要以靜態變量的方式在Application中保存數據

    5.布局

    6.mainThread

  d)冷啟動流程-----總結

    Application的構造方法-------->attachBaseContext () --------->  onCreate () ----------->Activity的構造方法------------> onCreate () ------------>  配置主題中背景等屬性------------->onStart () ---------> onResume() --------> 測量布局繪制顯示在界面上

7、其他優化面試問題講解

  a)android 不用靜態變量存儲數據

    1.靜態變量等數據由於進程已經被殺死而被初始化

    2.使用其它的數據傳輸方式:文件 / sp / contentProvider

  b)有關Sharepreference的安全問題

    1.不能跨進程同步數據讀寫

    2.存儲Sharepreference的文件過大問題

      過大的話會讀取緩慢,造成UI卡頓

      讀取頻繁的key和不易變動的key不要放在一起

  c)內存對象序列化

   序列化:將對象的狀態信息轉化為可以存儲或傳輸的形式的過程

   1.實現Serializable接口(會產生大量的臨時變量,頻繁的垃圾回收,這樣會造成UI卡頓,內存抖動,) 

   2.實現Parcelable接口 

    總結:1>.Serializable是Java的序列化方式,Parcelable是android特有的序列化方式

       2>.在使用內存的時候,Parcelable比Serializable性能高

       3>.Serializable序列化的時候會產生大量的臨時變量,從而引起頻繁的GC

         4>.Parcelable不能使用在要數據存儲在磁盤上的情況

  d)避免在UI線程中做繁重的操作

    

    

       

        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM