天天看點

oppo提前批Android開發崗面經(附問題答案)

       7月17日喜提oppo提前批offer,oppo确實如他們企業核心價值觀所說的那樣:本分。給提前批的福利是可以在9月30日之前解除協定無需違約金,一面、部長面和HR面都蠻順利(一千個人可能要了一百左右,但沒有感受到競争的激烈,說白了提高自己的核心競争力才是硬道理,自己強了面試也會更順利)。

oppo一面:

1.你要面的是Android開發工程師,那麼Android開發主要是做什麼的?

         Android大緻分為4層系統架構,分别是:Linux核心層、系統運作庫層、應用架構層和應用層。

     (1)Linux核心層:

       Android是基于Linux核心的,這一層為Android裝置的各種硬體提供了底層的驅動,譬如藍牙驅動、音頻驅動、Wifi驅動、鍵盤驅動、照相機驅動等。

     (2)系統運作庫層:

       分為Android運作時層和系統庫,運作時層包括了核心庫和DVM虛拟機,核心庫允許開發者使用Java來編寫Android應用,DVM虛拟機在5.0以前是DVM,5.0之後是ART。核心庫包括了SQLite資料庫、OpenGL3D繪圖支援、WebKit庫浏覽器核心支援等。

      (3)應用架構層:

       主要是系統建構時所需要的若幹API,譬如Activity、ContentProvider、View、Notification、Location等等。可以使用這些API來進行處理。

      (4)應用層:

        所有手機上的App都是屬于這一層的,譬如系統自帶的聯系人、短信、小遊戲、自己開發小程式等等。

2.你的問題是什麼?

      (1)Kotlin是不是大勢所趨?Flutter的Dart語言公司在用麼?Fuchsia會替代Android麼?

      (2)您覺得我在哪些方面需要加強學習?

3.Java中的難點?

        集合機制(List、Set和Map)、同步機制(加鎖Synchorized、Lock和unLock、wait和notify、notifyall)。

4.Java當中遇到的鎖?

        主要分為:(1)樂觀鎖/悲觀鎖;(2)獨享鎖/共享鎖;(3)互斥鎖/讀寫鎖;(4)可重入鎖;(5)公平鎖/非公平鎖;(6)自旋鎖;(7)分段鎖。

(1)樂觀鎖/悲觀鎖

       樂觀鎖與悲觀鎖并不是特指某兩種類型的鎖,是人們定義出來的概念或思想,主要是指看待并發同步的角度。

       樂觀鎖:每次擷取資料的時候,都不會擔心資料被修改,是以每次擷取資料的時候都不會進行加鎖,但是在更新資料的時候需要判斷該資料是否被别人修改過。如果資料被其他線程修改,則不進行資料更新,如果資料沒有被其他線程修改,則進行資料更新。由于資料沒有進行加鎖,期間該資料可以被其他線程進行讀寫操作。一般使用version方式和CAS操作方式。eg:CAS(比較并交換)。

       悲觀鎖:每次擷取資料的時候,都會擔心資料被修改,是以每次擷取資料的時候都會進行加鎖,確定在自己使用的過程中資料不會被别人修改,使用完成後進行資料解鎖。由于資料進行加鎖,期間對該資料進行讀寫的其他線程都會進行等待。在Java中,synchronized的思想也是悲觀鎖。

       樂觀鎖适合讀取操作比較頻繁的場景,如果出現大量的寫入操作,資料發生沖突的可能性就會增大,為了保證資料的一緻性,應用層需要不斷的重新擷取資料,這樣會增加大量的查詢操作,降低了系統的吞吐量。

       悲觀鎖使用場景:比較适合寫入操作比較頻繁的場景,如果出現大量的讀取操作,每次讀取的時候都會進行加鎖,這樣會增加大量的鎖的開銷,降低了系統的吞吐量。

(2)獨享鎖/共享鎖

        獨享鎖是指該鎖一次隻能被一個線程所持有。共享鎖是指該鎖可被多個線程所持有。

