如何才能通過一線網際網路公司面試?請掌握這些!

想念你的Android發表於2019-02-20

1.如何匯入外部資料庫?

把原資料庫包括在專案原始碼的 res/raw

android系統下資料庫應該存放在 /data/data/com.

.

(package name)/ 目錄下,所以我們需要做的是把已有的資料庫傳入那個目錄下.操作方法是用FileInputStream讀取原資料庫,再用FileOutputStream把讀取到的東西寫入到那個目錄.


2.本地廣播和全域性廣播有什麼差別?

因廣播資料在本應用範圍內傳播,不用擔心隱私資料洩露的問題。
不用擔心別的應用偽造廣播,造成安全隱患。
相比在系統內傳送全域性廣播,它更高效。


3.intentService作用是什麼,AIDL解決了什麼問題-小米

生成一個預設的且與主執行緒互相獨立的工作者執行緒來執行所有傳送至onStartCommand() 方法的Intetnt。

生成一個工作佇列來傳送Intent物件給你的onHandleIntent()方法,同一時刻只傳送一個Intent物件,這樣一來,你就不必擔心多執行緒的問題。在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去呼叫stopSelf()方法來停止。

該服務提供了一個onBind()方法的預設實現,它返回null

提供了一個onStartCommand()方法的預設實現,它將Intent先傳送至工作佇列,然後從工作佇列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent對相應的處理。

AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成可以在Android裝置上兩個程式之間進行程式間通訊(interprocess communication, IPC)的程式碼。如果在一個程式中(例如Activity)要呼叫另一個程式中(例如Service)物件的操作,就可以使用AIDL生成可序列化的引數。
AIDL IPC機制是面向介面的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞資料。

4.Ubuntu編譯安卓系統-百度

  1. 進入原始碼根目錄
  2. . build/envsetup.sh
  3. lunch
  4. full(編譯全部)
  5. userdebug(選擇編譯版本)
  6. make -j8(開啟8個執行緒編譯)

5.LaunchMode應用場景-百度-小米-樂視

standard,建立一個新的Activity。

singleTop,棧頂不是該型別的Activity,建立一個新的Activity。否則,onNewIntent。

singleTask,回退棧中沒有該型別的Activity,建立Activity,否則,onNewIntent+ClearTop。

注意:

  1. 設定了"singleTask"啟動模式的Activity,它在啟動的時候,會先在系統中查詢屬性值affinity等於它的屬性值taskAffinity的Task存在; 如果存在這樣的Task,它就會在這個Task中啟動,否則就會在新的任務棧中啟動。因此, 如果我們想要設定了"singleTask"啟動模式的Activity在新的任務中啟動,就要為它設定一個獨立的taskAffinity屬性值。
  2. 如果設定了"singleTask"啟動模式的Activity不是在新的任務中啟動時,它會在已有的任務中檢視是否已經存在相應的Activity例項, 如果存在,就會把位於這個Activity例項上面的Activity全部結束掉,即最終這個Activity 例項會位於任務的Stack頂端中。
  3. 在一個任務棧中只有一個”singleTask”啟動模式的Activity存在。他的上面可以有其他的Activity。這點與singleInstance是有區別的。

singleInstance,回退棧中,只有這一個Activity,沒有其他Activity。

singleTop適合接收通知啟動的內容顯示頁面。

例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都開啟一個新聞內容頁面是很煩人的。

singleTask適合作為程式入口點。

例如瀏覽器的主介面。不管從多少個應用啟動瀏覽器,只會啟動主介面一次,其餘情況都會走onNewIntent,並且會清空主介面上面的其他頁面。

singleInstance應用場景:

鬧鈴的響鈴介面。 你以前設定了一個鬧鈴:上午6點。在上午5點58分,你啟動了鬧鈴設定介面,並按 Home 鍵回桌面;在上午5點59分時,你在微信和朋友聊天;在6點時,鬧鈴響了,並且彈出了一個對話方塊形式的 Activity(名為 AlarmAlertActivity) 提示你到6點了(這個 Activity 就是以 SingleInstance 載入模式開啟的),你按返回鍵,回到的是微信的聊天介面,這是因為 AlarmAlertActivity 所在的 Task 的棧只有他一個元素, 因此退出之後這個 Task 的棧空了。如果是以 SingleTask 開啟 AlarmAlertActivity,那麼當鬧鈴響了的時候,按返回鍵應該進入鬧鈴設定介面。

6.多執行緒-360

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable),View.postDelay(Runnable,long)
  • Handler
  • AsyncTask

7.Handler,Thread和HandlerThread的差別-小米

從Android中Thread(java.lang.Thread -> java.lang.Object)描述可以看出,Android的Thread沒有對Java的Thread做任何封裝,但是Android提供了一個繼承自Thread的類HandlerThread(android.os.HandlerThread -> java.lang.Thread),這個類對Java的Thread做了很多便利Android系統的封裝。

android.os.Handler可以通過Looper物件例項化,並執行於另外的執行緒中,Android提供了讓Handler執行於其它執行緒的執行緒實現,也是就HandlerThread。HandlerThread物件start後可以獲得其Looper物件,並且使用這個Looper物件例項Handler。

8.什麼情況導致記憶體洩漏-美團

8.1.資源物件沒關閉造成的記憶體洩漏

描述:
資源性物件比如(Cursor,File檔案等)往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時回收記憶體。它們的緩衝不僅存在於 java虛擬機器內,還存在於java虛擬機器外。如果我們僅僅是把它的引用設定為null,而不關閉它們,往往會造成記憶體洩漏。因為有些資源性物件,比如 SQLiteCursor(在解構函式finalize(),如果我們沒有關閉它,它自己會調close()關閉),如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。因此對於資源性物件在不使用的時候,應該呼叫它的close()函式,將其關閉掉,然後才置為null.在我們的程式退出時一定要確保我們的資源性物件已經關閉。
程式中經常會進行查詢資料庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小,對記憶體的消耗不容易被發現,只有在常時間大量操作的情況下才會復現記憶體問題,這樣就會給以後的測試和問題排查帶來困難和風險。

8.2.構造Adapter時,沒有使用快取的convertView

描述:
以構造ListView的BaseAdapter為例,在BaseAdapter中提供了方法:
public View getView(int position, ViewconvertView, ViewGroup parent)
來向ListView提供每一個item所需要的view物件。初始時ListView會從BaseAdapter中根據當前的螢幕佈局例項化一定數量的 view物件,同時ListView會將這些view物件快取起來。當向上滾動ListView時,原先位於最上面的list item的view物件會被回收,然後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參View convertView就是被快取起來的list item的view物件(初始化時快取中沒有view物件則convertView是null)。由此可以看出,如果我們不去使用 convertView,而是每次都在getView()中重新例項化一個View物件的話,即浪費資源也浪費時間,也會使得記憶體佔用越來越大。 ListView回收list item的view物件的過程可以檢視:
android.widget.AbsListView.java --> voidaddScrapView(View scrap) 方法。
示例程式碼:

public View getView(int position, ViewconvertView, ViewGroup parent) {
View view = new Xxx(...); 
... ... 
return view; 
} 複製程式碼

修正示例程式碼:

public View getView(int position, ViewconvertView, ViewGroup parent) {
View view = null; 
if (convertView != null) { 
view = convertView; 
populate(view, getItem(position)); 
... 
} else { 
view = new Xxx(...); 
... 
} 
return view; 
} 複製程式碼

8.3.Bitmap物件不在使用時呼叫recycle()釋放記憶體

描述:
有時我們會手工的操作Bitmap物件,如果一個Bitmap物件比較佔記憶體,當它不在被使用的時候,可以呼叫Bitmap.recycle()方法回收此物件的畫素所佔用的記憶體,但這不是必須的,視情況而定。可以看一下程式碼中的註釋:

/** 
•Free up the memory associated with thisbitmap's pixels, and mark the 
•bitmap as "dead", meaning itwill throw an exception if getPixels() or 
•setPixels() is called, and will drawnothing. This operation cannot be 
•reversed, so it should only be called ifyou are sure there are no 
•further uses for the bitmap. This is anadvanced call, and normally need 
•not be called, since the normal GCprocess will free up this memory when 
•there are no more references to thisbitmap. 
*/ 複製程式碼

8.4.試著使用關於application的context來替代和activity相關的context

這是一個很隱晦的記憶體洩漏的情況。有一種簡單的方法來避免context相關的記憶體洩漏。最顯著地一個是避免context逃出他自己的範圍之外。使用Application context。這個context的生存週期和你的應用的生存週期一樣長,而不是取決於activity的生存週期。如果你想保持一個長期生存的物件,並且這個物件需要一個context,記得使用application物件。你可以通過呼叫 Context.getApplicationContext() or Activity.getApplication()來獲得

8.5.註冊沒取消造成的記憶體洩漏

一些Android程式可能引用我們的Anroid程式的物件(比如序號產生器制)。即使我們的Android程式已經結束了,但是別的引用程式仍然還有對我們的Android程式的某個物件的引用,洩漏的記憶體依然不能被垃圾回收。呼叫registerReceiver後未呼叫unregisterReceiver。

比如:假設我們希望在鎖屏介面(LockScreen)中,監聽系統中的電話服務以獲取一些資訊(如訊號強度等),則可以在LockScreen中定義一個 PhoneStateListener的物件,同時將它註冊到TelephonyManager服務中。對於LockScreen物件,當需要顯示鎖屏介面的時候就會建立一個LockScreen物件,而當鎖屏介面消失的時候LockScreen物件就會被釋放掉。

