Activity的加載模式
standard,singleTop,singleTask,singleInstance。standard為普通模式只要調(diào)用就會(huì)在棧中創(chuàng)建實(shí)例,singleTop棧頂復(fù)用模式如果次Activity在棧頂直接復(fù)用不再創(chuàng)建實(shí)例一般用作SettingsActivity的啟動(dòng),singleTask任務(wù)棧模式創(chuàng)建此實(shí)例在其之上的實(shí)例都會(huì)被退出一般用作HomeActivity的啟動(dòng),singleInstance單獨(dú)創(chuàng)建一個(gè)新的任務(wù)棧,單例模式一般用作接受分享的ShareActivity。
Activity的生命周期
Activity
從未被初始化到出現(xiàn)在Window中可見再到銷毀所經(jīng)歷的生命周期:onCreate()-->onStart()-->onResume()-->onPause()-->onStop()-->onDestroy()。在面試中會(huì)提出一個(gè)場景讓你描述一下生命周期,注意back鍵和home鍵的區(qū)別,
- Activity啟動(dòng)到可見:onCreate()-->onStart()-->onResume()
- 按下back鍵執(zhí)行過程:onPause()-->onStop()-->onDestroy()
- 按下home鍵:onPause()-->onStop()
- 按下home鍵回到桌面在進(jìn)入:onPause()-->onStop()-->onStart()-->onResume()
- 息屏:onPause()-->onStop()
- 息屏到點(diǎn)亮:onPause()-->onStop()-->onStart()-->onResume()
- 當(dāng)?shù)诙?code>Activity出現(xiàn)在它上面:onPause()-->(新開頁面onCreate()-->onStart()-->onResume())-->onStop()
- 從新開頁面退回:onPause()-->(目標(biāo)頁面onStart()-->onResume())-->onStop()-->onDestroy()
Fragment生命周期
Fragment
是為了解決Android碎片化的問題是一段具有自己完整的生命周期并依附在其他具有生命周的組件上的可執(zhí)行片段。
生命周期 onAttach()-->onCreate()-->onCreateView()-->onActivityCreated()-->onStart()-->onResume()-->onPause()-->onStop()-->onDestroyView()-->onDestroy()--onDetach()
- 從不可見到可見:onAttach()-->onCreate()-->onCreateView()-->onActivityCreated()-->onStart()-->onResume()
- 如果加入到任務(wù)棧按返回鍵:onPause()-->onStop()-->onDestroyView()-->onDestroy()--onDetach()
- 按home鍵:onPause()-->onStop()
- 再回到Window:onStart()-->onResume()
Service的兩種啟動(dòng)模式生命周期
- startService()
- bindService()
Service是Android四大組件之一,也是可執(zhí)行的程序,有自己的生命周期。創(chuàng)建、配置Service和創(chuàng)建、配置Activity的過程相似。和Activity一樣,都是從Context派生出來的。
區(qū)別按啟動(dòng)方式:
start方式:
??生命周期:onCreate()--->onStartCommand()(onStart()方法已過時(shí)) ---> onDestory()
??說明:如果服務(wù)已經(jīng)開啟,不會(huì)重復(fù)的執(zhí)行onCreate(),而是會(huì)調(diào)用onStart()和onStartCommand()。服務(wù)停止的時(shí)候調(diào)用 onDestory()。服務(wù)只會(huì)被停止一次。
??特點(diǎn):一旦服務(wù)開啟跟調(diào)用者(開啟者)就沒有任何關(guān)系了。開啟者退出了,開啟者掛了,服務(wù)還在后臺(tái)長期的運(yùn)行。開啟者不能調(diào)用服務(wù)里面的方法。
bind方式:
??生命周期:onCreate()--->onBind()----->onunBind() ---> onDestory()
??注意:綁定服務(wù)不會(huì)調(diào)用onstart()或者onstartcommand()方法
??特點(diǎn):bind的方式開啟服務(wù),綁定服務(wù),調(diào)用者掛了,服務(wù)也會(huì)跟著掛掉。綁定者可以調(diào)用服務(wù)里面的方法。
exported屬性
Android四大組件中Activity
,Service
,BroadcastReceiver
創(chuàng)建后需要在manifest
文件中注冊
<!-- 顯式啟動(dòng) -->
<activity
android:name=".weather.WeatherStandActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:label="@string/weather_info"
android:launchMode="singleTask"
android:theme="@style/Theme.Dark.Weather"
android:windowSoftInputMode="adjustResize|stateHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 隱式啟動(dòng) -->
<activity
android:name="com.master.android.test.LaunchModeActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize|stateHidden">
<intent-filter>
<action android:name="android.intent.action.LaunchMode" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="info" />
</intent-filter>
</activity>
<!-- service -->
<service
android:name="com.master.android.test.aidl.AIDLService"
android:exported="true">
<intent-filter>
<action android:name="com.master.android.test.aidl.IMyAidlInterface" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
注意:防止屏幕旋轉(zhuǎn)導(dǎo)致Activity
重新onCreate()
配置時(shí)加入屬性android:configChanges="orientation|screenSize"
,對軟盤的配置android:windowSoftInputMode="adjustResize|stateHidden"
保證內(nèi)容區(qū)域不被擠壓。如果此組件可供外部調(diào)用則都需要配置android:exported="true"
屬性。
Handler機(jī)制?Handler,Looper,Message,MessageQueue,Thread之間的關(guān)系
Handler機(jī)制即事Android線程之間通訊機(jī)制,Handler允許您發(fā)送和處理{@link Message}和Runnable
與線程的{@link MessageQueue}相關(guān)聯(lián)的對象。每個(gè)處理者實(shí)例與單個(gè)線程和該線程的消息相關(guān)聯(lián)隊(duì)列。當(dāng)你創(chuàng)建一個(gè)新的處理程序時(shí),它綁定到線程/正在創(chuàng)建它的線程的消息隊(duì)列-從那時(shí)起,它會(huì)將消息和可運(yùn)行文件傳遞到該消息隊(duì)列并執(zhí)行他們從消息隊(duì)列中出來。
1.Handler依賴與線程
2.主線程已經(jīng)有自己的消息循環(huán)器(Looper.getMainLooper())
3.在子線程中更新消息必須手動(dòng)創(chuàng)建Looper,線程不會(huì)幫你創(chuàng)建
4.具有消息循環(huán)功能的子線程的Handler就可以在任何線程發(fā)送消息
5.一個(gè)線程是否只有一個(gè)Looper?不一定,但是應(yīng)該盡力保證他只有一個(gè)。參考:ThreadLocal
多線程的方式有哪些?
- new Thread()
- AsyncTask
- Handler
- IntentService
- ThreadPoolExecutor
在子線程更新UI的方式
- runOnUiThread(new Runnable());
- View.post(new Runnable())
- View.postDelayed(new Runnable(), long delayMillis)
- Handler.sendMessage(message)或Handler.sendEmptyMessage(int what)
其實(shí)前面三種方法最終都是調(diào)用主線程Handler發(fā)送了一個(gè)非掩飾的空消息Handler.sendEmptyMessage(int what)
ANR異常發(fā)生條件和處理方式
- 5s內(nèi)沒有響應(yīng)用戶輸入事件
- 10s內(nèi)廣播接收器沒有處理完畢
- 20s內(nèi)服務(wù)沒有處理完畢
處理方式:log+trace.txt
事件處理
- 分發(fā),dispatchTouchEvent(MotionEvent ev)
- 攔截,onInterceptTouchEvent(MotionEvent ev)
- 處理,onTouchEvent(MotionEvent event)
注:只是View沒有攔截處理。ViewGroup具有攔截事件,View和Activity沒有ViewGroup類中的源碼實(shí)現(xiàn)就是{return false;}表示不攔截該事件,事件將向下傳遞(傳遞給其子View);若手動(dòng)重寫該方法,使其返回true則表示攔截,事件將終止向下傳遞,事件由當(dāng)前ViewGroup類來處理,就是調(diào)用該類的onTouchEvent()方法
??關(guān)于事件處理的方法:
float rawX = event.getRawX(); // 對屏幕而言,絕對
float rawY = event.getRawY();
float x = event.getX(); // 對起點(diǎn)坐標(biāo)而言,相對
float y = event.getY();
// 兩次滑動(dòng)距離常量最小值 , 用于過濾掉滑動(dòng)距離太短的值
int scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
// 速度追蹤
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000); // 計(jì)算當(dāng)前速度,時(shí)間T為1000ms
float xVelocity = velocityTracker.getXVelocity(); // 橫向水平速度
float yVelocity = velocityTracker.getYVelocity(); // 縱向水平速度
velocityTracker.recycle(); // 回收
/**
* 手勢檢測,用于檢測用戶的單擊、滑動(dòng)、長按、雙擊等行為。
*/
private GestureDetector mGestureDetector = new GestureDetector(getContext(), onGestureListener);
mGestureDetector.setOnDoubleTapListener(onDoubleTapListener);
/**
* 手勢監(jiān)聽
*/
private GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
/**
* 手指輕觸的一瞬間 觸發(fā)MotionEvent.ACTION_DOWN
*/
@Override
public boolean onDown(MotionEvent e) {
return false;
}
/**
* 手指輕觸的一瞬間 尚未松開或拖動(dòng) 觸發(fā)MotionEvent.ACTION_DOWN
*/
@Override
public void onShowPress(MotionEvent e) {
}
/**
* 手指輕觸屏幕后松開 這是單擊行為 觸發(fā)MotionEvent.ACTION_UP
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
/**
* 手指按下屏幕并拖動(dòng) 這是拖動(dòng)行為 觸發(fā)MotionEvent.ACTION_DOWN
*和一系列MotionEvent.ACTION_MOVE
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
/**
* 用戶長久的按著屏幕不放 這是長安行為
*/
@Override
public void onLongPress(MotionEvent e) {
}
/**
* 用戶按下屏幕快速滑動(dòng)后松開 這是快速滑動(dòng)行為 觸發(fā)一個(gè)MotionEvent.ACTION_DOWN
* 多個(gè)MotionEvent.ACTION_MOVE和一個(gè)MotionEvent.ACTION_UP
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
};
/**
* 監(jiān)聽雙擊事件
*/
private GestureDetector.OnDoubleTapListener onDoubleTapListener = new GestureDetector.OnDoubleTapListener() {
/**
* 嚴(yán)格的單擊行為
*/
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
/**
* 雙擊 兩次連續(xù)的單擊 它不可能和onSingleTapConfirmed共存
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
/**
* 雙擊行為 并觸發(fā)MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP
*/
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
};
/**
* 彈性滑動(dòng)對象,實(shí)現(xiàn)彈性的過度效果
*/
private Scroller mScroller = new Scroller(getContext());
四大引用
- 強(qiáng) (StrongReference)
- 軟 (SoftReference)
- 弱(WeakReference)
- 虛 (PhantomReference)
弄懂對GC的影響,
- 強(qiáng)引用(StrongReference)
我們使用的大部分引用實(shí)際上都是強(qiáng)引用,這是使用最普遍的引用。如果一個(gè)對象具有強(qiáng)引用,那就類似于必不可少的生活用品,垃圾回收器絕不會(huì)回收它。當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對象來解決內(nèi)存不足問題。 - 軟引用(SoftReference)
如果內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了,就會(huì)回收這些對象的內(nèi)存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。 - 弱引用(WeakReference)
在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存。不過,由于垃圾回收器是一個(gè)優(yōu)先級很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對象。 弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。 - 虛引用(PhantomReference)
如果一個(gè)對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收。虛引用主要用來跟蹤對象被垃圾回收的活動(dòng)。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用。當(dāng)垃 圾回收器準(zhǔn)備回收一個(gè)對象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。程序可以通過判斷引用隊(duì)列中是否已經(jīng)加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動(dòng)。
動(dòng)畫
- View動(dòng)畫(補(bǔ)間動(dòng)畫)
- 屬性動(dòng)畫
- 幀動(dòng)畫
補(bǔ)間動(dòng)畫和屬性動(dòng)畫的區(qū)別:補(bǔ)間動(dòng)畫改變的是View的位置和狀態(tài)但本質(zhì)上它的原始坐標(biāo)并未改變,屬性動(dòng)畫是通過改變View具有g(shù)et/set屬性的值位置和狀態(tài)發(fā)生本質(zhì)變化。屬性動(dòng)畫的多樣性更多不只移動(dòng),旋轉(zhuǎn),伸縮,透明度的變化。
Dalvik和Art區(qū)別?
- Just In Time(即時(shí)編譯技術(shù))和Ahead Of Time(預(yù)編譯技術(shù))的區(qū)別
- Dalvik基于寄存器 JVM基于棧
- Art(Android Runtime)
參考:Android 中的Dalvik和ART是什么,有啥區(qū)別?
自定義View和ViewGroup
- onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- onDraw(Canvas canvas)
- onLayout(boolean changed, int left, int top, int right, int bottom)
最要的三個(gè)方法,onMeasure
的widthMeasureSpec
,heightMeasureSpec
是父布局封裝后傳過來的規(guī)格通過MeasureSpec獲取模式和尺寸
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
在通過模式來控制View的寬高,MeasureSpec.EXACTLY
模式相當(dāng)于macth_parent
那么View的寬度或高度就等于widthSize,heightSize。MeasureSpec.AT_MOST
模式相當(dāng)于wrap_content
則View的寬高就等于繪制區(qū)域或?qū)嶋H或設(shè)置的寬高。MeasureSpec.UNSPECIFIED
模式一般很少用到。
switch (mode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
break;
}
在自定義View時(shí)在布局文件中設(shè)置寬度為macth_parent
和wrap_content
是等效的,原因是:View源碼MeasureSpec.AT_MOST
和MeasureSpec.EXACTLY
時(shí)寬高都等于屏幕剩余的寬高。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
怎么獲取繪制文本的寬高,首先在構(gòu)造方法中執(zhí)行TextPaint.getTextBounds方法,然后通過Rect區(qū)域?qū)嵗@取寬高。
TextPaint.getTextBounds(text, 0, text.length(), mRect); // 獲取文本寬高
int width = mRect.width();
int height = mRect.height();
關(guān)于坐標(biāo)系:Android系統(tǒng)的坐標(biāo)系和其他平臺(tái)一樣采用相對坐標(biāo),并以矩形構(gòu)圖。對父布局而言左上角是原始起點(diǎn)(0,0),height相當(dāng)于y軸坐標(biāo),width相當(dāng)于x軸坐標(biāo)。
在onDraw方法中注意過度繪制(OverDraw)
onLayout在自定義View就不需要重寫了,關(guān)鍵是自定義ViewGroup時(shí)子控件的測量與擺放。
注意:自定義View時(shí)顏色盡量轉(zhuǎn)化為ColorStateList
,字體的大小注意dp與px的轉(zhuǎn)化使用
TypedValue.applyDimension(unit, size, DisplayMetrics)
轉(zhuǎn)換,在面試中被問到自定義控件時(shí)講一個(gè)自己自定義的控件就好了
GC算法
- 標(biāo)記清除(Mark & Sweep 算法)
- 引用計(jì)數(shù)算法
- 節(jié)點(diǎn)復(fù)制算法
- 分代回收
基本概念:有向可達(dá)圖與根集(GC Roots),引用鏈,可達(dá)性,標(biāo)記位。如果節(jié)點(diǎn)向下檢索未找到對象的引用鏈則沒有可達(dá)性建議回收,這時(shí)也不是一定會(huì)被回收,GC會(huì)做一次標(biāo)記,如果第二次依然被標(biāo)記才會(huì)被回收。
什么是過渡繪制,如何防止過渡繪制
定義:過度繪制造成UI卡頓的原因是因?yàn)樗速M(fèi)大量的CPU以及GPU資源。手機(jī)原本為了保持視覺的流暢度,其屏幕刷新頻率是60hz
,即在1000/60=16.67ms
內(nèi)更新一幀。如果沒有完成任務(wù),就會(huì)發(fā)生掉幀的現(xiàn)象,也就是我們所說的卡頓。
??怎么查看過度繪制:調(diào)試手機(jī)開發(fā)者選項(xiàng)打開強(qiáng)制GPU渲染
怎么避免過度繪制:
- 盡量多使用
RelativeLayout
和LinearLayout
,不要使用絕對布局AbsoluteLayout
, - 在布局層次一樣的情況下,建議使用
LinearLayout
代替RelativeLayout
,因?yàn)?code>LinearLayout性能要稍高一點(diǎn). - 在完成相對較復(fù)雜的布局時(shí),建議使用
RelativeLayout
,RelativeLayout
可以簡單實(shí)現(xiàn)LinearLayout
嵌套才能實(shí)現(xiàn)的布局. - 將可復(fù)用的組件抽取出來并通過
include
標(biāo)簽使用; - 使用
ViewStub
標(biāo)簽來加載一些不常用的布局; - 動(dòng)態(tài)地
inflationview
性能要比SetVisiblity
性能要好.當(dāng)然用VIewStub
是最好的選擇. - 使用
merge
標(biāo)簽減少布局的嵌套層次 - 去掉多余的背景顏色
- 對于有多層背景顏色的
Layout
來說,留最上面一層的顏色即可,其他底層的顏色都可以去掉 - 對于使用Selector當(dāng)背景的
Layout
(比如ListView
的Item
,會(huì)使用Selector
來標(biāo)記點(diǎn)擊,選擇等不同的狀態(tài)),可以將normal
狀態(tài)的color
設(shè)置為@android:color/transparent
,來解決對應(yīng)的問題 - 內(nèi)嵌使用包含
layout_weight
屬性的LinearLayout
會(huì)在繪制時(shí)花費(fèi)昂貴的系統(tǒng)資源,因?yàn)槊恳粋€(gè)子組件都需要被測量兩次。在使用ListView
與GridView
的時(shí)候,這個(gè)問題顯的尤其重要,因?yàn)樽咏M件會(huì)重復(fù)被創(chuàng)建.所以要盡量避免使用Layout_weight
- 使得
Layout
寬而淺,而不是窄而深(在HierarchyViewer
的Tree
視圖里面體現(xiàn))
線程與進(jìn)程的區(qū)別
- 線程
- 進(jìn)程
- synchronized
進(jìn)程:當(dāng)一個(gè)程序第一次啟動(dòng)的時(shí)候,Android會(huì)啟動(dòng)一個(gè)LINUX進(jìn)程和一個(gè)主線程。默認(rèn)的情況下,所有該程序的組件都將在該進(jìn)程和線程中運(yùn)行。 同時(shí),Android會(huì)為每個(gè)應(yīng)用程序分配一個(gè)單獨(dú)的LINUX用戶。Android會(huì)盡量保留一個(gè)正在運(yùn)行進(jìn)程,只在內(nèi)存資源出現(xiàn)不足時(shí),Android會(huì)嘗試停止一些進(jìn)程從而釋放足夠的資源給其他新的進(jìn)程使用, 也能保證用戶正在訪問的當(dāng)前進(jìn)程有足夠的資源去及時(shí)地響應(yīng)用戶的事件。
??我們可以將一些組件運(yùn)行在其他進(jìn)程中,并且可以為任意的進(jìn)程添加線程。組件運(yùn)行在哪個(gè)進(jìn)程中是在manifest
文件里設(shè)置的,其中Activity
,Service
,receiver
和provider
都有一個(gè)process
屬性來指定該組件運(yùn)行在哪個(gè)進(jìn)程之中。我們可以設(shè)置這個(gè)屬性,使得每個(gè)組件運(yùn)行在它們自己的進(jìn)程中,或是幾個(gè)組件共同享用一個(gè)進(jìn)程,或是不共同享用。application
元素也有一個(gè)process
屬性,用來指定所有的組件的默認(rèn)屬性。
進(jìn)程按級別分類: 分為5個(gè)級別
- 前臺(tái)進(jìn)程
- 其中運(yùn)行著正與用戶交互的Activity(Activity對象的 onResume() 方法已被調(diào)用)。
- 其中運(yùn)行著被正與用戶交互的activity綁定的服務(wù)Service。
- 其中運(yùn)行著“前臺(tái)”服務(wù)Service——服務(wù)以startForeground()方式被調(diào)用。
- 其中運(yùn)行著正在執(zhí)行生命周期回調(diào)方法(onCreate()、onStart()或onDestroy())的服務(wù)Service。
- 其中運(yùn)行著正在執(zhí)行onReceive()方法的BroadcastReceiver。
- 可見進(jìn)程
- 其中運(yùn)行著不在前臺(tái)的Activity,但用戶仍然可見到此activity(onPause()方法被調(diào)用了)。比如以下場合就可能發(fā)生這種情況:前臺(tái)activity打開了一個(gè)對話框,而之前的activity還允許顯示在后面。
- 其中運(yùn)行著被可見(或前臺(tái))activity綁定的服務(wù)Service。
- 服務(wù)進(jìn)程
- 后臺(tái)進(jìn)程
- 空進(jìn)程
線程: 應(yīng)用程序啟動(dòng)時(shí),系統(tǒng)會(huì)為它創(chuàng)建一個(gè)名為main
的主線程。主線程非常重要,因?yàn)樗?fù)責(zé)把事件分發(fā)給相應(yīng)的用戶界面widget——包括屏幕繪圖事件。它也是應(yīng)用程序與Android UI
組件包(來自android.widget
和android.view
包)進(jìn)行交互的線程。因此,主線程有時(shí)也被叫做UI
線程。
??系統(tǒng)并不會(huì)為每個(gè)組件的實(shí)例都創(chuàng)建單獨(dú)的線程。運(yùn)行于同一個(gè)進(jìn)程中的所有組件都是在UI線程中實(shí)例化的,對每個(gè)組件的系統(tǒng)調(diào)用也都是由UI
線程分發(fā)的。因此,對系統(tǒng)回調(diào)進(jìn)行響應(yīng)的方法(比如報(bào)告用戶操作的onKeyDown()
或生命周期回調(diào)方法)總是運(yùn)行在UI
線程中。
??如果UI線程需要處理每一件事情,那些耗時(shí)很長的操作——諸如訪問網(wǎng)絡(luò)或查詢數(shù)據(jù)庫等——將會(huì)阻塞整個(gè)UI
(線程)。一旦線程被阻塞,所有事件都不能被分發(fā),包括屏幕繪圖事件。從用戶的角度看來,應(yīng)用程序看上去像是掛起了。更糟糕的是,如果UI
線程被阻塞超過一定時(shí)間(目前大約是5秒鐘),用戶就會(huì)被提示(ANR)對話框。如果引起用戶不滿,他可能就會(huì)決定退出并刪除這個(gè)應(yīng)用程序。
??Andoid的UI組件包并不是線程安全的。因此不允許從工作線程中操作UI——只能從UI線程中操作用戶界面。于是,Andoid的單線程模式必須遵守兩個(gè)規(guī)則:1.不要阻塞UI線程。2.不要在UI線程之外訪問Andoid的UI組件包。
synchronized: Synchronized
是Java中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法。
?? 作用:
- 確保線程互斥的訪問同步代碼
- 保證共享變量的修改能夠及時(shí)可見
- 有效解決重排序問題
用途:
- 修飾普通方法
- 修飾靜態(tài)方法
- 修飾代碼塊
volatile
?? 緩存一致性問題,一旦一個(gè)共享變量(類的成員變量、類的靜態(tài)成員變量)被volatile
修飾之后,那么就具備了兩層語義:
- 保證了不同線程對這個(gè)變量進(jìn)行操作時(shí)的可見性,即一個(gè)線程修改了某個(gè)變量的值,這新值對其他線程來說是立即可見的。
- 禁止進(jìn)行指令重排序。
在多進(jìn)程中,Application會(huì)啟動(dòng)幾次
這個(gè)問題涉及到Android進(jìn)程分配問題,在默認(rèn)情況下Application
只在一個(gè)默認(rèn)的進(jìn)程中啟動(dòng),只有顯示的指定process
進(jìn)程中啟動(dòng),所在多進(jìn)程中Application
會(huì)啟動(dòng)多次。
獲取進(jìn)程名方法:
private String getProcessName(Context context) {
ActivityManager activityManager = (ActivityManager) context.
getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses =
activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo process : runningAppProcesses) {
if (process.processName != null) {
return process.processName;
}
}
return null;
}
在onCreate()
方法中將獲取的進(jìn)程與配置的進(jìn)程進(jìn)行比較,如果是在該進(jìn)程中啟動(dòng)的就做該進(jìn)程的事。
跨進(jìn)程間通訊
- Binder機(jī)制
由于Android系統(tǒng)對進(jìn)程的設(shè)計(jì)保證了應(yīng)用程序之間不能共享內(nèi)存,但有時(shí)需要不同程序之間要進(jìn)行數(shù)據(jù)交互,所Android推出了跨進(jìn)程間通訊的幾種機(jī)制。
??什么是Binder機(jī)制:傳統(tǒng)的IPC
機(jī)制是不安全,完全依賴上層協(xié)議。傳統(tǒng)IPC
的接收方無法獲得對方進(jìn)程可靠的UID
和PID
(用戶ID進(jìn)程ID),從而無法鑒別對方身份。Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID
,故進(jìn)程的UID
是鑒別進(jìn)程身份的重要標(biāo)志。使用傳統(tǒng)IPC
只能由用戶在數(shù)據(jù)包里填入UID
和PID
,但這樣不可靠,容易被惡意程序利用。可靠的身份標(biāo)記只有由IPC
機(jī)制本身在內(nèi)核中添加。其次傳統(tǒng)IPC訪問接入點(diǎn)是開放的,無法建立私有通道。比如命名管道的名稱,systemV
的鍵值,socket
的ip
地址或文件名都是開放的,只要知道這些接入點(diǎn)的程序都可以和對端建立連接,不管怎樣都無法阻止惡意程序通過猜測接收方地址獲得連接。所以Android需要建立一套新的IPC機(jī)制來滿足系統(tǒng)對通信方式,傳輸性能和安全性的要求,這就是Binder。Binder基于Client-Server通信模式,傳輸過程只需一次拷貝,為發(fā)送發(fā)添加UID/PID身份,既支持實(shí)名Binder也支持匿名Binder,安全性高。對Binder
而言,Binder
可以看成Server
提供的實(shí)現(xiàn)某個(gè)特定服務(wù)的訪問接入點(diǎn),Client
通過這個(gè)‘地址’向Server
發(fā)送請求來使用該服務(wù);對Client
而言,Binder
可以看成是通向Server
的管道入口,要想和某個(gè)Server
通信首先必須建立這個(gè)管道并獲得管道入口。
??其中Activity可以跨進(jìn)程調(diào)用其他應(yīng)用程序的Activity
;Content Provider
可以跨進(jìn)程訪問其他應(yīng)用程序中的數(shù)據(jù)(以Cursor
對象形式返回),當(dāng)然,也可以對其他應(yīng)用程序的數(shù)據(jù)進(jìn)行增、刪、改操 作;Broadcast
可以向android系統(tǒng)中所有應(yīng)用程序發(fā)送廣播,而需要跨進(jìn)程通訊的應(yīng)用程序可以監(jiān)聽這些廣播;Service
和Content Provider
類似,也可以訪問其他應(yīng)用程序中的數(shù)據(jù),但不同的是,Content Provider
返回的是Cursor
對象,而Service
返回的是Java對象,這種可以跨進(jìn)程通訊的服務(wù)叫AIDL
服務(wù)。
Activity的跨進(jìn)程間訪問
ComponentName componentName = new ComponentName(String pkg, String cls);
MyParcelable parcel = new MyParcelable();
Intent intent = new Intent();
intent.setComponent(componentName);
intent.putExtra("parcel_key", parcel);
startActivity(intent);
Service和AIDL結(jié)合
// 第一步定義一個(gè)AIDL接口
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
// 與Service相關(guān)聯(lián)
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IMyAidlInterface.Stub stub;
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
stub = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) throws RemoteException {
Log.e(TAG, "basicTypes: " + anInt + aLong + aBoolean + aFloat + aDouble + aString);
}
};
return stub;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
// client端,第一步需要在同包下寫一個(gè)相同aidl文件
private IMyAidlInterface anInterface;
private boolean mBound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
anInterface = IMyAidlInterface.Stub.asInterface(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
private void initAIDL() {
Intent intent = new Intent();
intent.setAction("com.master.android.test.aidl.IMyAidlInterface");
intent.setPackage("com.master.android.test.aidl");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStart() {
super.onStart();
if (!mBound) {
initAIDL();
}
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(connection);
mBound = false;
}
}
// 調(diào)用方法
anInterface.basicTypes(1, 2, true, 3, 4, "hello aidl");
// Stub類中asInterface函數(shù)作用是通過binder拿到aidl的Java類
總結(jié): 跨進(jìn)程間通訊不管你是使用Messager+Handler+Service還是Service+aidl其本質(zhì)都是以Binder機(jī)制為基礎(chǔ)
單例模式,雙鎖原理,volatile原理,靜態(tài)內(nèi)部類實(shí)現(xiàn)單例的原理###
單例模式懶漢普通版:
public class DateUtil {
private static DateUtil INSTANCE;
private DateUtil() {}
public static DateUtil getInstance() {
if (INSTANCE == null) {
INSTANCE = new DateUtil();
}
return INSTANCE;
}
}
單例模式懶漢開掛模式:雙鎖原理,volatile原理。雙鎖原理保證了在多線程下線程絕對安全,volatile又保證了在棧中取出對象地址和new對象順序的一致性。
public class DateUtil {
private static volatile DateUtil INSTANCE;
private DateUtil() {
}
public static synchronized DateUtil getInstance() {
if (INSTANCE == null) {
synchronized (DateUtil.class) {
INSTANCE = new DateUtil();
}
}
return INSTANCE;
}
}
單例模式餓漢模式
public class DateUtil {
private static DateUtil INSTANCE = new DateUtil();
private DateUtil(){}
public static DateUtil getInstance() {
return INSTANCE;
}
}
靜態(tài)內(nèi)部類實(shí)現(xiàn)單例的原理:將這個(gè)單實(shí)例的引用變量定義在靜態(tài)內(nèi)部類中,來實(shí)現(xiàn)單例,這樣可以做到不用if條件進(jìn)行判斷,并且是多線程安全的(由jvm保證)
public class DateUtil {
private DateUtil() {
}
private static class Singleton {
private static DateUtil INSTANCE;
static {
INSTANCE = new DateUtil();
}
}
public static DateUtil getInstance() {
return Singleton.INSTANCE;
}
}
ListView的優(yōu)化
ListView 的核心原理就是重用 View。ListView 中有一個(gè)回收器,Item 滑出界面的時(shí)候 View 會(huì)回收到這里,需要顯示新的 Item 的時(shí)候,就盡量重用回收器里面的 View。
- 在adapter中的getView方法中盡量少使用邏輯
- 盡最大可能避免GC(將創(chuàng)建對象放到ViewHolder里)
- 滑動(dòng)的時(shí)候不加載圖片
- 將ListView的scrollingCache和animateCache設(shè)置為false
- item的布局層級越少越好
- 使用ViewHolder
- 善用convertView(容器)
- 有圖片加載時(shí)滑出屏幕不加載
// 滑動(dòng)時(shí)不加載圖片
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView listView, int scrollState) {
//停止加載圖片
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
imageLoader.stopProcessingQueue();
} else {
//開始加載圖片
imageLoader.startProcessingQueue();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
HashMap的實(shí)現(xiàn)原理
概述: HashMap
是基于哈希表的Map
接口的非同步實(shí)現(xiàn)。此實(shí)現(xiàn)提供所有可選的映射操作,并允許使用null
值和null
鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變。
??數(shù)據(jù)結(jié)構(gòu):在java編程語言中,最基本的結(jié)構(gòu)就是兩種,一個(gè)是數(shù)組,另外一個(gè)是模擬指針(引用),所有的數(shù)據(jù)結(jié)構(gòu)都可以用這兩個(gè)基本結(jié)構(gòu)來構(gòu)造的,HashMap
也不例外。HashMap
實(shí)際上是一個(gè)“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體。最上層是一組數(shù)組每個(gè)元素在數(shù)組中的位置(即下標(biāo))對應(yīng)一個(gè)向下的鏈表,數(shù)組中的位置相當(dāng)于key
鏈表縱深相當(dāng)于value
每次put數(shù)據(jù)時(shí)新的放到最上層老數(shù)據(jù)向下排。
解決hash沖突:
- 開放尋址法
- 再散列法
- 拉鏈法(鏈地址法)
- 建立公共溢出區(qū)
Android中的內(nèi)存泄漏和內(nèi)存溢出有什么區(qū)別?
內(nèi)存溢出: 是指程序在申請內(nèi)存的時(shí)候,沒有足夠的內(nèi)存可以分配,導(dǎo)致Out Of Memory
錯(cuò)誤,也就是OOM
。
??內(nèi)存泄漏:對象都有生命周期的,在生命周期完成之后,就該被垃圾回收和釋放,如果得不到及時(shí)的釋放,就會(huì)一直占用內(nèi)存,造成內(nèi)存泄漏。隨著內(nèi)存泄漏的堆積,可用的內(nèi)存空間越來越少,最后會(huì)導(dǎo)致內(nèi)存溢出。導(dǎo)致內(nèi)存泄漏有很多原因,最常見的有內(nèi)部類的使用,因?yàn)閮?nèi)部類持有外部引用。還有就是對Activity Contex
t的使用,如果沒有特別的要求,盡量使用Application context.
避免其他地方持有Activity而得不到釋放,如果必須要用Activity Context
,可以用弱引用。
在Activity中如何保存/恢復(fù)狀態(tài)?
分別調(diào)用onSaveInstanceState和onRestoreInstanceState保存和恢復(fù)狀態(tài)。
Android 中序列化有哪些方式?區(qū)別?
- Serializable(Java自帶)
- Parcelable(android 專用)
除了Serializable
之外,使用Parcelable
也可以實(shí)現(xiàn)相同的效果,不過不同于將對象進(jìn)行序列化,Parcelable
方式的實(shí)現(xiàn)原理是將一個(gè)完整的對象進(jìn)行分解,而分解后的每一部分都是Intent所支持的數(shù)據(jù)類型,這樣也就實(shí)現(xiàn)傳遞對象的功能了。
??區(qū)別:Parcelable
比Serializable
性能高,所以應(yīng)用內(nèi)傳遞數(shù)據(jù)推薦使用Parcelable
,但是Parcelable
不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤上的情況,因?yàn)?code>Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管Serializable
效率低點(diǎn),但此時(shí)還是建議使用Serializable
。
MVP架構(gòu)的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
-
View
和Model
之間的耦合度降低,使其更關(guān)注自身業(yè)務(wù)邏輯,結(jié)構(gòu)清晰,維護(hù)方便; - 便于單元測試;
- 代碼復(fù)用率提高;
- 代碼框架更適用于快速迭代開發(fā);
缺點(diǎn):
-
View
層過大,Activity
復(fù)雜,加入模板方法,分離出BaseActivity
用于處理公共邏輯 -
Model
層過大,做好模塊劃分,進(jìn)行接口隔離,在內(nèi)部進(jìn)行分層。 - 還有一個(gè)問題是,MVP額外的增加了很多類和接口,這個(gè)可以根據(jù)項(xiàng)目實(shí)際情況進(jìn)行相應(yīng)地優(yōu)化
OkHttp 的addInterceptor 和 addNetworkInterceptor 的區(qū)別?
addInterceptor(應(yīng)用攔截器):
- 不需要擔(dān)心中間過程的響應(yīng),如重定向和重試.
- 總是只調(diào)用一次,即使HTTP響應(yīng)是從緩存中獲取.
- 觀察應(yīng)用程序的初衷. 不關(guān)心OkHttp注入的頭信息如: If-None-Match.
- 允許短路而不調(diào)用 Chain.proceed(),即中止調(diào)用.
- 允許重試,使 Chain.proceed()調(diào)用多次.
addNetworkInterceptor(網(wǎng)絡(luò)攔截器):
- 能夠操作中間過程的響應(yīng),如重定向和重試.
- 當(dāng)網(wǎng)絡(luò)短路而返回緩存響應(yīng)時(shí)不被調(diào)用.
- 只觀察在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù).
- 攜帶請求來訪問連接.