對于Java ReentrantLock(互斥鎖)而言,其是獨享鎖。但是對于Lock的另一個實作類ReadWriteLock(讀寫鎖),其讀鎖是共享鎖,其寫鎖是獨享鎖。

       讀鎖的共享鎖可保證并發讀是非常高效的,讀寫,寫讀,寫寫的過程是互斥的,對于Synchronized而言,當然是獨享鎖。

(3)互斥鎖/讀寫鎖

       互斥鎖/讀寫鎖就是獨享鎖/共享鎖具體的實作。互斥鎖在Java中的具體實作就是ReentrantLock。讀寫鎖在Java中的具體實作就是ReadWriteLock。

(4)可重入鎖

  可重入鎖又名遞歸鎖,是指在同一個線程在外層方法擷取鎖的時候,在進入内層方法會自動擷取鎖。

    對于Java ReetrantLock而言,從名字就可以看出是一個重入鎖,其名字是ReentrantLock 重新進入鎖。

       對于Synchronized而言,也是一個可重入鎖。可重入鎖的一個好處是可一定程度避免死鎖。

       不可重入鎖:隻判斷這個鎖有沒有被鎖上,隻要被鎖上申請鎖的線程都會被要求等待。實作簡單;

       可重入鎖:不僅判斷鎖有沒有被鎖上,還會判斷鎖是誰鎖上的,當就是自己鎖上的時候,那麼他依舊可以再次通路臨界資源,并把加鎖次數加一。

       設計了加鎖次數,以在解鎖的時候,可以確定所有加鎖的過程都解鎖了,其他線程才能通路。不然沒有加鎖的參考值,也就不知道什麼時候解鎖?解鎖多少次?才能保證本線程已經通路完臨界資源了可以喚醒其他線程通路了。實作相對複雜。)

(5)公平鎖/非公平鎖

   公平鎖是指多個線程按照申請鎖的順序來擷取鎖。

        非公平鎖是指多個線程擷取鎖的順序并不是按照申請鎖的順序,有可能後申請的線程比先申請的線程優先擷取鎖。有可能,會造成優先級反轉或者饑餓現象。ReetrantLock和Synchronized都是非公平鎖;

(5)分段鎖

        分段鎖是一種設計,對于ConcurrentHashMap而言,其并發的實作就是通過分段鎖的形式來實作高效的并發操作。

分段鎖稱為Segment,它即類似于HashMap(JDK7和JDK8中HashMap的實作)的結構,即内部擁有一個Entry數組,數組中的每個元素又是一個連結清單;同時又是一個ReentrantLock。

(6)自旋鎖

       在Java中,自旋鎖是指嘗試擷取鎖的線程不會立即阻塞,而是采用循環的方式去嘗試擷取鎖,這樣的好處是減少線程上下文切換的消耗,缺點是循環會消耗CPU。

     具體來說:

     Synchronized,它就是一個:非公平,悲觀,獨享,互斥,可重入的重量級鎖,以下兩個鎖都在JUC包下,是API層面上的實作

     ReentrantLock,它是一個:預設非公平但可實作公平的,悲觀,獨享,互斥,可重入,重量級鎖。

     ReentrantReadWriteLocK,它是一個,預設非公平但可實作公平的,悲觀,寫獨享,讀共享,讀寫,可重入,重量級鎖。

5.線程的了解?線程的建立?線程的切換?AsyncTask什麼時候是在工作線程?什麼時候是在主線程?

        (1)線程建立的三種方法:(1)繼承Thread實作run方法;(2)上一中耦合性較高,實作Runnable接口并重寫run方法;(3)使用匿名内部類實作并重寫run方法。

        (2)Android中線程的切換:Handler、AsyncTask、HandlerThread。