但是如果在釋放 LockScreen物件的時候忘記取消我們之前註冊的PhoneStateListener物件,則會導致LockScreen無法被垃圾回收。如果不斷的使鎖屏介面顯示和消失,則最終會由於大量的LockScreen物件沒有辦法被回收而引起OutOfMemory,使得system_process 程式掛掉。

雖然有些系統程式,它本身好像是可以自動取消註冊的(當然不及時),但是我們還是應該在我們的程式中明確的取消註冊,程式結束時應該把所有的註冊都取消掉。

8.6.集合中物件沒清理造成的記憶體洩漏

我們通常把一些物件的引用加入到了集合中,當我們不需要該物件時,並沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。

9.什麼情況導致oom-樂視-美團

1)使用更加輕量的資料結構
2)Android裡面使用Enum
3)Bitmap物件的記憶體佔用
4)更大的圖片
5)onDraw方法裡面執行物件的建立
6)StringBuilder

10.Service與Activity之間通訊的幾種方式

  • 通過Binder物件
  • 通過broadcast(廣播)的形式

11.如何保證service在後臺不被Kill

一.onStartCommand方法,返回START_STICKY

  1. START_STICKY
    在執行onStartCommand後service程式被kill後,那將保留在開始狀態,但是不保留那些傳入的intent。不久後service就會再次嘗試重新建立,因為保留在開始狀態,在建立 service後將保證呼叫onstartCommand。如果沒有傳遞任何開始命令給service,那將獲取到null的intent。

  2. START_NOT_STICKY
    在執行onStartCommand後service程式被kill後,並且沒有新的intent傳遞給它。Service將移出開始狀態,並且直到新的明顯的方法(startService)呼叫才重新建立。因為如果沒有傳遞任何未決定的intent那麼service是不會啟動,也就是期間onstartCommand不會接收到任何null的intent。

  3. START_REDELIVER_INTENT
    在執行onStartCommand後service程式被kill後,系統將會再次啟動service,並傳入最後一個intent給onstartCommand。直到呼叫stopSelf(int)才停止傳遞intent。如果在被kill後還有未處理好的intent,那被kill後服務還是會自動啟動。因此onstartCommand不會接收到任何null的intent。

二、提升service優先順序

在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數字越小則優先順序越低,同時適用於廣播。

三、提升service程式優先順序

Android中的程式是託管的,當系統程式空間緊張的時候,會依照優先順序自動進行程式的回收。Android將程式分為6個等級,它們按優先順序順序由高到低依次是:

  1. 前臺程式( FOREGROUND_APP)
  2. 可視程式(VISIBLE_APP )
  3. 次要服務程式(SECONDARY_SERVER )
  4. 後臺程式 (HIDDEN_APP)
  5. 內容供應節點(CONTENT_PROVIDER)
  6. 空程式(EMPTY_APP)

當service執行在低記憶體的環境時,將會kill掉一些存在的程式。因此程式的優先順序將會很重要,可以使用startForeground 將service放到前臺狀態。這樣在低記憶體時被kill的機率會低一些。

四、onDestroy方法裡重啟service

service +broadcast 方式,就是當service走ondestory的時候,傳送一個自定義的廣播,當收到廣播的時候,重新啟動service;

五、Application加上Persistent屬性

六、監聽系統廣播判斷Service狀態

通過系統的一些廣播,比如:手機重啟、介面喚醒、應用狀態改變等等監聽並捕獲到,然後判斷我們的Service是否還存活,別忘記加許可權啊。

12.Android動畫框架實現原理

Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製檢視時View所在的ViewGroup中的drawChild函式獲取該View的Animation的Transformation值,然後呼叫canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續呼叫invalidate()函式,啟動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函式所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能相應事件

13.Android為每個應用程式分配的記憶體大小是多少-美團

android程式記憶體一般限制在16M,也有的是24M。近幾年手機發展較快,一般都會分配兩百兆左右,和具體機型有關


14.優化自定義view百度-樂視-小米

為了加速你的view,對於頻繁呼叫的方法,需要儘量減少不必要的程式碼。先從onDraw開始,需要特別注意不應該在這裡做記憶體分配的事情,因為它會導致GC,從而導致卡頓。在初始化或者動畫間隙期間做分配記憶體的動作。不要在動畫正在執行的時候做記憶體分配的事情。

你還需要儘可能的減少onDraw被呼叫的次數,大多數時候導致onDraw都是因為呼叫了invalidate().因此請儘量減少呼叫invaildate()的次數。如果可能的話,儘量呼叫含有4個引數的invalidate()方法而不是沒有引數的invalidate()。沒有引數的invalidate會強制重繪整個view。

另外一個非常耗時的操作是請求layout。任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有衝突的值,它會需要重新計算好幾次。另外需要儘量保持View的層級是扁平化的,這樣對提高效率很有幫助。

