Android中高級工程師面試題


https://www.cnblogs.com/huangjialin/p/8657565.html(存在不少答案錯誤,可參照知識點復習,答案不可全信)  上

https://www.cnblogs.com/huangjialin/p/8657696.html  下  25、

 

https://www.cnblogs.com/huangjialin/p/8622506.html  面試經歷

 

19String為什么要設計成不可變的? 

1、字符串池的需求

字符串池是方法區(Method Area)中的一塊特殊的存儲區域。當一個字符串已經被創建並且該字符串在 池 中,該字符串的引用會立即返回給變量,而不是重新創建一個字符串再將引用返回給變量。如果字符串是可變的,那么改變一個引用(如: string2)的字符串將會導致另一個引用(如: string1)出現臟數據。

2、允許字符串緩存哈希碼

在java中常常會用到字符串的哈希碼,例如: HashMap 。String的不變性保證哈希碼始終一,因此,他可以不用擔心變化的出現。 這種方法意味着不必每次使用時都重新計算一次哈希碼——這樣,效率會高很多。

3、安全

String廣泛的用於java 類中的參數,如:網絡連接(Network connetion),打開文件(opening files )等等。如果String不是不可變的,網絡連接、文件將會被改變——這將會導致一系列的安全威脅。操作的方法本以為連接上了一台機器,但實際上卻不是由於反射中的參數都是字符串,同樣,也會引起一系列的安全問題。

 

39、如何控制某個方法允許並發訪問線程的個數?

semaphore.acquire() 請求一個信號量,這時候的信號量個數-1(一旦沒有可使用的信號量,也即信號量個數變為負數時,再次請求的時候就會阻塞,直到其他線程釋放了信號量)

semaphore.release() 釋放一個信號量,此時信號量個數+1

 

45、線程間操作List 

List list = Collections.synchronizedList(new ArrayList());  //封裝好的使用synchronized實現的線程安全List

 

53、死鎖的發生必須滿足以下四個條件

  • 互斥條件:一個資源每次只能被一個進程使用。
  • 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
  • 不可剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
  • 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。

避免死鎖最簡單的方法就是阻止循環等待條件,將系統中所有的資源設置標志位、排序,規定所有的進程申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。

56、什么是線程池,如何使用?

創建線程要花費昂貴的資源和時間,如果任務來了才創建線程那么響應時間會變長,而且一個進程能創建的線程數有限。為了避免這些問題,在程序啟動的時候就創建若干線程來響應處理,它們被稱為線程池,里面的線程叫工作線程。

從JDK1.5開始,Java API提供了Executors框架讓你可以創建不同的線程池。比如

單線程池:newSingleThreadExecutor,每次處理一個任務;

數目固定的線程池:newFixedThreadPool

緩存線程池:newCachedThreadPool,適合很多生存期短的任務的可擴展線程池)

周期性執行線程池:newScheduledThreadPool

58、線程間通信

Android中主要是使用handler。handler通過調用sendmessage方法,將保存消息的Message發送到Messagequeue中,而looper對象不斷的調用loop方法,從messageueue中取出message,交給同一個handler處理,從而完成線程間通信。

 

59.Binder的工作機制

 直觀來說,Binder是Android中的一個類,它實現了IBinder接口,從IPC的角度來說,Binder是Android中的一種跨進程通信的一種方式,同時還可以理解為是一種虛擬的物理設備,它的設備驅動是/dev/binder/。從Framework角度來說,Binder是ServiceManager的橋梁。從應用層來說,Binder是客戶端和服務端進行通信的媒介。

我們先來了解一下這個類中每個方法的含義:

DESCRIPTOR:Binder的唯一標識,一般用於當前Binder的類名表示。

asInterface(android.os.IBinder obj):用於將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉化過程是區分進程的,如果客戶端和服務端位於同一個進程,那么這個方法返回的是服務端的stub對象本身,否則返回的是系統封裝后的Stub.proxy對象。 

asBinder():用於返回當前Binder對象

onTransact:該方法運行在服務端的Binder線程池中,當客戶端發起跨進程通信請求的時候,遠程請求通過系統底層封裝后交給該方法處理。注意這個方法public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服務端通過code可以確定客戶端所請求的目標方法是什么,接着從data中取出目標方法所需的參數,然后執行目標方法。當目標方法執行完畢后,就向reply中寫入返回值。這個方法的執行過程就是這樣的。如果這個方法返回false,客戶端是會請求失敗的,所以我們可以在這個方法中做一些安全驗證。 

