android架構師要換工作? 還不來看看面試題?
4 人贊了文章
前幾天整理了Java面試題集合,今天再來整理下Android相關的面試題集合.如果你希望能得到最新的消息,可以關注 github.com/closedevice…,我會不斷的增加和修正相關問題的描述.
基礎談談Activity的生命周期介紹不同場景下Activity生命周期的變化過程
- 啟動Activity: onCreate()—>onStart()—>onResume(),Activity進入運行狀態。
- Activity退居後台: 當前Activity轉到新的Activity界面或按Home鍵回到主屏: onPause()—>onStop(),進入停滯狀態。
- Activity返回前台: onRestart()—>onStart()—>onResume(),再次回到運行狀態。
- Activity退居後台,且系統內存不足, 系統會殺死這個後台狀態的Activity,若再次回到這個Activity,則會走onCreate()–>onStart()—>onResume()
- 鎖定屏與解鎖屏幕 只會調用onPause(),而不會調用onStop方法,開屏後則調用onResume()
Activity銷毀但Task如果沒有銷毀掉,當Activity重啟時這個AsyncTask該如何解決?
還是屏幕旋轉這個例子,在重建Activity的時候,會回調Activity.onRetainNonConfigurationInstance()
重新傳遞一個新的對象給AsyncTask,完成引用的更新Asynctask為什麼要設置為只能夠一次任務考慮到線程安全問題若Activity已經銷毀,此時AsynTask執行完並返回結果,會報異常么?當一個App旋轉時,整個Activity會被銷毀和重建。當Activity重啟時,AsyncTask中對該Activity的引用是無效的,因此onPostExecute()就不會起作用,若AsynTask正在執行,折會報 view not attached to window manager 異常同樣也是生命周期的問題,在 Activity 的onDestory()方法中調用Asyntask.cancal方法,讓二者的生命周期同步內存不足時,系統會殺死後台的Activity,如果需要進行一些臨時狀態的保存,在哪個方法進行
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並不是生命周期方法,不同於 onCreate()、onPause()等生命周期方法,它們並不一定會被觸發。當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷毀一個Activity,onSaveInstanceState() 會被調用。但是當用戶主動去銷毀一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。除非該activity是被用戶主動銷毀的,通常onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
介紹Activity 四中launchMode:- standard
- singleTop
- singleTask
- singleInstance
我們可以在AndroidManifest.xml配置的android:launchMode屬性為以上四種之一。
1. standard standard模式是默認的啟動模式,不用為配置android:launchMode屬性即可,當然也可以指定值為standard。standard啟動模式,不管有沒有已存在的實例,都生成新的實例。 2. singleTop 我們在上面的基礎上為指定屬性android:launchMode=」singleTop」,系統就會按照singleTop啟動模式處理跳轉行為。跳轉時系統會先在棧結構中尋找是否有一個Activity實例正位於棧頂,如果有則不再生成新的,而是直接使用。如果系統發現存在有Activity實例,但不是位於棧頂,重新生成一個實例。 這就是singleTop啟動模式,如果發現有對應的Activity實例正位於棧頂,則重複利用,不再生成新的實例。 3. singleTask 如果發現有對應的Activity實例,則使此Activity實例之上的其他Activity實例統統出棧,使此Activity實例成為棧頂對象,顯示到幕前。 4. singleInstance 這種啟動模式比較特殊,因為它會啟用一個新的棧結構,將Acitvity放置於這個新的棧結構中,並保證不再有其他Activity實例進入。LaunchMode使用場景- singleTop適合接收通知啟動的內容顯示頁面。 例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都打開一個新聞內容頁面是很煩人的。
- singleTask適合作為程序入口點。
例如瀏覽器的主界面。不管從多少個應用啟動瀏覽器,只會啟動主界面一次,其餘情況都會走onNewIntent,並且會清空主界面上面的其他頁面。
- singleInstance應用場景: 鬧鈴的響鈴界面。 你以前設置了一個鬧鈴:上午6點。在上午5點58分,你啟動了鬧鈴設置界面,並按 Home 鍵回桌面;在上午5點59分時,你在微信和朋友聊天;在6點時,鬧鈴響了,並且彈出了一個對話框形式的 Activity(名為 AlarmAlertActivity) 提示你到6點了(這個 Activity 就是以 SingleInstance 載入模式打開的),你按返回鍵,回到的是微信的聊天界面,這是因為 AlarmAlertActivity 所在的 Task 的棧只有他一個元素, 因此退出之後這個 Task 的棧空了。如果是以 SingleTask 打開 AlarmAlertActivity,那麼當鬧鈴響了的時候,按返回鍵應該進入鬧鈴設置界面。
如何把一個應用設置為系統應用
- Android設置是Debug版本,且root,直接將該apk用adb工具push到system/app或system/priv-app
- 如果是非root設備,需要編譯後燒寫鏡像
- 有些許可權(如WRITE_SECURE_SETTINGS)不開放給第三方應用,只能在對應設備源碼總編譯然後作為系統app使用
Activity,Window,View三者的聯繫和區別?
Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖) LayoutInflater像剪刀,Xml配置像窗花圖紙。Activity啟動Service的兩種方式- startService:生命周期和調用者不同.啟動後若調用者未調用stopService而直接退出,Service仍會運行
- bindService:生命周期與調用者綁定,調用者一旦退出,Service就會調用unBind->onDestory
Android兩個應用能在同一個任務棧嗎?
棧一般以包名命名,兩個應用的簽名和udid要相同Fragment是什麼?你曾經遇到哪些有關Fragment的問題?Fragment可以作為Activity界面的一部分組成出現.一個Activity中可以同時出現多個Fragment,並一個Fragment也可以在多個Activity中使用.在Activity中可以添加,刪除,替換Fragment.Fragment可以響應自己的輸入時間,並且有自己的生命周期,但其生命周期收Activity影響.Fragment生命周期
如何實現Activity窗口快速變暗* @param from>=0&&from<=1.0f * @param to>=0&&to<=1.0f * * */ private void dimBackground(final float from, final float to) { final Window window = getWindow();ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
valueAnimator.setDuration(500); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { WindowManager.LayoutParams params = window.getAttributes(); params.alpha = (Float) animation.getAnimatedValue(); window.setAttributes(params); } });valueAnimator.start();
}Fragment重疊問題是否使用過本地廣播,和全局廣播有什麼區別?本地廣播在本應用範圍內傳播,不用擔心隱私數據泄露,不用擔心別的應用偽造廣播.相比全局廣播,本地廣播更高效.註冊廣播的幾種方法?1.靜態註冊:在清單文件中註冊, 常見的有監聽設備啟動,常駐註冊不會隨程序生命周期改變 2.動態註冊:在代碼中註冊,隨著程序的結束,也就停止接受廣播了補充一點:有些廣播只能通過動態方式註冊,比如時間變化事件、屏幕亮滅事件、電量變更事件,因為這些事件觸發頻率通常很高,如果允許後台監聽,會導致進程頻繁創建和銷毀,從而影響系統整體性能為什麼Android引入廣播機制?a:從MVC的角度考慮(應用程序內) 其實回答這個問題的時候還可以這樣問,android為什麼要有那4大組件,現在的移動開發模型基本上也是照搬的web那一套MVC 架構,只不過是改了點嫁妝而已。android的四大組件本質上就是為了實現移動或者說嵌入式設備上的MVC架構,它們之間有時候是一種相互依存的關係,有時候又是一種補充關係,引入廣播機制可以方便幾大組件的信息和數據交互。
b:程序間互通消息(例如在自己的應用程序內監聽系統來電) c:效率上(參考UDP的廣播協議在區域網的方便性) d:設計模式上(反轉控制的一種應用,類似監聽者模式)BroadCastReceiver的安全性問題具體參見:[blog.csdn.net/t12x3456/ar…了解IntentServices嗎?IntentService是Service的子類,是一個非同步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷毀Service的問題生成一個默認的且與線程相互獨立的工作線程執行所有發送到onStartCommand()方法的Intent,可以在onHandleIntent()中處理.串列隊列,每次只運行一個任務,不存在線程安全問題,所有任務執行完後自動停止服務,不需要自己手動調用stopSelf()來停止.Service的onCreate運行在哪個線程中?
UI線程提升Service進程優先順序在AndroidManifest.xml文件中對於intent-filter可以通過android:priority = 「1000」這個屬性設置最高優先順序,1000是最高值,如果數字越小則優先順序越低,同時適用於廣播。介紹Android下的數據存儲方式- SharedPreference
- 內部存儲
- 外部存儲
- SQLite
- 網路存儲
ContentProvider是如何實現數據共享
當一個應用程序需要把自己的數據暴露給其他程序使用時,該就用程序就可通過提供ContentProvider來實現;其他應用程序就可通過ContentResolver來操作ContentProvider暴露的數據。 一旦某個應用程序通過ContentProvider暴露了自己的數據操作介面,那麼不管該應用程序是否啟動,其他應用程序都可以通過該介面來操作該應用程序的內部數據,包括增加數據、刪除數據、修改數據、查詢數據等。ContentProvider以某種Uri的形式對外提供數據,允許其他應用訪問或修改數據;其他應用程序使用ContentResolver根據Uri去訪問操作指定數據。 步驟: 1. 定義自己的ContentProvider類,該類需要繼承Android提供的ContentProvider基類。 2. 在AndroidManifest.xml文件中註冊個ContentProvider,註冊ContenProvider時需要為它綁定一個URL。 例: android:authorities=」com.myit.providers.MyProvider」 /> 說明:authorities就相當於為該ContentProvider指定URL。 註冊後,其他應用程序就可以通過該Uri來訪問MyProvider所暴露的數據了。3. 接下來,使用ContentResolver操作數據,Context提供了如下方法來獲取ContentResolver對象。 一般來說,ContentProvider是單例模式,當多個應用程序通過ContentResolver來操作 ContentProvider提供的數據時,ContentResolver調用的數據操作將會委託給同一個ContentProvider處理。 使用ContentResolver操作數據只需兩步: 1、調用Activity的ContentResolver獲取ContentResolver對象。 2、根據需要調用ContentResolver的insert()、delete()、update()和query()方法操作數據即可
ContentProvider和sql的區別ContentProvider的主要還是用於數據共享,其可以對Sqlite,SharePreferences,File等進行數據操作用來共享數據。而sql的可以理解為資料庫的一門語言,可以使用它完成CRUD等一系列的操作數據存儲相關- 文件存儲: 通過Java.io.FileInputStream和java.io.FileOutputStream這兩個類來實現對文件的讀寫,java.io.File類則用來構造一個具體指向某個文件或者文件夾的對象。
- SharedPreferences: SharedPreferences是一種輕量級的數據存儲機制,他將一些簡單的數據類型的數據,包括boolean類型,int類型,float類型,long類型以及String類型的數據,以鍵值對的形式存儲在應用程序的私有Preferences目錄(/data/data/<包名>/shared_prefs/)中,這種Preferences機制廣泛應用於存儲應用程序中的配置信息。
- SQLite資料庫: 當應用程序需要處理的數據量比較大時,為了更加合理地存儲、管理、查詢數據,我們往往使用關係資料庫來存儲數據。Android系統的很多用戶數據,如聯繫人信息,通話記錄,簡訊息等,都是存儲在SQLite資料庫當中的,所以利用操作SQLite資料庫的API可以同樣方便的訪問和修改這些數據。
- ContentProvider: 主要用於在不同的應用程序之間實現數據共享的功能,不同於sharepreference和文件存儲中的兩種全局可讀寫操作模式,內容提供其可以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄漏的風險
如何導入外部資料庫?
如何將打開res aw目錄中的資料庫文件?在Android中不能直接打開res aw目錄中的資料庫文件,而需要在程序第一次啟動時將該文件複製到手機內存或SD卡的某個目錄中,然後再打開該資料庫文件。複製的基本方法是使用getResources().openRawResource方法獲得res aw目錄中資源的 InputStream對象,然後將該InputStream對象中的數據寫入其他的目錄中相應文件中。在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法來打開任意目錄中的SQLite資料庫文件。一條最長的簡訊息約佔多少byte?中文70(包括標點),英文160,160個位元組。Context與ApplicationContext的區別,Application的Context是一個全局靜態變數,SDK的說明是只有當你引用這個context的生命周期超過了當前activity的生命周期,而和整個應用的生命周期掛鉤時,才去使用這個application的context。 在android中context可以作很多操作,但是最主要的功能是載入和訪問資源。在android中有兩種context,一種是 application context,一種是activity context,通常我們在各種類和方法間傳遞的是activity context。什麼是aar?aar是jar有什麼區別?「aar」包是 Android 的類庫項目的二進位發行包。 文件擴展名是.aar,maven 項目類型應該也是aar,但文件本身是帶有以下各項的 zip 文件: /AndroidManifest.xml (mandatory) /classes.jar (mandatory) /res/ (mandatory) /R.txt (mandatory) /assets/ (optional) /libs/*.jar (optional) /jni//*.so (optional) /proguard.txt (optional) /lint.jar (optional) 這些條目是直接位於 zip 文件根目錄的。 其中R.txt 文件是aapt帶參數–output-text-symbols的輸出結果。 jar打包不能包含資源文件,比如一些drawable文件、xml資源文件之類的,aar可以。SQLite支持事務嗎?添加刪除如何提高性能?SQLite作為輕量級的資料庫,比MySQL還小,但支持SQL語句查詢,提高性能可以考慮通過原始經過優化的SQL查詢語句方式處理SQLite優化參考:[www.cnblogs.com/devinzhang/…如何將SQLite資料庫(dictionary.db文件)與apk文件一起發布?可以將dictionary.db文件複製到Eclipse Android工程中的res aw目錄中。所有在res aw目錄中的文件不會被壓縮,這樣可以直接提取該目錄中的文件。可以將dictionary.db文件複製到res aw目錄中Webview中的漏洞[jiajixin.cn/2014/09/16/…Service和Activity通信- 通過Binder
- 通過broadcast
如何保證Service在後台不被kill
- Service設置成START_STICKY kill 後會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣
- 通過 startForeground將進程設置為前台進程, 做前台服務,優先順序和前台應用一個級別?,除非在系統內存非常缺,否則此進程不會被 kill
- 雙進程Service: 讓2個進程互相保護**,其中一個Service被清理後,另外沒被清理的進程可以立即重啟進程
- QQ黑科技: 在應用退到後台後,另起一個只有 1 像素的頁面停留在桌面上,讓自己保持前台狀態,保護自己不被後台清理工具殺死
- 在已經root的設備下,修改相應的許可權文件,將App偽裝成系統級的應用 Android4.0系列的一個漏洞,已經確認可行
- 用C編寫守護進程(即子進程) : Android系統中當前進程(Process)fork出來的子進程,被系統認為是兩個不同的進程。當父進程被殺死的時候,子進程仍然可以存活,並不受影響。鑒於目前提到的在Android->- Service層做雙守護都會失敗,我們可以fork出c進程,多進程守護。死循環在那檢查是否還存在,具體的思路如下(Android5.0以上的版本不可行)
- 用C編寫守護進程(即子進程),守護進程做的事情就是循環檢查目標進程是否存在,不存在則啟動它。
- 在NDK環境中將1中編寫的C代碼編譯打包成可執行文件(BUILD_EXECUTABLE)。主進程啟動時將守護進程放入私有目錄下,賦予可執行許可權,啟動它即可。
- 聯繫廠商,加入白名單
談談你對Android中Context的理解
參考:[blog.csdn.net/qinjuning/a…RemoteView的應用widget和Notification中Android中如何獲得手機的唯一標示.1 首先嘗試讀取IMEI、Mac地址、CPU號等物理信息(有不少工具可以修改IMEI); 2 如果均失敗,可以自己生成UUID然後保存到文件(文件也可能被篡改或刪除)參考:[blog.csdn.net/xushuaic/ar…Android應用中驗證碼登錄都有哪些實現方案驗證碼應該只有兩種獲取方式: 從伺服器端獲取圖片, 通過簡訊服務,將驗證碼發送給客戶端這兩種為什麼要設計Bundle而不是直接使用Map?Map里實現了Serializable介面,而在Bundle實現了Parcelable的介面 Bundle 父類 BaseBundle內部確實有個 ArrayMapAndroid中XML解析方式的比較急優缺點DOM,SAX,Pull解析。 SAX解析器的優點是解析速度快,佔用內存少; DOM在內存中以樹形結構存放,因此檢索和更新效率會更高。但是對於特別大的文檔,解析和載入整個文檔將會很耗資源,不適合移動端; PULL解析器的運行方式和SAX類似,都是基於事件的模式,PULL解析器小巧輕便,解析速度快,簡單易用,非常適合在Android移動設備中使用,Android系統內部在解析各種XML時也是用PULL解析器。布局相關
LinearLayout和RelativeLayout性能對比1 RelativeLayout會讓子View調用2次onMeasure,LinearLayout 在有weight時,也會調用子View2次onMeasure 2 RelativeLayout的子View如果高度和RelativeLayout不同,則會引發效率問題,當子View很複雜時,這個問題會更加嚴重。如果可以,盡量使用padding代替margin。 3 在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。 最後再思考一下文章開頭那個矛盾的問題,為什麼Google給開發者默認新建了個RelativeLayout,而自己卻在DecorView中用了個LinearLayout。因為DecorView的層級深度是已知而且固定的,上面一個標題欄,下面一個內容欄。採用RelativeLayout並不會降低層級深度,所以此時在根節點上用LinearLayout是效率最高的。而之所以給開發者默認新建了個RelativeLayout是希望開發者能採用盡量少的View層級來表達布局以實現性能最優,因為複雜的View嵌套對性能的影響會更大一些。屏幕適配相關dp, dip, dpi, px, sp是什麼意思以及他們的換算公式?layout-sw400dp, layout-h400dp分別代表什麼意思布局優化- 避免OverDraw過渡繪製
- 優化布局層級
- 避免嵌套過多無用布局
- 當我們在畫布局的時候,如果能實現相同的功能,優先考慮相對布局,然後在考慮別的布局,不要用絕對布局。
- 使用
<include />
標籤把複雜的界面需要抽取出來 - 使用
<merge />
標籤,因為它在優化UI結構時起到很重要的作用。目的是通過刪減多餘或者額外的層級,從而優化整個Android Layout的結構。核心功能就是減少冗餘的層次從而達到優化UI的目的! - ViewStub 是一個隱藏的,不佔用內存空間的視圖對象,它可以在運行時延遲載入布局資源文件。
mipmap文件夾和drawable文件夾的區別
它只是用來放啟動圖標的,好處就是,你只用放一個mipmap圖標,它就會給你各種版本(比如平板,手機)的apk自動生成相應解析度的圖標,以節約空間。ListView卡頓的原因以及優化策略- 重用converView: 通過復用converview來減少不必要的view的創建,另外Infalte操作會把xml文件實例化成相應的View實例,屬於IO操作,是耗時操作。
- 減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態類,通過converview的setTag和getTag方法將view與相應的holder對象綁定在一起,避免不必要的findviewbyid操作
- 避免在 getView 方法中做耗時的操作: 例如載入本地 Image 需要載入內存以及解析 Bitmap ,都是比較耗時的操作,如果用戶快速滑動listview,會因為getview邏輯過於複雜耗時而造成滑動卡頓現象。用戶滑動時候不要載入圖片,待滑動完成再載入,可以使用這個第三方庫glide
- Item的布局層次結構盡量簡單,避免布局太深或者不必要的重繪
- 盡量能保證 Adapter 的 hasStableIds() 返回 true 這樣在 notifyDataSetChanged() 的時候,如果item內容並沒有變化,ListView 將不會重新繪製這個 View,達到優化的目的
- 在一些場景中,ScollView內會包含多個ListView,可以把listview的高度寫死固定下來。 由於ScollView在快速滑動過程中需要大量計算每一個listview的高度,阻塞了UI線程導致卡頓現象出現,如果我們每一個item的高度都是均勻的,可以通過計算把listview的高度確定下來,避免卡頓現象出現
- 使用 RecycleView 代替listview: 每個item內容的變動,listview都需要去調用notifyDataSetChanged來更新全部的item,太浪費性能了。RecycleView可以實現當個item的局部刷新,並且引入了增加和刪除的動態效果,在性能上和定製上都有很大的改善
- ListView 中元素避免半透明: 半透明繪製需要大量乘法計算,在滑動時不停重繪會造成大量的計算,在比較差的機子上會比較卡。 在設計上能不半透明就不不半透明。實在要弄就把在滑動的時候把半透明設置成不透明,滑動完再重新設置成半透明。
- 盡量開啟硬體加速: 硬體加速提升巨大,避免使用一些不支持的函數導致含淚關閉某個地方的硬體加速。當然這一條不只是對 ListView。
如何實現一個局部更新的ListView
如何實現ListView多種布局ViewHolder為什麼要被聲明成靜態內部類這個是考靜態內部類和非靜態內部類的主要區別之一。非靜態內部類會隱式持有外部類的引用,就像大家經常將自定義的adapter在Activity類里,然後在adapter類裡面是可以隨意調用外部activity的方法的。當你將內部類定義為static時,你就調用不了外部類的實例方法了,因為這時候靜態內部類是不持有外部類的引用的。聲明ViewHolder靜態內部類,可以將ViewHolder和外部類解引用。大家會說一般ViewHolder都很簡單,不定義為static也沒事吧。確實如此,但是如果你將它定義為static的,說明你懂這些含義。萬一有一天你在這個ViewHolder加入一些複雜邏輯,做了一些耗時工作,那麼如果ViewHolder是非靜態內部類的話,就很容易出現內存泄露。如果是靜態的話,你就不能直接引用外部類,迫使你關注如何避免相互引用。 所以將 ViewHolder內部類 定義為靜態的,是一種好習慣進程,線程
有哪些進程通信的方式?- Intent
- Binder(AIDL
- Messenger
- BroadcastReceiver
AIDL是什麼?
AIDL全程是Android Interface Definition Language,用於生成兩個進程之間通信(IPC)的代碼.AIDL 體現了哪些設計思想代理Binder機制很多人吧Binder的原理解釋的很複雜,讓人看著就頭大,但是熟悉Linux開發的小夥伴可以一下想到Binder驅動本質就是文件,實現採用了代理而已.具體看下圖: 簡單的說說Handler機制- Handler通過調用sendmessage方法把消息放在消息隊列MessageQueue中,Looper負責把消息從消息隊列中取出來,重新再交給Handler進行處理,三者形成一個循環
- 通過構建一個消息隊列,把所有的Message進行統一的管理,當Message不用了,並不作為垃圾回收,而是放入消息隊列中,供下次handler創建消息時候使用,提高了消息對象的復用,減少系統垃圾回收的次數
- 每一個線程,都會單獨對應的一個looper,這個looper通過ThreadLocal來創建,保證每個線程只創建一個looper,looper初始化後就會調用looper.loop創建一個MessageQueue,這個方法在UI線程初始化的時候就會完成,我們不需要手動創建
動畫相關
Android中的動畫有哪些?- 逐幀動畫(Drawable Animation): 載入一系列Drawable資源來創建動畫,簡單來說就是播放一系列的圖片來實現動畫效果,可以自定義每張圖片的持續時間
- 補間動畫(Tween Animation): Tween可以對View對象實現一系列簡單的動畫效果,比如位移,縮放,旋轉,透明度等等。但是它並不會改變View屬性的值,只是改變了View的繪製的位置,比如,一個按鈕在動畫過後,不在原來的位置,但是觸發點擊事件的仍然是原來的坐標。
- 屬性動畫(Property Animation): 動畫的對象除了傳統的View對象,還可以是Object對象,動畫結束後,Object對象的屬性值被實實在在的改變了
Android動畫原理
Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然後調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啟動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能相應事件Android屬性動畫特性如果你的需求中只需要對View進行移動、縮放、旋轉和淡入淡出操作,那麼補間動畫確實已經足夠健全了。但是很顯然,這些功能是不足以覆蓋所有的場景的,一旦我們的需求超出了移動、縮放、旋轉和淡入淡出這四種對View的操作,那麼補間動畫就不能再幫我們忙了,也就是說它在功能和可擴展方面都有相當大的局限性,那麼下面我們就來看看補間動畫所不能勝任的場景。 注意上面我在介紹補間動畫的時候都有使用「對View進行操作」這樣的描述,沒錯,補間動畫是只能夠作用在View上的。也就是說,我們可以對一個Button、TextView、甚至是LinearLayout、或者其它任何繼承自View的組件進行動畫操作,但是如果我們想要對一個非View的對象進行動畫操作,抱歉,補間動畫就幫不上忙了。可能有的朋友會感到不能理解,我怎麼會需要對一個非View的對象進行動畫操作呢?這裡我舉一個簡單的例子,比如說我們有一個自定義的View,在這個View當中有一個Point對象用於管理坐標,然後在onDraw()方法當中就是根據這個Point對象的坐標值來進行繪製的。也就是說,如果我們可以對Point對象進行動畫操作,那麼整個自定義View的動畫效果就有了。顯然,補間動畫是不具備這個功能的,這是它的第一個缺陷。 然後補間動畫還有一個缺陷,就是它只能夠實現移動、縮放、旋轉和淡入淡出這四種動畫操作,那如果我們希望可以對View的背景色進行動態地改變呢?很遺憾,我們只能靠自己去實現了。說白了,之前的補間動畫機制就是使用硬編碼的方式來完成的,功能限定死就是這些,基本上沒有任何擴展性可言。 最後,補間動畫還有一個致命的缺陷,就是它只是改變了View的顯示效果而已,而不會真正去改變View的屬性。什麼意思呢?比如說,現在屏幕的左上角有一個按鈕,然後我們通過補間動畫將它移動到了屏幕的右下角,現在你可以去嘗試點擊一下這個按鈕,點擊事件是絕對不會觸發的,因為實際上這個按鈕還是停留在屏幕的左上角,只不過補間動畫將這個按鈕繪製到了屏幕的右下角而已。View繪製相關
SurfaceView和View的區別SurfaceView中採用了雙緩存技術,在單獨的線程中更新界面 View在UI線程中更新界面介紹下自定義view的基本流程- 明確需求,確定你想實現的效果
- 確定是使用組合控制項的形式還是全新自定義的形式,組合控制項即使用多個系統控制項來合成一個新控制項,你比如titilebar,這種形式相對簡單,參考
- 如果是完全自定義一個view的話,你首先需要考慮繼承哪個類,是View呢,還是ImageView等子類。
- 根據需要去複寫View#onDraw、View#onMeasure、View#onLayout方法
- 根據需要去複寫dispatchTouchEvent、onTouchEvent方法
- 根據需要為你的自定義view提供自定義屬性,即編寫attr.xml,然後在代碼中通過TypedArray等類獲取到自定義屬性值 7.需要處理滑動衝突、像素轉換等問題
談談View的繪製流程
measure()方法,layout(),draw()三個方法主要存放了一些標識符,來判斷每個View是否需要再重新測量,布局或者繪製,主要的繪製過程還是在onMeasure,onLayout,onDraw這個三個方法中1.onMesarue() 為整個View樹計算實際的大小,即設置實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性: mMeasureWidth),每個View的控制項的實際寬高都是由父視圖和本身視圖決定的。2.onLayout() 為將整個根據子視圖的大小以及布局參數將View樹放到合適的位置上。3. onDraw() 開始繪製圖像,繪製的流程如下- 首先繪製該View的背景
- 調用onDraw()方法繪製視圖本身 (每個View都需要重載該方法,ViewGroup不需要實現該方法)
- 如果該View是ViewGroup,調用dispatchDraw ()方法繪製子視圖
- 繪製滾動條
自定義View執行invalidate()方法,為什麼有時候不會回調onDraw()
- 自定義一個view時,重寫onDraw。調用view.invalidate(),會觸發onDraw和computeScroll()。前提是該view被附加在當前窗口.
view.postInvalidate(); //是在非UI線程上調用的
- 自定義一個ViewGroup,重寫onDraw。onDraw可能不會被調用,原因是需要先設置一個背景(顏色或圖)。表示這個group有東西需要繪製了,才會觸發draw,之後是onDraw。因此,一般直接重寫dispatchDraw來繪製viewGroup.自定義一個ViewGroup,dispatchDraw會調用drawChild.
如何實現一個字體的描邊與陰影效果
事件傳遞機制
談談touch事件的傳遞流程1. 所有Touch事件都被封裝成了MotionEvent對象,包括Touch的位置、時間、歷史記錄以及第幾個手指(多指觸摸)等。 2. 事件類型分為ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每個事件都是以ACTION_DOWN開始ACTION_UP結束。 3. 對事件的處理包括三類,分別為傳遞——dispatchTouchEvent()
函數、攔截——onInterceptTouchEvent()
函數、消費——onTouchEvent()
函數和OnTouchListener()
簡單來說:
- 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對事件進行處理。
- 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。
- 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子View沒有消費事件,事件會反嚮往上傳遞,這時父View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最後會到Activity的onTouchEvent()函數。
- 如果View沒有對ACTION_DOWN進行消費,之後的其他事件不會傳遞過來。
- OnTouchListener優先於onTouchEvent()對事件進行消費。 上面的消費即表示相應函數返回值為true。
View中setOnTouchListener中的onTouch,onTouchEvent,onClick的執行順序
onTouch優於onTouchEvent,onTouchEvent優於onClickAndroid下滑衝突的常見解決思路相關的滑動組件 重寫onInterceptTouchEvent,然後判斷根據xy值,來決定是否要攔截當前操作高效使用Bitmap
談談你對Bitmap的理解,以及什麼時候該bitmap.recycle()Bitmap是android中經常使用的一個類,它代表了一個圖片資源。 Bitmap消耗內存很嚴重,如果不注意優化代碼,經常會出現OOM問題,優化方式通常有這麼幾種: 1. 使用緩存; 2. 壓縮圖片; 3. 及時回收;至於什麼時候需要手動調用recycle,這就看具體場景了,原則是當我們不再使用Bitmap時,需要回收。另外,我們需要注意,2.3之前Bitmap對象與像素數據是分開存放的,Bitmap對象存在JavaHeap中而像素數據存放在Native Memory中,這時很有必要調用recycle回收內存。但是2.3之後,Bitmap對象和像素數據都是存在Heap中,GC可以回收其內存。反射相關
什麼時候會用到反射?JAVA反射機制是在#運行時#,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 Java反射機制主要提供了以下功能: a)在運行時判斷任意一個對象所屬的類; b)在運行時構造任意一個類的對象; c)在運行時判斷任意一個類所具有的成員變數和方法; d)在運行時調用任意一個對象的方法;生成動態代理。你曾經利用反射做過什麼?JNI系列
NDK是什麼?NDK是一些列工具的集合, NDK提供了一系列的工具,幫助開發者迅速的開發C/C++的動態庫,並能自動將so和java 應用打成apk包。 NDK集成了交叉編譯器,並提供了相應的mk文件和隔離cpu、平台等的差異,開發人員只需簡單的修改mk文件就可以創建出soAndroid ndk主要在哪些場景下使用?有啥坑?- 加密
- 音視頻解碼
- 圖像操作
- 安全相關,比如hookt
- 增量更新
- 遊戲開發
NDK開發需要注意什麼?
[developer.android.com/training/ar…使用JNI的流程?- JAVA中聲明native 方法如private native String printJNI(String inputStr);
- 使用javah工具生成.h頭文件這時候頭文件中就會自動生成對應的函數JNIEXPORT jstring JNICALL Java_com_wenming_HelloWorld_printJNI
- 實現JNI原生函數源文件,新建HelloWorld.c文件,對剛才自動生成的函數進行具體的邏輯書寫,例如返回一個java叫做HelloWorld的字元串等
- 編譯生成動態鏈接so文件**
- Java中調用Sysytem.load方法把剛才的so庫載入進來,就可以調用native方法了
如何通過JNI傳遞String對象
Java的String和C++的string是不能對等起來的,所以當我們拿到.h文件下面的jstring對象,會做一次轉換我們把jstring轉換為C下面的char*類型, 獲取值constchar* str;
str = env->GetStringUTFChars(prompt,false);
賦予值 char* tmpstr ="return string succeeded";
jstring rtstr = env->NewStringUTF(tmpstr);
網路優化
移動端獲取數據優化的幾個點- 連接復用 : 節省連接建立時間,如開啟 keep-alive。 對於 Android 來說默認情況下 HttpURLConnection 和 HttpClient 都開啟了 keep-alive。只是 2.2 之前 HttpURLConnection 存在影響連接池的 Bug,具體可見:Android HttpURLConnection 及 HttpClient 選擇
- 請求合併: 即將多個請求合併為一個進行請求,比較常見的就是網頁中的 CSS Image Sprites。如果某個頁面內請求過多,也可以考慮做一定的請求合併。
- 減少請求數據的大小: 對於post請求,body可以做gzip壓縮的,header也可以作數據壓縮(不過只支持http 2.0)。
- 返回的數據的body也可以作gzip壓縮,body數據體積可以縮小到原來的30%左右。(也可以考慮壓縮返回的json數據的key數據的體積,尤其是針對返回數據格式變化不大的情況,支付寶聊天返回的數據用到了)
- 根據用戶的當前的網路質量來判斷下載什麼質量的圖片(電商用的比較多)。
如何設計一個良好的網路層?
[blog.csdn.net/column/deta…如何防止重複發送網路請求點擊activity上的一個按鈕,發送網路請求,在網路比較慢的情況下,用戶可能會繼續去點擊按鈕,這個時候,發送其他無謂的請求,不知道大家是怎麼處理這類問題來攔截? HTTP header中加入max-age,這樣某個固定的時間內都將返回empty body,當然這個方法是死的,把時間完全限制了,這個方法回掉也會同樣要執行多次。 還有個暈招,就是直接設置按鈕的clickable為false,或者使用progressbar,類似於樓主的方法,比如點贊的場景。 使用Map的話,在回掉的時候,還是需要回收HashMap的,維護Map還不如只維護一個boolean呢。 Volley中如果開了緩存的話, 相同的請求同時只會有一個去真正的請求, 後續都走緩存, 雖然不會請求多次, 但是回調是會執行多次的, 和這個需求不match如何實現wap聯網參考:[blog.csdn.net/asce1885/ar…測試與調試
如何調試Android應用程序Debug LogAndroid中常用的測試工具?內存分析:mat,ddms,leakcanary 靜態分析:find bugs 壓力測試:monkey 自動化測試:UIAutomatorMonkeyRunner,Rubotium內存泄漏/內存溢出相關
內存泄漏問題- 資源對象沒有關閉造成,如查詢資料庫沒有關閉游標
- 構造Adapter時,沒有使用緩存ConvertView
- Bitmap對象在不使用時調用recycle()釋放內存
- context逃逸問題
- 註冊沒有取消,如動態註冊廣播在Activity銷毀前沒有unregisterReceiver
- 集合對象未清理,如無用時沒有釋放對象的引用
- 在Activity中使用非靜態的內部類,並開啟一個長時間運行的線程,因為內部類持有Activity的引用,會導致Activity本來可以被gc時卻長期得不到回收
哪些情況下發生OOM
- 類的靜態變數持有大數據對象 靜態變數長期維持到大數據對象的引用,阻止垃圾回收。
- 非靜態內部類存在靜態實例 非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持著外部類的引用,阻止被回收掉。
- 資源對象未關閉 資源性對象比如(Cursor,File文件等)往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們, 以便它們的緩衝及時回收內存。它們的緩衝不僅存在於java虛擬機內,還存在於java虛擬機外。 如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內存泄露。 解決辦法: 比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉), 如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。 因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經關閉。 程序中經常會進行查詢資料庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小, 對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險,記得try catch後,在finally方法中關閉連接
- Handler內存泄漏 Handler作為內部類存在於Activity中,但是Handler生命周期與Activity生命周期往往並不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。 解決辦法:
- 聲明handler為static類,這樣內部類就不再持有外部類的引用了,就不會阻塞Activity的釋放
- 如果內部類實在需要用到外部類的對象,可在其內部聲明一個弱引用引用外部類
一些不良代碼習慣 有些代碼並不造成內存泄露,但是他們的資源沒有得到重用,頻繁的申請內存和銷毀內存,消耗CPU資源的同時,也引起內存抖動 解決方案 如果需要頻繁的申請內存對象和和釋放對象,可以考慮使用對象池來增加對象的復用。 例如ListView便是採用這種思想,通過復用converview來避免頻繁的GC
如何排查OOM參見:www.jcodecraeer.com/a/anzhuokai…如何避免OOM1. 使用更加輕量的數據結構 例如,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。通常的HashMap的實現方式更加消耗內存,因為它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在於他們避免了對key與value的自動裝箱(autoboxing),並且避免了裝箱後的解箱。2. 避免在Android裡面使用Enum Android官方培訓課程提到過「Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.」,具體原理請參考《Android性能優化典範(三)》,所以請避免在Android裡面使用到枚舉。3. 減小Bitmap對象的內存佔用 Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存佔用可謂是重中之重,,通常來說有以下2個措施: inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。 decode format:解碼格式,選擇ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異4.Bitmap對象的復用 縮小Bitmap的同時,也需要提高BitMap對象的復用率,避免頻繁創建BitMap對象,復用的方法有以下2個措施 LRUCache : 「最近最少使用演算法」在Android中有極其普遍的應用。ListView與GridView等顯示大量圖片的控制項里,就是使用LRU的機制來緩存處理好的Bitmap,把近期最少使用的數據從緩存中移除,保留使用最頻繁的數據, inBitMap高級特性:利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小4. 使用更小的圖片 在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。盡量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。5.StringBuilder 在有些時候,代碼中會需要使用到大量的字元串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的「+」。4.避免在onDraw方法裡面執行對象的創建 類似onDraw等頻繁調用的方法,一定需要注意避免在這裡做創建對象的操作,因為他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。5. 避免對象的內存泄露 android中內存泄漏的場景以及解決辦法,參考上一問ANR錯誤
什麼是ANRANR全稱Application Not Responding,意思就是程序未響應。如果一個應用無法響應用戶的輸入,系統就會彈出一個ANR對話框,用戶可以自行選擇繼續等待亦或者是停止當前程序。一旦出現下面兩種情況,則彈出ANR對話框 1. 應用在5秒內未響應用戶的輸入事件(如按鍵或者觸摸) 2. BroadcastReceiver未在10秒內完成相關的處理Service在特定的時間內無法處理完成 超時的原因一般有兩種: (1)當前的事件沒有機會得到處理(UI線程正在處理前一個事件沒有及時完成或者looper被某種原因阻塞住) (2)當前的事件正在處理,但沒有及時完成 UI線程盡量只做跟UI相關的工作,耗時的工作(資料庫操作,I/O,連接網路或者其他可能阻礙UI線程的操作)放入單獨的線程處理,盡量用Handler來處理UI thread和thread之間的交互。 UI線程主要包括如下: ? Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick() ? AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel() ? Mainthread handler: handleMessage(), post(runnable r)如何定位ANR錯誤開發機器上,查看/data/anr/traces.text.最新的ANR信息在最開始部分.如何避免ANR避免ANR最核心的一點就是在主線程減少耗時操作.通常需要從以下幾個方案下手:- 使用子線程處理耗時IO操作。
- 降低子線程優先順序使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先順序,否則仍然會降低程序響應,因為默認Thread的優先順序和主線程相同。
- 使用Handler處理子線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。
- Activity的onCreate和onResume回調中盡量避免耗時的代碼
- BroadcastReceiver中onReceive代碼也要盡量減少耗時操作建議使用IntentService處理。IntentService是一個非同步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷毀Service的問題
安全相關
本地存儲的數據怎麼加密好?比如:shared_prefs,sqlite數據,用戶名,密碼等.如果是aes加密,怎麼保存key?密碼不要存在本地,一般保存token。最基本的要代碼混淆,可以的話加殼,防止反編譯系統相關
Android各版本API區別參考:[blog.csdn.net/lijun952048…什麼是Dalvik虛擬機Dalvik虛擬機是Android平台的核心。它可以支持.dex格式的程序的運行,.dex格式是專為Dalvik設計的一種壓縮格式,可以減少整體文件尺寸,提高I/O操作的速度,適合內存和處理器速度有限的系統Dalvik虛擬機和JVM有什麼區別- Dalvik 基於寄存器,而 JVM 基於棧。基於寄存器的虛擬機對於更大的程序來說,在它們編譯的時候,花費的時間更短。
- Dalvik執行.dex格式的位元組碼,而JVM執行.class格式的位元組碼
Android為每個應用程序分配的內存大小是多少
一般是16m或者24m,但是可以通過android:largeHeap申請更多內存,具體參考: [liuzhichao.com/2016/use-an… [www.cnblogs.com/mythou/p/32…如何解決方法數65k問題?使用Android Studio 的gradle 可以構建MutilDexAndroid系統啟動流程分析- 打開adb shell 然後執行ps命令,可以看到首先執行的是init方法!找到init.c這個文件.
- 然後走init裡面的main方法,在這main方法裡面執行mkdir進行創建很多的文件夾,和掛載一些目錄
- 然後回去初始化init.rc這個配置文件!在這個配置文件裡面回去啟動孵化器這個服務,這個服務會去啟動app_process這個文件夾,這個文件夾裡面有個app_main.cpp這個文件!
- 然後在app_main.cpp這個c文件裡面在main方法裡面它會去啟動安卓的虛擬機,然後安卓虛擬機會去啟動os.zygoteinit這個服務!
- zygoteinit這是個java代碼寫的,然後我們找到了main方法,在這個方法裡面我們看到他首先設置虛擬機的最小堆內存為5兆,然後走到preloadclasses()這個方法來載入安卓系統所有的2000多個類通過類載入器載入進來,比如activity,contentx,http,…(其實沒有必要一下子全部載入下來,我們可以等用到的時候在載入也可以!)
- 然後又走preloadresources()這個方法來預載入安卓中定義好的資源比如顏色,圖片,系統的id等等。。。都載入了!(其實這也是沒必要的! )
- 然後又走startSystemServer(),這個方法來載入系統的服務!他會先使用natvieJNI去調用C去初始化界面和聲音的服務,這就是我們為什麼先聽到聲音和界面的原因!
- 最後等服務載入完成後也就啟動起來了!
總結 linux啟動->init進程啟動(載入init.rc配置)->zygote啟動->systemServer啟動,systemServer會通過init1和init2啟動navite世界和java世界_
- Android
推薦閱讀: