2018最新 Android 面試題總結(三)

weixin_34185560發表於2018-10-23

Q:載入一張超級大的圖片,導致OOM的處理方式
(1)將圖片裝化成bigmap,設定Config preferredConfig = Config.RGB_565;不支援透明度,如果需要透明度,就替換成ARGB_8888,但是記憶體佔用會增加一倍.具體可以參考原始碼的解釋
(2)可以將視訊進行質量壓縮(本地圖片轉化為縮圖載入等)
(3)可以修改原圖片的寬高為螢幕解析度或view的寬高
a.得到圖片的寬和高
b.得到螢幕的寬和高(因為整個螢幕的畫素都載入進記憶體,也不會造成記憶體溢位)
c.得到 圖片的寬和螢幕寬(或控制元件寬)的比值,圖片高和螢幕高(或控制元件高)的比值,選擇比值大的那個
d.設定 Option的inSampleSize 屬性來縮小圖片
imageview.setImageBitmap(decodeSampledBitmapFromResource(getResources(),name[i], 100, 100));

Q:專案中如何做效能優化的?

舉例說明專案注意了哪些方面的效能優化,如佈局優化、繪製優化、記憶體洩漏優化、 響應速度優化、列表優化、Bitmap優化、 執行緒優化......
效能優化一
效能優化二

Q:瞭解哪些效能優化的工具?

效能優化工具
思路:做專案時是否使用過的系統自帶的效能優化工具?公司是否有自己的效能優化工具?實現原理怎樣的? LeakCanary工具

Q:佈局上如何優化?

佈局優化的核心就是儘量減少佈局檔案的層級,常見的方式有:
多巢狀情況下可使用RelativeLayout減少巢狀。
佈局層級相同的情況下使用LinearLayout,它比RelativeLayout更高效。
使用<include>標籤重用佈局、<merge>標籤減少層級、<ViewStub>標籤懶載入(適合在某些條件顯示的佈局,如:網路錯誤,資料載入失敗等)。

Q:記憶體洩漏是什麼?為什麼會發生?常見哪些記憶體洩漏的例子?都是怎麼解決的?

記憶體洩漏(Memory Leak)是指程式在申請記憶體後,無法釋放已申請的記憶體空間。簡單地說,發生記憶體洩漏是由於長週期物件持有對短週期物件的引用,使得短週期物件不能被及時回收。常見的幾個例子和解決辦法:
單例模式導致的記憶體洩漏:單例傳入引數this來自Activity,使得持有對Activity的引用。
解決辦法:傳參context.getApplicationContext()
Handler導致的記憶體洩漏:Message持有對Handler的引用,而非靜態內部類的Handler又隱式持有對外部類Activity的引用,使得引用關係會保持至訊息得到處理,從而阻止了Activity的回收。
解決辦法:使用靜態內部類+WeakReference弱引用;當外部類結束生命週期時清空訊息佇列。
執行緒導致的記憶體洩漏:AsyncTask/Runnable以匿名內部類的方式存在,會隱式持有對所在Activity的引用。
解決辦法:將AsyncTask和Runnable設為靜態內部類或獨立出來;線上程內部採用弱引用儲存Context引用
資源未關閉導致的記憶體洩漏:未及時登出資源導致記憶體洩漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
解決辦法:在Activity銷燬的時候要及時關閉或者登出。
BraodcastReceiver:呼叫unregisterReceiver()登出;
Cursor,Stream、File:呼叫close()關閉;
動畫:在Activity.onDestroy()中呼叫Animator.cancel()停止動畫
引申:談談專案中是如何注意記憶體洩漏的問題

記憶體洩漏和記憶體溢位的區別

記憶體洩漏(Memory Leak)是指程式在申請記憶體後,無法釋放已申請的記憶體空間。是造成應用程式OOM的主要原因之一。
記憶體溢位(out of memory)是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用。

Q:什麼情況會導致記憶體溢位?

記憶體洩漏是導致記憶體溢位的主要原因;直接載入大圖片也易造成記憶體溢位
引申:談談如何避免記憶體溢位(如何避免記憶體洩漏、避免直接載入大圖片)
開源框架(略)

Q:使用那些版本控制工具?Git和SVN的區別?`

參考回答:Git和SVN的區別有以下幾點:
Git是分散式的,而SVN是集中式的(核心區別)
Git按後設資料方式儲存內容,而SVN按檔案儲存內容
在Git上每個工作成員可以任意在自己的本地版本庫開啟無限個分支且互不影響,而對於SVN分支是一個完整的目錄且這個目錄擁有完整的實際檔案
Git沒有一個全域性的版本號,而SVN有
Git 的內容完整性要優於SVN
在Git中的絕大多數操作都只需要訪問本地檔案和資源,不必聯網就可以看到所有的歷史版本記錄,而SVN 卻需要聯網
引申:談談兩種版本控制工具的優缺點:SVN與GIT的優缺點對比

Q:瞭解Git工具嗎?用過哪些命令?解決衝突時git merge和git rebase的區別?`