Binder的工作機制但是要注意一些問題:

1、當客戶端發起請求時,由於當前線程會被掛起,直到服務端返回數據,如果這個遠程方法很耗時的話,那么是不能夠在UI線程,也就是主線程中發起這個遠程請求的。

2、由於Service的Binder方法運行在線程池中,所以Binder方法不管是耗時還是不耗時都應該采用同步的方式,因為它已經運行在一個線程中了。

 

60、Android中性能優化

由於手機硬件的限制,內存和CPU都無法像pc一樣具有超大的內存,Android手機上,過多的使用內存,會容易導致oom,過多的使用CPU資源,會導致手機卡頓,甚至導致anr。我主要是從一下幾部分進行優化:

App啟動優化,布局優化,繪制優化,內存泄漏優化,響應速度優化,listview優化,圖片優化,線程優化,電池優化,網絡優化

 

App啟動優化
App啟動的方式有三種:
冷啟動:App沒有啟動過或App進程被killed, 系統中不存在該App進程, 此時啟動App即為冷啟動。
熱啟動:熱啟動意味着你的App進程只是處於后台, 系統只是將其從后台帶到前台, 展示給用戶。
暖啟動:介於冷啟動和熱啟動之間, 一般來說在以下兩種情況下發生:
(1)用戶back退出了App, 然后又啟動. App進程可能還在運行, 但是activity需要重建。
(2)用戶退出App后, 系統可能由於內存原因將App殺死, 進程和activity都需要重啟, 但是可以在onCreate中將被動殺死所保存的狀態(onSaveInstanceState)恢復。
 
優化(針對冷啟動):
Application的onCreate(特別是第三方SDK初始化),首屏Activity的渲染都不要進行耗時操作,如果有,就可以放到子線程或者IntentService中
 

布局優化:工具 hierarchyviewer,解決方式:

1、刪除無用的空間和層級。

2、選擇性能較低的viewgroup,如Relativelayout,如果可以選擇Relativelayout也可以使用LinearLayout,就優先使用LinearLayout,因為相對來說Relativelayout功能較為復雜,會占用更多的CPU資源。

3、使用標簽<include/>重用布局,<Merge/>減少層級,<viewStub/>進行預加載,使用的時候才加載。

 

繪制優化

繪制優化指view在ondraw方法中避免大量的耗時操作,由於ondraw方法可能會被頻繁的調用。

1、ondraw方法中不要創建新的局部變量,ondraw方法被頻繁的調用,很容易引起GC。

2、ondraw方法不要做耗時操作。

 

內存優化(部分)

1)少用枚舉,枚舉占用空間大。

2)使用Android特有的數據結構,如SparseArray來代替hashMap。

3)適當的使用軟引用和弱引用。

 

響應優化

ANR方面:主線程不能做耗時操作,觸摸事件5s,廣播10s,service20s。

UI刷新方面:Android系統每隔16ms會發出VSYNC信號重繪我們的界面(Activity)。
頁面卡頓的原因:
(1)過於復雜的布局.
(2)UI線程的復雜運算
(3)頻繁的GC,導致頻繁GC有兩個原因:  1、內存抖動, 即大量的對象被創建又在短時間內馬上被釋放 -->對象池  .2、瞬間產生大量的對象會嚴重占用內存區域  -->避免。

 

listview優化

1、getview方法中避免耗時操作。

2、view的復用和viewholder的使用。

3、滑動不適合開啟異步加載。

4、分頁處理數據。

 

圖片優化
(1)對圖片本身進行操作。盡量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource來設置一張大圖,因為這些方法在完成decode后,最終都是通過java層的createBitmap來完成的,需要消耗更多內存.
(2)等比例壓縮圖片。另外,圖片進行縮放的比例,SDK中建議其值是2的指數值,值越大會導致圖片不清晰。
(3)不用的圖片記得調用圖片的recycle()方法
(4)圖片加載使用三級緩存。

 

線程優化

線程優化的思想是使用線程池來管理和復用線程,避免程序中有大量的Thread,同時可以控制線程的並發數,避免相互搶占資源而導致線程阻塞。 

