什么情況下會導致內存泄露
如何對Android 應用進行性能分析以及優化;
說一款你認為當前比較火的應用并設計(直播APP);
OOM的避免異常及解決方法;
1.什么是ANR 如何避免它?
Android App優化之ANR詳解
ANR 應用程序無響應 根本原因不要在主線程(UI線程做繁重的工作)
使用主線程的地方
1.Activity的所有生命周期回調都是執行在主線城的
2.Service默認是執行在主線城的
3.BroadCastReceiver的onReceive回調是在主線程的
4.沒有使用子線程的looper的Handler的handlerMessage,post(Runnable) 是在主線城的
5.AsyncTask的回調中除了doInBackground()方法,其他回調都是在主線城的
6.View的post(Runnable)是執行在主線城的
使用子線程的地方
1.使用Thread 方式
1)繼承Thread 2)實現Runnable接口
2.使用AsyncTask方式
3.使用HandlerThread方式
Android中結合Handler和Thread的一種方式. 默認情況下Handler的handleMessage是執行在主線程的, 但是如果我給這個Handler傳入了子線程的looper, handleMessage就會執行在這個子線程中的. HandlerThread正是這樣的一個結合體:
// 啟動一個名為new_thread的子線程
HandlerThread thread = new HandlerThread("new_thread");
thread.start();
// 取new_thread賦值給ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 此時handleMessage是運行在new_thread這個子線程中了.
}
}
4.IntentService
Service是運行在主線程的, 然而IntentService是運行在子線程的.
實際上IntentService就是實現了一個HandlerThread + ServiceHandler的模式.
以上HandlerThread的使用代碼示例也就來自于IntentService源碼
5.Loader
Android 3.0引入的數據加載器, 可以在Activity/Fragment中使用. 支持異步加載數據, 并可監控數據源在數據發生變化時傳遞新結果. 常用的有CursorLoader, 用來加載數據庫數據.
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
// 使用LoaderManager來初始化Loader
getLoaderManager().initLoader(0, null, this);
//如果 ID 指定的加載器已存在,則將重復使用上次創建的加載器。
//如果 ID 指定的加載器不存在,則 initLoader() 將觸發 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以實現代碼以實例化并返回新加載器
// 創建一個Loader
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
// 加載完成
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
View的繪制流程
View的繪制是從ViewRoot的performTraversals()方法開始的,經過measure,layout,draw這個3大步驟。
measure的過程:
measure過程是對整個view樹的所有控件計算寬高
measure是沖ViewRoot類中的host.measure開始的,內部調用的是View的measure(int widthMeasureSpec,int heightMeasureSpec)方法,measure方法里面調用了onMeasure(int widthMeasureSpec,int heightMeasureSpec)方法,方法中的兩個參數都是是MeasureSpec類型(指父控件對子控件寬高的期望值,它是一個32位的int類型數,前兩位表示測量模式,后30位表示測量大小)
測量模式一共有3種:
1)EXACTLY 精確測量模式,xml文件中寫200dp,march_parent等代表使用該模式,
2)AT_MOST 最大模式,xml文件中寫wrap_content表示使用該模式。
3)UNSPECIFIED 無限大測量模式,只有在繪制特定自定義View時才用的到這個模式。
真正代表測量結束的方法是setMeasuredDimension方法,該方法傳入的兩個參數是寬高的SpecSize。測量結束后我們可以通過getMeasureHeight和getMeasureWidth來獲取測量寬高。
自定義ViewGroup一定要重寫onMeasure方法,用于測量子View的寬高,不重寫的話子View沒有寬高。
自定義View如果在xml中使用了wrap_content屬性,就需要重寫onMeasure方法來設置wrap_content的默認大小,不然會顯示出match_parent的效果。
layout的過程:
ViewGroup用來將子View放在合適的位置上。
layout是從ViewRoot類中的host.layout開始的,內部調用的是ViewGroup的layout方法。在ViewGroup的layout方法中,先調用setFrame來確定自己的左上右下的位置,再調用onLayout來確定子View的位置。
自定義ViewGroup一定要重寫layout方法來確定子View的位置,自定義View一般不需要重寫該方法,它的位置是右父控件確定的。
draw過程:
此過程是真正將內容展示在屏幕上讓我們能夠看到的過程。
draw是從ViewRoot類中的host.draw開始的,內部調用的是View的draw方法。
draw的步驟:
1)繪制背景。
2)繪制內容,也就是調用onDraw方法。
3)繪制子View,調用的是dispatchDraw方法。
4)繪制裝飾,如listview的滾動條等。
對于View的繪制過程,既可以說是簡單的,也可以說是復雜的,簡單的在于Google已經幫我們將draw框架寫好了,我們在自定義ViewGroup時不用管draw過程,只需要實現measure和layout過程。復雜在于,我們寫繼承View的自定義控件的時候需要重寫onDraw方法,這樣才能繪制出你自定義的View的內容,onDraw(Canvas canvas)方法中最重要的兩個東西是Paint和Canvas,這個使用起來算是比較復雜的。
Android View的繪制流程
自定義View如何考慮機型適配
- 合理使用warp_content,match_parent.
- 盡可能的是使用RelativeLayout
- 針對不同的機型,使用不同的布局文件放在對應的目錄下,android會自動匹配。
- 盡量使用點9圖片。
- 使用與密度無關的像素單位dp,sp
- 引入android的百分比布局。
- 切圖的時候切大分辨率的圖,應用到布局當中。在小分辨率的手機上也會有很好的顯示效果。
自定義View的事件分發機制
安卓自定義View進階-事件分發機制詳解
安卓自定義View進階-事件分發機制原理
View和ViewGroup分別有哪些事件分發相關的回調方法
自定義View如何提供獲取View屬性的接口
Art和Dalvik對比
在程序運行過程中Dalvik虛擬機不斷的進行將字節碼轉換為機器碼的工作。
而Art引入了AOT這種預編譯技術,在應用程序的安裝過程中已經將所有的字節碼編譯為了機器碼,在運行的時候直接調用。Art極大的提高了程序的運行效率,同時減少了手機的耗電量,在垃圾回收機制上也有很大的優化,但是Art模式下應用程序的安裝需要消耗更多的時間,同時也需要跟多的安裝空間。
Dalvik 是Android4.4及以下平臺的虛擬機。
Art 是在Android4.4以上平臺使用的虛擬機。
虛擬機原理,如何自己設計一個虛擬機(內存管理,類加載,雙親委派);
JVM內存模型及類加載機制
內存對象的循環引用及避免
ddms 和 traceView的區別
1, ddms:是android開發環境中的dalvik虛擬機調試監控服務;
ddms能夠提供,測試設備截屏,針對特定的進程查看正在運行的線程以及堆信息,Logcat,廣播狀態信息,模擬電話呼叫,接收sms,虛擬地理坐標等。
2,traceView是android平臺配備的性能分析的工具;它可以通過圖形化讓我們了解要跟蹤的程序的性能,并且能具體到方法。
區別:ddms是一個程序執行查看器,在里面可以看見線程和堆棧等信息,traceView是程序性能分析器。
內存回收機制與GC算法(各種算法的優缺點以及應用場景)
gc是java的垃圾回收機制
引用計數法(Reference Counting Collector):使用計數器來區分存活對象和不再使用的對象。一般來說,堆中的每個對象對應一個引用計數器。當每一次創建一個對象并賦給一個變量時,引用計數器置為1。當對象被賦給任意變量時,引用計數器每次加1當對象出了作用域后(該對象丟棄不再使用),引用計數器減1,一旦引用計數器為0,對象就滿足了垃圾收集的條件。
標記算法(Tracing Collector):使用了根集的概念,基于tracing算法的垃圾收集器從根集開始掃描,識別出哪些對象可達,哪些對象不可達,并用某種方式標記可達對象。
整理算法(Compacting Collecotr):該算法會將所有的對象移到堆的一端。能解決堆碎片的問題。
復制算法:將內存分為兩個區域(from space 和 to space)。所有的對象都分配到from space。清理時先將所有標為活動對象copy到to space,然后清除from space空間。然后互換from space和to apce的身份,每次清理都重復上述過程。
gc收集器:serial收集器:單線程,工作時必須暫停其他工作線程,多用于client機器上,使用復制算法。
ParNew收集器:serial的多線程版本,server模式下jvm首選的新生代收集器。復制算法。
Parallel Scavenge收集器:可控制吞吐量的收集器,吞吐量指有效運行時間。復制算法。
Serial Old收集器:serial的老年代版本,使用整理算法。
Parallel Old收集器:Parallel Scavenge收集器的老版本,多線程,標記整理。
CMS收集器:整理算法。最短回收停頓時間,缺點是產生碎片。
GI收集器:基本思想是化整為零,將堆分為多個Region,優先回收價值最大的Region。并行并發,分代收集,空間整合。整理算法。
GC原理時機以及GC對象
內存泄露場景及解決方法
屏幕適配的處理技巧都有哪些
四大組件及生命周期
Activity
Service
Service 官方資料 需翻墻
Service 資料
ContentProvider
BroadcastReceiver
ContentProvider的權限管理(讀寫分離,權限控制-精確到表級,URL控制);
Activity的四種啟動模式對比
Android 深入解析 Activity 的 launchMode 啟動模式,Intent Flag,taskAffinity
Activity狀態保存于恢復
什么是AIDL 以及如何使用
AIDL:android interface definition language的縮寫。
AIDL是用來實現進程間通信的,可以幫我們實現發布以及調用遠程服務。
使用:
1)服務端:創建一個Service用來監聽客戶端的連接請求,然后創建一個AIDL文件,將服務端暴露給客戶端的接口在這個文件中聲明,最后在Service中實現這個AIDL接口。
2)客戶端:首先綁定服務端的Service,綁定成功后將服務端返回的Binder對象轉成AIDL接口所屬的類型,接著就可以調用AIDL中的方法。
請解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關系
Android--異步消息處理機制(Handler、Looper、Message、MessageQueue)
Fragment生命周期
onAttach(),onCreate(),onCreateView(),onActivityCreated(),onViewStateRestore(),onStart(),onResume(),onPause(),onStop(),onDestroy(),onDestroyView(),onDetach(),
Fragment狀態保存
startActivityForResult是哪個類的方法,在什么情況下使用,如果在Adapter中使用應該如何解耦
AsyncTask原理及不足
intentService原理
HandlerThread+ServiceHandler
Activity 怎么和Service 綁定,怎么在Activity 中啟動自己對應的Service
請描述一下Service 的生命周期;
啟動服務的生命周期 onCreate->onStartCommand()->onDestroy()
綁定服務的生命周期 onCreate()->onBind()->onUnBind()->onDestroy()
AsyncTask+HttpClient與AsyncHttpClient有什么區別;
如何保證一個后臺服務不被殺死,比較省電的方式是什么
Android中通過Service實現后臺任務。
方法一:
通過將Service綁定到Notification,成為一個前提服務,可以提高存活率
在Service中創建一個Notification,再調用Service.startForeground(int id,Notification notification)方法運行在前臺即可。這個方式使用360等如阿健管家可以殺死。
方法二:
通過定時警報來不斷啟動Service,這樣就算Service被殺死,也能再啟動。同時也可以監聽網絡切換,開鎖屏等廣播啟動Service。
參考:
Intent intent = new Intent(mContext,MyService.class);
PendingIntent sender = PendingIntent.getService(mContext,0,intent,0);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),5*10000,sender);
這種方式不斷啟動的邏輯處理起來很麻煩。
方法三:
通過jni調用c,在c語音中啟動一個進程fork()。 可以保證360等手機管家不會清理。但是帶來了jni交互,稍微有點麻煩。
如何通過廣播攔截和abort一條短信;
廣播是否可以請求網絡
廣播引起anr的時間限制
進程間通信,AIDL
事件分發中的onTouch 和onTouchEvent 有什么區別,又該如何使用?
說說ContentProvider、ContentResolver、ContentObserver 之間的關系;
請介紹下ContentProvider 是如何實現數據共享的
Android提供了ContentProvider,一個程序可以通過實現一個ContentProvider的抽象接口將自己的數據完全暴露出去,而且ContentProvicer是以類似數據庫中表的方式將數據暴露。也就是說ContentProvider就像一個“數據庫”。那么外界獲取其提供的數據,也就應該與從數據庫中獲取數據的操作基本一樣,只不過是采用URI來表示外界需要訪問的“數據庫”。外部訪問通過ContentResolver去訪問并操作這些被暴露的數據。
Handler機制及底層實現
Handler包括四個角色:
Handler:負責發送消息處理消息。
Message:消息實體對象,handler通過sendMessage將實體放到消息隊列中。
MessageQueQue:存放消息的隊列。
Looper:消息輪詢器,不停的從消息隊列中取出消息交給handler處理。
在主線程創建Handler,在需要發送消息的地方創建一個Message,通過handler發送。這個消息回到MessageQueQue中,然后Looper會將這個消息取出交給handler處理。
Handler可以有多個,但是在同一線程中Looper和MessageQueQue只能有一個。
Binder機制及底層實現
Binder包含四個角色:
Server 服務器
Client 客戶終端 ,獲得實名Binder的引用。Server向ServiceManger注冊了Binder實體及名字后,Client就可以通過名字獲得該Binder的引用。例如我們申請獲得名字叫張三的Binder的引用,ServiceManager收到這個連接請求,從請求數據包里獲得Binder的名字。再找到該名字對應的條目,從條目中取出Binder的引用。將該引用作為回復發送給發起請求的Client。
ServiceManager 域名服務器(DNS),負責將字符形式的Binder名字轉化成Client中對該Binder的應用,使得Client能通過Binder名字獲得Server中Binder實體的引用。
Binder驅動 可以理解為路由器。Binder驅動負責進程之間Binder通信的建立,Binder在進程間的傳遞。
Binder使用Client-Server通信方式,安全性好,簡單高效。再加上其面向對象的設計思想,獨特的接收緩存管理和線程池管理方式,成為Android進程間通信的中流砥柱。
ListView 中圖片錯位的問題是如何產生的
錯位原理: 如果我們只是簡單的顯示數據,沒有convertView的復用和異步操作,就不會產生圖片錯位。重用convertView但沒有異步操作也不會有錯位現象。例如我們的listView中剛好顯示7個item,當向下滑動時,顯示出item8,而item8是重用的item1,如果此時異步網絡請求item8的圖片,比item1的圖片慢,那么item8就會顯示item1的圖片。當item8下載完成,此時用戶向上滑顯示item1時,又復用的item8的image。這樣就導致的圖片錯位。
解決方法: 對imageview設置tag,并預設一張圖片。向下滑動后,item8顯示,item1隱藏。但由于item1是第一次進來就顯示所以一般情況下,item1都會比item8先下載完,此時可見的item8的tag和隱藏了的item1的url不匹配,所以就算item1的圖片下載完也不會顯示到item8中,因為tag標識的永遠是可見圖片中的url
holder.img.setTag(imgUrl);
holder.img.setImageResource(R.drawable.ic_launcher);
if(imageView.getTag() != null && imageView.getTag().equals(imageUrl)){ imageView.setImageBitmap(result);}
在manifest 和代碼中如何注冊和使用BroadcastReceiver
<receiver android:name="包名.自己擴展的廣播接收者名">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
說說Activity、Intent、Service 是什么關系
ApplicationContext和ActivityContext的區別
這是兩種不同的context,也是最常見的兩種.第一種中context的生命周期與Application的生命周期相關的,context隨著Application的銷毀而銷毀,伴隨application的一生,與activity的生命周期無關.第二種中的context跟Activity的生命周期是相關的,但是對一個Application來說,Activity可以銷毀幾次,那么屬于Activity的context就會銷毀多次.至于用哪種context,得看應用場景,個人感覺用Activity的context好一點,不過也有的時候必須使用Application的context.application context
一張Bitmap所占內存以及內存占用的計算
Serializable 和Parcelable 的區別
在Android上應該盡量采用Parcelable,它效率更高。
Parcelabe代碼比Serializable多一些。
Parcelabe比Serializable速度高十倍以上。
Serializable只需要對某個類以及它的屬性實現Serializable接口即可,無需實現方法。缺點是使用的反射,序列化的過程較慢,這種機制會在序列化的時候創建許多的臨時對象。容易觸發GC。
Parcable方法實現的原理是將一根完整的對象進行分解,而分解后的每一部分都是Intent所支持的數據類型,這樣也就實現傳遞對象的功能。
請描述一下BroadcastReceiver
請描述一下Android 的事件分發機制
請介紹一下NDK
一 : NDK是一系列工具的集合
NDK提供了一系列的工具,幫助開發者快速開發C或C++的動態庫,并能自動將so和java應用一起打包成apk。這些工具對開發者的幫助是巨大的。
NDK集成了交叉編譯器,并提供了相應的mk文件隔離CPU,平臺,ABI等差異。開發人員只需要簡單修改mk文件(指出哪些文件需要編譯,編譯特性要求等)就可以創建出so。
NDK可以自動將so和java應用一起打包,極大的減輕了開發人員的打包工作。
二 : NDK提供了一份穩定,功能有限的API頭文件聲明。
Google明確聲明該API是穩定的,在后續所有版本中都穩定支持當前發布的API。從該版本的NDK中看出,這些API支持的功能非常有限,包含:c標準庫,標準數學庫,壓縮庫,Log庫。
什么是NDK庫,如何在jni中注冊native函數,有幾種注冊方式
AsyncTask 如何使用;
對于應用更新這塊是如何做的?(灰度,強制更新,分區域更新);
混合開發,RN,weex,H5,小程序(做Android的了解一些前端js等還是很有好處的)
混合開發就是在一個App中內嵌一個輕量級的瀏覽器,一部分源生的功能改為Html5來開發,這部分功能不僅能夠在不升級App的情況下動態更新,也可以在Android或者iOS的App上同時運行,讓用戶的體驗更好又可以節省開發的資源。
混合開發最主要的是Html5和Native的交互
在Android中WebView本來就支持js和java相互調用,只需要開啟WebView的腳本執行,然后通過mWebView.addJavascriptInterface(new JsBridge(),"bxbxbai");向Html5頁面注入一股Java對象,然后就可以在Html5頁面中調用Native的功能了。
Android4.2以后的系統規定允許被js調用的Java方法必須以@JavascriptInterface進行注解。
兩個Activity 之間跳轉時必然會執行的是哪幾個方法?
答:一般情況下比如說有兩個activity,分別叫A,B,當在A 里面激活B 組件的時候, A 會調用onPause()方法,然后B 調用onCreate() ,onStart(), onResume()。
這個時候B 覆蓋了窗體, A 會調用onStop()方法. 如果B 是個透明的,或者是對話框的樣式, 就不會調用A 的onStop()方法。