記錄2019年安卓面試

  • 你在項目里都用到了那些設(shè)計模式,以及你了解知道的設(shè)計模式,和運用場景。

a. 建造者模式:將一個復(fù)雜的對象的構(gòu)建拆分,一個復(fù)雜的對象可能存在多種不同的表現(xiàn)形式,使用同樣的構(gòu)造過程,可以創(chuàng)建出來不同的表現(xiàn)。

比如:常見的安卓使用這種模式的有AlertDialog,在開發(fā)的例子Camera開發(fā)過程中,可能需要設(shè)置一個初始化的相機配置,設(shè)置攝像頭方向,閃光燈開閉,成像質(zhì)量等等,這種場景下就可以使用建造者模式。

b.裝飾者模式:動態(tài)地給一個對象添加一些額外的職責(zé)。裝飾者可以在不改變原有類的結(jié)構(gòu)上增強類的功能。

比如:需要擴展一個類的功能,或給一個類增加附加功能時,可以使用裝飾者模式,這樣的話就可以使用裝飾者模式,比如在項目里面封裝好的網(wǎng)絡(luò)請求,在后期開發(fā)中,需要添加新的功能,可以在向外包裝一層。

c.觀察者模式:定義對象間的一種一個對多個的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生變化的時候,所有依賴它的對象都要得到通知并自動更新。

比如:項目中有用到的地方可以說在網(wǎng)絡(luò)框架這里,封裝網(wǎng)絡(luò)狀態(tài)的時候,跟住不同的網(wǎng)路狀態(tài)來設(shè)置不同的功能,比如WIFI,移動網(wǎng)絡(luò),或者是無網(wǎng)絡(luò)的時候的一些處理,可以通過觀察網(wǎng)絡(luò)的狀態(tài)來實現(xiàn)。

  • 說說java的線程,以及你對線程池的理解。
線程的創(chuàng)建方法

1.繼承Thread類實現(xiàn)多線程
2.實現(xiàn)Runnable接口
3.實現(xiàn)Callable接口

線程與進程的區(qū)別

進程包括線程,一個進程可以有多個線程,不同的進程使用不同的內(nèi)存空間,所有的線程同享同一個內(nèi)存空間。

Java中Runnable和Callable有什么不同?

Runnable和Callable都代表著在不同線程里面執(zhí)行的任務(wù)。Ruannable是在java1.0出來的,而Callable是在java1.5后出來的,主要區(qū)別是Callable的call()方法有返回值或者拋出異常,而runnable的run()沒有這個功能。Callable可以返回裝載有計算結(jié)果的Future對象。

java中的關(guān)鍵字volatile是什么

volatile關(guān)鍵字只能作用在成員變量上,被volatile修飾的變量,可以保證下一個讀取操作會在前一個寫操作之后發(fā)生。

一個線程發(fā)生異常會怎么樣?

簡單的說,如果異常沒有被捕獲的話,該線程就會被停止。
對于沒有捕獲的異常,我們的方法是通過給線程設(shè)置UncaughtExceptionHandler,即對未知異常的處理,這時你就可以進行相關(guān)日志操作了.

class MyExceptionHandler implements UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {//在這你可以記錄相關(guān)錯誤日志到文件中
        System.out.printf("An exception has been captured\n");
      System.out.printf("Thread:%s\n", t.getName());
      System.out.printf("Exception: %s: %s:\n", e.getClass().getName(), e.getMessage());
      System.out.printf("Stack Trace:\n");
      e.printStackTrace();
      System.out.printf("Thread status:%s\n", t.getState());
}
//然后把你創(chuàng)建的異常處理類,設(shè)置給你的線程就可以了。
  Thread thread = new Thread(new ThreadTest1()); 
        thread.setUncaughtExceptionHandler(new MyExceptionHandler());
        thread.start();
如何在多線程中共享數(shù)據(jù)

這個問題你首先要想到的是我們常舉得例子,賣票和存款的例子。
賣票的例子是做的同一個操作,也就是多個線程使用同一個runnable就可以。
存款的例子是讓封裝共同數(shù)據(jù),讓共享數(shù)據(jù)成為成員變量,書寫不同的runnable,來調(diào)用共享類的成員變量,使用像阻塞隊列這樣并發(fā)的數(shù)據(jù)結(jié)構(gòu)。用wait和notify方法可以實現(xiàn)生產(chǎn)者消費者模型。實現(xiàn)數(shù)據(jù)的共享。

java中堆和棧有什么不同

每個線程都有自己的棧內(nèi)存,而多個線程共享同一個堆內(nèi)存。最主要的區(qū)別就是棧內(nèi)存用來存儲局部變量和方法調(diào)用。而堆內(nèi)存用來存儲Java中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內(nèi)存中。