通過圖記憶Git常用命令,詳見Git、GitHub、Stash
參考回答: 常用命令見圖,源自一篇文章,教你學會Git
合併用到的命令git merge與git rebase的區別是,git merge會生成一個新的節點,並將之前的提交分開顯示;git rebase操作不會生成新的節點,而是將兩個分支

1878534-46977bd4f949700b.png
image.png

融合成一個線性的提交。

Q:記憶體洩漏的場景,Handler記憶體洩漏的原因以及解決方法

原因:使用Handler傳送一條延時訊息,然後關閉Activity,在Activity被關閉之後,Message將在訊息佇列中存在10分鐘才能被執行到,這個Message持有一個對Handler的引用,Handler又持有一個對當前Activity的引用,這些引用會在Message被執行之前一直保持,這樣當前Activity就不會被垃圾回收機制回收,從而導致記憶體洩漏。
解決方法
1.定義一個靜態的Handler,這樣Acitivity就不會被洩漏了,同時讓Handler持有一個對Activity的弱引用,這樣就可以happy的在Handler中呼叫Activity中的資源或者方法了。
2.如果在關閉Activity之後,不需要對訊息佇列中的訊息進行處理了,可以在onDestory方法中加入下面這段程式碼:

Q:Handler機制,主執行緒如何向子執行緒傳送訊息,Handler能否多程式通訊

   private final int MSG_HELLO = 0;
    private Handler mHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        new CustomThread().start();//新建並啟動CustomThread例項
        findViewById(R.id.send_btn).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {//點選介面時傳送訊息
                String str = "hello";
                Log.d("Test", "MainThread is ready to send msg:" + str);
                mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//傳送訊息到CustomThread例項
            }
        });
    }
   //採用內部類的方式定義一個類繼承自Thread並重寫run()方法,在主執行緒將會新建執行緒物件並開啟這個執行緒
    class CustomThread extends Thread {
        @Override
        public void run() {
            //**下面的程式碼是從工作執行緒接收主執行緒訊息的關鍵程式碼**
            //建立訊息迴圈的步驟
            Looper.prepare();//1、初始化Looper:讓Looper開始工作,從訊息佇列裡取訊息,處理訊息。 
            mHandler = new Handler(){//2、繫結handler到CustomThread例項的Looper物件
                public void handleMessage (Message msg) {//3、定義處理訊息的方法
                    switch(msg.what) {
                    case MSG_HELLO:

                        Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);
                    }
                }
            };
            Looper.loop();//4、啟動訊息迴圈
            //注意:loop裡面是一個迴圈,在呼叫looper.loop()後,這個迴圈就是一個死迴圈,有訊息就處理,無訊息就掛起!
            //也就是說,寫在looper.loop()這行程式碼下的其他程式碼都不會再執行了,除非你呼叫mHandler.getLooper().quit()後,
            //loop才會中止,其後的程式碼才能得以執行!需要注意,退出之後這個執行緒也就無法繼續監聽主執行緒發來的訊息了!! 
            //
        }
    }

Q:單例模式的幾種實現具體的區別
最安全,效率最高的寫法 事例

public class Singleton {
    private volatile static Singleton singleton;
    public Singleton() {
    }