2.1.AsyncTask底層是線程池,其餘兩個直接使用了線程。

     onPreExecute(界面的初始化操作):在主線程執行,在異步任務執行之前被調用,一般用于一些準備工作

     doInBackground(處理具體耗時任務):線上程池中執行,此方法用于執行異步任務,params參數表示異步任務的輸入參數,在此方法中可以通過publishProgress方法來更新任務的進度,publishProgress會調用onProgressUpdate方法,此外此方法需要傳回計算結果給onPostExecute

      onProgressUpdate(進行UI操作):在主線程執行,當背景任務執行進度發生改變時,傳回結果

      onPostExecute(任務收尾工作):在主線程中執行,在異步任務結束後,此方法被調用,其中result參數是背景任務的傳回值。

       AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用于任務的排隊,而線程池THREAD_POOL_EXECUTOR用于真正的執行任務,IntentalHandler用于将執行環境從線程池切換到主線程。

2.2.HandlerThread

    使用步驟如下:

   (1)建立HandlerThread對象,可以重寫onLooperPrepared()方法做一些初始化操作;

   (2)調用handlerThread.start()方法;

   (3)建立Handler,在構造中傳入handlerThread.getLooper()方法建立的Looper對象,重寫handleMessage(Message msg)方法,在子線程處理消息;

   (4)使用Handler對象在相關場景發送處理消息;

   (5)适時退出異步操作quit方法

     源碼:

    (1)HandlerThread 繼承了Thread,是一種可以使用Handler的Thread,在run方法中通過Looper.prepare()來建立消息隊列,并通過loop()來開啟消息循環,可以通過quit方法或者quitSafely方法來終止線程進行,允許在HandlerThread中建立Handler了。

    (2)普通Thread主要用于在run方法中執行一個耗時操作,而HandlerThread在内部建立了消息隊列,外界需要通過Handler的消息方式來通知HandlerThread執行一個具體的任務。

      整個HandlerThread做的工作本質是在Thread中封裝了Looper對象,以此在建立的Handler中能夠進行異步的消息處理。

6.Violate關鍵字和變量不可見性。

    (1)保持記憶體可見性:所有線程都能看到共享記憶體的最新狀态。每次讀取前必須先從主記憶體重新整理最新的值。每次寫入後必須立即同步回主記憶體當中。(2)禁止指令重排序。通過“記憶體屏障”來防止指令被重排序。保證了共享變量的“可見性”,可見性的意思是一個線程修改一個共享變量時,另一個線程可以讀到這個修改的值,它不會引起線程的上下文切換和排程。

     可見性、同步性;修飾對象;阻塞與否;編譯器優化與否。

     (1)volatile關鍵字解決的是變量在多個線程之間的可見性(一個線程修改的狀态對另一個線程是可見的。);而sychronized關鍵字解決的是多個線程之間通路共享資源的同步性。 

     (2)volatile隻能用于修飾變量,而synchronized可以修飾變量,方法,以及代碼塊。

     (3)volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。

     (4)volatile标記的變量不會被編譯器優化;synchronized标記的變量可以被編譯器優化

補充:synchronized和Lock的差別

     (1)synchronized不會引起死鎖,而Lock發生異常時,如果沒有unLock解鎖,可能會引起死鎖現象。

     (2)Lock是接口,Sychronsized是關鍵字;

     (3)當競争資源激烈時,Lock性能高于Sychronized。

     (4)Lock可以讓等待的鎖響應中斷,但sychronsized不可以。一直等待下去。

7.Android SDK是個啥?如何開發?SDK如何使用?

       SDK是software development kit的縮寫,顧名思義,是一套軟體開發工具集。SDK最大的好處就是可以對基礎的代碼進行封裝,将很多配置、平台相容、固定用法的部分打包起來,隻專注于提供邏輯方法。這樣,頂層應用開發人員就不需要過多地去關注底層的實作,而隻需要專注于邏輯的開發,功能的實作。然後,整個軟體開發過程就可以嚴格地流程化,底層開發與應用功能開發互不影響,實作并行開發,這樣就大大提高了軟體開發的合作效率。

       類似于Jar包,直接add as a library即可。