活鎖與死鎖

活鎖:優(yōu)先級相同的線程,互相禮讓,都不執(zhí)行,最終導(dǎo)致活鎖
死鎖:多個線程需要互相等待對方的操作資源的時候,其他線程持有資源不釋放而導(dǎo)致的,常見的情況就是線程持有的鎖不同而導(dǎo)致的。其實就是互相不禮讓,最終導(dǎo)致死鎖。
餓死:有一個線程一直禮讓,最終導(dǎo)致這個線程的任務(wù)一直不執(zhí)行。

單例模式的雙檢鎖問題
public class Singleton {
 //正確的是加上 volatile關(guān)鍵字  private static volatile Singleton uniqueInstance; 
    private static Singleton uniqueInstance; 


    private Singleton(){
    }

    public static Singleton getInstance(){
        if(uniqueInstance == null){ //#1
            synchronized(Singleton.class){ //#2
                if(uniqueInstance == null){ //#3
                    uniqueInstance = new Singleton(); //#4
                    System.out.println(Thread.currentThread().getName() + ": uniqueInstance is initalized..."); //#5.1
                } else {
                    System.out.println(Thread.currentThread().getName() + ": uniqueInstance is not null now..."); //#5.2
                }
            }
        }
        return uniqueInstance;
    }
}

情況假如線程1進來,走到#1,判斷實例為空,讓出cpu的執(zhí)行權(quán)給線程2.
線程2進來執(zhí)行#1 判斷為空,讓出cpu的執(zhí)行權(quán),給線程1。
線程1一直走到#2,#3,#4,出去,線程2進來,由于線程2在#1中持有的實例為空,所以也就會走#2,#3,#4.從而創(chuàng)建出兩個對象。
加上volatile后,這個關(guān)鍵字可以及時更新每個線程里面的數(shù)據(jù)變化,也就是可以保證下一個讀取操作會在前一個寫操作之后發(fā)生。

什么是線程池,如何使用
  • 什么是線程池:java提供了Executor接口的實現(xiàn)用于創(chuàng)建線程池,線程池主要是用來管理線程的。
    其中有四個常用的線程池
    ①newSingleThreadExecutor
    單個線程的線程池,即線程池中每次只有一個線程工作,單線程串行執(zhí)行任務(wù)
    ②newFixedThreadExecutor(n)
    固定數(shù)量的線程池,沒提交一個任務(wù)就是一個線程,直到達到線程池的最大數(shù)量,然后后面進入等待隊列直到前面的任務(wù)完成才繼續(xù)執(zhí)行
    ③newCacheThreadExecutor(推薦使用)
    可緩存線程池,當(dāng)線程池大小超過了處理任務(wù)所需的線程,那么就會回收部分空閑(一般是60秒無執(zhí)行)的線程,當(dāng)有任務(wù)來時,又智能的添加新線程來執(zhí)行。
    ④newScheduleThreadExecutor
    大小無限制的線程池,支持定時和周期性的執(zhí)行線程
線程池的工作原理

線程池可以減少創(chuàng)建和銷毀線程的次數(shù),從而減少系統(tǒng)資源的消耗,當(dāng)一個任務(wù)提交到線程池時

a. 首先判斷核心線程池中的線程是否已經(jīng)滿了,如果沒滿,則創(chuàng)建一個核心線程執(zhí)行任務(wù),否則進入下一步

b. 判斷工作隊列是否已滿,沒有滿則加入工作隊列,否則執(zhí)行下一步