    public static  Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

Q:工廠模式的種類以及區別

簡單工廠模式:簡單工廠模式不是23種裡的一種,簡而言之,就是有一個專門生產某個產品的類。
比如下圖中的滑鼠工廠,專業生產滑鼠,給引數0,生產戴爾滑鼠,給引數1,生產惠普滑鼠。
工廠模式:工廠模式也就是滑鼠工廠是個父類,有生產滑鼠這個介面。
戴爾滑鼠工廠,惠普滑鼠工廠繼承它,可以分別生產戴爾滑鼠,惠普滑鼠。
生產哪種滑鼠不再由引數決定,而是建立滑鼠工廠時,由戴爾滑鼠工廠建立。
後續直接呼叫滑鼠工廠.生產滑鼠()即可
抽象工廠模式:抽象工廠模式也就是不僅生產滑鼠,同時生產鍵盤。 也就是PC廠商是個父類,有生產滑鼠,生產鍵盤兩個介面。 戴爾工廠,惠普工廠繼承它,可以分別生產戴爾滑鼠+戴爾鍵盤,和惠普滑鼠+惠普鍵盤。 建立工廠時,由戴爾工廠建立。 後續工廠.生產滑鼠()則生產戴爾滑鼠,工廠.生產鍵盤()則生產戴爾鍵盤。

Q:Android第三方框架的原始碼實現原理

Q:資料的儲存方式與其的應用場景,ContentProvider的底層實現,如何保證多程式讀寫安全

作為一個完整的應用程式,資料儲存操作是必不可少的。因此,Android系統一共提供了四種資料儲存方式。分別是:SharePreference、檔案儲存、SQLite、 ContentProvider。對這幾種方式的不同和應用場景整理如下。
第一種: 使用SharedPreferences儲存資料
  適用範圍:儲存少量的資料,且這些資料的格式非常簡單:字串型、基本型別的值。比如應用程式的各種配置資訊(如是否開啟音效、是否使用震動效果、小遊戲的玩家積分等),解鎖口 令密碼等
核心原理:儲存基於XML檔案儲存的key-value鍵值對資料,通常用來儲存一些簡單的配置資訊。通過DDMS的File Explorer皮膚,展開檔案瀏覽樹,很明顯SharedPreferences資料總是儲存在/data/data//shared_prefs目錄下。SharedPreferences物件本身只能獲取資料而不支援儲存和修改,儲存修改是通過
SharedPreferences物件與SQLite資料庫相比,免去了建立資料庫,建立表,寫SQL語句等諸多操作,相對而言更加方便,簡潔。但是SharedPreferences也有其自身缺陷,比如其職能儲存boolean,int,float,long和String五種簡單的資料型別,比如其無法進行條件查詢等。所以不論SharedPreferences的資料儲存操作是如何簡單,它也只能是儲存方式的一種補充,而無法完全替代如SQLite資料庫這樣的其他資料儲存方式。
第二種: 檔案儲存資料
  可以在裝置本身的儲存裝置或者外接的儲存裝置中建立用於儲存資料的檔案。同樣在預設的狀態下,檔案是不能在不同的程式間共享。
  寫檔案:呼叫Context.openFileOutput()方法根據指定的路徑和檔名來建立檔案,這個方法會返回一個FileOutputStream物件。
  讀取檔案:呼叫Context.openFileInput()方法通過制定的路徑和檔名來返回一個標準的Java FileInputStream物件。
第三種:SQLite儲存資料
  SQLite Database資料庫。Android對資料庫的支援很好,它本身整合了SQLite資料庫,每個應用都可以方便的使用它,或者更確切的說,Android完全依賴於SQLite資料庫,它所有的系統資料和用到的結構化資料都儲存在資料庫中。 它具有以下優點: a. 效率出眾,這是無可否認的 b. 十分適合儲存結構化資料 c. 方便在不同的Activity,甚至不同的應用之間傳遞資料。  
第四種:ContentProvider
  Android系統中能實現所有應用程式共享的一種資料儲存方式,由於資料通常在各應用間的是互相私密的,所以此儲存方式較少使用,但是其又是必不可少的一種儲存方式。例如音訊,視訊,圖片和通訊錄,一般都可以採用此種方式進行儲存。每個ContentProvider都會對外提供一個公共的URI(包裝成Uri物件),如果應用程式有資料需要共享時,就需要使用ContentProvider為這些資料定義一個URI,然後其他的應用程式就通過Content Provider傳入這個URI來對資料進行操作。
  總結一下,檔案適用於儲存一些簡單的文字資料或者二進位制資料,SharedPreferences適用於儲存一些鍵值對,而資料庫則適用於那些複雜的關係型資料。

Q:場景題分析:手機掃網頁端的二維碼如何提起的登入的過程,是如何實現的?

開啟網頁版微信登入介面後,瀏覽器會獲得一個臨時id,通過長連線等待客戶端掃描帶有此id的二維碼後,從長連線中獲得客戶端上報給Server的帳號資訊進行展示。並在客戶端點選確認後,獲得伺服器授權的令牌(Token),進行隨後的資訊互動過程。(在超時,網路斷開,其他裝置上登入後,此前獲得的令牌或丟失、或失效,對授權過程你形成有效的安全防護)

Q:代理模式

Q:多執行緒併發,sychronized,類鎖和物件鎖

每一個類都是一個物件,每個物件都擁有一個物件鎖

MVP+Retrofit+RxJava的原始碼分析可以看這幾篇文章
Android 教你一步步搭建MVP+Retrofit+RxJava網路請求框架
RxJava + Retrofit完成網路請求

ButterKnife的原始碼分析可以看這幾篇文章
ButterKnife使用和原理

Glide的原始碼分析可以看這幾篇文章
Glide圖片原理解析

EventBus的原始碼分析可以看這幾篇文章
EventBus 原理解析

LeakCanary的原始碼分析可以看這幾篇文章
Java記憶體問題 及 LeakCanary 原理分析

activity進行網路請求,在網路請求過程中finish當前的頁面,網路請求是否中斷

相關文章