網(wǎng)絡(luò)篇:
1
.TCP的三次握手和四次揮手:
答:
三次握手
:為了知道雙方已經(jīng)準(zhǔn)備好發(fā)送數(shù)據(jù)了平项,二次握手會(huì)造成延遲到達(dá)的請(qǐng)求到達(dá)服務(wù)端使服務(wù)端進(jìn)入等待狀態(tài),造成浪費(fèi)栗菜,而三次握手可以識(shí)別是首次握手和第三次握手(第一次握手發(fā)送seq=x;第二次握手ack=x+1,seq=y庇茫,第三次握手發(fā)送seq=x+1,ack=y+1螃成,這次握手其實(shí)可以攜帶數(shù)據(jù))旦签。
四次揮手
:為了知道雙方已經(jīng)結(jié)束發(fā)送數(shù)據(jù),客戶端通知服務(wù)端自己已經(jīng)不能發(fā)送數(shù)據(jù)寸宏,需要知道服務(wù)端是否收到宁炫,這個(gè)需要兩次握手,反之服務(wù)端也需要通過兩次握手才能知道(為什么不能第二次的時(shí)候直接發(fā)送關(guān)閉請(qǐng)求呢氮凝,這樣不是三次就夠了嗎羔巢? 因?yàn)檫@個(gè)時(shí)候不確定是否不能確定服務(wù)端不能發(fā)送信息,所以只是一個(gè)應(yīng)答的過程)罩阵,客戶端等待2個(gè)傳遞時(shí)間的原因:客戶端接收到服務(wù)端的傳送結(jié)束響應(yīng)竿秆,這個(gè)時(shí)候可能有前一個(gè)數(shù)據(jù)還沒到客戶端。
第一次握手
第二次握手
第三次握手
https解決的問題:正確獲得服務(wù)端的共鑰稿壁,采用證書機(jī)制袍辞,另外為了優(yōu)化解密速度,采取對(duì)稱加密
2
.TCP流量控制:
答:
解決方案:滑動(dòng)窗口常摧,滑動(dòng)窗口的原理:
接收窗口動(dòng)態(tài)改變
3
.TCP擁塞控制:
答:
指數(shù)增長(zhǎng)階段稱之為慢啟動(dòng)
搅吁。
線性增長(zhǎng)階段稱之為擁塞避免
。
快重傳算法
首先要求接收方每收到一個(gè)失序的報(bào)文段后就立即發(fā)出重復(fù)確認(rèn)(為的是使發(fā)送方及早知道有報(bào)文段沒有到達(dá)對(duì)方)而不要等到自己發(fā)送數(shù)據(jù)時(shí)才進(jìn)行捎帶確認(rèn)落午。
4
.Https:
答:添加證書機(jī)制谎懦,目的獲取正確的共鑰對(duì)一個(gè)隨機(jī)數(shù)加密傳給服務(wù)端,之后用這個(gè)隨機(jī)數(shù)加密通信過程(加快解密速度)溃斋。
第三方庫(kù):
1
.EventBus:
用于線程間通信界拦,訂閱模式(觀察者模式)的經(jīng)典應(yīng)用。
原理速講:
//在activity里注冊(cè)梗劫,這個(gè)就相當(dāng)于把發(fā)布者(EventBus.getDefault())和 訂閱者(activity)聯(lián)系起來
EventBus.getDefault().register(this);
看看register怎么做的(版本不同 大體思路差不多 根據(jù)V2版本)
public void register(Object subscriber) {
// 暫時(shí)不考慮粘后兩個(gè)參數(shù)性事件和優(yōu)先級(jí)
this.register(subscriber, false, 0);
}
主體部分享甸,代碼都很簡(jiǎn)潔截碴,往下看
private synchronized void register(Object subscriber, boolean sticky, int priority) {
// 獲取這個(gè)Activity對(duì)應(yīng)的SubscriberMethod的列表 ,SubscriberMethod是一個(gè)描述當(dāng)前Activity對(duì)應(yīng)的訂閱方法的類蛉威,有成員變量method日丹,methodString,eventType蚯嫌,threadMode哲虾,這幾個(gè)變量就可以確認(rèn)具體是哪個(gè)訂閱方法
List<SubscriberMethod> subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
Iterator i$ = subscriberMethods.iterator();
while(i$.hasNext()) {
SubscriberMethod subscriberMethod = (SubscriberMethod)i$.next();
// subscribe 訂閱方法是核心 單獨(dú)拿出來分析一下也很簡(jiǎn)潔
this.subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
接著看下subscribe
方法,去掉了粘性相關(guān)代碼
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
// 獲取方法的eventType
Class<?> eventType = subscriberMethod.eventType;
// Subscription是一個(gè)描述activity和對(duì)應(yīng)訂閱方法的類 择示,這里獲取所有訂閱了這個(gè)方法的Subscription對(duì)象
CopyOnWriteArrayList<Subscription> subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventType);
// 創(chuàng)建了一個(gè)本次Activity對(duì)應(yīng)的Subscription對(duì)象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
// 檢測(cè)是否已經(jīng)有不同的的activity訂閱過這方法束凑,沒有就新建,相同activity訂閱就拋異常栅盲。
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList();
this.subscriptionsByEventType.put(eventType, subscriptions);
} else if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
int size = subscriptions.size();
// 按照優(yōu)先級(jí)插入對(duì)應(yīng)的位置(包含該訂閱方法的集合)
for(int i = 0; i <= size; ++i) {
if (i == size || newSubscription.priority > ((Subscription)subscriptions.get(i)).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscriber是一個(gè)activity包含所有訂閱方法的集合
List<Class<?>> subscribedEvents = (List)this.typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList();
this.typesBySubscriber.put(subscriber, subscribedEvents);
}
((List)subscribedEvents).add(eventType);
}
這里可以看到有兩個(gè)集合 一個(gè)是包含相同事件的對(duì)象集合 一個(gè)是某個(gè)activity包含所有訂閱方法的集合汪诉。說到底, 注冊(cè)過程就是往集合里添加數(shù)據(jù)的一個(gè)過程
,可以猜想post發(fā)送事件谈秫,是一個(gè)從集合里獲取對(duì)象執(zhí)行的一個(gè)過程
扒寄,取消注冊(cè),就是一個(gè)從集合remove事件的過程
孝常。
EventBus可以從下面幾點(diǎn)開講:
1.觀察者模式
2.線程切換
3.事件優(yōu)先級(jí)
4.APT技術(shù)(之前的版本用的是反射獲取信息)
2. Glide圖片緩存
從下面幾點(diǎn)分散開講:
1.圖片緩存 - 內(nèi)存緩存,硬盤緩存
2.線程切換 - 請(qǐng)求圖片的時(shí)候放在子線程蚓哩,設(shè)置Imageview的時(shí)候轉(zhuǎn)換到主線程
3.OOM問題 - 弱引用构灸,LRuCache
4.內(nèi)存泄漏
3.OkHttp
里面幾個(gè)重要的組成部分:
1.RealCall:
2.Dispatcher:
3.OkHttpClient:
4.Interceptor:
攔截器設(shè)計(jì),是OkHttp最重要的設(shè)計(jì)
Android高頻問題
1
.消息機(jī)制
通常消息機(jī)制的幾個(gè)大佬:Handler
岸梨,Looper
,MessageQueue
喜颁,Message
分析代碼看的不是很清楚,借用下大佬畫的圖還有我自己的圖
這邊一般會(huì)展開講為什么Loop()無限循環(huán)不會(huì)造成卡頓曹阔,首先一個(gè)點(diǎn)app不退出的條件就是依賴于這個(gè)Loop循環(huán)半开,退出循環(huán)意味著app就停止工作了,那為什么不會(huì)造成卡頓呢赃份?線程在沒有事件的時(shí)候是會(huì)進(jìn)入阻塞狀態(tài)寂拆。
2
.相關(guān)優(yōu)化
內(nèi)存優(yōu)化:內(nèi)存泄漏,大圖優(yōu)化
UI優(yōu)化:過渡渲染抓韩,View Inspecter查看層級(jí)
網(wǎng)絡(luò)優(yōu)化: 減少請(qǐng)求次數(shù)纠永,圖片按需請(qǐng)求,數(shù)據(jù)緩存谒拴,弱網(wǎng)情況下不加載圖片等尝江。
ANR:耗時(shí)方法分析Systrace
3
.自定義View
對(duì)于自定義View需要知道的基礎(chǔ)大概有這么幾點(diǎn):
1.了解一個(gè)View的MeasureSpec怎么來的,通常是這么一張圖
2.了解measure layout draw三個(gè)過程的作用
measure
過程主要是測(cè)量控件的大小英上,單個(gè)View或者ViewGroup都需要自己去實(shí)現(xiàn)onMeasure方法實(shí)現(xiàn)自己的大小炭序。
layout
過程啤覆,主要是用于ViewGroup布局子View,單個(gè)View不需要重寫惭聂。
draw
過程:主要是用于單個(gè)View繪制內(nèi)容窗声。
4
.React Native的原理(Android角度)
從兩個(gè)角度分析:UI繪制
UI繪制:
Android的頁面就是一個(gè)Activity與主體視圖c
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
看下mReactRootView是怎么實(shí)現(xiàn)的?
mReactRootView整個(gè)看下來就是一個(gè)繼承SizeMonitoringFrameLayout的普通View彼妻,布局子View交給了 UIManagerModule
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// No-op since UIManagerModule handles actually laying out children.
}
UIManagerModule里面很多注冊(cè)的交互事件比如更新View
@ReactMethod
public void updateView(int tag, String className, ReadableMap props) {
if (DEBUG) {
String message =
"(UIManager.updateView) tag: " + tag + ", class: " + className + ", props: " + props;
FLog.d(ReactConstants.TAG, message);
PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message);
}
mUIImplementation.updateView(tag, className, props);
}
Js會(huì)把一些操作通過JsBridge
(C++實(shí)現(xiàn))傳遞給UIManagerModule進(jìn)行操作嫌佑。
1.js調(diào)用java代碼是從NativeModules.xxxModule.xxxMethod()
2.JSCExecutor::getNativeModule>()獲取js所要調(diào)用的java層的方法的配置表
3.NativeModules.js根據(jù)得到的配置表調(diào)用BatchedBridge.enqueueNativeCall(),將module:moduleID, methodID, params等參數(shù)傳遞到c層
4.c層遍歷獲取對(duì)應(yīng)moduleId的javamoudlewrapper侨歉,通過反射調(diào)用ReactContextBaseJavaModule中的代碼
參考:https://blog.csdn.net/kakaxiqianxin/article/details/80666443
5
.觸摸事件分發(fā)
主要角色:ViewGroup`` View
他們分別都有dispatchTouchEvent事件的實(shí)現(xiàn)屋摇,
ViewGroup
主要做了幾件事:
1.判斷是否攔截
2.沒有攔截則在子View中查看響應(yīng)子View
3.有的話執(zhí)行子View的dispatchTouchEvent,mFirstTarget
不為空,
View
主要做的是響應(yīng)事件的事:
OnTouch先于onTouchEvent執(zhí)行,onTouchEvent中執(zhí)行onClick方法
子View可點(diǎn)擊幽邓,必會(huì)返回true炮温。
面試考察點(diǎn):不同的觸摸情況,對(duì)應(yīng)什么樣的事件牵舵。eg:子View響應(yīng) DOWN
事件柒啤,但是父View攔截了MOVE
事件,這個(gè)時(shí)候畸颅,還是會(huì)有事件傳遞到子View担巩,但是是CANCEL
事件。
6.UI卡頓相關(guān):
卡頓原因
:
1.應(yīng)用會(huì)在要繪制的時(shí)候會(huì)請(qǐng)求vsync信號(hào)没炒,收到的時(shí)候會(huì)往消息隊(duì)列插入一條消息涛癌,此時(shí)主線程前一條消息要是耗時(shí)久的話會(huì)造成doframe無法在16ms內(nèi)被執(zhí)行。
doframe 繪制超復(fù)雜UI送火,消耗超過16ms拳话。
7. 啟動(dòng)加速:
1.可以影響的有Application的onCreate方法,一般會(huì)把不重要的操作放到子線程中初始化种吸。
2.還有一個(gè)障眼法弃衍,就是給啟動(dòng)頁面換一個(gè)背景,造成瞬間響應(yīng)點(diǎn)擊圖標(biāo)啟動(dòng)的操作
8.Activity和Window,DecorView的關(guān)系
想要記住很容易坚俗,可以從Activity的setContentView入手
public void setContentView(@LayoutRes int layoutResID) {
// 這個(gè)是PhoneWindow
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow----getWindow().setContentView(layoutResID);
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
// 首次肯定先執(zhí)行這 看下DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
installDecor就是創(chuàng)建DecorView的代碼镜盯,前面可以看出來Activity包含PhoneWindow
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
總結(jié):
Activity包含了一個(gè)PhoneWindow,PhoneWindow就是繼承于Window
Activity通過setContentView將View設(shè)置到了DecorView上
PhoneWindow里面包含了DecorView猖败,最終布局被添加到Decorview上.
9. ViewRootImpl,WindowManager,WindowManagerService(WMS)形耗,SurfaceFlinger之間的關(guān)系
想要說明這三個(gè)的關(guān)系需要先明白這三個(gè)分別有什么作用:
ViewRootImpl:里面有兩個(gè)個(gè)重要的類
1.W:負(fù)責(zé)和WindowManagerService交互,接受觸摸事件(這里服務(wù)端通過Socket傳遞給客戶端)等辙浑。
2.ViewRootHandler:接受W的異步消息激涤。
除此之外也負(fù)責(zé)和服務(wù)端交互,負(fù)責(zé)更新View
WindowManager:
addView等API與服務(wù)端交流,感覺是個(gè)為了解耦而出現(xiàn)的類倦踢,具體的邏輯都是通過其他類完成與Service的交互送滞。
WindowManagerService:
服務(wù)端負(fù)責(zé)Window的管理,不負(fù)責(zé)View繪制
他們之間的關(guān)系可以從一下這段代碼開始分析
SurfaceFlinger:
服務(wù)端負(fù)責(zé)圖層的管理
他們之間的關(guān)系可以從一下這段代碼開始分析
(WindowManager)wm.addView(mDecor, getWindow().getAttributes());
看下addView
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
root.setView(view, wparams, panelParentView);
}
public ViewRootImpl(Context context, Display display) {
mContext = context;
// 首先通過getWindowManagerService 獲取WMS的代理辱挥,之后通過WMS的代理在服務(wù)端open一個(gè)Session犁嗅,并在APP端獲取該Session的代理
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
}
很明顯在addView的時(shí)候創(chuàng)建了ViewRootImpl,看下ViewRootImpl如何通過setView將視圖添加到WMS的:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
// requestLayout是在addWindow之后執(zhí)行的
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
// IPC操作 通過Session add一個(gè)View
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
}
}
看下如何addView的
代碼在以下路徑 代碼很長(zhǎng)精簡(jiǎn)一些 /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
....
// win是WIndowState 這一步會(huì)向SuraceFlinger申請(qǐng)Suface
win.attach();
....
主要是對(duì)窗口分組晤碘,判斷是否添加過窗口等操作褂微。
之后WMS會(huì)向SurfaceFlinger申請(qǐng)Suface,這一步也是一個(gè)IPC的過程
總的流程:
首先APP端新建一個(gè)Surface圖層的容器殼子园爷,
APP通過Binder通信將這個(gè)Surface的殼子傳遞給WMS宠蚂,
WMS為了填充Surface去向SurfaceFlinger申請(qǐng)真正的圖層,
SurfaceFlinger收到WMS請(qǐng)求為APP端的Surface分配真正圖層
將圖層相關(guān)的關(guān)鍵信息Handle及Producer傳遞給WMS
之后更新UI就可以通過代理和SurfaceFlinger進(jìn)行通信了
參考:http://www.reibang.com/p/40776c123adb
10.一個(gè)App的啟動(dòng)流程:
從點(diǎn)擊App的圖標(biāo)開始
1.點(diǎn)擊App圖標(biāo) Launcher進(jìn)程發(fā)送startActivity IPC請(qǐng)求到AMS
2.AMS startProcessLocked
會(huì)去請(qǐng)求Zygote進(jìn)程(socket方式)去創(chuàng)建進(jìn)程
3.Zygote進(jìn)程fork一個(gè)新的進(jìn)程
4.被創(chuàng)建的app會(huì)執(zhí)行ActivityThread的main方法
public static void main(String[] args) {
// 展示主體代碼
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop();
}
Looper這個(gè)看著是不是很熟悉童社,這個(gè)就是UI線程Looper求厕,所以在UI線程不需要Looper.loop()
thread.attach這個(gè)方法進(jìn)行了IPC
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
// 這里把ApplicationThread這個(gè)Binder傳遞給遠(yuǎn)程服務(wù),這個(gè)時(shí)候相當(dāng)于本身app變成遠(yuǎn)程服務(wù)端扰楼,Service變成發(fā)起者呀癣。常規(guī)Binder需要向ServiceManager獲取遠(yuǎn)程Service的代理。
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
5.Service會(huì)往App發(fā)送BIND_APPLICATION弦赖,創(chuàng)建Application
6.接著會(huì)往App發(fā)送LAUNCH_ACTIVITY项栏,反射創(chuàng)建Activity
Java基礎(chǔ)篇:
1
.線程篇:
線程并發(fā)基礎(chǔ)知識(shí)
:
三個(gè)特性:
原子性
:由于操作不是一步完成:打個(gè)比方加法的CPU指令,現(xiàn)在寄存器中計(jì)算結(jié)果蹬竖,這個(gè)可以被中斷沼沈,原子性意味著加法操作是整個(gè)完成或者就沒執(zhí)行。
有序性
:代碼的順序和虛擬機(jī)執(zhí)行順序不一定一樣
可見性
:Java的內(nèi)存模型決定案腺,變量都存儲(chǔ)在主內(nèi)存庆冕,線程中存儲(chǔ)的是一份copy康吵。
如何保證這三個(gè)特性劈榨?:
volatile
可以保證可見性(lock指令 cpu執(zhí)行到時(shí)馬上刷新數(shù)據(jù)到主內(nèi)存,并馬上通知其他線程數(shù)據(jù)地址失效)晦嵌,也可以防止指令重排序同辣,不能保證原子性,通過內(nèi)存屏障
防止指令排序惭载。
Synchronized
:悲觀鎖旱函,可以保證同一時(shí)間只有一個(gè)線程可以訪問代碼段,實(shí)現(xiàn)原理: monitorenter 和 monitorexit
執(zhí)行同步代碼塊描滔,首先會(huì)執(zhí)行monitorenter指令棒妨,然后執(zhí)行同步代碼塊中的代碼,退出同步代碼塊的時(shí)候會(huì)執(zhí)行monitorexit指令 含长。
CAS:樂觀鎖券腔,compare and set伏穆,變量volatile保證可見性,unsafe操作保證原子性纷纫。