8.項目上的問題?

        視訊展示這塊。性能方面:從D2C和DexVMP兩方面去說。Dex檔案的加載過程(DexClassLoader)和Dex檔案的格式,多重虛拟化的實作,根據Smali指令類型進行指令解析轉換;Android的項目超音波和藍牙防丢器。協處理器有了解麼?圖網絡的背景。

       超音波:

       用Matlab同樣實作揚聲器發送17.5KHZ高頻超音波、麥克風以44100采樣率收集原始資料,對原始信号的頻譜進行相幹解調,濾波處理之後對靜态向量進行去除,通過相位變化計算總距離變化。

        發射子產品(揚聲器17.5KHZ高頻超音波)、接收子產品(麥克風以44100采樣率收集原始資料)、存儲子產品(pcm格式)、處理子產品(處理子產品分為調頻子產品(将原始信号(數組)下變頻成為基帶信号a+bi,将載波去掉)、CIC濾波子產品(進行低通濾波,去掉高頻部分的信号)、高通濾波子產品(去掉振幅較低靜态物體反射信号、去除靜态向量)、計算相位子產品(高通濾波之後利用相關庫及自定義函數計算相位))以及顯示子產品(子線程更新UI)。

       華為榮耀3C不能發送高頻聲波,三星蓋世S5、小米5可以。

       藍牙防丢器:

       界面采用ViewPager+Fragment,地圖服務采用了LBS SDK,聊天機器人采用了圖靈機器人。

       首先,我們要判斷手機是否支援BLE,并且獲得各種權限,才能讓我們之後的程式能正常運作。 

       然後,我們去搜尋BLE裝置,得到它的MAC位址。 

       其次,我們通過這個MAC位址去連接配接,連接配接成功後,去周遊得到Characteristic的uuid。 

       在我們需要發送資料的時候,通過這個uuid找到Characteristic,去設定其值,最後通過writeCharacteristic(characteristic)方法發送資料。 

       如果我們想知道手機與BLE裝置的距離,則可以通過readRemoteRssi()去得到rssi值,通過這個信号強度,就可以換算得到距離。 隻要我們連接配接上,我們就可以用BluetoothGatt的各種方法進行資料的讀取等操作。

Oppo二面(部長面):

       部長面其實答得不好,有些問題不是很清楚。或許這是白菜價的原因吧,靜下心來複習才是硬道理。

1.項目介紹:

2.Android虛拟機允許的最大記憶體

       Max Heap Size,是堆記憶體的上限值,Android的預設值是16M(某些機型是24M)。一般是動态配置設定的。

       堆(HEAP)是DVM中占用記憶體最多的部分,通常是動态配置設定的。堆的大小不是一成不變的,通常有一個配置設定機制來控制它的大小。比如初始的HEAP是4M大,當4M的空間被占用超過75%的時候,重新配置設定堆為8M大;當8M被占用超過75%,配置設定堆為16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小為8M大。重新設定堆的大小,尤其是壓縮,一般會涉及到記憶體的拷貝,是以變更堆的大小對效率有不良影響。

3.職業規劃

      第一點:自己是一個什麼樣的人?第二點:自己對應聘崗位是否了解?第三點自己的規劃。

      自己是一個比較踏實肯幹(考研、導師評價)、目标明确、有一定抗壓能力(項目中的經曆)的人。

      應聘崗位的了解:應用層、系統架構層、Android運作時層、Linux Kernel層。

      Linux Kernel層:Android是基于Linux核心的,這一層為Android裝置的各種硬體提供了底層的驅動,主要是寫一些驅動,譬如藍牙驅動、音頻驅動等。

      Android運作時層:分為Android運作時層和系統庫,運作時層包括了核心庫和DVM虛拟機,核心庫允許開發者使用Java來編寫Android應用,DVM虛拟機在5.0以前是DVM,5.0之後是ART。核心庫包括了SQLite資料庫、OpenGL3D繪圖支援、WebKit庫浏覽器核心支援等。DexVMP屬于Android運作時層。

      系統架構層:主要是系統建構時所需要的若幹API,譬如Activity、ContentProvider、View、Notification、Location等等。可以使用這些API來進行處理。

      應用層:所有手機上的App都是屬于這一層的,譬如系統自帶的聯系人、短信、小遊戲、自己開發小程式等等

      自己的職業規劃(以自己研究所學生的經曆為例):

      第一階段,我希望從現在開始,1-2年之内能夠在我目前申請的這個職位上沉澱下來,積累最起碼的工作經驗,把基礎打牢;

      第二階段,我希望利用3-5年的時間,成為一個在自己的專業方面能夠獨當一面的人,能夠獨自承擔責任,發現問題,解決問題,不讓上司操心;

      第三階段,成為該領域的一名專業化人士,在工作中能有創新與發展,能為公司帶來更大的價值。