電池使用優化(使用工具:Batterystats & bugreport)
(1)優化網絡請求
(2)定位中使用GPS, 請記得及時關閉
(3)JobService喚醒頻率及條件控制
(4)減少IO交互
 
網絡優化(網絡連接對用戶的影響:流量,電量,用戶等待)可在Android studio下方logcat旁邊那個工具Network Monitor檢測
API設計:App與Server之間的API設計要考慮網絡請求的頻次, 資源的狀態等. 以便App可以以較少的請求來完成業務需求和界面的展示.
Gzip壓縮: 使用Gzip來壓縮request和response, 減少傳輸數據量, 從而減少流量消耗.
圖片的Size:可以在獲取圖片時 告知服務器需要的圖片的寬高, 以便服務器給出合適的圖片, 避免浪費.
網絡緩存:適當的 緩存, 既可以讓我們的應用看起來更快, 也能避免一些不必要的流量消耗.

 

4、廣播是分為有序廣播和無序廣播。

 

5、HttpClient與HttpUrlConnection的區別 
此處延伸:Volley里用的哪種請求方式(2.3前HttpClient,2.3后HttpUrlConnection)
 
首先HttpClient和HttpUrlConnection 這兩種方式都支持Https協議,都是以流的形式進行上傳或者下載數據,也可以說是以流的形式進行數據的傳輸,還有ipv6,以及連接池等功能。HttpClient這個擁有非常多的API,所以如果想要進行擴展的話,並且不破壞它的兼容性的話,很難進行擴展,也就是這個原因,Google在Android6.0的時候,直接就棄用了這個HttpClient.
而HttpUrlConnection相對來說就是比較輕量級了,API比較少,容易擴展,並且能夠滿足Android大部分的數據傳輸。比較經典的一個框架volley,在2.3版本以前都是使用HttpClient,在2.3以后就使用了HttpUrlConnection。
 
7、進程保活(不死進程)
此處延伸:進程的優先級是什么
當前業界的Android進程保活手段主要分為** 黑、白、灰 **三種,其大致的實現思路如下:
黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)
白色保活:啟動前台Service
灰色保活:利用系統的漏洞啟動前台Service
黑色保活
所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:
場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3
場景3:假如你手機里裝了支付寶、淘寶、天貓、UC等阿里系的app,那么你打開任意一個阿里系的app后,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)
白色保活
白色保活手段非常簡單,就是調用系統api啟動一個前台的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行着,哪怕當前的app退到了后台。如下方的LBE和QQ音樂這樣:
灰色保活
灰色保活,這種保活手段是應用范圍最廣泛。它是利用系統的漏洞來啟動一個前台的Service進程,與普通的啟動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行着一個后台Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行着一個前台進程(因為看不到Notification),但你的進程優先級又是高於普通后台進程的。那么如何利用系統的漏洞呢,大致的實現思路和代碼如下:
思路一:API < 18,啟動前台Service時直接傳入new Notification();
思路二:API >= 18,同時啟動兩個id相同的前台Service,然后再將后啟動的Service做stop處理
熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到后台時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,后台緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low Memory Killer ,它是基於Linux內核的 OOM Killer(Out-Of-Memory killer)機制誕生。
進程的重要性,划分5級:
前台進程 (Foreground process)
可見進程 (Visible process)
服務進程 (Service process)
后台進程 (Background process)
空進程 (Empty process)
 
了解完 Low Memory Killer,再科普一下oom_adj。什么是oom_adj?它是linux內核分配給每個系統進程的一個值,代表進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收。對於oom_adj的作用,你只需要記住以下幾點即可:
進程的oom_adj越大,表示此進程優先級越低,越容易被殺回收;越小,表示進程優先級越高,越不容易被殺回收
普通app進程的oom_adj>=0,系統進程的oom_adj才可能<0
有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,為了盡量避免被殺,還是老老實實去做好優化工作吧。
所以,進程保活的根本方案終究還是回到了性能優化上,進程永生不死終究是個徹頭徹尾的偽命題!
 
8、講解一下Context 
Context是一個抽象基類。在翻譯為上下文,也可以理解為環境,是提供一些程序的運行環境基礎信息。Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各種承擔着不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場景下,我們只能使用Activity類型的Context,否則將會出錯。
 
