-
Android Studio JNI流程
- 首先在java代碼聲明本地方法 用到native關鍵字 本地方法不用去實現,最好聲明在一個新類中(rebuild project)
- 項目根目錄下創建jni文件夾(java文件夾同級)
- 在jni文件夾下創建.c文件
- 本地函數命名規則: Java包名
_
類名_本地方法名 - 也可以cd到當前項目的 build/intermediates/classes/debug/ 目錄下執行javah native方法下的包名類名
- 得到頭文件拷貝到.c文件下
- 本地函數命名規則: Java包名
- System.loadLibrary("hello"),把so庫加載進來(可以調用native本地方法了)
- local.properties 設置ndk.dir目錄
- gradle.properties 添加一行 android.useDeprecatedNdk=true
- 在當前Module下的build.gradle 文件下的defaultConfig節添加
ndk { moduleName "hello" abiFilter "x86" abiFilter "armeabi" }
-
C調用JAVA
- 找到字節碼對象
- jclass (FindClass)(JNIEnv, const char*);
- 第二個參數 要回調的java方法所在的類的路徑 "com/itheima/callbackjava/JNI"
- 通過字節碼對象找到方法對象
- jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);
- 第二個參數 字節碼對象 第三個參數 要反射調用的java方法名 第四個參數 要反射調用的java方法簽名
- javap -s 要獲取方法簽名的類的全類名 項目/bin/classes 運行javap
- 通過字節碼創建 java對象(可選) 如果本地方法和要回調的java方法在同一個類里可以直接用 jni傳過來的java對象 調用創建的Method
- jobject obj =(*env)->AllocObject(env,claz);
- 當回調的方法跟本地方法不在一個類里 需要通過剛創建的字節碼對象手動創建一個java對象
- 再通過這個對象來回調java的方法
- 需要注意的是 如果創建的是一個activity對象 回調的方法還包含上下文 這個方法行不通!!!回報空指針異常
- 反射調用java方法
- void (CallVoidMethod)(JNIEnv, jobject, jmethodID, ...);
- 第二個參數 調用java方法的對象 第三個參數 要調用的jmethodID對象 可選的參數 調用方法時接收的參數
- 找到字節碼對象
-
NDK的理解
- NDK提供了一系列的工具,幫助開發者快速開發 C (或 C++ )的動態庫,并能自動將 so 和 java 應用一起打包成 apk 。這些工具對開發者的幫助是巨大的
- NDK 提供了一份穩定、功能有限的 API 頭文件聲明
- Google明確聲明該 API 是穩定的,在后續所有版本中都穩定支持當前發布的 API 。從該版本的 NDK 中看出,這些 API 支持的功能非常有限,包含有: C 標準庫( libc )、標準數學庫( libm )、壓縮庫( libz )、 Log 庫( liblog )
-
四大組件相關
- 四大組件之間通過Intent來傳數據
- Service啟動Activity需要加上
intentv.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- Service如何彈出dialog
- show() 調用之前添加以下代碼:
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
- 清單文件添加權限:
android.permission.SYSTEM_ALERT_WINDOW
- show() 調用之前添加以下代碼:
-
Handler機制
- 作用:將一個任務切換到某個指定的線程中去執行和分發消息,主要涉及類:Handler、Message、MessageQueue、Looper
- 創建:Handler構造函數通過
Looper.myLooper()
會獲取當前線程中的Looper對象(Looper通過prepare/prepareMainLooper
創建的,通過ThreadLocal存儲,每個線程只有一個),通過Looper的構造函數創建MessageQueue(內部是鏈表結構),從而三者關聯起來 - 發送:Handler通過
sendMessage/post
將Message(Message.obtain()
復用)發送到Looer對應的MessageQueue,MessageQueue通過enqueueMessage()
把當前消息加入到鏈表對應位置 - 處理:Looper通過
loop()
不斷從MessageQueue中取出Messagequeue.next()
(沒有消息則阻塞,異步阻塞IO),然后調用與Message綁定的Handler對象的dispatchMessage()
交給發送該消息的Handler進行處理,如果是post的message會調用handleCallback()
,直接處理事件,否則會調用我們復寫handleMessage()
處理,處理完成后調用Message.recycle()
將其放入Message對象池中,最終三者形成一個循環 - 高級:NativeMessageQueue、 Native Looper
- 獲取消息首先調用
nativePollOnce
,從底層epoll(異步阻塞IO),發送消息加入消息隊列首先會調用nativeWake
,向管道寫入字符,喚醒IO,從而MessageQueue.next()
可以返回一個Message
- 獲取消息首先調用
-
分析AsyncTask
- 主要方法有:
onPreExecute()、doInBackground()、 publishProgress()、onProgressUpdate()、onPostExecute()
- 創建:AsyncTask(泛型參數:Params、Progress、Result)時會創建一個WorkerRunnable(實現Callable接口)對象mWorker,一個FutureTask對象mFuture,mWorker會保存到mFuture屬性中
- 運行:調用
executeOnExecutor()
,設置狀態 -> 回調onPreExecute()
-> 設置mWorker參數(doInBackground參數) ->sDefaultExecutor.execute(mFuture)
,有兩個線程池(線性入隊線程池、工作線程池)從線性線程池(offer()/poll()
輪訓任務)執行FutureTask的run()
封裝成Runnable并交到工作線程池中,此時是運行在子線程中) - FutureTask#run()
result = c.call()
->postResult(doInBackground(mParams));
,這個過程中doInBackground將會被調用,其中mFuture中result變量保存了doInBackground返回值,通過postResult()
傳遞給InternalHandler()
(此時是主線程了...),handler中會調用mTask.finish(結果),最后正常結束會調用onPostExecute(result)
,FutureTask#finishCompletion()->done()
,最后傳到FutureTask復寫的Done()
->postResultIfNotInvoked(get())
通過get()
可以獲取子線程傳遞過來的值 - 如果復寫的doInBackground方法中方調用了publishProgress方法,則通過InternalHandler實例sHandler發送一條Message,更新進度,sHandler處理消息onProgressUpdate方法將被調用
- 主要方法有:
-
ThreadLocal
- ThreadLocal是一個線程內部的數據存儲類(內部數據結構是map),通過它可以在指定的線程中存儲數據,數據存儲后,只有在指定的線程中獲取存儲的數據,其它的線程無法獲取,實現了隔離多個線程的數據共享,主要用在Looper、ActivityThread以及AMS中
- ThreadLocalMap類似HashMap,key為ThreadLocal,value為實際放入的值,采用線性探測法來解決散列沖突
-
HandlerThread
- HandlerThread繼承了Thread,是可以使用Handler的Thread,內部run方法中通過Looper.prepare()創建消息隊列,并通過Looper.loop()開啟消息循環,具體使用場景有IntentService,它可以通過quit/quitSafely方法終止線程的執行(區別是quitSafely沒有立即終止把當前時間之前的任務執行完才退出)
-
IntentService
- 當Service中執行耗時操作時,20s會產生ANR,而IntentService是一個特殊的Service,則可用于執行后臺耗時的任務,無需處理多線程問題,IntentService封裝了HandlerThread和Handler,通過復寫onHandleIntent方法操作耗時操作,當任務執行完后會自動停止,無需調用stopSelf()方法停止Service,避免了Service的內存泄漏,Intentservice若未執行完成上一次的任務,將不會新開一個線程,是等待之前的任務完成后,再執行新的任務,等任務完成后再次調用stopSelf,適合執行一些高優先級的后臺任務,因為不容易被殺死
-
保證后臺進程不被kill
- 提供進程優先級,降低進程被殺死的概率
- 方法一:監控手機鎖屏解鎖事件,在屏幕鎖屏時啟動1個像素的 Activity,在用戶解鎖時將 Activity 銷毀掉。
- 方法二:啟動前臺service(setForeground)
- 方法三:提升service優先級:
在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,(適用于廣播)
- 在進程被殺死后,進行拉活
- 方法一:注冊高頻率廣播接收器,喚起進程。如網絡變化,解鎖屏幕,開機等
- 方法二:Native雙進程相互喚起(fork+JNI)
- 方法三:依靠系統喚起(賬號同步機制)
- 方法四:onDestroy方法里重啟service:service +broadcast 方式,就是當service走ondestory的時候,發送一個自定義的廣播,當收到廣播的時候,重新啟動service;
- 方法五:手機QQ、微信、支付寶、UC瀏覽器等,以及友盟、信鴿、個推等 SDK,找出它們外發的廣播,在應用中進行監聽,這樣當這些應用發出廣播時,就會將我們的應用拉活
- 提供進程優先級,降低進程被殺死的概率
-
怎么保證 Service 不被殺死
- 雙進程守護(JNI,Fork)只有5.0以下有效
- 在Service的onStartCommand方法里返回 STATR_STICK(kill 后會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣)(詳情)
- 提升service 優先級 //android:priority="1000",1000是最高值,同時適用于廣播
- 提升service進程優先級(startForeground 將service放到前臺進程)
- 在 onDestory 中通過廣播再次啟動 Service(Service+Broadcast,發送一個自定義的廣播,當收到廣播的時候,重新啟動service)
- Application加上Persistent屬性(保證應用的持久性,這種應用會頑固地運行于系統之中,從系統一啟動,一直到系統關機)
- root之后放到system/app變成系統級應用
-
廣播的兩種注冊方法,有什么區別
- 動態注冊:動態注冊的優先級高于靜態注冊,Activity關閉不能接受廣播,無需在清單文件注冊
- 靜態注冊:Activity關閉還能接受廣播,只要設備處于開啟狀態就能接收
-
服務的類型
- 按照使用范圍分類:本地服務、遠程服務
- 按照運行類別分類:前臺服務、后臺服務
-
Android啟動Service的兩種方式是什么
- startService:生命周期與調用者不同,啟動后若調用者未調用stopService而直接退出,Service仍會運行
- bindService:生命周期與調用者綁定(Service的生命周期依附于Context),調用者一旦全部退出,Service就會調用unBind->onDestroy
-
Service兩種啟動方式的區別
- startService只是啟動Service,啟動它的組件(如Activity)和Service并沒有關聯,只有當Service調用stopSelf或者其他組件調用stopService服務才會終止
- bindService方法啟動Service,其他組件可以通過回調獲取Service的代理對象和Service交互,而這兩方也進行了綁定,當啟動方銷毀時,Service也會自動進行unBind操作,當發現所有綁定都進行了unBind時才會銷毀Service
-
Android組件之間數據傳遞方法
- 基于消息的通信機制 Intent--bundle,extra
- 利用static靜態數據,public static成員變量
- 基于外部存儲的傳輸 ,File/Preference/Sqlite
- 基于ipc的通信機制
- context與service之間的傳輸,如Activity與Service之間的通信
- 基于Application Context
-
Android怎么加速啟動Activity(優化的細節)
- 減少onCreate的時間,耗時操作
- 移除背景
- 在很多的應用一開始點擊的時候總會出現黑屏或者白屏,甚至前段時間微信也有同樣的問 題。其實白屏或者黑屏還是一些其他的東西,都是因為 Android 主題的問題,只要自己自 定義一個啟動主題,問題完美解決
-
View繪制流程
- View的繪制是由ViewRootImpl來負責的,每個應用程序窗口的decorView都有一個與之關聯的ViewRootImpl對象(ViewRootImpl是DecorView添加到Window中創建的)
- View繪制的起點:
requestLayout()->scheduleTraversals()->performTraversals()
- performTraversals整個繪制流程可以分為以下三個階段
-
performMeasure()->measure()->onMeasure
: 判斷是否需要重新計算View的大小,需要的話則計算-
MeasureSpec
:由SpecMode和SpecSize兩部分組成EXACTLY
(match_parent、數值)
AT_MOST
(wrap_content)
UNSPECIFIED
(不限制,ScrollView) - 具體自定義view復寫
onMeasure()
,處理AT_MOST,setMeasuredDimension()
設置測量的結果
-
-
performLayout()->layout()->onLayout()
: 據上一階段得到的View的測量寬高來確定View的最終顯示位置,判斷是否需要重新計算View的位置,ViewGroup類的onLayout()
是abstract -
performDraw()->draw()->onDraw()
判斷是否需要重新繪制ViewdrawBackground()->onDraw()-> dispatchDraw()-> onDrawForeground()
-
- 重繪
-
requestLayout()
:調用performTraversals()
但不執行draw()
mPrivateFlags
-
invalidate()
:調用performTraversals()
但不執行measure()、layout()
-
-
事件分發機制
-
Android 長連接,怎么處理心跳機制
- 維護任何一個長連接都需要心跳機制,客戶端發送一個心跳給服務器,服務器給客戶端一個心跳應答,這樣就形成客戶端服務器的一次完整的握手,這個握手是讓雙方都知道他們之間的連接是沒有斷開,客戶端是在線的
- 如果超過一個時間的閾值,客戶端沒有收到服務器的應答,或者服務器沒有收到客戶端的心跳,那么對客戶端來說則斷開與服務器的連接重新建立一個連接,對服務器來說只要斷開這個連接即可
- 推送的實現方式
- 客戶端不斷的查詢服務器,檢索新內容,也就是所謂的pull 或者輪詢方式
- 客戶端和服務器之間維持一個TCP/IP長連接,服務器向客戶端push
- 服務器有新內容時,發送一條類似短信的信令給客戶端,客戶端收到后從服務器中下載新內容,也就是SMS的推送方式
- Android推送技術
- 微信Android客戶端后臺?;罱涷灧窒?/a>
-
Android 系統啟動流程
- 通過打開adb shell 然后執行ps命令,我們可以看到首先執行的是0號進程init方法!然后我們找到init.c這個文件,
- 然后走init里面的main方法,在這main方法里面執行mkdir進行創建很多的文件夾,和掛載一些目錄
- 然后回去初始化init.rc這個配置文件!在這個配置文件里面回去啟動zygote這個服務,這個服務會去啟動app_process這個文件夾,這個文件夾里面有個app_main.cpp這個文件!
- 然后在app_main.cpp這個c文件里面在main方法里面它會去啟動安卓的虛擬機,然后安卓虛擬機會去啟動os.zygoteinit這個服務!
- zygoteinit這是個java代碼寫的,然后我們找到了main方法,在這個方法里面我們看到他首先設置虛擬機的最小堆內存為5兆,然后走到preloadclasses()這個方法來加載安卓系統所有的2000多個類通過類加載器加載進來,比如activity,contentx,http,...(其實沒有必要一下子全部加載下來,我們可以等用到的時候在加載也可以?。?/li>
- 然后又走preloadresources()這個方法來預加載安卓中定義好的資源比如顏色,圖片,系統的id等等。。。都加載了?。ㄆ鋵嵾@也是沒必要的! )
- 然后又走startSystemServer(),這個方法來加載系統的服務!他會先使用natvieJNI去調用C去初始化界面和聲音的服務,這就是我們為什么先聽到聲音和界面的原因!
- 最后等服務加載完成后也就啟動起來了! 簡之: linux啟動->init進程啟動(加載init.rc配置)->zygote啟動->systemServer啟動,systemServer會通過init1和init2啟動navite世界和java世界
版本2:
- BootLoder引導,然后加載Linux內核.
- 0號進程init啟動.加載init.rc配置文件,啟動了守護進程、MediaServer、zygote進程
- zygote開始fork出SystemServer進程
- SystemServer加載各種JNI庫,然后init1出Native服務,init2方法,init2方法中開啟了新線程ServerThread.
在SystemServer中會創建一個socket客戶端,創建出FrameWork層的服務,后續AMS(ActivityManagerService)會通過此客戶端和zygote通信 - ServerThread的run方法中開啟了AMS,還孵化新進程ServiceManager,加載注冊了一溜的服務,最后一句話進入loop 死循環
- run方法的SystemReady調resumeTopActivityLocked打開鎖屏界面
-
Zygote 的啟動過程
-
Activity啟動過程
- Activity調用ActivityManagerService啟動應用
- ActivityManagerService調用Zygote孵化應用進程
- Zygote孵化應用進程
- 新進程啟動ActivityThread
- 應用進程綁定到ActivityManagerService
- ActivityThread的Handler處理啟動Activity的消息
- 概要:用戶在Launcher程序里點擊應用圖標時,會通知ActivityManagerService啟動應用的入口Activity,ActivityManagerService發現這個應用還未啟動,則會通知Zygote進程孵化出應用進程,然后在這個dalvik應用進程里執行ActivityThread的main方法。應用進程接下來通知ActivityManagerService應用進程已啟動,ActivityManagerService保存應用進程的一個代理對象,這樣ActivityManagerService可以通過這個代理對象控制應用進程,然后ActivityManagerService通知應用進程創建入口Activity的實例,并執行它的生命周期方法
-
總體類圖
-
ListView的工作原理
- ListView 針對每個item,要求 adapter “返回一個視圖” (getView),也就是說ListView在開始繪制的時候,系統首先調用getCount()函數,根據他的返回值得到ListView的長度,然后根據這個長度,調用getView()一行一行的繪制ListView的每一項
- 如果你的getCount()返回值是0的話,列表一行都不會顯示,如果返回1,就只顯示一行。返回幾則顯示幾行。如果我們有幾千幾萬甚至更多的item要顯示怎么辦?
- 實際上Android早已經緩存了這些視圖,Android中有個叫做Recycler的構件,下面加載的一條Item會復用最上一條滑動出去的Item,
-
優化ListView
- 復用convertView,減少不必要的view的創建與銷毀
- 使用靜態viewHolder,減少findViewById()操作(FB搜索是樹形View的,有點耗時)
- item條目層級盡量簡單,避免布局太深或者不必要的重繪
- 盡量能保證 Adapter 的 hasStableIds() 返回 true這樣在 notifyDataSetChanged() 的時候,如果item內容并沒有變化,ListView 將不會重新繪制這個 View,達到優化的目的
- 避免在 getView 方法中做耗時的操作
- 圖片加載使用多級緩存或者使用成熟框架
- 快速滑動時圖片不加載
- 分頁加載
- 將ListView的scrollingCache和animateCache這兩個屬性設置為false(默認是true)
- 避免在Adapter中使用static來定義全局靜態變量
- 處理listview圖片錯位
-
優化RecyclerView
- 通過布局管理器LayoutManager顯示不同的顯示方式
- 默認Item是沒有間隔的,想要控制Item間的間隔(可繪制),請通過ItemDecoration
- 想要控制Item增刪的動畫,請通過ItemAnimator
- 想要控制點擊、長按事件,請自己寫(在adapter里面綁定控件加)
-
優化自定義view
- onDraw,需要特別注意不應該在這里做內存分配的事情,因為它會導致GC,從而導致卡頓。不要在動畫正在執行的時候做內存分配的事情(耗時操作)
- 盡可能的減少onDraw被調用的次數,大多數時候導致onDraw都是因為調用了invalidate()
- 任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有沖突的值,它會需要重新計算好幾次。另外需要盡量保持View的層級是扁平化的,這樣對提高效率很有幫助
- 復雜的UI,考慮自定義的ViewGroup來執行他的layout操作。與內置的view不同,自定義的view可以使得程序僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小
- 如果是繼承view或者viewGroup,讓view支持wrap_content
- View中如果有動畫或者線程,要在onDetachedFromWindow中及時停止。當view的Activity退出或者當前view被remove時,調用它
- View帶有滑動嵌套時,處理好滑動沖突
-
Android處理UI耗時操作的方法
- Handler:主線程創建handler對象,當執行耗時操作時,新建一個線程通過sendMessage/post等方法,更新ui界面(優點是結構清晰,功能明確,但是代碼過多)
- AsyncTask:內部封裝了線程和Handler,當需要操作ui界面時,會和工作線程通過handler傳遞消息(簡單,快捷,但是占用資源有點多)
- 子線程執行耗時操作,調用Activity的runOnUiThread()方法更新ui,這種方法需要把contex對象強制轉換成activity后使用(簡單好用,只是需要轉遞contex對象)
- Loader/AsyncTaskLoader/CursorLoader
- IntentService
-
ANR
- Application Not Responding,"應用無響應"對話框
- 產生原因:Activity 5s、Service 10s、Broadcast 20s
- 分析log,/data/anr/traces.txt,主要看main Thread CPU usage和是否block死鎖
- 避免ANR方法看上一節
-
Android動畫框架實現原理
- Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪制視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然后調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啟動下次繪制來驅動動畫,動畫過程中的幀之間間隙時間是繪制函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,并不能相應事件
-
淺析LruCache原理
- 用LruCache來取代原來強引用和軟引用實現內存緩存
- LruCache使用一個LinkedHashMap簡單的實現內存的緩存,沒有軟引用,都是強引用。如果添加的數據大于設置的最大值,就刪除最先緩存的數據來調整內存。
- maxSize是通過構造方法初始化的值,他表示這個緩存能緩存的最大值是多少。size在添加和移除緩存都被更新值,他通過safeSizeOf這個方法更新值。safeSizeOf默認返回1,但一般我們會根據maxSize重寫這個方法,比如認為maxSize代表是KB的話,那么就以KB為單位返回該項所占的內存大小。
- 除異常外首先會判斷size是否超過maxSize,,如果超過了就取出最先插入的緩存,如果不為空就刪掉(一般來說只要map不為空都不會返回null,因為他是個雙休鏈表),并把size減去該項所占的大小。這個操作將一直循環下去,直到size比maxSize小或者緩存為空。
public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize) { break; } Map.Entry<K, V> toEvict = map.eldest(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }
-
Service和Activity的交互方式
- Server端將目前的下載進度,通過廣播的方式發送出來,Client端注冊此廣播的監聽器,當獲取到該廣播后,將廣播中當前的下載進度解析出來并更新到界面上。
- 共享文件指的是Activity和Service使用同一個文件來達到傳遞數據的目的。我們使用SharedPreferences來實現共享,當然也可以使用其它IO方法實現,通過這種方式實現交互時需要注意,對于文件的讀寫的時候,同一時間只能一方讀一方寫,不能兩方同時寫。
- 在Server端與Client端之間通過一個Messenger對象來傳遞消息,該對象類似于信息中轉站,所有信息通過該對象攜帶。
- 自定義一個接口,該接口中有一個獲取當前下載進度的空方法。Server端用一個類繼承自Binder并實現該接口,覆寫了其中獲取當前下載進度的方法。Client端通過ServiceConnection獲取到該類的對象,從而能夠使用該獲取當前下載進度的方法,最終實現實時交互。
- AIDL屬于Android的IPC機制,常用于跨進程通信,主要實現原理基于底層Binder機制。
-
UI線程和非UI線程的交互方式
- handler
- Activity.runOnUIThread(Runnable)
- View.Post(Runnable)
- View.PostDelayed(Runnabe,long)
- AsyncTask
-
如何避免ARN
- UI線程盡量只做跟UI相關的工作
- 耗時的工作(比如數據庫操作,I/O,連接網絡或者別的有可能阻礙UI線程的操作)把它放入單獨的線程處理
- 盡量用Handler來處理UIthread和別的thread之間的交互
-
獲取LinearLayout的寬度和高度
- 由于Android程序的運行機制決定了無法再組件類外部使用getWidth和getHeight方法獲得高度和寬度(在自定義組件類中可以實現),必須使用View.getMeasuredWidth和View.getMeasureHeight方法獲得當前組件的寬度和高度,在調用這兩個方法之前,必須調用View.measure方法先測量組件寬度和高度。
- 如果想直接獲取在布局文件中定義的組件的寬度和高度,可以直接使用 View.getLayoutParams().width和View.getLayoutParams().height
View view = getLayoutInflater().inflate(R.layout.activity_main, null); LinearLayout linearlayout = (LinearLayout)view.findViewById(R.id.linearlayout); //measure方法的參數值都設為0即可 linearlayout.measure(0,0); //獲取組件寬度 int width = linearlayout.getMeasuredWidth(); //獲取組件高度 int height = linearlayout.getMeasuredHeight();
- ViewTreeObserver
-
Android 內存管理機制、異常、垃圾回收
- 當 Android 應用程序退出時,并不清理其所占用的內存,Linux 內核進程也相應的繼續存在,所謂“退出但不關閉”。從而使得用戶調用程序時能夠在第一時間得到響應。
- Android 基于進程中運行的組件及其狀態規定了默認的五個回收優先級
- IMPORTANCE_FOREGROUND:
- IMPORTANCE_VISIBLE:
- IMPORTANCE_SERVICE:
- IMPORTANCE_BACKGROUND:
- IMPORTANCE_EMPTY:
-
內存泄漏場景
- 單例造成的內存泄漏(Context持久引用)
- 非靜態內部類創建靜態實例造成的內存泄漏(Context持久引用)
- Handler造成的內存泄漏(Context持久引用、Message對象引用)
- 線程、TimeTask造成的內存泄漏(Activity的隱式引用、合適的時候cancel)
- 資源未關閉、監聽器未反注冊造成的內存泄漏(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)
- 使用ListView時造成的內存泄漏(getView未復用convertView)
- 集合造成內存泄漏(static引用造成大量垃圾回收不了)
- WebView造成的泄露(長期占用的內存不能被回收)
-
避免內存泄露
- 概念:指程序在申請內存后,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積后果很嚴重,無論多少內存,遲早會被占光
- 不要為Context長期保存引用(要引用Context就要使得引用對象和它本身的生命周期保持一致)
- 如果要使用到Context,盡量使用ApplicationContext去代替Context,因為ApplicationContext的生命周期較長,引用情況下不會造成內存泄露問題
- 在你不控制對象的生命周期的情況下避免在你的Activity中使用static變量。盡量使用WeakReference去代替一個static
- 垃圾回收器并不保證能準確回收內存,這樣在使用自己需要的內容時,主要生命周期和及時釋放掉不需要的對象。盡量在Activity的生命周期結束時,在onDestroy中把我們做引用的其他對象做釋放,比如:cursor.close()
-
橫豎屏切換時候Activity的生命周期
- 不設置Activity的android:configChanges時,切屏會重新調用各個生命周期,切橫屏時會執行一次,切豎屏時會執行兩次
- 設置Activity的android:configChanges= orientation 時,切屏還是會重新調用各個生命周期,切橫、豎屏時只會執行一次
- 設置Activity的android:configChanges= orientation|keyboardHidden 時,切屏不會重新調用各個生命周期,只會執行onConfigurationChanged方法
-
requestLayout在什么時候用呢
- 當view確定自身已經不再適合現有的區域時,該view本身調用這個方法要求parent view(父類的視圖)重新調用他的onMeasure onLayout來重新設置自己位置。特別是當view的layoutparameter發生改變,并且它的值還沒能應用到view上時,這時候適合調用這個方法
- 區別于invalidate與postInvalidate:重新mesure、layout、draw
-
FragmentPagerAdapter與FragmentStatePagerAdapter使用詳解與區別
- FragmentPagerAdapter不會銷毀Fragment, 適用于那些相對靜態的頁,數量也比較少的那種
- 如果需要處理有很多頁,并且數據動態性較大、占用內存較多的情況,應該使用FragmentStatePagerAdapter,會銷毀不需要的Fragment,當擁有大量的頁面時,不必在內存中占用大量的內存,更省內存
- FragmentPagerAdapter詳解
-
提升SQLite數據插入效率低、速度慢的方法
- 慢速——最粗暴的方法(SQLite的API中直接執行SQL)
- 中速——顯式開啟事務(就是指一組SQL命令,這些命令要么一起執行,要么都不被執行)
- 高速——寫同步(synchronous)
- 極速——執行準備(一種是使用前文提到的函數sqlite3_exec(),該函數直接調用包含SQL語句的字符串;另一種方法就是“執行準備”(類似于存儲過程)操作,即先將SQL語句編譯好,然后再一步一步(或一行一行)地執行。)
- SQLite插入數據效率最快的方式就是:事務+關閉寫同步+執行準備(存儲過程),如果對數據庫安全性有要求的話,就開啟寫同步
-
Activity與Fragment之間的區別
- 區別:
- fragment顯得更加靈活??梢灾苯釉赬ML文件中添加
<fragment/>
,Activity則不能 - 可以在一個界面上靈活的替換一部分頁面,activity不可以,做不到
- fragment顯得更加靈活??梢灾苯釉赬ML文件中添加
- 通信(也就是控件的相互操控):
- fragment控制fragment:得到一個Activity,然后通過這個Activity的getFragmentManager()獲得該Fragment的實例。
- fragment控制Activity:這個很簡單。每個Fragment都有getActivity()得到一個活動。MainActivity activity=getActivity();
- Activity控制fragment:
getFragmentManager().findFragmentById();
- Activity控制Activity:這個顯然是通過Intent活動之間的通信完成。別忘了在被打開的活動中創建Intent和得到Intent一起進行,寫個靜態的actionStart()。
- 區別:
-
Activity與Fragment通信方案
- handler:該方案存在的缺點:
- Fragment對具體的Activity存在耦合,不利于Fragment復用
- 不利于維護,若想刪除相應的Activity,Fragment也得改動
- 沒法獲取Activity的返回數據
- handler的使用個人感覺就很不爽(不知大家是否有同感)
- 廣播方案缺點:
- 用廣播解決此問題有點大材小用了,個人感覺廣播的意圖是用在一對多,接收廣播者是未知的情況
- 廣播性能肯定會差
- 傳播數據有限制(必須得實現序列化接口才可以)
- EventBus方案:
- EventBus是用反射機制實現的,性能上會有問題
- EventBus難于維護代碼
- 沒法獲取Activity的返回數據
- 接口方案
- 假如項目很大了,Activity與Fragment的數量也會增加
- handler:該方案存在的缺點:
-
Activity與Service通信方案(區別于是否同個進程)
- Aidl,回調(listener)
- Messager
- 廣播 (推薦LocalBroadcastManager)
- 開源組件(EventBus,otto)
-
Android常見的內存緩存算法
- LRU即Least RecentlyUsed,近期最少使用算法:當內存緩存達到設定的最大值時將內存緩存中近期最少使用的對象移除,有效的避免了OOM的出現
- Least Frequently Used(LFU):
對每個緩存對象計算他們被使用的頻率。把最不常用的緩存對象換走 - First in First out(FIFO):
這是一個低負載的算法,并且對緩存對象的管理要不高。通過一個隊列去跟蹤所有的緩存對象,最近最常用的緩存對象放在后面,而更早的緩存對象放在前面,當緩存容量滿時,排在前面的緩存對象會被踢走,然后把新的緩存對象加進去 - LargestLimitedMemoryCache:超過指定緩存的話,每次移除棧最大內存的緩存的對象
-
Context相關
- Context直系子類有兩個,一個是ContextWrapper,一個是ContextImpl。那么從名字上就可以看出,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity
- Context類型:
- 分別是Application、Activity和Service
- 每一個Activity和Service以及Application的Context都是一個新的ContextImpl對象
- Application/Activity/Service作為Context的區別
- Context獲取:
- getApplicationContext() 返回應用的上下文,生命周期是整個應用,應用摧毀它才摧毀
- Activity.this的context 返回當前activity的上下文,屬于activity ,activity 摧毀他就摧毀
-
getBaseContext() 返回由構造函數指定或setBaseContext()設置的上下文
類結構圖
-
Service與Thread區別
- Thread:Thread是程序執行的最小單元,它是分配CPU的基本單位??梢杂?Thread 來執行一些異步的操作
- Service:Service 是android的一種機制,當它運行的時候如果是Local Service,那么對應的 Service 是運行在主進程的 main 線程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程的 main 線程上運行的。如果是Remote Service,那么對應的 Service 則是運行在獨立進程的 main 線程上
- Thread 的運行是獨立于 Activity 的,也就是說當一個 Activity 被 finish 之后,如果你沒有主動停止 Thread 或者 Thread 里的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這里會出現一個問題:當 Activity 被 finish 之后,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制
- Android的后臺就是指,它的運行是完全不依賴UI的。即使Activity被銷毀,或者程序被關閉,只要進程還在,Service就可以繼續運行。比如說一些應用程序,始終需要與服務器之間始終保持著心跳連接,就可以使用Service來實現
- 在一個Activity中創建的子線程,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的實例,因此,使用Service來處理后臺任務,Activity就可以放心地finish,完全不需要擔心無法對后臺任務進行控制的情況。
-
Apk打包過程
- (aapt) 打包資源文件,生成R.java文件
- 把xml文件編譯成二進制文件,解析速度更快并減少文件大小
- (aidl) 處理AIDL文件,生成相應的Java文件
- (javac) 編譯工程源代碼,生成相應的class文件
- (dex) 轉換所有的class文件(包括3rd庫),生成classes.dex文件
- (apkbuilder) 將classes.dex、resources.arsc、res文件夾(res/raw不變)、Other Resources(assets文件夾)、AndroidManifest.xml打包成apk文件
- (jarsigner) 對apk包進行簽名
- (zipalign) 對簽名后的apk文件進行對對齊處理
- (aapt) 打包資源文件,生成R.java文件
-
如何退出Activity,如何安全退出已調用多個Activity的Application?
- 拋異常強制退出,android.os.Process.killProcess(android.os.Process.myPid());
- 在Appliction類中記錄打開的Activity,每打開一個Activity,就記錄下來。在需要退出時,關閉每一個Activity即可
- 創建發送特定廣播的Activity,在需要結束應用時,發送一個特定的廣播,每個Activity收到廣播后,關閉即可
- 遞歸退出,在打開新的Activity時使用startActivityForResult,然后自己加標志,在onActivityResult中處理,遞歸關閉
- 用intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一個新的activity,然后在新的activity的oncreate方法里面 finish掉
-
自定義控件的生命周期
- onFinishInflate() 當View中所有的子控件均被映射成xml后觸發
- onMeasure( int , int ) 確定所有子元素的大小
- onLayout( boolean , int , int , int , int ) 當View分配所有的子元素的大小和位置時觸發
- onSizeChanged( int , int , int , int ) 當view的大小發生變化時觸發,啟動時間在onDraw之前
- onDraw(Canvas) view渲染內容的細節
- onKeyDown( int , KeyEvent) 有按鍵按下后觸發
- onKeyUp( int , KeyEvent) 有按鍵按下后彈起時觸發
- onTrackballEvent(MotionEvent) 軌跡球事件
- onTouchEvent(MotionEvent) 觸屏事件
- onFocusChanged( boolean , int , Rect) 當View獲取或失去焦點時觸發
- onWindowFocusChanged( boolean ) 當窗口包含的view獲取或失去焦點時觸發
- onAttachedToWindow() 當view被附著到一個窗口時觸發
- onDetachedFromWindow() 當view離開附著的窗口時觸發,Android123提示該方法和 onAttachedToWindow() 是相反的
- onWindowVisibilityChanged( int ) 當窗口中包含的可見的view發生變化時觸發
-
Force Close相關
- 比如空指針啦,類沒有找到啦,資源沒找到,就連Android API使用的順序錯誤也可能導致比如setContentView()之前進行了findViewById()操作)
- 如何避免彈出Force Close窗口 可以在Application類中實現Thread.UncaughtExceptionHandler接口的uncaughtException方法
-
縮減APK包大小
- 保持良好的編程習慣,不要重復或者不用的代碼,謹慎添加libs,移除使用不到的libs,使用proguard混淆代碼,它會對不用的代碼做優化,并且混淆后也能夠減少安裝包的大小
- 使用Lint工具查找沒有使用到的資源。去除不使用的圖片,String,XML等等
- 能用代碼繪制實現的功能,盡量不要使用大量的圖片
-
android 關于安全的問題,你所知道的所有的安全問題
- 錯誤導出組件
- 參數校驗不嚴
- WebView引入各種安全問題
- 不混淆、不防二次打包
- 明文存儲關鍵信息
- 錯誤使用HTTPS
- 山寨加密方法
-
Android之安全機制
- 代碼安全:反編譯,混淆proguard
- 應用權限的設置:操作增加限制,防止惡意應用進行非法操作給用戶造成敏感數據泄漏和設備被非法控制
- 數字證書:只有同一包名且采用同一數字證書的應用才被認為是同一個應用
- 網絡安全:DES,RSA,SSL
- 文件訪問控制:采用的UGO權限機制
-
關于APK數字簽名
- 所有的應用程序都必須有數字證書,Android系統不會安裝一個沒有數字證書的應用程序
- Android程序包使用的數字證書可以是自簽名的,不需要一個權威的數字證書機構簽名認證
- 如果要正式發布一個Android ,必須使用一個合適的私鑰生成的數字證書來給程序簽名,而不能使用adt插件或者ant工具生成的調試證書來發布
- 數字證書都是有有效期的,Android只是在應用程序安裝的時候才會檢查證書的有效期。如果程序已經安裝在系統中,即使證書過期也不會影響程序的正常功能
-
新特性
- Android5.0新特性
- MaterialDesign設計風格
- 支持多種設備
- 支持64位ART虛擬機
- Android6.0新特性
- 運行時權限
- 大量漂亮流暢的動畫
- 支持快速充電的切換
- 支持文件夾拖拽應用
- 相機新增專業模式
- Android7.0新特性
- 分屏多任務
- 增強的Java8語言模式
- 夜間模式
- Android8.0新特性
- 畫中畫模式
- Android5.0新特性
-
如何避免Overdraw
- 合理選擇控件容器:LinearLayout易用,效率高,表達能力有限。RelativeLayout復雜,表達能力強,效率稍遜
- 去掉window的默認背景:可以在onCreate()中setContentView()之后調用
getWindow().setBackgroundDrawable(null);
或者在theme中添加android:windowbackground="null";
- 去掉其他不必要的背景
- ViewStu延時加載布局
- Merge減少層級的嵌套
-
SparseArray
- 是稀疏數組,為了節省內存空間,并且不影響數組中原有的內容值,采用一種壓縮的方式來表示稀疏數組的內容
- SparseArray是android里為<Interger,Object>這樣的Hashmap而專門寫的類,目的是提高內存效率
- 避免了自動裝箱(auto-boxing)
- 數據結構不會依賴于外部對象映射,用數組數據結構來保存映射,然后通過折半查找來找到對象
- 一般來說,SparseArray執行效率比HashMap要慢一點,因為查找需要折半查找,而添加刪除則需要在數組中執行,而HashMap都是通過外部映射。但相對來說影響不大,最主要是SparseArray不需要開辟內存空間來額外存儲外部映射,從而節省內存
-
Jar和Aar的區別
- Jar包里面只有代碼,aar里面不光有代碼還包括代碼還包括資源文件,比如 drawable 文件,xml 資源文件。對于一些不常變動的 Android Library,我們可以直接引用 aar,加快編譯速度
-
ART和Dalvik區別
- Dalvik:
- Dalvik是Google公司自己設計用于Android平臺的Java虛擬機
- .dex格式是專為Dalvik應用設計的一種壓縮格式,工作在寄存器
- Dalvik經過優化,允許在有限的內存中同時運行多個虛擬機的實例,并且每一個Dalvik應用作為獨立的Linux進程執行
- 獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉
- ART:
- Dalvik是依靠一個Just-In-Time(JIT)編譯器去解釋字節碼,開發者編譯后的應用代碼需要通過一個解釋器在用戶的設備上運行,并不高效
- ART在應用安裝的時候就預編譯字節碼到機器語言,這一機制叫Ahead-Of-Time(AOT)編譯,在移除解釋代碼這一過程后,應用程序執行將更有效率,啟動更快
- ART優點:1.系統性能的顯著提升應用啟動更快、2. 運行更快、3. 體驗更流暢、觸感反饋更及時、4. 更長的電池續航能力支持更低的硬件
- ART缺點:1. 更大的存儲空間占用、2. 可能會增加10%-20%更長的應用安裝時間
- 區別:
- ART:Ahead of Time ,Dalvik: Just in Time
- ART上應用啟動快,運行快,但是耗費更多存儲空間,安裝時間長,總的來說ART的功效就是”空間換時間”
- Dalvik:
總結的Android面試題
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
推薦閱讀更多精彩內容
- Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
- 1.什么是Activity?問的不太多,說點有深度的 四大組件之一,一般的,一個用戶交互界面對應一個activit...