4.Activity和Service之間的互動

      (1)BindService;(2)基于BindService的Handler;(2)BroadCastReceiver;(3)通過共享檔案;(4)Messenger;(5)AIDL。

      (1)使用BindService進行資料的傳遞,Activity向Service傳遞資料靠的是Intent的Bundle和BindService;Service向Activity傳遞資料靠的是繼承自Binder類的binder對象。最後在Acitivity的onServiceConnected中擷取。也可以通過BindSerrvice

         基于BindService的Handler。(1)在Service中的onCreate方法中建立Service端的Handler和Messenger對象,并且在handleMessage方法中擷取Activity端的Messenger;    

     (2)在Service的onBind方法中傳回Messenger的Binder對象;(3)在Activity中建立一個Handler對象,用來處理消;(4)在Activity中建立一個ServiceConnection對象,并且在onServiceConnected方法中,擷取Service端的Messenger對象;(5)在Activity建立Messenger,并封裝在Message中傳遞給Service端。

oppo提前批Android開發崗面經(附問題答案)

     (3)通過共享檔案,譬如SharedPreference可以實作Activity和Service的互動。

     (4)Messenger:通過它可以在不同程序間傳遞Meesage對象,Messenger是一種輕量級的IPC方案,底層是用AIDL實作的;

     (5)AIDL:AIDL屬于Android的IPC機制,常用于跨程序通信,主要實作原理基于底層Binder機制;

     (6)通過廣播進行互動:通過廣播實作Activity和Service的互動簡單容易實作,缺點是發送不廣播受系統制約,系統會優先發送系統級的廣播,自定義的廣播接收器可能會有延遲,在廣播裡也不能有耗時操作,否則會導緻程式無響應。

5.Bundle的最大限制,Bundle有什麼要求?

      Buddle的最大限制為1Mb。大于1Mb會崩潰掉。

      Bundle的場景大多數為小資料量,ArrayMap内部是使用兩個數組進行資料存儲,一個數組記錄key的hash值,另一個數組記錄value值,内部使用二分法對key進行排序,并使用二分法進行添加、删除、查找資料,是以它隻适合于小資料量操作,在資料量較大的情況下它的性能将會退化。而HashMap内部則是數組+連結清單的結構,在資料量較少的情況下,HashMap的Entry Array比ArrayMap占用更多的記憶體。

      由于使用Bundle的場景大多數為小資料量,是以相比之下,使用ArrayMap儲存資料在操作速度和記憶體占用上都具有優勢,是以使用Bundle來傳遞資料,可以保證更快的速度和更少的記憶體占用。

      Bundle用于攜帶資料,類似于 Map集合,用來存放Key-Value鍵值對,但是它相對于Map,提供了常用類型的 putXxx()和getXxx()方法,比如putString()/getString()、putInt()/getInt(),putXxx()用于往Bundle中放入對象,getXxx()用于從Bundle中取出資料。

      Bundle可傳遞的資料類型:(1)基本類型的資料,如int、String、Float等等;(2)使用Serializable 和 Parceable 傳遞對象(Serializable:代碼少,效率低;Parcelable:代碼多,效率高)

6.OOM及其解決方案

       OOM是記憶體溢出,指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,比如你需要100Mb,但系統隻有90Mb,這樣的話肯定會引起OOM。

      (1)合理加載資源:合理加載資源,既如果展示圖檔的ImageView隻有128*96的像素大小,這時候把一張1024*768的圖檔完全加載到記憶體中,很明顯是錯誤的行為。這個時候,就需要把要加載的圖檔進行壓縮加載,就是合理地加載資源。