如果你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操作。與內建的view不同,自定義的view可以使得程式僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展示瞭如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據他自身的layout法則,直接設定它們的大小。

15.Android屬性動畫特性-樂視-小米

如果你的需求中只需要對View進行移動、縮放、旋轉和淡入淡出操作,那麼補間動畫確實已經足夠健全了。但是很顯然,這些功能是不足以覆蓋所有的場景的,一旦我們的需求超出了移動、縮放、旋轉和淡入淡出這四種對View的操作,那麼補間動畫就不能再幫我們忙了,也就是說它在功能和可擴充套件方面都有相當大的侷限性,那麼下面我們就來看看補間動畫所不能勝任的場景。

注意上面我在介紹補間動畫的時候都有使用“對View進行操作”這樣的描述,沒錯,補間動畫是隻能夠作用在View上的。也就是說,我們可以對一個Button、TextView、甚至是LinearLayout、或者其它任何繼承自View的元件進行動畫操作,但是如果我們想要對一個非View的物件進行動畫操作,抱歉,補間動畫就幫不上忙了。可能有的朋友會感到不能理解,我怎麼會需要對一個非View的物件進行動畫操作呢?這裡我舉一個簡單的例子,比如說我們有一個自定義的View,在這個View當中有一個Point物件用於管理座標,然後在onDraw()方法當中就是根據這個Point物件的座標值來進行繪製的。也就是說,如果我們可以對Point物件進行動畫操作,那麼整個自定義View的動畫效果就有了。顯然,補間動畫是不具備這個功能的,這是它的第一個缺陷。

然後補間動畫還有一個缺陷,就是它只能夠實現移動、縮放、旋轉和淡入淡出這四種動畫操作,那如果我們希望可以對View的背景色進行動態地改變呢?很遺憾,我們只能靠自己去實現了。說白了,之前的補間動畫機制就是使用硬編碼的方式來完成的,功能限定死就是這些,基本上沒有任何擴充套件性可言。

最後,補間動畫還有一個致命的缺陷,就是它只是改變了View的顯示效果而已,而不會真正去改變View的屬性。什麼意思呢?比如說,現在螢幕的左上角有一個按鈕,然後我們通過補間動畫將它移動到了螢幕的右下角,現在你可以去嘗試點選一下這個按鈕,點選事件是絕對不會觸發的,因為實際上這個按鈕還是停留在螢幕的左上角,只不過補間動畫將這個按鈕繪製到了螢幕的右下角而已。

16.Activity Window View三者的差別,fragment的特點-360

Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示檢視)
LayoutInflater像剪刀,Xml配置像窗花圖紙。

  1. 在Activity中呼叫attach,建立了一個Window
  2. 建立的window是其子類PhoneWindow,在attach中建立PhoneWindow
  3. 在Activity中呼叫setContentView(R.layout.xxx)
  4. 其中實際上是呼叫的getWindow().setContentView()
  5. 呼叫PhoneWindow中的setContentView方法
  6. 建立ParentView:作為ViewGroup的子類,實際是建立的DecorView(作為FramLayout的子類)
  7. 將指定的R.layout.xxx進行填充,通過佈局填充器進行填充【其中的parent指的就是DecorView】
  8. 呼叫到ViewGroup
  9. 呼叫ViewGroup的removeAllView(),先將所有的view移除掉
  10. 新增新的view:addView()

Fragment 特點

  • Fragment可以作為Activity介面的一部分組成出現;
  • 可以在一個Activity中同時出現多個Fragment,並且一個Fragment也可以在多個Activity中使用;
  • 在Activity執行過程中,可以新增、移除或者替換Fragment;
  • Fragment可以響應自己的輸入事件,並且有自己的生命週期,它們的生命週期會受宿主Activity的生命週期影響。

17.View重新整理機制-百度-美團

由ViewRoot物件的performTraversals()方法呼叫draw()方法發起繪製該View樹,值得注意的是每次發起繪圖時,並不會重新繪製每個View樹的檢視,而只會重新繪製那些“需要重繪”的檢視,View類內部變數包含了一個標誌位DRAWN,當該檢視需要重繪時,就會為該View新增該標誌位。

呼叫流程 :

mView.draw()開始繪製,draw()方法實現的功能如下:

  1. 繪製該View的背景
  2. 為顯示漸變框做一些準備操作(見5,大多數情況下,不需要改漸變框)
  3. 呼叫onDraw()方法繪製檢視本身 (每個View都需要過載該方法,ViewGroup不需要實現該方法)
  4. 呼叫dispatchDraw ()方法繪製子檢視(如果該View型別不為ViewGroup,即不包含子檢視,不需要過載該方法)值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw ()的功能實現,應用程式一般不需要重寫該方法,但可以過載父類函式實現具體的功能。


相關文章