c. 判斷線程數(shù)是否達到了最大值,如果不是,則創(chuàng)建非核心線程執(zhí)行
任務(wù),否則執(zhí)行飽和策略,默認(rèn)拋出異常
結(jié)合項目的例子:多張圖片的上傳,在使用線程池后可以減少上傳的時間。原本上傳需要9秒,用線程池后需要2秒。

  • 內(nèi)存泄漏問題
  1. 非靜態(tài)內(nèi)部類造成的內(nèi)存泄漏
    非靜態(tài)的內(nèi)部類會持有外部類的指引,當(dāng)在內(nèi)部類中做一些不可控制生命周期的操作就有可能會產(chǎn)生內(nèi)存泄漏,比如在線程中執(zhí)行耗時操作就有可能發(fā)生內(nèi)存泄漏,導(dǎo)致外部類無法被回收,直到耗時任務(wù)結(jié)束,解決辦法是在頁面退出時結(jié)束線程中的任務(wù)
  2. 非靜態(tài)內(nèi)部類的靜態(tài)實例造成的內(nèi)存泄漏。
    非靜態(tài)內(nèi)部類會持有外部類的指引,如果非靜態(tài)內(nèi)部類的靜態(tài)實例,就會長期持有外部類的指引,這樣外部類就不會被系統(tǒng)回收,解決方法是把非靜態(tài)內(nèi)部類寫成靜態(tài)內(nèi)部類。
  3. handler內(nèi)存泄漏
    handler也是非靜態(tài)內(nèi)部類造成的內(nèi)存泄漏,因為handler內(nèi)部有一個MessageQueue用來存放message,有些message不能被及時處理會長時間存在于內(nèi)存中,導(dǎo)致handler無法被回收,如果handler屬于非靜態(tài)內(nèi)部類,所以持有外部類的指引,導(dǎo)致外部類不能被回收,解決方法1.使用靜態(tài)handler,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息。
  4. Context導(dǎo)致的內(nèi)存泄漏
    跟住場景需要使用Activity還是Application的Context,因為他們的生命周期不一樣,一些不需要使用Activity的Context的地方就換成Application的Context。最常見的就是單例傳入的上下文引起的內(nèi)存泄漏,比如傳入一個Activity的Context被靜態(tài)類引用,導(dǎo)致無法回收。
  5. 靜態(tài)View引起的內(nèi)存泄漏
    使用靜態(tài)View可以避免每次啟動Activity都去讀取并渲染View,但是靜態(tài)View會持有Activity的引用,導(dǎo)致無法回收,解決辦法是在Activity銷毀的時候?qū)㈧o態(tài)View設(shè)置為null(View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是我們的Activity,聲明一個靜態(tài)變量引用這個View,也就引用了activity)
  6. 資源對象未關(guān)閉導(dǎo)致的內(nèi)存泄漏
    file,Cursor,內(nèi)部都設(shè)置了緩存,在不適用的時候一定要關(guān)閉。
  7. bitmap造成的內(nèi)存泄漏
    bitmap是比較占內(nèi)存的,所以一定要在不使用的時候及時進行清理,避免靜態(tài)變量持有大的bitmap對象
  8. 集合造成的內(nèi)存泄漏
    集合用于保存對象,如果集合越來越大,不進行合理的清理,尤其是入股集合是靜態(tài)的
  9. 監(jiān)聽器未關(guān)閉造成的內(nèi)存泄漏
    注冊的廣播啊,服務(wù)啊等,不用的時候一定要取消掉。
  • onRestart的調(diào)用場景

按home鍵后再回到界面
從A界面跳到B界面,又按返回鍵返回A界面
從本Activity切換到其他的應(yīng)用,然后再從其他應(yīng)用切換回來,會調(diào)用onRestart();

  • 橫豎屏切換時生命周期

1、不設(shè)置Activity的android:configChanges時,切屏?xí)匦抡{(diào)用各個生命周期,切橫屏?xí)r會執(zhí)行一次,切豎屏?xí)r會執(zhí)行兩次

2、設(shè)置Activity的android:configChanges="orientation"時,切屏還是會重新調(diào)用各個生命周期,切橫、豎屏?xí)r只會執(zhí)行一次

3、設(shè)置Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調(diào)用各個生命周期,只會執(zhí)行onConfigurationChanged方法

  • SurfaceView與View的區(qū)別。
  1. View是底層單緩沖機制,SurfaceView是雙緩沖機制
  2. View主要試用于主動更新,SurfaceView適用于被動更新,頻繁更新。
    3 .View的繪制是在UI主線程,SurfaceView的繪制是單獨開一個線程。

SurfaceView的內(nèi)容不在應(yīng)用窗口上,所以不能使用變換(平移、縮放、旋轉(zhuǎn)等)。也難以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()

View:顯示視圖,內(nèi)置畫布,提供圖形繪制函數(shù)、觸屏事件、按鍵事件函數(shù)等;必須在UI主線程內(nèi)更新畫面,速度較慢。

SurfaceView:基于view視圖進行拓展的視圖類,更適合2D游戲的開發(fā);是view的子類,類似使用雙緩機制,在新的線程中更新畫面所以刷新界面速度比view快,Camera預(yù)覽界面使用SurfaceView。

  • Android 中的線程有那些,原理與各自特點
  • ANR的原因

1.耗時的網(wǎng)絡(luò)訪問
2.大量的數(shù)據(jù)讀寫
3.數(shù)據(jù)庫操作
4.硬件操作(比如camera)
5.調(diào)用thread的join()方法、sleep()方法、wait()方法或者等待線程鎖的時候
6.service binder的數(shù)量達到上限
7.system server中發(fā)生WatchDog ANR
8.service忙導(dǎo)致超時無響應(yīng)
9.其他線程持有鎖,導(dǎo)致主線程等待超時
10.其它線程終止或崩潰導(dǎo)致主線程一直等待

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容