下面來進行圖檔的壓縮講解,設定BitmapFactory.Options中inSampleSize的值就可以實作等比例壓縮。比如我們有一張2048*1536像素的圖檔,将inSampleSize的值設定為4,就可以把這張圖檔壓縮成512*384像素。原本加載這張圖檔需要占用26M的記憶體,壓縮後就隻需要占用1.5M了。

      (2)合理回收資源:合理回收資源,既對加載在記憶體中的圖檔資源進行合理的回收,避免因為不再使用的圖檔資源還留存在記憶體中的情況出現。而要實作合理回收資源,最核心的一個類就是:LruCache,這個類非常适用于儲存圖檔記憶體,它的主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,并且把最近最少使用的對象在緩存值達到預設定值之前從記憶體中移除。(比如LruCache、DiskLruCache、對象重複并且頻繁調用可以考慮對象池)

     (3)記憶體洩漏引起的記憶體溢出:靜态變量導緻的記憶體洩漏;Handler引起的記憶體洩漏;單例模式引起的記憶體洩漏;資源對象未關閉引起的記憶體洩漏;注冊/反注冊引起的記憶體洩漏等。

7.BroadCastReceiver有幾種?開機啟動的廣播是什麼廣播?

       廣播類型:标準廣播(異步執行,同時收到、無法截斷)和有序廣播(同步執行、先後順序,可截斷)。開機啟動的廣播是靜态注冊的系統廣播:action的名字為android.intent.action.BOOT_COMPLETED。

7.1.接收系統廣播:

       代碼中注冊稱為動态注冊;在AndroidManifest.xml中注冊稱為靜态注冊。

       動态注冊監聽網絡變化;步驟一:建立類繼承自BroadCastReceiver,重寫父類的onReceiver方法;具體處理邏輯放在其中;步驟二:onCreate方法中建立IntentFilter執行個體,系統發出android.net.conn.CONNECTIVITY_CHANGE的廣播,在IntentFilter添加該Action;步驟三:在onCreate方法中使用registerReceiver進行注冊;在onDestroy中使用unregisterReceiver進行取消注冊。步驟四:添權重限:android.permission.ACCESS_NETWORK_STATE。

      靜态注冊實作開機啟動:動态廣播雖然靈活,但隻有在程式啟動之後才能接收到廣播,靜态注冊可以在程式未啟動的情況下接收到廣播。步驟一:右鍵快速建立BroadCastReceiver,在XML中注冊,在onReceiver中寫一個簡單的Toast;步驟二:系統啟動完成之後會發出一條android.intent.action.BOOT_COMPLETED的廣播,需要在<intent-filter>中添加相應Action;步驟三:聲明權限:android.permission.RECEIVE_BOOT_COMPLETED。

7.2.發送自定義廣播

      發送标準廣播:步驟一:快速建立廣播接收器,複寫onReceiver方法,簡單Toast;

      發送有序廣播;将sendBroadcast(intent);改成 sendOrderedBroadcast(intent,null)。

7.3.使用本地廣播

      系統全局廣播可被任何程式接收到,不安全,Android引入一套本地廣播機制,使得發出廣播隻能在應用程式内部傳遞。而且廣播接收器隻能接收到來自本應用程式發出的廣播,安全性問題得以解決。優勢安全高效。LocalBroadcastManager的getInstance得到它的一個執行個體,注冊、登出都一樣,發出一條com.example.hzk.LOCAL_BROADCAST的廣播。

8.幹過什麼學生工作麼?

      班級團支書、義務維修站站長。

9.Java虛拟機的最大記憶體是多少?

      JVM虛拟機的預設記憶體使用大小為64MB,也就是你不更改的話,他的JVM記憶體使用大小就是64MB,如果超出這個記憶體使用限度,就會報java head space錯誤。可以使用-Xmx900m進行更改堆大小避免溢出。

10.對導師專利一作是怎麼看的?

       感恩感謝吧。

11.可以問我一個問題?

    需要提高的地方有哪些?