getApplicationContext()和getApplication()方法得到的對象都是同一個application對象,只是對象的類型不一樣。
Context數量 = Activity數量 + Service數量 + 1 (1為Application)
 
13、保存Activity狀態
onSaveInstanceState(Bundle)會在activity轉入后台狀態之前被調用,也就是onStop()方法之前,onPause方法之后被調用;
 
15、Android中跨進程通訊的幾種方式
Android 跨進程通信,像intent,contentProvider,廣播,service都可以跨進程通信。
intent:這種跨進程方式並不是訪問內存的形式,它需要傳遞一個uri,比如說打電話。
contentProvider:這種形式,是使用數據共享的形式進行數據共享。
service:遠程服務,aidl
廣播
 
16、AIDL理解
AIDL: 每一個進程都有自己的Dalvik VM實例,都有自己的一塊獨立的內存,都在自己的內存上存儲自己的數據,執行着自己的操作,都在自己的那片狹小的空間里過完自己的一生。而aidl就類似與兩個進程之間的橋梁,使得兩個進程之間可以進行數據的傳輸,跨進程通信有多種選擇,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系統資源比較多,如果是頻繁的跨進程通信的話顯然是不可取的;Messenger 進行跨進程通信時請求隊列是同步進行的,無法並發執行。
 
Binde機制簡單理解:
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程序組成的,其中Client,service,Service Manager運行在用戶空間,Binder驅動程序是運行在內核空間的。而Binder就是把這4種組件粘合在一塊的粘合劑,其中核心的組件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程序和Service Manager提供的基礎設施上實現C/S 之間的通信。其中Binder驅動程序提供設備文件/dev/binder與用戶控件進行交互,
Client、Service,Service Manager通過open和ioctl文件操作相應的方法與Binder驅動程序進行通信。而Client和Service之間的進程間通信是通過Binder驅動程序間接實現的。而Binder Manager是一個守護進程,用來管理Service,並向Client提供查詢Service接口的能力。
 

19、QQ空間熱修復的原理
我們知道Java虛擬機 —— JVM 是加載類的class文件的,而Android虛擬機——Dalvik/ART VM 是加載類的dex文件,
而他們加載類的時候都需要ClassLoader,ClassLoader有一個子類BaseDexClassLoader,而 BaseDexClassLoader下有一個
數組——DexPathList,是用來存放dex文件,當BaseDexClassLoader通過調用findClass方法時,實際上就是遍歷數組,
找到相應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,並且是在舊的dex的前面,
所以就會優先被取出來並且return返回。
 
25、HybridApp WebView和JS交互
(一)Android調用JS的代碼
1. 通過WebView的loadUrl(),使用該方法比較簡潔,方便。但是效率比較低,獲取返回值比較困難。
2. 通過WebView的evaluateJavascript(),可以直接傳入結果的回調方法。該方法效率高,但是4.4以上的版本才支持,4.4以下版本不支持。所以建議兩者混合使用。
 
(二)JS調用Android的代碼
1.通過WebView的addJavascriptInterface()進行對象映射
該方法使用簡單,僅將Android對象和JS對象映射即可,但是存在比較大的漏洞。漏洞產生原因是:當JS拿到Android這個對象后,就 可以調用這個Android對象中所有的方法,包括系統類(java.lang.Runtime 類),從而進行任意代碼執行。
解決方式:
(1)Google 在Android 4.2 版本中規定對被調用的函數以 @JavascriptInterface進行注解從而避免漏洞攻擊。
(2)在Android 4.2版本之前采用攔截prompt()進行漏洞修復
2.通過 WebViewClient 的shouldOverrideUrlLoading ()方法回調攔截 url
這種方式的優點:不存在方式1的漏洞; 缺點:JS獲取Android方法的返回值復雜。(ios主要用的是這個方式)
(1)Android通過 WebViewClient 的回調方法shouldOverrideUrlLoading ()攔截 url
(2)解析該 url 的協議
(3)如果檢測到是預先約定好的協議,就調用相應方法
3. 通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt()消息
這種方式的優點:不存在方式1的漏洞;缺點:JS獲取Android方法的返回值復雜。
 https://www.cnblogs.com/zhangqie/p/6387433.html
https://www.cnblogs.com/itpepe/p/4882012.html


免責聲明!

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



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