1.Volley優(yōu)缺點(5優(yōu)5缺)
-
Volley 的優(yōu)點
① 非常適合進(jìn)行數(shù)據(jù)量不大,但通信頻繁的網(wǎng)絡(luò)操作;
② 可直接在主線程調(diào)用服務(wù)端并處理返回結(jié)果稼钩;
③ 可以取消請求肢扯,容易擴展负蚊,面向接口編程铝宵;
④ 網(wǎng)絡(luò)請求線程NetworkDispatcher默認(rèn)開啟了4個绸贡,可以優(yōu)化浩考,通過手機CPU數(shù)量屠缭;
⑤ 通過使用標(biāo)準(zhǔn)的HTTP緩存機制保持磁盤和內(nèi)存響應(yīng)的一致掘猿;
-
Volley 的缺點
① 使用的是httpclient两波、HttpURLConnection滔蝉;
② Android 6.0不支持httpclient了击儡;
③ 對大文件下載 Volley的表現(xiàn)非常糟糕;
④ 只支持http請求蝠引;
⑤ 圖片加載性能一般阳谍;
Volley能否下載電影以及加載大圖片?
Volley的request是在內(nèi)存上操作數(shù)據(jù)螃概,所以不適用大文件的操作矫夯。比如:ImageRequest去加載大圖片的時候,也是在內(nèi)存中讀取的吊洼,這個時候就可能會出現(xiàn)OOM训貌。
使用
private void getStringRequest() {
String url="http://api.k780.com:88/?app=phone.get&phone=13800138000&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json";
RequestQueue queue= Volley.newRequestQueue(this);
StringRequest request=new StringRequest(url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Log.e("success",s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
}
});
queue.add(request);
}
源碼解析:http://www.reibang.com/p/33be82da8f25
2. handler工作機制
Handler、Looper冒窍、Message递沪、MessageQueue四兄弟
關(guān)系如下:
MessageQueue:裝食物的容器
Message:被裝的食物
Handler(msg.target實際上就是Handler):食物的消費者
Looper:負(fù)責(zé)分發(fā)食物的人
當(dāng)我們調(diào)用
handler.sendMessage(msg)
方法發(fā)送一個Message時,實際上這個Message是發(fā)送到與當(dāng)前線程綁定的一個MessageQueue
中综液,然后與當(dāng)前線程綁定的Looper
將會不斷的從MessageQueue
中取出新的Message款慨,最后調(diào)用msg.target.dispathMessage(msg)
(msg.target指的是當(dāng)前的handler
)方法將消息分發(fā)到與Message綁定的Runnable
和handler.handleMessage()
方法中。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 調(diào)用run方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 調(diào)用handleMessage方法
}
}
由此谬莹,不難發(fā)現(xiàn):
- 一個Thread可以對應(yīng)多個Handler檩奠;
- 一個Thread對應(yīng)一個Looper和MessageQueue
- Handler與Thread共享Looper和MessageQueue。
- Message只是消息的載體附帽,將會被發(fā)送到與線程綁定的唯一的MessageQueue中埠戳,并且被與線程綁定的唯一的Looper分發(fā),被與其自身綁定的Handler消費蕉扮。
我們都知道Handler是運行在主線程上的整胃,那我們?nèi)绾卧谧泳€程上使用Handler呢?
你可能會寫下如下代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
childThread = new MyThread();
childThread.start();
//這樣之后喳钟,childHandler和childLooper就關(guān)聯(lián)起來了爪模。
Handler childHandler = new Handler(childThread.childLooper){
public void handleMessage(Message msg) {
Log.d("LK","childThread========"+Thread.currentThread());
};
};
childHandler.sendMessage(1);
}
private class MyThread extends Thread{
public Looper childLooper;
@Override
public void run() {
Looper.prepare();//創(chuàng)建與當(dāng)前線程相關(guān)的Looper
childLooper = Looper.myLooper();//獲取當(dāng)前線程的Looper對象
Looper.loop();//調(diào)用此方法欠啤,消息才會循環(huán)處理
}
}
但是運行時會發(fā)現(xiàn)上述代碼偶爾會由于空指針錯誤而崩潰。這是因為當(dāng)子線程start
的時候屋灌,雖然子線程的run方法
得到執(zhí)行,但是主線程中代碼依然會向下執(zhí)行应狱,當(dāng)我們new Handler(childThread.childLooper)
的時候共郭,run方法
中的Looper
對象可能還沒初始化。
其實疾呻,Android早已經(jīng)為我們提供好解決上述問題的方法除嘹,即HandlerThread
類。
HandlerThread
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler mHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("LK","childThread========"+Thread.currentThread());//子線程
}
};
Log.d("LK","mainThread======="+Thread.currentThread());//主線程
mHandler.sendEmptyMessage(1);
}
創(chuàng)建HandlerThread
對象的時候岸蜗,有個參數(shù)尉咕,是指定線程名字的。上面的代碼不管運行多少次都不會奔潰Aг馈D甓小!并且這種方法創(chuàng)建的handler
的handleMessage方法
運行在子線程中铃慷。所以我們可以在這里處理一些耗時的邏輯单芜。到此我們完成了主線程給子線程發(fā)通知,在子線程做耗時邏輯的操作犁柜。
為什么使用HandlerThread就可以避免空指針那洲鸠?
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
HandlerThread類的getLooper方法如上,我們看到當(dāng)我們獲取當(dāng)前線程Looper對象的時候馋缅,會先判斷當(dāng)前線程是否存活扒腕,然后還要判斷Looper對象是否為空,都滿足之后才會返回給我Looper對象萤悴,否則處于等待狀態(tài)q!既然有等待稚疹,那就有喚醒的時候居灯,在那里那?内狗?怪嫌?我們發(fā)現(xiàn)HandlerThread的run方法中,有如下代碼:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
所以柳沙,當(dāng)
Looper
對象初始化完畢才會喚醒之前getLooper
中的等待岩灭,返回Looper
對象。HandlerThread
很好的避免了之前空指針的產(chǎn)生赂鲤。
所以以后要想創(chuàng)建非主線程的Handler時噪径,我們用HandlerThread類提供的Looper對象即可柱恤。
ZXing源碼處理handler在子線程使用
handlerInitLatch = new CountDownLatch(1);
Handler getHandler() {
try {
handlerInitLatch.await();
} catch (InterruptedException ie) {
// continue?
}
return handler;
}
@Override
public void run() {
Looper.prepare();
handler = new DecodeHandler(context, cameraManager, captureHandler, hints);
handlerInitLatch.countDown();
Looper.loop();
}
Looper.loop()為什么可以一直運行而不卡死?
先看源碼:
public static void loop() {
final Looper me = myLooper();//獲得當(dāng)前線程綁定的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲得與Looper綁定的MessageQueue
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進(jìn)入死循環(huán)找爱,不斷地去取對象梗顺,分發(fā)對象到Handler中消費
for (;;) {
Message msg = queue.next(); // 不斷的取下一個Message對象,在這里可能會造成堵塞车摄。
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//在這里寺谤,開始分發(fā)Message了
//至于這個target是神馬?什么時候被賦值的吮播?
//我們一會分析Handler的時候就會講到
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//當(dāng)分發(fā)完Message之后变屁,當(dāng)然要標(biāo)記將該Message標(biāo)記為 *正在使用* 啦
msg.recycleUnchecked();
}
}
epoll+pipe,有消息就依次執(zhí)行意狠,沒消息就block住粟关,讓出CPU,等有消息了环戈,epoll會往pipe中寫一個字符闷板,把主線程從block狀態(tài)喚起
3.Android運行過程
Zygote和System進(jìn)程的啟動過程(系統(tǒng)啟動運行)
+------------+ +-------+ +-----------+
|Linux Kernel+--> |init.rc+-> |app_process|
+------------+ +-------+ +-----------+
create and public
server socket
- /system/bin/app_process 是Zygote服務(wù)啟動的進(jìn)程名。
- Zygote啟動完成之后谷市,會啟動System進(jìn)程蛔垢。
- 在System進(jìn)程中會創(chuàng)建一個socket客戶端,后續(xù)AMS(ActivityManagerService)會通過此客戶端和zygote通信迫悠。
- 在System進(jìn)程中啟動各種Service(例如AMS鹏漆,PMS,WMS等)创泄,AMS會啟動系統(tǒng)界面以及Home程序艺玲。。
App進(jìn)程啟動
① AMS向Zygote發(fā)起請求(通過之前保存的socket)鞠抑,攜帶各種參數(shù)饭聚,包括“android.app.ActivityThread”。
② Zygote進(jìn)程fork自己搁拙,然后在新Zygote進(jìn)程中調(diào)用RuntimeInit.zygoteInit方法進(jìn)行一系列的初始化(commonInit秒梳、Binder線程池初始化等)。
③ 新Zygote進(jìn)程中調(diào)用ActivityThread的main函數(shù)箕速,并啟動消息循環(huán)酪碘。
- 首先,
ActivityThread
(ActivityThread是應(yīng)用程序的入口)從static main()
函數(shù)開始盐茎,調(diào)用prepareMainLooper()
為UI線程創(chuàng)建一個消息對列(MessageQueue)兴垦。 - 然后,創(chuàng)建一個
ActivityThread
對象,在其初始化代碼中會創(chuàng)建一個H(Handler)
對象和一個AppplicationThread(Binder)
對象探越,Binder
負(fù)責(zé)接收遠(yuǎn)程AmS的IPC(進(jìn)程間通信)調(diào)用狡赐,收到消息后,通過Handler
將消息發(fā)送到消息隊列钦幔,UI主線程會異步的從消息隊列中取出消息并執(zhí)行操作枕屉。 - 接著,UI主線程調(diào)用
Looper.loop()
進(jìn)入消息循環(huán)體鲤氢。當(dāng)ActivityThread
接收到AmS發(fā)送start某個activity
后搀庶,就會創(chuàng)建指定的Activity
對象。 -
Activity
又會創(chuàng)建PhoneWindow
類--->DecroView
類--->相應(yīng)的View
創(chuàng)建完成后铜异,Activity
需要把創(chuàng)建好的界面顯示到屏幕上,于是調(diào)用WindowManager
秸架,他會創(chuàng)建一個ViewRoot
對象揍庄,創(chuàng)建完ViewRoot
后,WindowManager
調(diào)用WindowManagerService
提供的遠(yuǎn)程接口完成添加一個窗口并顯示到屏幕上东抹。
-------------------------以上摘自柯元旦<Android內(nèi)核剖析>
4.Android 事件分發(fā)機制
注意:當(dāng)ViewGroup的
onInterceptTouchEvent
返回super
和false
此處需要注意B熳印!g郧食茎!當(dāng)自定義控件繼承自ViewGroup時,上圖是正確的馏谨;而當(dāng)自定義控件繼承自控件(MyView)時别渔,返回super調(diào)用的是MyView的onInterceptTouchEvent
,最后返回什么由MyView的onInterceptTouchEvent
為準(zhǔn)惧互。
- 紅色的箭頭代表ACTION_DOWN 事件的流向
- 藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
5.RelativeLayout與LinearLayout的性能分析
- 測試
主要是3個方法哎媚,onMeasure(),onLayout(),onDraw()
可以自己自定義控件測試。
通過測試喊儡,我們發(fā)現(xiàn)LinearLayout的onMeasure()耗時僅是RelativeLayout的一半左右拨与。
- 原因
通過查看RelativieLayout與LinearLayout的onMeasure()方法發(fā)現(xiàn),RelativeLayout的對子View測量了兩次艾猜。為什么要測量兩次呢买喧?
在RelativeLayout中View存在橫向和縱向上的依賴。所以匆赃,源碼中是對子View分別進(jìn)行了橫向(第一次)和縱向(第二次)的測量淤毛。
在LinearLayout中,是先判斷方向炸庞,然后onMeasure()钱床。但是如果遇到有weight屬性的子View,會對其進(jìn)行第二次的onMeasure()埠居。so, try not to use “weight” as possible as you can.
5.EventBus源碼
6.MVC和MVP
(最主要區(qū)別)View與Model并不直接交互查牌,而是通過與Presenter交互來與Model間接交互事期。而在MVC中View可以與Model直接交互
視圖(View):用戶界面
控制器(Controller):業(yè)務(wù)邏輯
模型(Model):數(shù)據(jù)保存
Models--負(fù)責(zé)主要的數(shù)據(jù)或者操作數(shù)據(jù)的數(shù)據(jù)訪問層。包括http數(shù)據(jù)交互纸颜,sqlite數(shù)據(jù)交互兽泣,sharePreference,contentProvider等胁孙。
Views--負(fù)責(zé)展示層(GUI)唠倦,可以聯(lián)想一下以 UI 開頭的所有類。
Controller/Presenter/ViewModel--負(fù)責(zé)協(xié)調(diào) Model 和 View涮较,通常根據(jù)用戶在View上的動作在Model上作出對應(yīng)的更改稠鼻,同時將更改的信息返回到View上。
7.TCP/IP 三次握手和四次揮手
【HTTP與TCP/IP】和其他的協(xié)議在最初OSI模型中的位置:
握手&揮手流程
握手
- 第一次握手:客戶端發(fā)送了一個帶有SYN的Tcp報文到服務(wù)器狂票,這個三次握手中的開始候齿。表示客戶端想要和服務(wù)端建立連接。
- 第二次握手:服務(wù)端接收到客戶端的請求闺属,返回客戶端報文慌盯,這個報文帶有SYN和ACK標(biāo)志,詢問客戶端是否準(zhǔn)備好掂器。
- 第三次握手:客戶端再次響應(yīng)服務(wù)端一個ACK亚皂,表示我已經(jīng)準(zhǔn)備好。
揮手
- 第一次揮手:TCP發(fā)送一個FIN(結(jié)束)国瓮,用來關(guān)閉客戶到服務(wù)端的連接灭必。
- 第二次揮手:服務(wù)端收到這個FIN,他發(fā)回一個ACK(確認(rèn))巍膘,確認(rèn)收到序號為收到序號+1厂财,和SYN一樣,一個FIN將占用一個序號峡懈。
- 第三次揮手:服務(wù)端發(fā)送一個FIN(結(jié)束)到客戶端璃饱,服務(wù)端關(guān)閉客戶端的連接。
- 第四次揮手:客戶端發(fā)送ACK(確認(rèn))報文確認(rèn)肪康,并將確認(rèn)的序號+1荚恶,這樣關(guān)閉完成。
Q: 為什么揮手是四次磷支,而不是三次谒撼,即第二次和第三次FIN+ACK明明可以一起發(fā)送,就像握手是SYN和ACK一起發(fā)送一樣雾狈?
A:第二次揮手時廓潜,回復(fù)客戶端一個ACK,然后同時繼續(xù)傳輸數(shù)據(jù),當(dāng)數(shù)據(jù)傳輸完畢后辩蛋,發(fā)起第三次揮手呻畸,所以需要分成兩次發(fā)送。
8.Dalvik和ART區(qū)別
Android主流的Dalvik和ART兩個虛擬機悼院,它們最大的區(qū)別就是是否支持多個dex文件的加載伤为。
ART也就是Android 5.0以上的虛擬機本身就支持多個dex文件加載,而Dalvik卻不支持多個dex加載据途,只支持一個dex加載绞愚,如果需要支持多個dex加載則需要引入multi-dex方案。
ART上應(yīng)用啟動快颖医,運行快位衩,但是耗費更多存儲空間,安裝時間長熔萧,總的來說ART的功效就是"空間換時間"蚂四。
ART:Ahead of Time Dalvik: Just in Time
什么是Dalvik:Dalvik是Google公司自己設(shè)計用于Android平臺的Java虛擬機。Dalvik虛擬機是Google等廠商合作開發(fā)的Android移動設(shè)備平臺的核心組成部分之一哪痰,它可以支持已轉(zhuǎn)換為.dex(即Dalvik Executable)格式的Java應(yīng)用程序的運行,.dex格式是專為Dalvik應(yīng)用設(shè)計的一種壓縮格式久妆,適合內(nèi)存和處理器速度有限的系統(tǒng)晌杰。Dalvik經(jīng)過優(yōu)化,允許在有限的內(nèi)存中同時運行多個虛擬機的實例筷弦,并且每一個Dalvik應(yīng)用作為獨立的Linux進(jìn)程執(zhí)行肋演。獨立的進(jìn)程可以防止在虛擬機崩潰的時候所有程序都被關(guān)閉。
什么是ART:Android操作系統(tǒng)已經(jīng)成熟烂琴,Google的Android團(tuán)隊開始將注意力轉(zhuǎn)向一些底層組件爹殊,其中之一是負(fù)責(zé)應(yīng)用程序運行的Dalvik運行時。Google開發(fā)者已經(jīng)花了兩年時間開發(fā)更快執(zhí)行效率更高更省電的替代ART運行時奸绷。ART代表Android Runtime,其處理應(yīng)用程序執(zhí)行的方式完全不同于Dalvik梗夸,Dalvik是依靠一個Just-In-Time(JIT)編譯器去解釋字節(jié)碼。開發(fā)者編譯后的應(yīng)用代碼需要通過一個解釋器在用戶的設(shè)備上運行号醉,這一機制并不高效反症,但讓應(yīng)用能更容易在不同硬件和架構(gòu)上運行。ART則完全改變了這套做法畔派,在應(yīng)用安裝的時候就預(yù)編譯字節(jié)碼到機器語言铅碍,這一機制叫Ahead-Of-Time(AOT)編譯。在移除解釋代碼這一過程后线椰,應(yīng)用程序執(zhí)行將更有效率胞谈,啟動更快。
ART優(yōu)點:
- 系統(tǒng)性能的顯著提升
- 應(yīng)用啟動更快、運行更快烦绳、體驗更流暢卿捎、觸感反饋更及時
- 更長的電池續(xù)航能力
- 支持更低的硬件
ART缺點:
*更大的存儲空間占用,可能會增加10%-20%
*更長的應(yīng)用安裝時間
9.熱修復(fù)
https://yq.aliyun.com/articles/231111
10.AsyncTask工作原理
從構(gòu)造函數(shù)開始說起爵嗅,
AsyncTask()
中構(gòu)造了一個InternalHandler
和FutureTask(可以理解為有返回值的Runnable)娇澎。
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//這里!6蒙埂L俗!這里N焙堋F萆丁!锉试!
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
//取消AsyncTask時走這里
postResultIfNotInvoked(null);
}
}
};
}
當(dāng)執(zhí)行AsyncTask的時候猫十,
execute()
最終會調(diào)用executeOnExecutor()
,先走onPreExecute()
呆盖,然后線程池執(zhí)行FutureTask
對象拖云。
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
//這里!Sτ帧宙项!這里!V昕浮尤筐!
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
doInBackground
所在的call()
方法執(zhí)行結(jié)束后,直接回調(diào)FutureTask
的done()
方法洞就,當(dāng)取消異步線程時此處會拋出異常從而執(zhí)行postResultIfNotInvoked(null)
盆繁。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
... ...
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
//onPostExecute和onCancelled
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//這里!Q油昂!這里!G惴 秕狰!
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
··· ···
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Q: onProgressUpdate在源碼中沒有觸發(fā)?
A: 只有當(dāng)開發(fā)者在復(fù)寫的doInBackground
方法中調(diào)用publishProgress
方法躁染,通過InternalHandler
實例發(fā)送一條MESSAGE_POST_PROGRESS
消息更新進(jìn)度鸣哀,onProgressUpdate
方法才將被調(diào)用; 如果遇到異常吞彤,則發(fā)送一條MESSAGE_POST_CANCEL
的消息取消任務(wù)我衬,onCancelled()方法將被調(diào)用叹放。
11.LeakCanary 的原理(如何確定是否發(fā)生內(nèi)存泄露)
- 監(jiān)聽 Activity 的生命周期
- 在
onDestroy
的時候,創(chuàng)建相應(yīng)的Refrence
和RefrenceQueue
挠羔,并啟動后臺進(jìn)程去檢測井仰。 - 一段時間之后,從
RefrenceQueue
讀取破加,若讀取不到相應(yīng) activity 的refrence
俱恶,有可能發(fā)生泄露了,這個時候范舀,再促發(fā) gc合是,一段時間之后,再去讀取锭环,若在從RefrenceQueue
還是讀取不到相應(yīng) activity 的refrence
聪全,可以斷定是發(fā)生內(nèi)存泄露了。 - 發(fā)生內(nèi)存泄露之后辅辩,dump难礼,分析 hprof 文件,找到泄露路徑(使用 haha 庫分析)玫锋。
其中蛾茉,比較重要的是如何確定是否發(fā)生內(nèi)存泄露,而如何確定發(fā)生內(nèi)存泄露最主要的原理是通過 Refrence
和 RefrenceQueue
撩鹿。
弱引用
WeakReference
和引用隊列ReferenceQueue
聯(lián)合使用時臀稚,如果弱引用持有的對象被垃圾回收,Java 虛擬機就會把這個弱引用加入到與之關(guān)聯(lián)的引用隊列中三痰。
12.一個線程是否只有一個Looper,如何保證一個線程只有一個Looper?
A:
第一個問題窜管,一個線程最多只能有一個Looper散劫。
第二個問題,源碼中使用ThreadLocal來存儲每個線程的Looper對象幕帆,源碼如下:
Looper.class
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
應(yīng)用實現(xiàn):EventBus
用于保存post狀態(tài)等信息获搏。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
··· ···
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
13. ANR異常發(fā)生條件
- 5s內(nèi)沒有響應(yīng)用戶輸入事件;
- 10s內(nèi)廣播接收器沒有處理完畢失乾;
- 20s內(nèi)服務(wù)沒有處理完畢常熙;
14. Http和Https的區(qū)別?
- Https是ssl加密傳輸碱茁,Http是明文傳輸裸卫;
- Https是使用端口443,而Http使用80纽竣;
- Https是SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸墓贿、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議要比Http協(xié)議安全茧泪;
-
Https協(xié)議需要到CA申請證書;
image.png
-
15.常用的圖片壓縮方式
圖片壓縮方式常用的有尺寸壓縮聋袋、質(zhì)量壓縮队伟、格式變化以及通過JNI調(diào)用libjpeg庫來進(jìn)行壓縮,下面就先分別介紹下常見的質(zhì)量壓縮和尺寸壓縮幽勒。(尺寸壓縮嗜侮,質(zhì)量壓縮底層也是通過調(diào)用native的方法進(jìn)行壓縮的,而native中的則是通過Skia這個庫實現(xiàn)的啥容,但是锈颗,最終還是調(diào)用了libjpeg庫進(jìn)行壓縮的。)
- 格式變化
現(xiàn)在android支持的圖片格式有三種:png干毅、jpeg宜猜、WebP;
- png: 無損圖片的壓縮類型硝逢,能保存透明等圖姨拥,它同時提供24位和48位真彩色圖像支持以及其他諸多技術(shù)性支持。
- Jpeg:有損圖片的壓縮類型渠鸽,有損壓縮方式去除冗余的圖像和彩色數(shù)據(jù)叫乌,獲取得極高的壓縮率的同時能展現(xiàn)十分豐富生動的圖像,換句話說徽缚,就是可以用最少的磁盤空間得到較好的圖像質(zhì)量憨奸。但是,bitmap quality屬性越小凿试,圖片的清晰度越差排宰。
- WebP:WebP(發(fā)音 weppy,項目主頁)那婉,是谷歌推出的一種支持有損壓縮和無損壓縮的圖片文件格式板甘,派生自圖像編碼格式VP8。
- 質(zhì)量壓縮
設(shè)置bitmap quality屬性详炬,降低圖片的質(zhì)量盐类,像素不會減少(就是指bitmap所占的內(nèi)存大小)呛谜,第一個參數(shù)為需要壓縮的bitmap圖片對象在跳,第二個參數(shù)為壓縮后圖片保存的位置設(shè)置quality屬性0-100,來實現(xiàn)壓縮隐岛。(因為png是無損壓縮猫妙,所以該屬性對png是無效的人乓。)
/**
* 質(zhì)量壓縮
*
* @param format 圖片格式 PNG图贸,JPEG仆葡,WEBP
* @param quality 圖片的質(zhì)量 1-100
*/
public void compress(Bitmap.CompressFormat format, int quality) {
FileOutputStream fos = null;
try {
//得到一個儲存路徑
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
//得到一個文件輸入流
fos = new FileOutputStream(file);
//開始壓縮
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_icon);
bitmap.compress(format, quality, fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.尺寸壓縮
尺寸壓縮由于是減小了圖片的像素肖卧,所以它直接對bitmap產(chǎn)生了影響,當(dāng)然最終生成的file文件也是相對的變小了韭脊。
- 通過縮放圖片像素來減少圖片占用內(nèi)存大小
public static void compressBitmapToFile(Bitmap bmp, File file){
// 尺寸壓縮倍數(shù),值越大童谒,圖片尺寸越小
int ratio = 2;
// 壓縮Bitmap到對應(yīng)尺寸
Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
canvas.drawBitmap(bmp, null, rect, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把壓縮后的數(shù)據(jù)存放到baos中
result.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 設(shè)置圖片的采樣率,降低圖片像素
public static void compressBitmap(String filePath, File file){
// 數(shù)值越高沪羔,圖片像素越低
int inSampleSize = 2;
BitmapFactory.Options options = new BitmapFactory.Options();
//采樣率
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把壓縮后的數(shù)據(jù)存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
16.WebViewClient與WebChromeClient的區(qū)別
- WebViewClient主要幫助WebView處理各種通知饥伊、請求事件的。
//處理webView的按鍵事件
boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
//截取url請求蔫饰,在當(dāng)前視圖加載琅豆,避免在跳轉(zhuǎn)到自帶瀏覽器
boolean shouldOverrideUrlLoading(WebView view, String url)
//WebView改變時調(diào)用
void onScaleChanged(WebView view, float oldScale, float newScale)
//對https的請求處理
void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
//獲取返回信息授權(quán)
void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
//處理報錯信息(API23以后用第二個)
void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
//頁面開始載入時調(diào)用
void onPageStarted(WebView view, String url, Bitmap favicon)
//頁面載入結(jié)束時調(diào)用
void onPageFinished(WebView view, String url)
//加載資源時調(diào)用
void onLoadResource(WebView view, String url)
- WebChromeClient主要輔助WebView處理Javascript的對話框、網(wǎng)站圖標(biāo)篓吁、網(wǎng)站title茫因、加載進(jìn)度等。
//webview關(guān)閉時調(diào)用
void onCloseWindow(WebView window)
//Js的彈窗
boolean onJsAlert(WebView view, String url, String message, JsResult result)
//Js提示框
boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
//Js確認(rèn)框
boolean onJsConfirm(WebView view, String url, String message, JsResult result)
//加載進(jìn)度
void onProgressChanged(WebView view, int newProgress)
//全屏模式(API18以后用第二種)
onShowCustomView(View view, WebChromeClient.CustomViewCallback callback)
onShowCustomView(View view, int requestedOrientation, WebChromeClient.CustomViewCallback callback)
17.URI和URL概念以及區(qū)別
什么是URL杖剪,什么是URI冻押?
URL(統(tǒng)一資源定位符):A Uniform Resource Locator thatidentifies the location of an Internet resource as specified by RFC 1738.(用于標(biāo)示網(wǎng)絡(luò)資源的位置)
URI(統(tǒng)一資源標(biāo)識符):A Uniform Resource Identifier that identifies an abstract or physical resource, as specified by RFC 2396.(用于標(biāo)示一個抽象或者物理資源)
也就是說URI是以一種抽象的,高層次概念定義統(tǒng)一資源標(biāo)識盛嘿,而URL則是具體的資源標(biāo)識的方式洛巢。URL是一種URI。
Android中的Uri與Java中的URI類
利用URL Scheme打開APP并傳遞數(shù)據(jù)
Android業(yè)務(wù)組件化之URL Scheme使用
18.WebView相關(guān)
Android:這是一份全面 & 詳細(xì)的Webview使用攻略
最全面總結(jié) Android WebView與 JS 的交互方式
Q:loadUrl()和evaluateJavascript()區(qū)別
A:后者執(zhí)行不會使頁面刷新次兆,而loadUrl的執(zhí)行則會稿茉。
優(yōu)化
加載提升網(wǎng)頁打開的速度
加載時先加載文本,后加載圖片調(diào)用方式如下
WebSettings settings = wView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setBuiltInZoomControls(true);
settings.setBlockNetworkImage(true);
wView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
// 網(wǎng)頁加載完成
loadDialog.dismiss();
wView.getSettings().setBlockNetworkImage(false);
} else {
// 網(wǎng)頁加載中
loadDialog.show();
}
}
});
setBlockNetworkImage (boolean flag)
是否禁止從網(wǎng)絡(luò)(通過http和https URI schemes訪問的資源)下載圖片資源芥炭,默認(rèn)值為false漓库。注意,除非getLoadsImagesAutomatically()返回 true,否則該方法無效园蝠。當(dāng)設(shè)置的值從true變?yōu)閒alse渺蒿,WebView當(dāng)前顯示的內(nèi)容所引用的網(wǎng)絡(luò)圖片資源會自動獲取。
19.WebView.loadUrl使用誤區(qū)
當(dāng)使用loadUrl加載網(wǎng)頁的時候砰琢,有時候會出現(xiàn)調(diào)用系統(tǒng)瀏覽器加載網(wǎng)頁的現(xiàn)象,網(wǎng)上大部分的解決方案是 :
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
return true;
}
}
這確實可以達(dá)到在當(dāng)前webview加載網(wǎng)頁的效果良瞧,但是卻做了多余的工作陪汽,以及不合理的返回值。
實際上褥蚯,如果你只需要避免啟動系統(tǒng)瀏覽器來加載頁面的情況挚冤,只需要這么寫就可以了:
webView.setWebViewClient(new WebViewClient());
默認(rèn)返回的super.shouldOverrideUrlLoading(view, url);
其實就是false
。
return true 表示當(dāng)前url即使是重定向url也不會再執(zhí)行赞庶。
return false 表示由系統(tǒng)執(zhí)行url训挡,直到不再執(zhí)行此方法澳骤,即加載完重定向的url。
所以:
view.loadUrl(url);
return true;
等價于
return false;
注意:前者在應(yīng)用場景復(fù)雜的時候澜薄,會出現(xiàn)重定向后無法回退的問題为肮。
比如:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!Tools.urlCheck(url)) {
return true;
}
if (!TextUtils.isEmpty(url) && url.endsWith("apk")) {
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(viewIntent);
} else {
if (!WebViewActivity.processCustomUrl(WebActivity.this, url, null, false)) {
url = WebViewActivity.addCookie2Url(WebActivity.this, url);
// 注意這里!7艟<昭蕖!
view.loadUrl(url);
return true;
} else {
return true;
}
}
Logcat.dLog("url loading = " + url);
return super.shouldOverrideUrlLoading(view, url);
}
若loadUrl執(zhí)行則會重新加載webView,并且不執(zhí)行return true忘分,則由于會執(zhí)行return super.shouldOverrideUrlLoading(view, url)棋枕,導(dǎo)致返回false,則系統(tǒng)也會加載一次妒峦,導(dǎo)致執(zhí)行了兩次重斑,當(dāng)執(zhí)行webview.goBack()返回上一頁時就會一直在當(dāng)前頁打轉(zhuǎn)。
android webview對shouldOverrideUrlLoading的理解肯骇,對于重定向的url
20.ViewGroup為什么不會調(diào)用onDraw
造成這種現(xiàn)象的原因是繼承自LinearLayout
窥浪,而LinearLayout
這是一個容器,它本身并沒有任何可畫的東西累盗,它是一個透明的控件寒矿,因些并不會觸發(fā)onDraw
,但是你現(xiàn)在給LinearLayout
設(shè)置一個背景色若债,其實這個背景色不管你設(shè)置成什么顏色符相,系統(tǒng)會認(rèn)為,這個LinearLayout
上面有東西可畫了蠢琳,因此會調(diào)用onDraw
方法啊终。
我們可以仔細(xì)分析View的源碼,它有一個方法View#draw(Canvas)
方法傲须,這里面有兩個地方調(diào)用onDraw蓝牲,它的條件都是:
if (!dirtyOpaque) onDraw(canvas);
如果dirtyOpaque
是true透明的話,onDraw就不會調(diào)用泰讽。
View還提供了一個重要的方法:setWillNotDraw
例衍,setFlags(WILL_NOT_DRAW, DRAW_MASK);
相于調(diào)用了setWillNotDraw(true)
,它就認(rèn)為是透明的了已卸。如果我們想要重寫onDraw
佛玄,就需要調(diào)用setWillNotDraw(false)
。
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
所以累澡,
-
ViewGroup
默認(rèn)情況下梦抢,會被設(shè)置成WILL_NOT_DRAW
,這是從性能考慮愧哟,這樣一來奥吩,onDraw
就不會被調(diào)用了哼蛆。 - 如果我們要重要一個
ViweGroup
的onDraw
方法,有兩種方法:- 在構(gòu)造函數(shù)里面霞赫,給其設(shè)置一個顏色腮介,如#00000000。
- 在構(gòu)造函數(shù)里面绩脆,調(diào)用
setWillNotDraw(false)
萤厅,去掉WILL_NOT_DRAW的flag
。
21.getWidth()方法和getMeasureWidth()區(qū)別
- 首先
getMeasureWidth()
方法在measure()
過程結(jié)束后就可以獲取到了靴迫,而getWidth()
方法要在layout()
過程結(jié)束后才能獲取到惕味。 - 另外,
getMeasureWidth()
方法中的值是通過setMeasuredDimension()
方法來進(jìn)行設(shè)置的玉锌,而getWidth()
方法中的值則是通過視圖右邊的坐標(biāo)減去左邊的坐標(biāo)計算出來的名挥。
22. Binder
1)Binder IPC 通信過程
- 首先 Binder 驅(qū)動在內(nèi)核空間創(chuàng)建一個數(shù)據(jù)接收緩存區(qū);
- 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū)主守,建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系禀倔,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系;
- 發(fā)送方進(jìn)程通過系統(tǒng)調(diào)用
copyfromuser()
將數(shù)據(jù) copy 到內(nèi)核中的數(shù)據(jù)接收緩存區(qū)参淫,由于內(nèi)核緩存區(qū)和數(shù)據(jù)接收緩存區(qū)以及接收進(jìn)程的用戶空間存在內(nèi)存映射救湖,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信涎才。
2)Binder通訊模型
Binder是基于C/S架構(gòu)的鞋既,其中定義了4個角色:Client、Server耍铜、Binder驅(qū)動和ServiceManager邑闺。
- Binder驅(qū)動:類似網(wǎng)絡(luò)通信中的路由器,負(fù)責(zé)將Client的請求轉(zhuǎn)發(fā)到具體的Server中執(zhí)行棕兼,并將Server返回的數(shù)據(jù)傳回給Client陡舅。
- ServiceManager:類似網(wǎng)絡(luò)通信中的DNS服務(wù)器,負(fù)責(zé)將Client請求的Binder描述符轉(zhuǎn)化為具體的Server地址伴挚,以便Binder驅(qū)動能夠轉(zhuǎn)發(fā)給具體的Server靶衍。Server如需提供Binder服務(wù),需要向ServiceManager注冊茎芋。
具體的通訊過程
- Server向ServiceManager注冊颅眶。Server通過Binder驅(qū)動向ServiceManager注冊,聲明可以對外提供服務(wù)败徊。ServiceManager中會保留一份映射表帚呼。
- Client向ServiceManager請求Server的Binder引用掏缎。Client想要請求Server的數(shù)據(jù)時皱蹦,需要先通過Binder驅(qū)動向ServiceManager請求Server的Binder引用(代理對象)煤杀。
- 向具體的Server發(fā)送請求。Client拿到這個Binder代理對象后沪哺,就可以通過Binder驅(qū)動和Server進(jìn)行通信了沈自。
- Server返回結(jié)果。Server響應(yīng)請求后辜妓,需要再次通過Binder驅(qū)動將結(jié)果返回給Client枯途。
Q: ServiceManager是一個單獨的進(jìn)程,那么Server與ServiceManager通訊是靠什么呢籍滴?
A: 當(dāng)Android系統(tǒng)啟動后酪夷,會創(chuàng)建一個名稱為servicemanager的進(jìn)程,這個進(jìn)程通過一個約定的命令BINDERSETCONTEXT_MGR向Binder驅(qū)動注冊孽惰,申請成為為ServiceManager晚岭,Binder驅(qū)動會自動為ServiceManager創(chuàng)建一個Binder實體。并且這個Binder實體的引用在所有的Client中都為0勋功,也就說各個Client通過這個0號引用就可以和ServiceManager進(jìn)行通信坦报。Server通過0號引用向ServiceManager進(jìn)行注冊,Client通過0號引用就可以獲取到要通信的Server的Binder引用狂鞋。
23.Fragment的懶加載實現(xiàn)
Fragment可見狀態(tài)改變時會被調(diào)用setUserVisibleHint()
方法片择,可以通過復(fù)寫該方法實現(xiàn)Fragment的懶加載,但需要注意該方法可能在onVIewCreated之前調(diào)用骚揍,需要確保界面已經(jīng)初始化完成的情況下再去加載數(shù)據(jù)字管,避免空指針。
當(dāng)fragment被用戶可見時疏咐,setUserVisibleHint()
會調(diào)用且傳入true值纤掸,當(dāng)fragment不被用戶可見時,setUserVisibleHint()
則得到false值浑塞。而在傳統(tǒng)的fragment生命周期里也看不到這個函數(shù)借跪。
http://www.reibang.com/p/cf1f4104de78
24.Retrofit的實現(xiàn)與原理
Retrofit采用動態(tài)代理,創(chuàng)建聲明service接口的實現(xiàn)對象酌壕。當(dāng)我們調(diào)用service的方法時候會執(zhí)行InvocationHandler的invoke方法掏愁。在這方法中:首先,通過method把它轉(zhuǎn)換成ServiceMethod卵牍,該類是對聲明方法的解析果港,可以進(jìn)一步將設(shè)定參數(shù)變成Request ;然后糊昙,通過serviceMethod, args獲取到okHttpCall 對象辛掠,實際調(diào)用okhttp的網(wǎng)絡(luò)請求方法就在該類中,并且會使用serviceMethod中的responseConverter對ResponseBody轉(zhuǎn)化;最后萝衩,再把okHttpCall進(jìn)一步封裝成聲明的返回對象(默認(rèn)是ExecutorCallbackCall,將原本call的回調(diào)轉(zhuǎn)發(fā)至UI線程)回挽。
Retrofit2使用詳解及從源碼中解析原理
Retrofit2 完全解析 探索與okhttp之間的關(guān)系
25.View的加載流程
通過Activity的
setContentView
方法間接調(diào)用Phonewindow的setContentView()
,在PhoneWindow中通過getLayoutInflate()
得到LayoutInflate
對象猩谊。通過LayoutInflate對象去加載View千劈,主要步驟是
通過xml的Pull方式去解析xml布局文件,獲取xml信息牌捷,并保存緩存信息墙牌,因為這些數(shù)據(jù)是靜態(tài)不變的
根據(jù)xml的tag標(biāo)簽通過反射創(chuàng)建View逐層構(gòu)建View
遞歸構(gòu)建其中的子View,并將子View添加到父ViewGroup中暗甥。
View的繪制流程
View的繪制從ActivityThread類中Handler的處理RESUME_ACTIVITY事件開始喜滨,在執(zhí)行performResumeActivity之后,創(chuàng)建Window以及DecorView并調(diào)用WindowManager的addView方法添加到屏幕上撤防,addView又調(diào)用ViewRootImpl的setView方法鸿市,最終執(zhí)行performTraversals方法,依次執(zhí)行performMeasure即碗,performLayout焰情,performDraw。也就是view繪制的三大過程剥懒。
26.OnTouchListener内舟,onTouchEvent,onClickListener執(zhí)行順序
結(jié)果:首先執(zhí)行OnTouchListener初橘,之后為onTouchEvent验游,最后才執(zhí)行onClickListener內(nèi)的方法,至于為什么OnTouchListener和onTouchEvent執(zhí)行了兩次保檐,是因為在DOWN和UP時兩個方法都被調(diào)用耕蝉,至于onClickListener則只在UP的時候調(diào)用。
與事件分發(fā)相關(guān)聯(lián)的三個方法分別為dispatchTouchEvent夜只,onInterceptTouchEvent垒在,onTouchEvent,直接去看View的dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent event) {
......
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
......
return result;
}
我們先看ListenerInfo 扔亥,它是View的一個內(nèi)部靜態(tài)類场躯。
static class ListenerInfo {
protected OnFocusChangeListener mOnFocusChangeListener;
private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
protected OnScrollChangeListener mOnScrollChangeListener;
private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
public OnClickListener mOnClickListener;
protected OnLongClickListener mOnLongClickListener;
protected OnContextClickListener mOnContextClickListener;
protected OnCreateContextMenuListener mOnCreateContextMenuListener;
......
}
再看dispatchTouchEvent方法,只有OnTouchListener返回false旅挤,onTouchEvent才可能被觸發(fā)踢关。
繼續(xù)看onTouchEvent代碼:
public boolean onTouchEvent(MotionEvent event) {
......
switch (action) {
case MotionEvent.ACTION_UP:
......
performClick();
......
break;
......
}
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
在View的onTouchEvent方法中,如果判斷事件為MotionEvent.ACTION_UP時粘茄,則會調(diào)用performClick签舞,而在performClick中則會回調(diào)mOnClickListener的onClick方法,即點擊事件被回調(diào),同時直接返回true儒搭。
27.okHttp工作流程
OkHttpClient通過newCall可以將一個Request構(gòu)建成一個Call撒会,Call表示準(zhǔn)備被執(zhí)行的請求。Call調(diào)用executed或enqueue會調(diào)用Dispatcher對應(yīng)的方法在當(dāng)前線程或者異步開始執(zhí)行請求师妙,經(jīng)過RealInterceptorChain獲得最終結(jié)果,RealInterceptorChain是一個攔截器鏈屹培,其中依次包含以下攔截器:
- 自定義的攔截器
- retryAndFollowUpInterceptor 請求失敗重試
- BridgeInterceptor 為請求添加請求頭默穴,為響應(yīng)添加響應(yīng)頭
- CacheInterceptor 緩存get請求
- ConnectInterceptor 連接相關(guān)的攔截器,分配一個Connection和HttpCodec為最終的請求做準(zhǔn)備
- CallServerInterceptor 該攔截器就是利用HttpCodec完成最終請求的發(fā)送