1益兄、Handler:
1):系統(tǒng)Handler的創(chuàng)建過程及相關對象的創(chuàng)建:在ActivityThread的main方法中調用getHandler方法創(chuàng)建文虏,getHandler方法的實現是直接new了一個Handler的子類H來創(chuàng)建Handler對象的边坤。創(chuàng)建完Handler對象后進行Looper對象的創(chuàng)建。Looper對象是通過調用Looper.prepareMainLooper()方法撩穿,該方法會調用prepare方法功舀,創(chuàng)建Looper對象并且存儲在sThreadLocal中,此方法只能調用一次毫别,超過會拋出一個線程只能創(chuàng)建一個Looper對象的異常娃弓。Looper對象只能創(chuàng)建一次然后通過Looper.myLooper()方法獲取(其實就是調用sThreadLocal.get()獲取)。在Looper的構造方法中進行MessageQueue的初始化工作岛宦,到此為止台丛,Handler的對象完成創(chuàng)建,Looper對象完成創(chuàng)建,MessageQueue對象完成創(chuàng)建挽霉,最后一步是在ActivityThread的main方法的最后進行Looper的Loop方法的調用防嗡,此方是一個死循環(huán)方法。到此Handler的初始化工作全部結束侠坎。
2):在子線程中創(chuàng)建Handler的方法蚁趁,直接進行new一個Handler出來(此種方法容易引起內存泄漏問題),如果沒有傳入Looper對象參數实胸,默認使用mainLooper對象他嫡,如果需要使用自己本線程的Looper對象,需要在參數中調用Looper.myLooper方法來實現子線程Looper對象的初始化工作庐完。一個線程只能有一個Looper對象钢属,意味著一個線程也只能有一個MessageQueue對象,因為MessageQueue對象是在Looper對象的構造方法中進行創(chuàng)建的假褪。
3):Handler是怎么實現延遲消息的署咽?Handler的延遲消息的實現是通過延遲發(fā)送來進行實現的,在發(fā)送延遲消息時生音,Handler會將延遲消息進行入隊操作并添加延遲時間宁否,在等待期間如果有新的及時消息進入會排在延時消息的前面并開始進行消息的發(fā)送,發(fā)送結束后再判斷延遲消息的等待時間是否到達缀遍,如果未達到再進行新的等待時間計算慕匠。其實及時消息和延遲消息的實現是一樣的,只是及時消息的等待時長為0而已域醇。
4):Handler導致內存泄漏的原因及處理辦法台谊?在Java中,非靜態(tài)內部類和匿名內部類都會隱式持有當前外部類的引用譬挚。Handler發(fā)送一個延遲消息锅铅,此時Message會在messageQueue里面存活一段時間,這段時間减宣,message持有Handler的引用盐须,Handler又持有當前Activity的引用,當我們finish當前Activity時漆腌,GC無法對這個Activity對象進行回收贼邓,導致內存泄漏。處理方式一般在當前Activity的內部寫一個靜態(tài)的內部類重寫handlerMessage方法闷尿,并在此靜態(tài)內部類中使用WeakReference<Activity>弱引用包裹當前的Activity對象或者在Activity的onDestroy方法中調用 handler.removeCallbacksAndMessages(null)將消息進行移除塑径。
2、Android中事件傳遞機制:如下圖所示
滑動沖突的解決方法:
- 外部攔截法
點擊事件都先經過父容器進行攔截處理填具。需要重寫父容器的onInterceptTouchEvent方法统舀。ACTION_DOWN事件必須返回false,否則后續(xù)事件都會交給父容器處理,子元素無法收到事件。ACTION_MOVE事件如果父容器需要進行攔截就返回true绑咱,不攔截的話就返回false绰筛。ACTION_UP事件必須返回false,如果返回true描融,子元素就無法收到UP事件,子元素設置的onclick事件就無法觸發(fā)衡蚂。
- 內部攔截法
所有事件都傳遞給子元素窿克,如果子元素需要處理,就直接處理消耗掉毛甲,否則就交給父容器進行處理年叮。需要重寫子元素的dispatchTouchEvent方法。在需要攔截的地方調用parent.requestDisallowInterceptTouchEvent(true)方法玻募,不需要攔截的時候調用parent.requestDisallowInterceptTouchEvent(false)方法只损。父元素需要重寫onInterceptTouchEvent方法,默認攔截除了ACTIONG_DOWN以外的其它事件(MotionEvent事件為ACTION_DOWN時返回ture)七咧,這樣子子元素在調用了parent.requestDisallowInterceptTouchEvent(false)方法時跃惫,父元素才能繼續(xù)攔截所有的事件。
3艾栋、Activity爆存、Window、ViewGroup蝗砾、View的關系:
Activity--->Window(唯一的實現類PhoneWindow)--->DecorView--->setContentView中的ViewGroup--->ViewGroup--->View
4先较、Activity的生命周期:
onCreate()-->[ onReStart() ]-->onStart()-->onResume()-->onPause()-->onStop()-->onDestroy();
A跳轉至B生命周期執(zhí)行順序:A-->onPause--->B-->onCreate-->B-->onStart-->B-->onResume-->A-->onStop;耗時操作不能再onPause里面操作悼粮,這樣才能快速打開新的Activity闲勺。
界面橫豎屏切換生命周期順序(沒有配置android:configChanges):onPause-->onSaveInstanceState-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState -->onResume;其中onSaveInstanceState和onPause沒有既定的先后順序即可前課后,onRestoreInstanceState是在onStart之后進行調用扣猫,只有Activity有可能被恢復的情況下才會調用onSaveInstanceState方法菜循,此方法將之前Activity的參數保存在Bundle中,我們可以在onRestoreInstanceState和onCreate中進行數據的恢復苞笨,不過一般建議在onRestoreInstanceState方法中進行操作债朵,無需判空,在onCreate方法中需要判斷Bundle的空值情況瀑凝。當只設置Activity的android:configChanges="orientation"時序芦,切屏還是會重新調用各個生命周期。而設置Activity的android:configChanges="orientation|keyboardHidden"時粤咪,切屏不會重新調用各個生命周期谚中,只會執(zhí)行onConfigurationChanged方法。
A界面資源內存不足導致優(yōu)先級低的Activity被kill:其恢復數據的方式是一樣的丈冬,同樣會調用onSaveInstanceState和onRestoreInstanceState方法呆奕。
如何讓A界面不重新創(chuàng)建:需要在manifests中添加Activity的configChanges屬性,一般常用的屬性有orientation(屏幕旋轉)钢坦、locale(本地設置該變某筐,一般指切換了系統(tǒng)語言)比搭、keyboardHidden(鍵盤的可訪問性,用戶調出鍵盤)南誊。Activity的跳轉有顯示和隱式兩種身诺,顯示就是我們平時用得到,隱式跳轉需要在manifests中的Activity中配置intent-filter標簽抄囚,一個Activity標簽中可以有多個intent-filter標簽霉赡,intent-filter標簽中可以有多組action、category幔托、data屬性穴亏,只有完全匹配其中的一個intent-filter中的一組屬性才能完成隱式跳轉。
5重挑、自定義View及View的工作流程:
自定義View的三個重要方法執(zhí)行順序:onMeasure-->onLayout-->onDraw嗓化,分別功能是---測量自身的大小寬高-->確定自己在父容器中的位置-->進行自身內容的繪制。
onMeasure(int widthMeasureSpec,int heightMeasureSpec);方法中的兩個參數是一個由32位的int值攒驰,高兩位代表SpecMode蟆湖,低30位代表SpecSize。
SpecMode有三種模式:UNSPECIFIED-->父容器不對View有任何限制玻粪,要多大給多大隅津。EXACTLY-->父容器已經檢測出view的精確大小。AT_MOST-->父容器制定一個可用大小劲室,View的大小不能超過這個值伦仍,具體情況看View自身的情況。普通View的大小需要根據父容器的MeasureSpec和子元素的LayoutParams共同決定很洋。子元素的LayoutParams根據父元素的MeasureSpec類型決定充蓝。總結一條規(guī)律:如果子元素是精確模式喉磁,子元素的LayoutParams的值就是精確值谓苟,不管父容器是什么模式,其他情況都是取父容器中LayoutParams中的值协怒。如果自定義view是繼承View涝焙,需要在設置wrap_content時在onMeasure方法中做特殊處理。一般情況是給一個默認的值孕暇。
在測量的時候可以調用setMeasuredDimension(width,height)方法設置測量的值仑撞。
ViewGroup位置確定后通過onLayout方法遍歷所有的子元素并調用其layout方法確定其位置赤兴。Layout方法通過setFrame方法設定view的四個頂點位置確定子元素的位置。
Draw過程:
繪制背景
繪制自己
繪制children
繪制裝飾
自定義View分類
繼承View重寫onDraw方法
繼承ViewGroup派生特殊的layout
繼承特定的Veiw(比如TextView)
繼承特定的ViewGroup(比如LinearLayout)
自定義View需要注意的地方
讓View支持wrap_content屬性隧哮,需要在onMeasure中處理桶良,傳入一個默認的寬高。
注意paddding屬性的支持沮翔,需要在onDraw方法中對padding值進行處理(寬度的話就要減去左右padding值)
盡量不要在View中使用到Handler陨帆,直接用post方法代替handler
View中有動畫或者線程需要及時停止。
6鉴竭、動畫的種類及區(qū)別:
三種動畫:View動畫歧譬、幀動畫、屬性動畫
View動畫:平移動畫(TranslateAnimation)搏存、縮放動畫(ScaleAnimation)、旋轉動畫(RotateAnimation)矢洲、透明度動畫(AlphaAnimation)璧眠。它只移動了View的內容,沒有改變View在布局中的位置读虏,做的只是影像動畫责静。View動畫的使用可以在XML中創(chuàng)建動畫文件也可以在java中直接new出相對應的動畫并傳遞參數再開啟動畫即可。View動畫的使用場景-->ListView的item入場動畫盖桥,在ListView布局中加入layoutAnimation 屬性灾螃,也可以在java代碼中創(chuàng)建LayoutAnimationController來使用。Activity進出動畫主要使用overridePendingTransition方法來實現Activity的專場動畫揩徊。
自定義View動畫:寫一個類繼承Animation腰鬼,重寫它的initialize()和applyTransformation()方法。在initialize方法中做一些初始化的工作塑荒,在applyTransformation方法中做一些矩陣變換的操作熄赡。
幀動畫:順序播放一組預先定義好的圖片。
屬性動畫:ValueAnimator齿税、ObjectAnimator彼硫、AnimatorSet;ObjectAnimator繼承自ValueAnimator凌箕,AnimatorSet是一個動畫集合拧篮,里面可以包含多個ObjectAnimator和animator。屬性動畫還提供了兩個監(jiān)聽屬性動畫播放的監(jiān)聽接口-->AnimatorUpdateListener 和 AnimatorListener牵舱,AnimatorListener需要實現4個方法串绩,onAnimationStart、onAnimationEnd仆葡、onAnimationCancel赏参、onAnimationRepeat志笼,開始、結束把篓、取消纫溃、重復。AnimatorUpdateListener只需要實現一個方法韧掩,onAnimationUpdate方法紊浩。其中AnimatorListener還提供了AnimatorListenerAdapter,里面有6個方法疗锐,多了onAnimationPause坊谁、onAnimationResume方法。
屬性動畫原理:根據外界傳入的屬性的初始值和最終值滑臊,以動畫的效果多次去調用set方法口芍,來達到動畫效果。所以必須提供該動畫作用對象屬性值的set方法雇卷。屬性動畫可以配置差值器和估值器實現非勻速動畫鬓椭。
使用動畫需要注意的問題:在Activity退出時需要及時關閉,不然會導致內存泄漏关划。使用幀動畫容易導致OOM內存溢出。View動畫后會出現setVisibility方法無效的問題贮折,需要調用view.clearAnimation方法清除View的動畫即可调榄。View動畫點擊事件還是在動畫原位置,而不是移動后的位置臼疫。
7、Android四大組件:Activity烫堤、Service、BroadcastReceiver富蓄、ContentProvider立倍;
Activity耗時無響應是5s口注,BroadcastReceiver是10s娇斑,Service是20s
除了BroadcastReceiver外其他的三種組件都必須在Manifest中進行注冊毫缆,對于BroadcastReceiver來說,可以在AndroidManifest中注冊也可以在代碼中進行注冊芬骄。在調用方式上來說,Activity泽本、Service、BroadcastRevceiver都需要借助Intent來調用赌莺,而ContentProvider無需借助Intent。
BroadcastReceiver注冊方式:靜態(tài)注冊-->在AndroidManifest中進行注冊巢音,這中注冊方式APP在安裝的時候就會被系統(tǒng)解析官撼,無需啟動就可以接收到相關的廣播(8.0版本后廣播需要動態(tài)注冊掠哥,不然可能沒有效果)续搀。動態(tài)注冊-->需要通過Context.registerReceiver來實現,并且在不需要的時候需要通過Context.unRegisterReceiver來解除廣播榛了。這種注冊方式需要開啟APP才能接收到廣播。BroadcastReceiver一般不需要停止战坤,也沒有停止的概念途茫。廣播的類型有普通廣播、有序廣播栅组、粘性廣播三種。有序廣播需要在AndroidManifest中設定廣播的優(yōu)先級priority司浪,優(yōu)先級大的先接收到廣播,后面的再接收到认罩,中途也可以攔截垦垂,發(fā)送廣播一般是全局廣播间校,要發(fā)送本地廣播需要借助LocalBroadcastManager來注冊廣播和發(fā)送廣播。
ContantProvider時一種數據共享型組件滓彰,用于向其他組件乃至其他應用共享數據揭绑。無法直接被用戶感知。對于ContentProvider組件來說需要實現增刪改查這四種操作邦蜜,在它的內部維持著一份數據集合。這四種方法需要保證線程安全,ContentProvider組件也不需要手動停止。
Service是運行在主線程的睡榆,Service中不能進行耗時操作,如有耗時操作需要創(chuàng)建子線程來進行執(zhí)行宿崭。Service中的Binder對象調用的方法是運行在Binder線程池中奖蔓。
啟動方式:startService和bindService吆鹤。結束方式:stopService和unBindService。
startService和stopService一一對應,一個開啟一個結束廊镜。
Service的生命周期:(startService)-->onCreate--->onStartCommand--->(stopService)--->onDestroy。
bindService和unbindService一一對應雹姊,一個開始一個結束。
Servicec的生命周期:(BindService)-->onCreate--->onBind--->(UnbindService)--->onUnbind--->onDestroy歧杏。
start和stop只能開啟和關閉,無法操作service凯力。bind和unbind可以操作service。
start開啟的service祈惶,調用者退出后service仍然存在奸腺。bind開啟的service,調用者退出后讹蘑,隨著調用者銷毀。
在整個生命周期內版仔,只有startCommand()能被多次調用。其他方法只能被調用一次。(即只能綁定和解綁一次)
綁定后沒有解綁变泄,無法使用stopService()將其停止
如果service已經啟動,那么調用startService()將只調用startCommand()方法
如果是以bindService開啟,那么使用unbindService時就會自動調用onDestroy銷毀
8、線程與線程池:
線程sleep和wait的區(qū)別?什么情況下會出現線程阻塞衣摩?有幾種鎖既琴?
sleep()方法 和wait()方法的區(qū)別:
1、這兩個方法來自不同的類磺箕,sleep()來自Thread,而wait()來自Object類。
2屠列、sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法蝌诡。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用甥捺,而sleep可以在任何地方使用(使用范圍)。
4屋休、sleep必須捕獲異常,而wait毅哗,notify和notifyAll不需要捕獲異常。
5、sleep是Thread類的靜態(tài)方法捕发。sleep的作用是讓線程休眠制定的時間遏匆,在時間到達時恢復凡纳,也就是說sleep將在接到時間到達事件事恢復線程執(zhí)行。wait是Object的方法暴氏,也就是說可以對任意一個對象調用wait方法,調用wait方法將會將調用者的線程掛起,直到其他線程調用同一個對象的notify或者notifyAll方法才會重新激活調用者端朵。
Sleep狀態(tài)是指自身調用了sleep方法或者中途有別的線程join到當前線程導致的睡眠狀態(tài)招狸,要解除這種狀態(tài)要么是睡眠時間結束乘凸,要么是join的線程執(zhí)行完畢,還可以通過調用interrupt方法來結束睡眠葛作。Sleep不會釋放鎖。
Wait狀態(tài),主要是在多個線程執(zhí)行同步方法中出現,要喚醒wait狀態(tài)的線程只能通過別的線程調用notify()或者notifyAll()方法來喚醒wait狀態(tài)的線程绍傲。Wait狀態(tài)也可以是因為執(zhí)行當前線程的條件不夠试读,wait也可以設置等待的時間比藻,當時間到達后進行自己喚醒慢叨。調用wait方法后會釋放鎖持有的鎖,Sleep不會釋放鎖轩拨。Notify和notifyAll不能指定喚醒某一個線程够坐。Notify和notifyAll只能在被鎖的方法塊內進行調用梯影,否則會拋異常。
線程阻塞,當有別的線程join到當前線程時陪白,該線程會處于阻塞狀態(tài)轧钓,CPU時間輪換機制也會導致線程阻塞弛房。
鎖的種類:
隱式鎖 synchronized (我們看不到synchronized獲取鎖和釋放鎖的過程荷逞,此過程不能被中斷)synchronized是可重入鎖镊叁,內部實現是非公平鎖都弹。也稱為排它鎖。
顯示鎖 Lock(需要我們自己手動的獲取鎖和釋放鎖宪郊,此過程可以被中斷依啰,即可以嘗試去獲取鎖叹誉,在鎖和釋放鎖之間執(zhí)行語句,有可能拋出異常,拋出異常后就不會執(zhí)行釋放鎖,所以一般需要在finally中進行釋放鎖),Lock鎖是一個接口璧亮,我們一般使用的是它的實現ReentrantLock(可重入鎖),默認的實現是非公平鎖及刻,公平鎖和非公平鎖的區(qū)別是按順序獲取鎖和非按順序獲取鎖骆莹。非公平鎖的效率比公平鎖高,這個涉及到線程的掛起疚察,線程的掛起涉及到CPU的上下文切換猜嘱。ReentrantLock也是排它鎖弦撩。
讀寫鎖点晴,寫鎖是排它鎖陪竿,讀鎖不是排它鎖。
線程隔離:ThreadLocal進行線程隔離长酗,當有多個線程對一個變量進行訪問時茉继,可以通過ThreadLock來進行隔離乙墙,即初始化一個ThreadLock并傳入初始值腥刹,在各個線程調用完后將變化二點值設置進去蛙粘。
開始一個線程--thread.start();(同一個線程只能調用一次start方法穴肘,調用兩次會報錯)伯复。當我們創(chuàng)建一個線程啸如,直接使用這個線程的實例進行run方法的調用時侍匙,此時調用在創(chuàng)建線程實例的線程一般是主線程。只有調用start方法后叮雳,run方法的執(zhí)行才是在我們創(chuàng)建的線程中執(zhí)行的筐骇。
Join();方法的理解,就是加入當前線程江滨,A線程總的需要執(zhí)行5分鐘铛纬,B在A執(zhí)行了2分鐘后join進去,B線程執(zhí)行3分鐘唬滑,當B線程執(zhí)行完后再繼續(xù)執(zhí)行A線程告唆。
終止一個線程:
thread.stop(立馬殺死線程,不管線程的事情有沒有做完晶密,不管占據的資源是否釋放)
thread.suspend(掛起一個線程擒悬,并不會釋放資源,拿到鎖的話也不會釋放鎖)
thread.resume(將掛起的線程重新運行起來)
以上幾種方法都不建議使用稻艰,真正要終止一個線程有如下幾種方法懂牧,
inturrupt();(中斷線程,協(xié)作式不是立馬中斷尊勿,在線程內部需要判斷isInturrupted()的信號)
Thread.interrupted();(靜態(tài)方法僧凤,判斷線程是否被中斷了,會同時將中斷標識位改寫為false)
isInterrupted();(線程是否中斷)元扔。
中斷一個線程的操作是在線程外部調用Thread類中的inturupt()方法躯保,在線程內部接收isInturrupted信號,通過此信號來結束自身的運行代碼澎语。
Android中的線程有:Thread途事、Runnable、Callback(Callback需要借助FutureTask來包裝再交給Thread執(zhí)行擅羞,可以有返回值尸变,返回值通過FutureTask.get()來獲取)减俏、AsyncTask振惰、IntentService、HandlerThread垄懂。
AsyncTask:的底層是使用線程池(所使用的線程是Callable和FutureTask的結合骑晶,內部使用了2個線程池,第一個線程池保證所有的任務都是串行執(zhí)行草慧,第二個線程池是執(zhí)行具體的任務)桶蛔,底層也封裝了Handler,AsyncTask的四個主要方法漫谷,onPreExecute-->doInBackground-->onProgressUpdate-->onPostExecute仔雷,這四個方法除了doInBackground在線程池中執(zhí)行,其他的三個方法都在主線程執(zhí)行,AsyncTask方法的三個參數分別是要執(zhí)行任務的輸入參數碟婆、后臺任務執(zhí)行進度類型电抚、后臺任務返回的結果類型。在異步任務執(zhí)行之前onPreExecute方法會被調用竖共,可以在此方法做一些初始化的工作蝙叛,onPostExecute是任務執(zhí)行結果返回回調方法,onProgressUpdate是任務執(zhí)行進度回調方法公给,doInBackground是后臺執(zhí)行任務線程借帘。AsyncTask還提供了onCancelled的方法,當任務被取消時會回調此方法淌铐,但不會回調onPostExecute方法肺然。AsyncTask必須在主線程創(chuàng)建,AsyncTask的execute方法必須在主線程執(zhí)行腿准。一個AsyncTask只能執(zhí)行一次即只能調用一次execute方法际起,否則會報運行時異常。
AsyncTask優(yōu)缺點:
優(yōu)點:AsyncTask是一個輕量級的異步任務處理類吐葱,輕量級體現在街望,使用方便、代碼簡潔上唇撬,而且整個異步任務的過程可以通過cancel()進行控制;
缺點:不適用于處理長時間的異步任務展融,一般這個異步任務的過程最好控制在幾秒以內窖认,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時告希,UI更新變得困難扑浸。
使用注意事項:
AsyncTask不與任何組件綁定生命周期,在Activity 或 Fragment中使用 AsyncTask時燕偶,最好在Activity 或 Fragment的onDestory()調用 cancel(boolean)喝噪;
若AsyncTask被聲明為Activity的非靜態(tài)內部類,當Activity需銷毀時指么,會因AsyncTask保留對Activity的引用 而導致Activity無法被回收酝惧,最終引起內存泄露,最好將AsyncTask聲明為Activity的靜態(tài)內部類
IntentService:是一個服務伯诬,系統(tǒng)對其進行封裝可以更方便的執(zhí)行后臺任務晚唇,內部采用HandlerThread來執(zhí)行任務,當任務執(zhí)行完畢后會自動退出盗似。
HandlerThread:是一種具有消息循環(huán)的線程哩陕,在它的內部可以使用Handler。使用:一個UI線程的Handler、一個HandlerThread悍及、一個使用了HandlerThread線程的Looper對象的內部Handler并重寫其handlerMessage方法闽瓢,在此方法內部進行耗時操作,耗時操作結束后再使用UI線程的Handler發(fā)送消息回調到UI線程的handlerMessage方法進行UI方面的操作心赶。
線程池ThreadPoolExecute:
優(yōu)點:1扣讼、重復使用線程池中的線程,避免因線程的創(chuàng)建和銷毀所帶來的性能開銷园担。
2届谈、能有效控制線程池的最大并發(fā)數,避免大量的線程之間相互搶占系統(tǒng)資源導致的阻塞現象弯汰。
3艰山、能夠對線程進行簡單的管理,并提供定時執(zhí)行及指定時間間隔循環(huán)執(zhí)行咏闪。
4種線程池:FixedThreadPool曙搬、CachedThreadPool、ScheduledThreadPool鸽嫂、SingleThreadPool纵装,這四種線程池都是直接或間接的通過配置ThreadPoolExecute來實現自身的功能特性。
FixedThreadPoole只有核心線程即核心線程數和最大線程數是一樣的据某,沒有超時機制橡娄,任務隊列也沒有大小限制,核心線程是不會被回收的癣籽,可以快速響應外界的請求挽唉。
CachedThreadPool沒有核心線程,最大線程數是int的最大值筷狼,空閑線程存活60秒會被回收瓶籽,隊列基本不會滿,一有請求就執(zhí)行了埂材。
ScheduledThreadPool核心線程數是固定的塑顺,而非核心線程是沒有限制的,非核心線程存活0秒俏险。
SingleThreadPool只有一個核心線程严拒,非核心線程也只有一個,就相當于沒有非核心線程竖独,意義在于幫外界處理需要線程同步的問題糙俗,將外界的任務統(tǒng)一到一個線程中,使得這些任務之間不需要處理線程同步的問題
自定義線程池其實就是自己去配置ThreadPoolExecute里面的參數從而達到自定義的效果预鬓。
當最大線程數已滿隊列已滿時線程池的4種策略:
1巧骚、DiscardOldestPolicy:接收新的任務赊颠,丟棄阻塞隊列中最靠前的任務即最新放入的隊列的任務。
2劈彪、AbortPolicy:直接拋出異常(默認策略)竣蹦。
3、CallerRunsPolicy:用調用者所在的線程來執(zhí)行任務沧奴,如果在UI線程出現這種情況會導致ANR的出現痘括。
4、DiscardPolicy:直接丟棄所提交的任務滔吠。
線程池的配置:
CPU密集型---配置CPU核心數N+1個核心線程數纲菌。
IO密集型----配置CPU核心數2*N個核心線程數。
混合型----盡可能的進行分離疮绷。
CPU核心數的獲群采唷:Runtime.getRuntime().availableProcessors()
9、進程間的通訊----IPC:
進程間通訊的方式:Intent傳遞數據冬骚、共享文件椅贱、SharedPreferences、基于Binder的Messenger和AIDL只冻、ContentProvider庇麦、Socket。
序列化和反序列化的方式:Java 實現Serializable接口 Android 實現Parcelable接口
1喜德、通過Intent的進行跨進程數據傳輸山橄,在Intent中添加Bundle來進行數據的傳輸,Bundle自身實現了序列化接口舍悯,Bundle中的數據類型也必須是實現了序列化的對象航棱。如果A進程的數據經過轉換了不能通過Bundle進行傳輸,則可以在B進程創(chuàng)建一個Service(可以使用IntentService)贱呐,A進程綁定這個Service丧诺,將原始數據交給Service入桂,在Service中進行數據的轉換奄薇,之后將結果直接交給B進程處理。
2抗愁、通過共享文件馁蒂,A進程將實現了序列化的對象通過輸出流寫入到一個文件中,文件的格式沒有特定的要求蜘腌,可以是普通文件也可以是XML類型文件沫屡,B進程通過輸入流去讀取這個文件即反序列化從而達到跨進程數據傳輸的目的。共享文件需要考慮線程同步的問題撮珠,在并發(fā)中會有問題沮脖。
3、SharedPreferences是使用XML文件形式來進行數據存儲,系統(tǒng)對他有一定的緩存策略勺届,會存在一個副本驶俊,在多進程的情況下會存在多個副本,在此情況下進行數據傳輸不可靠免姿。
4饼酿、Messenger的底層實現也是AIDL,具體使用方法胚膊,在B進程創(chuàng)建一個Service故俐,在Service內部寫一個靜態(tài)內部類MessengerHandler實現Handler并重寫handlerMessage方法,在Service內部創(chuàng)建一個Messenger對象mMessenger紊婉,參數傳入MessengerHander對象药版,在Service的onBind方法中返回通過mMessenger.getBinder();的Binder對象。在A進程進行Service的綁定肩榕,通過B進行返回的Binder對象創(chuàng)建Messenger對象刚陡,再創(chuàng)建一個Message對象,再Message對象中放入需要傳遞的數據株汉,再通過Messenger對象進行Message數據的發(fā)送筐乳。通過Messenger進行跨進程數據傳輸,數據必須放入Message中乔妈。如果需要回復客戶端蝙云,服務端通過接收到的Message對象的replyTo方法獲取客戶端的Messenger對象,再通過客戶端的Messenger對象發(fā)送Message消息路召,客戶端同樣需要準備一個Messenger和Handler對象勃刨,和服務端的Messenger一樣的實現,用來接收服務端的回復消息股淡,此Messenger對象需要在客戶端給服務端發(fā)送消息的時候放入Message對象的replyTo參數中身隐。
5、AIDL唯灵,創(chuàng)建AIDL接口文件丁稀、創(chuàng)建AIDL接口方法中使用到的變量AIDL文件并申明為parcelable 類型禽拔、創(chuàng)建服務端進程Service對象访锻,并創(chuàng)建AIDL接口的實現類话侄,將AIDL接口實現類在onBind方法中返回給客戶端、客戶端綁定服務端的Service并通過返回的Binder(AIDL接口實現類)對象調用AIDL接口方法敛瓷、如果需要服務端給客戶端回調還需要進行注冊與反注冊操作叁巨,需要用到一個特殊的容器RemoteCallbackList進行監(jiān)聽器的保存、如果是耗時操作發(fā)起調用的方法都能不能在UI線程中發(fā)起呐籽、AIDL接口中所有用到的包需要顯示導入锋勺、服務端和客戶端所有和AIDL文件有關的類需要放在包名相同的路徑下蚀瘸,這樣才能保證反序略化成功、保證服務的安全還需要權限的驗證庶橱,可以在onBind的方法中進行驗證也可以在AIDL接口中的onTransact方法中驗證包名類名苍姜,權限需要進行自定義。
6悬包、ContentProvider底層實現也是Binder衙猪,寫一個類繼承ContentProvider,重寫onCreate布近、query垫释、insert、delete棵譬、update、getType總共6個方法预伺。ContentProvider需要在Manifest中配置订咸,配置name、authorities(uri)酬诀、permission脏嚷、process配置信息÷饔客戶端通過getContentResolver獲取ContentProvider的代理對象(類似代理模式)調用其里面的增刪改查方法父叙,傳入Uri參數。
7肴裙、Socket是基于TCP鏈接來實現的趾唱,在服務端創(chuàng)建一個Service,在Service中開啟一個線程用于創(chuàng)建服務端的ServerSocket用于接收客戶端的請求鏈接蜻懦,監(jiān)聽相關的端口和host地址甜癞,通過accept接收到客戶端的Socket對象,再通過此Socket對象的輸入輸出流來讀取客戶端的消息和發(fā)送消息給客戶端宛乃∮圃郏客戶端創(chuàng)建Socket對象,再去鏈接服務端的Socket地址烤惊,再通過自己的Socket對象的輸入輸出流獲取服務端返回的信息和發(fā)送消息給服務端乔煞。最后都需要進行流的關閉和Socket鏈接的關閉操作吁朦。
8柒室、Binder連接池:使用Binder連接池可以有效的實現一個Service完成N個AIDL接口對應的IBinder對象的處理。共有A逗宜、B雄右、IBinderPool三個AIDL接口空骚,一個Service,一個BinderPool處理類擂仍,在IBinderPool的AIDL接口中定義一個返回IBinder對象參數為binderCode的接口方法囤屹,Service中返回IBinderPool類型的IBinder對象,在BinderPool處理類中進行Service的綁定逢渔,客戶端拿到BinderPool管理類的實例肋坚,調用其查詢IBinder對象的方法獲取對應的IBinder對象,通過此IBinder對象調用asInterface方法獲得對應的AIDL接口對象肃廓,再調用相應的AIDL接口方法智厌。
10、Activity的啟動模式:
Standard:這個模式是默認的啟動模式盲赊,即標準模式铣鹏,在不指定啟動模式的前提下,系統(tǒng)默認使用該模式啟動Activity哀蘑,每次啟動一個Activity都會重寫創(chuàng)建一個新的實例诚卸,不管這個實例存不存在,這種模式下绘迁,誰啟動了該模式的Activity合溺,該Activity就屬于啟動它的Activity的任務棧中。這個Activity它的onCreate()缀台,onStart()辫愉,onResume()方法都會被調用。
SingleTop:如果此 Activity 還未啟動将硝,則開啟這個 Activity 時會執(zhí)行的生命周期方法和 Standard 的模式是一樣的恭朗。如果此 Activity 已經啟動,并且在其棧的棧頂依疼,則再次開啟這個 Activity 時會執(zhí)行此 Activity 的 onPause痰腮、onNewIntent、onResume 方法律罢,如果此 Activity 已經啟動膀值,但不再其棧的棧頂,則再次開啟這個 Activity 時會執(zhí)行的生命周期方法和 Standard 的模式是一樣的误辑。
SingleTask:如果此 Activity 還未啟動沧踏,則開啟這個 Activity 時會執(zhí)行的生命周期方法和 Standard 的模式是一樣的。如果此 Activity 已經啟動巾钉,并且在其棧的棧頂翘狱,則再次開啟這個 Activity 時會執(zhí)行此 Activity 的 onPause->onNewIntent->onResume 方法。如果此 Activity 已經啟動砰苍,但不再其棧的棧頂潦匈,則再次開啟這個 Activity 時阱高,會將壓在此 Activity 上面的同一個棧的 Activity 全部清除(除了棧頂的Activity,都執(zhí)行其onDestroy方法)并執(zhí)行 :(棧頂Activity的onPause)-> onRestart->onStart->onNewIntent->onResume ->(棧頂的onStop->onDestroy) 生命周期方法茬缩。注意點:誰啟動它赤惊,它將和它在同一個棧中(前提條件它們屬于同一種類型的棧,standard singleTop singleTask 按照我的理解他們是同一種類型的棧)singleInstance 是別的一種棧凰锡,并且它全局只會有一個實例未舟。
SingleInstance:此啟動模式是 singleTask 的加強版,擁有 singleTask 的所有特性掂为,新增的特性是具有此種模式啟動的 Activity 只能單獨的位于一個任務棧中处面,棧內復用,此后均不會重新創(chuàng)建此 Activity 的實例菩掏,除非此棧被銷毀魂角,再次啟動此 Activity 會執(zhí)行的生命周期方法為onPause、onNewInstance智绸、onResume野揪。
A啟動B :A.onPause->B.onCreate->B.onStart->B.onResume->A.onStop->A.onSaveInstanceState
B返回A:B.onPause->A.onRestart->A.onStart->A.onResume->B.onStop->B.onDestroy
11、Fragment生命周期:
onAttach()-->onCreate()-->onCreateView()-->onActivityCreated()-->onStart()-->onResume()
-->onPause()-->onStop()-->onDestroyView()-->onDestroy()-->onDetach()瞧栗。
12斯稳、Java的四種引用:
強引用、軟引用迹恐、弱引用挣惰、虛引用。
強引用:Object obj =new Object();
屬于不可回收的資源殴边,垃圾回收器絕不會回收它憎茂。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤锤岸,使程序異常終止竖幔,也不會靠回收具有強引用的對象,來解決內存不足的問題是偷。
軟引用: Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
SoftReference reference = new SoftReference(obj, queue);
//強引用對象滯空拳氢,保留軟引用
obj = null;
如果一個對象只具有軟引用,那么它的性質屬于可有可無的那種蛋铆。如果此時內存空間足夠馋评,垃圾回收器就不會回收它,如果內存空間不足了刺啦,就會回收這些對象的內存留特。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的告訴緩存磕秤。軟引用可以和一個引用隊列聯合使用,如果軟件用所引用的對象被垃圾回收捧韵,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中市咆。當內存不足時,軟引用對象被回收時再来,reference.get()為null蒙兰,此時軟引用對象的作用已經發(fā)揮完畢,這時將其添加進ReferenceQueue 隊列中芒篷。
弱引用:Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
WeakReference reference = new WeakReference(obj, queue);
//強引用對象滯空搜变,保留軟引用
obj = null;
如果一個對象具有弱引用,那其的性質也是可有可無的狀態(tài)针炉。而弱引用和軟引用的區(qū)別在于:弱引用的對象擁有更短的生命周期挠他,只要垃圾回收器掃描到它,不管內存空間充足與否篡帕,都會回收它的內存(弱引用沒有被使用就會被回收)殖侵。同樣的弱引用也可以和引用隊列一起使用。
虛引用:Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference reference = new PhantomReference(obj, queue);
//強引用對象滯空镰烧,保留軟引用
obj = null;
虛引用和前面的軟引用拢军、弱引用不同,它并不影響對象的生命周期怔鳖。如果一個對象與虛引用關聯茉唉,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收结执。虛引用必須和引用隊列關聯使用度陆,當垃圾回收器準備回收一個對象時,如果發(fā)現它還有虛引用献幔,就會把這個虛引用加入到與之關聯的引用隊列中程序可以通過判斷引用隊列中是否已經加入了虛引用坚芜,來了解被引用的對象是否將要被垃圾回收。如果程序發(fā)現某個虛引用已經被加入到引用隊列斜姥,那么就可以在所引用的對象的內存被回收之前采取必要的行動鸿竖。
13、TCP和Socket
TCP是面向連接的铸敏,全雙工的缚忧,可靠的流協(xié)議,面向字節(jié)流杈笔。屬于傳輸層協(xié)議
UDP是面向無連接的闪水,不可靠的,面向報文蒙具,沒有擁塞控制的通訊協(xié)議
應用層->傳輸層->網絡層->數據鏈路層
TCP的三次握手:
客戶端發(fā)送請求建立連接 SYN=1,seq=J
服務端響應確認應答球榆,并且請求建立連接 SYN=1,ACK=1,ack=J+1,seq=K
客戶端對服務器請求確認應答 ACK=1,ack=k+1
ack 表示序列號朽肥,ACK表示標記位
TCP為什么是三次握手?
TCP是一個可靠的傳輸協(xié)議持钉,主要是為了交換通信的初始序列號 ISN衡招,2次的話無法確認雙方的系列號,4次的話第二次和第三次是重復的每强,可以進行合并始腾。tcp是全雙工,為保證傳輸的可靠性空执,需要給每次傳輸的數據段添加序號浪箭,那么初始的序列號就是tcp三次握手真正的意義所在,而為了確保交換雙方的初始序號辨绊,最少需要三次才行奶栖。
TCP三次握手存在漏洞,可以進行SYN洪泛攻擊门坷,通過偽造的客戶端ip地址向服務器發(fā)送請求(第一次握手),服務端進行響應(二次握手)驼抹,但是得不到真實的客戶端響應(三次握手),導致服務端資源消耗拜鹤,主機資源耗盡無法響應真實的客戶端框冀。解決方案:1,無效連接監(jiān)控釋放。2采用防火墻機制敏簿。
TCP四次揮手:
客戶端發(fā)送關閉請求
服務端響應客戶端的關閉請求
服務端發(fā)送關閉請求
客戶端發(fā)送關閉請求確認
TCP為什么需要四次揮手明也?
TCP是全雙工通信的,意味著客戶端和服務端都可以收發(fā)消息惯裕,客戶端發(fā)送關閉請求后温数,服務端需要進行響應,確認收到該消息蜻势,但是這只是客戶端往服務端消息發(fā)送通到關閉撑刺,服務端往客戶端的發(fā)送消息通道未進行關閉,所以服務端同樣也需要發(fā)送關閉請求將服務端到客戶端的消息通道關閉握玛。
TCP可以通過序列號與確認應答提高數據傳輸的可靠性够傍,比如A發(fā)送數據(1-100),B確認應答(下一個是101)挠铲,如果中間存在丟包的話就會重發(fā)冕屯。
TCP中有窗口機制,發(fā)送方的窗口大小由接收方確認拂苹,可以確保數據不丟失安聘,并且控制數據的發(fā)送速度。
Socket:
TCP用主機的IP地址加上端口號作為TCP連接的端點,這個端點就叫套接字Socket浴韭∏鹩鳎客戶端使用Socket主要就是創(chuàng)建一個Socket對象傳入服務端的ip和端口號,通過該Socket對象拿到輸入輸出流念颈,就可以向服務端收發(fā)數據了泉粉。注意發(fā)送數據后需要調用flush()方法進行刷新緩沖區(qū),因為TCP中存在一個輸入和輸出緩沖區(qū)舍肠。
Socket保持長連接可以使用心跳機制搀继,每隔一段時間(一般是4分鐘窘面,移動和聯通超時時間都是5min翠语,電信大于28min)向服務端發(fā)送一個自定義信息,確保連接存活和有效通信财边。也就是常說的心跳包肌括。
14、對稱加密酣难,非對稱加密谍夭,Http與Https:
對稱加密:對稱加密比較簡單,就是客戶端和服務器共用同一個密鑰憨募,該密鑰可以用于加密一段內容紧索,同時也可以用于解密這段內容。對稱加密的優(yōu)點是加解密效率高菜谣,但是在安全性方面可能存在一些問題珠漂,因為密鑰存放在客戶端有被竊取的風險。對稱加密的代表算法有:AES尾膊、DES媳危,3DES,RC5,RC6,IDEA,Blowfish等。
非對稱加密:非對稱加密則要復雜一點冈敛,它將密鑰分成了兩種:公鑰和私鑰待笑。公鑰通常存放在客戶端,私鑰通常存放在服務器抓谴。使用公鑰加密的數據只有用私鑰才能解密暮蹂,反過來使用私鑰加密的數據也只有用公鑰才能解密剔蹋。非對稱加密的優(yōu)點是安全性更高晴叨,因為客戶端發(fā)送給服務器的加密信息只有用服務器的私鑰才能解密,因此不用擔心被別人破解膀息,但缺點是加解密的效率相比于對稱加密要差很多措拇。非對稱加密的代表算法有:RSA我纪、ElGamal,背包算法等。
常用的加密算法:SHA1浅悉、AES趟据、RSA、MD5术健。
http與https的區(qū)別:
http協(xié)議傳輸的數據都是未加密的汹碱,明文傳輸導致隱私信息不安全。默認端口是80荞估,http有三大風險(竊聽咳促,篡改,冒充)勘伺,第三方可以獲取跪腹,修改內容,冒充身份參與通信飞醉。
https是安全的超文本傳輸協(xié)議冲茸,使用SSL協(xié)議對Http協(xié)議傳輸的數據進行加密,保證了會話過程總的安全性缅帘。默認端口是443轴术。https為解決三大風險而設計,加密傳輸钦无,校驗機制逗栽,配置身份證書CA。
SSL首先對對稱加密的秘鑰使用公鑰進行非對稱加密失暂,對傳輸內容使用對稱加密彼宠。CA機構通過網站開發(fā)者提供的公鑰、域名趣席、有效時長等信息來輔助校驗兵志。
http請求傳輸過程:
發(fā)送端:應用層(http數據 + TCP首部) -> 傳輸層(+ IP首部)->網絡層(+以太網首部)->數據鏈路層 --》接收端從數據鏈路層一層層反向解析
一次完整的http請求過程:
DNS域名解析
三次握手建立TCP連接
客戶端向服務器發(fā)送請求命令 (Get/域名/http協(xié)議版本)
客戶端發(fā)送請求頭信息
服務器對客戶端的響應進行應答(協(xié)議版本/狀態(tài)碼200/OK)
服務器返回響應頭信息
服務器向客戶端發(fā)送數據
服務器關閉TCP連接
HTTPS單向認證:
客戶端發(fā)送協(xié)議版本/域名/請求類型等信息給服務端
服務端返回證書,協(xié)議版本信息宣肚,隨機數以及服務器公鑰給客戶端
客戶端校驗證書是否合法想罕,合法繼續(xù),否則警告
客戶端發(fā)送自己可支持的對稱加密方案給服務端霉涨,供其選擇
服務端選擇加密程度高的加密方式
將選擇好的加密方案以明文的方式傳給客戶端
客戶端收到加密方式后按价,產生隨機碼,作為后面對稱加密的秘鑰笙瑟,使用服務端的公鑰進行加密楼镐,發(fā)送給服務端
服務端使用私鑰進行解密,獲取對稱加密的秘鑰
雙方使用對稱加密進行通信往枷,確保通信安全
HTTPS雙向認證就是客戶端也有一份CA證書框产,會發(fā)送給服務器凄杯,服務器會對證書校驗獲取其公鑰,后面使用客戶端的公鑰將加密方案進行加密發(fā)送給客戶端秉宿,客戶端收到后再進行解密戒突。
HTTP版本區(qū)別:
HTTP1.0 支持GET,HEAD,POST方法,支持長連接(默認使用短連接)
HTTP1.1 默認支持長連接,新增OPTIONS,PUT, DELETE, TRACE, CONNECT方法
HTTP2.0 多路復用(二進制分幀),頭部壓縮描睦,隨時復位膊存。主要是突破上一代標準的性能限制,改進傳輸性能忱叭,實現低延遲和高吞吐量隔崎。
15、ANR相關的日志文件名字:
日志文件地址:在data/anr文件下韵丑,文件的名稱是traces.txt爵卒。
出現ANR的情況:0
1.界面操作按鈕的點擊等待響應時間超過5秒
2.HandleMessage回調函數執(zhí)行超過10秒
3.BroadcasterReciver里的onRecive()方法處理超過10秒
4.Service 在20s內無法處理完成
16、內存泄漏檢測工具:
內存泄漏:長生命周期的對象持有了短生命周期對象的引用埂息,導致短生命周期的對象沒法被回收技潘,導致的內存泄漏遥巴。GC的主要戰(zhàn)場是在共享內存的堆區(qū)
檢測工具:AndroidStudio 里面的 profile千康、MAT、DDMS铲掐、Finder_Activity(騰訊內部使用拾弃,百度找不到),我們常用的是 profile 和 MAT的結合使用摆霉。第三發(fā)開源庫豪椿。
具體使用:
使用Profile工具截取一段時間,然后根據下方窗口heap堆內存查看分配的Allocation空間携栋,點擊的話右邊窗口Instance View可以看到具體在什么地方使用搭盾。可以將hprof文件導出婉支,然后使用sdk目錄下platform-tools文件夾下hprof-conv.exe工具進行轉換(命令行hprof-conv -z 源文件 目標文件.hprof文件)再放入MAT工具中進行分析鸯隅。Histogram查看當前程序的所有對象,Dominator Tree查看程序中的大對象向挖。右鍵進行Merge標簽下排除除了強引用的對象蝌以。從最底部看起看對象的引用鏈。
17何之、Java泛型中super 和 extends的區(qū)別:
PECS原則:
如果要從集合中讀取類型T的數據跟畅,并且不能寫入,可以使用 ? extends T通配符(Producer Extends)溶推。
如果要從集合中寫入類型T的數據徊件,并且不需要讀取奸攻,可以使用 ? super T通配符(Consumer Super)。
如果既要存又要取虱痕,那就不要使用任何通配符舞箍。
18、JVM內存結構皆疹,堆和棧的理解:
程序計數器:指向當前線程正在執(zhí)行的字節(jié)碼指令的地址疏橄。為什么需要?Java是多線程的略就,意味著線程切換捎迫,記錄地址確保多線程情況下程序的正常執(zhí)行。
為什么使用棧表牢?棧類似于Java中方法的調用窄绒,先進后出FILO,當一個棧幀無限遞歸調用而沒有返回時崔兴,超出一定的空間就會報棧內存溢出異常彰导。
每一個方法在執(zhí)行的同時會創(chuàng)建一個棧幀,棧幀還可以劃分為局部變量表敲茄,操作數棧位谋,動態(tài)連接,返回地址等堰燎。如果是在棧上分配掏父,就不需要垃圾回收,線程執(zhí)行完后就沒有了秆剪,共享的對象才需要垃圾回收
方法區(qū)主要存儲靜態(tài)變量和常量以及類信息(class) 還有即時編譯期編譯后的代碼赊淑。
堆:存放幾乎所有對象實例以及數組,但是有一種棧上分配仅讽,就是開啟逃逸分析進行優(yōu)化之后陶缺。
堆可以進一步劃分為新生代和老年代,新生代包括Eden空間洁灵,From Survivor空間和To Survivor空間饱岸。缺省比例為8:1:1
堆內存分配策略:
對象優(yōu)先在Eden區(qū)分配处渣,大對象直接進入老年代聂抢,長期存活的對象將進入老年代揽趾,動態(tài)對象年齡判定两波,空間分配擔保测蘑。
GC觸發(fā)條件:堆內存空間不夠了夕土,先進行Minor GC(新生代) 然后是Full GC(老年代)
GC的算法:
1、引用計數法:A===>B怨绣,A引用B哈踱,B的引用計數就加1,C===>B梨熙,B的引用計數就再加1就等于2开镣,當B的引用計數等于0的時候就回收B。
缺點咽扇,當A===>B,B===>A當A和B相互引用的時候就不能判斷是否能進行回收邪财。
2、可達性算法:對象之間的引用就像一棵樹一樣质欲,從根節(jié)點出發(fā)树埠,不可達的對象都是可以回收的。
3嘶伟、復制回收算法:如果內存有200兆怎憋,將其分成兩份,第一份進行放對象九昧,當進行GC的時候绊袋,將不能被回收的對象復制到第二份中,將第一份中可回收的對象全部回收铸鹰,不會有內存碎片癌别。缺點:內存只能使用一半。
4蹋笼、新生代老年代(也屬于復制回收算法):新生代的內存空間比例是8:1:1展姐,為什么?90%的對象是不需要進行回收的(朝生夕死)剖毯,只需要對10%的對象進行回收的圾笨,所以用10%的空間來進行新老交替就是復制回收,所以新生代能使用的內存空間就有80%逊谋。其中還有一個空間擔保機制擂达,就是新生代那邊要是放不下了就會放入老年代中。
5涣狗、標記清除算法:將內存中能回收的對象進行標記谍婉,當GC的時候就直接進行回收舒憾,內存使用率能達到100%,不需要進行內存復制穗熬。缺點镀迂,GC后會導致出現很多不連續(xù)的內存塊,放不下大的對象唤蔗,也就會引起內存碎片的問題探遵。
6、標記整理算法:和標記清除算法很相似妓柜,只是后面對沒有回收的內存進行整理箱季,消除內存碎片的問題,100%內存使用率棍掐,需要進行內存復制藏雏。效率比標記清除算法低。
哪些對象可以作為GC Root?
- Class系統(tǒng)類加載器加載的對象
- Thread 活著的線程
- Stack Local -Java方法的local變量或參數
- JNI Local -JNI方法的local變量或參數
Java中可作為GcRoot的對象
- 虛擬機棧(棧幀中的本地變量表)中引用的對象
- 方法區(qū)中的類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- 本地方法棧中JNI引用的對象
7作煌、JDK1.7之后提出了G1
19掘殴、static和static final的區(qū)別:
static 和 static final哪個執(zhí)行得到速度比較快?static final 的執(zhí)行速度比較快粟誓,static final 不需要進行編譯奏寨,直接以二進制的文件保存在dex文件中可以直接進行調用,并不會在類的初始化申請內存鹰服,基本數據變量類型可以寫成static final 類型病瞳,如果是類型變量最好不要寫成這樣,這樣會增加APK包的大小悲酷,static 變量會有編譯器調用classinit方法進行初始化套菜。
20、優(yōu)化:
布局優(yōu)化:
調試GPU過度繪制舔涎,將Overdraw降低到合理范圍內笼踩;減少嵌套層次及控件個數,保持view的樹形結構盡量扁平(使用Hierarchy Viewer可以方便的查看)亡嫌,同時移除所有不需要渲染的view
使用GPU配置渲染工具,定位出問題發(fā)生在具體哪個步驟掘而,使用TraceView精準定位代碼挟冠,進行層級的尋找,看是否有能進行合并的層級袍睡。
使用標簽知染,merge減少嵌套層次、viewStub延遲初始化斑胜、include布局重用 (與merge配合使用)
過度繪制:在自定義View時可能會出現過度繪制控淡,可以對Canves進行圖層的保存嫌吠、切割、恢復操作掺炭,從而達到只繪制可見區(qū)域的效果辫诅。在控件上添加背景會大概率增加過度繪制的問題,可以在application中的theme主題風style中增加”android:windowBackground”標簽進行統(tǒng)一的背景顏色設置涧狮,減少在每個Activity布局中進行相關的設置炕矮。
性能優(yōu)化:不要再onMeause()、onLayout()者冤、onDraw()方法中去刷新UI---requestLayout()肤视,不要在這些方法中進行一些對象的創(chuàng)建。避免在循環(huán)中創(chuàng)建對象涉枫,允許復用的情況下邢滑,使用對象池進行緩存。
圖片壓縮:通過NDK編譯libjpeg庫生成Android中能使用的靜態(tài)庫或動態(tài)庫愿汰,進行相關的API調用實現圖片的壓縮殊鞭。也可以通過SDK中提供的api進行圖片的壓縮。
Apk瘦身:
使用svg圖片
國際化資源的配置 build文件下android defaultConfig節(jié)點下配置 resConfigs ‘en’
動態(tài)so庫打包配置build文件下android defaultConfig節(jié)點下配置ndk{abiFilters “armeabi-v7a”}
開啟代碼(代碼混淆 minifyEnabled true)和資源壓縮(shrinkResources true)
資源文件混淆 微信開源框架AndResGuard
黑白屏啟動優(yōu)化:
黑白屏尼桶,可以在app的風格里面進行設置操灿,可以設置為透明,設置為透明的可能導致桌面上點擊了APP圖標會有幾秒鐘沒有反應泵督,還有一種是將這個透明的或者黑白的界面設置一張圖片上去接著再跳到啟動界面趾盐。這種是假的優(yōu)化,要是要做真正的優(yōu)化需要再application的onCreate方法調用之后和第一個Activity的onCreate的方法調用之前進行優(yōu)化小腊,再application的onCreate方法執(zhí)行的最前添加Debug.startMethodTracing();方法救鲤,和在onCreate的最后面加上Debug.stopMethodTracing();方法獲取application的onCreate方法執(zhí)行時具體的耗時事件在哪邊再進行相關的優(yōu)化,可以開啟子線程優(yōu)化或者在使用到的時候在進行優(yōu)化秩冈。
長圖加載優(yōu)化:
使用BitmapRegionDecoder進行分塊加載本缠,獲取指定的Rect區(qū)域圖片
21、靜態(tài)代理和動態(tài)代理的區(qū)別:
靜態(tài)代理:一個具有抽象方法的抽象類或者接口T入问、一個實現了接口T的真實對象A丹锹,具體的操作在A重寫的接口方法中實現、一個實現了接口T的代理對象B芬失,對象B持有A對象的引用楣黍、客戶端通過代理對象B進行調用接口方法,實際上是代理對象B通過持有的A對象調用A實現得到接口方法棱烂,在調用A對象的方法前可以做一些代理對象B想要做的事情租漂。靜態(tài)代理也體現出Java的多態(tài)。一個代理類只能代理一個被代理者。
動態(tài)代理:一個具有抽象方法的抽象類或者接口T哩治、一個實現了接口T的真實對象A秃踩、一個實現了InvocationHandler接口的類,通過Proxy.NewProxyInstance(被代理對象的ClassLoader业筏,接口T憔杨,InvocationHandler)方法來創(chuàng)建代理類,中間必然后使用反射驾孔,所有的代理類都是Proxy的子類芍秆,其實動態(tài)代理和靜態(tài)代理是一樣的,只是靜態(tài)代理為我們需要自己手動創(chuàng)建代理類翠勉,而動態(tài)代理使用Proxy結合反射來進行代理類的創(chuàng)建妖啥。一個動態(tài)代理可以實現N個被代理者的代理對象構建,實現被代理者和代理者之間的解耦对碌。創(chuàng)建動態(tài)代理類時荆虱,需要實現InvocationHandler接口,實現其唯一的invoke()方法朽们。通過Proxy類來創(chuàng)建動態(tài)代理類的實例對象朦蕴,通過該實例對象調用代理方法甩恼。通過動態(tài)代理JDK自動生成的類,其父類都是Proxy類。Proxy類中有成員變量InvocationHandler h,而自動生成的類中會實現其抽象角色的接口钳垮,在實現該接口的方法中調用h.invoke()方法疫向,來實現被代理類的邏輯
22襟雷、Android APP的啟動流程
MD風格UI的使用及其原理
recyclerView和listView的區(qū)別
數據庫升級冈止,表增加刪減字段的處理、
WebView 調用 webInterface 方法的內部實現拥娄,還有什么方法可以進行通訊蚊锹,在騰訊內部有使用webView的界面都是單獨開進程進行跑的。
24稚瘾、單例設計:
餓漢式:構造方法私有化牡昆,內部提供一個靜態(tài)變量直接new出對象。
懶漢式:構造方法是優(yōu)化摊欠,內部提供一個靜態(tài)的同步方法進行對象的創(chuàng)建丢烘,第一次調用會慢一下。
雙重檢測DCL:構造方法私有化凄硼,內部提供一個靜態(tài)方法铅协,在方法內進行兩次判空,在兩次判空操作中間進行同步鎖操作摊沉。為什么需要進行兩次判空操作?Instance = new Singleton()這行代碼的執(zhí)行實際上并不是一個原子操作痒给,這句代碼最終會編譯成多條匯編指令说墨,還涉及到JVM指令重排的問題骏全,上局代碼可以分為三個原子操作,1--給Singleton的實例分配內存尼斧,2--調用Singleton的構造方法姜贡,3--將Insatnce對象指向分配的內存空間。當1和3執(zhí)行完了2并未執(zhí)行完時棺棵,切換到B線程去判斷Instance不為空楼咳,直接將Instance取走,就會出問題烛恤,這就是DCL失效問題母怜,這樣的問題可以在申明Instance變量的時候加上volatile修飾符,也可以使用靜態(tài)內部類的單例方式進行處理缚柏。
Volatile關鍵字修飾的變量表示此變量是非常容易變化的即易變的苹熏,當我們使用此關鍵字進行修飾變量時,虛擬機每次讀取都會從主內存中進行讀取币喧,當有改變時也會及時的將其存入到主內存中轨域,但volatile并不能保證是原子操作。Java線程在工作中會直接訪問工作內存杀餐,工作內存的數據從主內存讀取干发,這就是工作內存和主內存的區(qū)別。只要的使用場景是一個變量多讀一寫的情況下史翘。
靜態(tài)內部類:構造方法私有化枉长,內部提供一個靜態(tài)內部類,靜態(tài)內部類中提供一個靜態(tài)對象直接去new外部類恶座。這種方式不僅線程安全搀暑,也能保證單例對象的唯一性。(靜態(tài)內部類不持有我外部類的引用跨琳,非靜態(tài)內部類持有)
枚舉單例:
使用容器實現單例:將單例對象統(tǒng)一保存到容器中自点,要使用的時候直接在容器中拿。
25脉让、MD風格UI使用:
ConstraintLayout--約束布局桂敛,其基本屬性介紹<u>https://juejin.im/post/5bac92f2f265da0aba70c1bf</u>、CoordinatorLayout溅潜、AppBarLayout术唬、Toolbar、TableLayout滚澜、Behavior(自定義Behavior)
26粗仓、ClassLoader:JVM加載的是.class文件,Android的Dalvik加載的是.dex文件,對 dex 文件進行驗證和優(yōu)化的操作借浊,其對dex文件的優(yōu)化結果變成了odex(Optimized dex)文件塘淑,這個文件和dex文件很像,只是使用了一些優(yōu)化操作碼蚂斤。安卓在5.0以后默認增加了ART(AndroidRunTime安卓預編譯機制)存捺,在安裝時對dex文件執(zhí)行dexopt優(yōu)化之后再將odex進行AOT 提前編譯操作,編譯為OAT(實際上是ELF文件)可執(zhí)行文件(機器碼)曙蒸。(相比做過ODEX優(yōu)化捌治,未做過優(yōu)化的DEX轉換成OAT在APP安裝的時候要花費更長的時間)。
任何一個 Java 程序都是由一個或多個 class 文件組成纽窟,在程序運行時肖油,需要將 class 文件加載到 JVM 中才可以使用,負責加載這些 class 文件的就是 Java 的類加載機制师倔。ClassLoader 的作用簡單來說就是加載 class 文件构韵,提供給程序運行時使用。每個 Class 對象的內部都有一個 classLoader 字段來標識自己是由哪個 ClassLoader 加載的趋艘。
BootClassLoader: 用于加載Android Framework層class文件疲恢。
PathClassLoader: 用于Android應用程序類加載器〈呻剩可以加載指定的dex显拳,以及jar、zip搓萧、apk中的classes.dex杂数。
DexClassLoader: 用于加載指定的dex,以及jar瘸洛、zip揍移、apk中的classes.dex。
PathClassLoader和DexClassLoader的區(qū)別:兩者唯一的區(qū)別在于:創(chuàng)建DexClassLoader需要傳遞一個optimizedDirectory參數反肋,并且會將其創(chuàng)建為File對象傳給super那伐,而PathClassLoader則直接給到null。其實optimizedDirectory參數就是dexopt的產出目錄(odex)石蔗。那PathClassLoader創(chuàng)建時罕邀,這個目錄為null,就意味著不進行dexopt养距?并不是诉探,optimizedDirectory為null時的默認路徑為:/data/dalvik-cache。
雙親委托機制:某個類加載器在接到加載類的請求時棍厌,首先將加載任務委托給父類加載器肾胯,依次遞歸竖席,如果父類加載器可以完成類加載任務,就成功返回阳液,只有父類加載器無法完成此加載任務時怕敬,才自己去加載揣炕。
熱修復:因此實際上帘皿,一種熱修復實現可以將出現Bug的class單獨的制作一份fix.dex文件(補丁包),然后在程序啟動時畸陡,從服務器下載fix.dex保存到某個路徑鹰溜,再通過fix.dex的文件路徑,用其創(chuàng)建Element
對象丁恭,然后將這個Element對象插入到我們程序的類加載器PathClassLoader的pathList中的dexElements數組頭部曹动。這樣在加載出現Bug的class時會優(yōu)先加載fix.dex中的修復類,從而解決Bug牲览。
熱修復需要在application中進行修復(一般不能修復application墓陈,參照Tinker可以修復,很麻煩第献,其余的都能修復)贡必,Activity加載后的話就不會重新加載了。
在application中啟動另外一個進程庸毫,然后創(chuàng)建一個Activity在這個進程中運行仔拟,達到后臺運行的效果,進而下載補丁包飒赃,避免ANR利花。
DexClassLoader中傳入的optimizedDirectory參數,是 dex優(yōu)化為odex之后保存的目錄载佳,必須是私有目錄炒事,不能是sd卡的目錄。
兩個classLoader的區(qū)別就是存放odex目錄不同蔫慧,PathClassLoader有一個默認的路徑挠乳,而DexClassLoader 需要指定一個私有目錄下的路徑
27、Android的打包流程:
28藕漱、RecyclerView:
LayoutManager:負責item的布局方向
RecyclerView.Adapter:為RecyclerView承載數據
ItemDecoration:為RecyclerView添加分割線
ItemAnimator:控制RecyclerView中item的動畫
29欲侮、Lifecycle:
基本使用一:1-->自己新建一個類ActivityLifecycleOwner實現LifecycleObserver接口,創(chuàng)建一個方法并添加@OnLifecycleEvent注解肋联,在注解中添加你需要監(jiān)聽的生命周期參數威蕉。
2-->在Activity的onCreate方法中調用getLifecycle().addObserver(new ActivityLifecycleOwner());方法進行生命周期監(jiān)聽的注冊。
3-->當你的Activity執(zhí)行了相關的生命周期方法時橄仍,添加注解的方法將會被調用韧涨,在此方法內進行相關對象的釋放和別的操作牍戚。
4-->當你拿到Activity的實例時,可以調用getLifecycle().getCurrentState();方法來獲取當前Activity的生命周期狀態(tài)虑粥,此方法在MVP中的P層進行使用(個人理解)如孝。
5-->這樣使用的前提條件是,你的Activity繼承的父類實現了LifecycleOwner接口娩贷,對于舊版本直接繼承Activity我們需要手動實現LifecycleOwner接口第晰,便在onCreate方法中創(chuàng)建LifecycleRegister對象,并在Activity得到每個生命周期方法中進行makeState方法的調用彬祖,在getLifecycle方法中返回我們創(chuàng)建的LifecycleRegister對象茁瘦。
基本使用二:直接在BaseActivity中進行LifecycleObserver接口的實現,在進行注冊生命周期監(jiān)聽時直接傳入this參數储笑,其他的和基本使用一一至甜熔。通過Base來統(tǒng)一管理全局的操作。
原理:在AppCompatActivity中實現了LifecycleOwner接口并創(chuàng)建了LifecycleRegister對象突倍,我們在自己的Activity中實現了LifecyclerObserverOwner接口腔稀,將自身注冊到LifecycleRegister中,最終調用的地方是ClassesInfoCache中的invokeCallbacks方法羽历,在invokeCallbacks方法中進行反射調用Activity中添加了注解的方法焊虏,從而起到生面周期監(jiān)聽的效果。
30窄陡、ViewModel炕淮、LiveData、DataBind:
ViewMode:在Activity創(chuàng)建的時候進行創(chuàng)建跳夭,只有當Activity銷毀的時候才會進行清除涂圆,在Activity橫豎屏旋轉的時候,Activity會調用onSaveInstanceState方法币叹,在此方法中將ViewMode進行了保存润歉,在Activity恢復的時候調用onRestoreInstanceState();方法時進行取出,確保Viewmodel的實例還存在颈抚。ViewModel的實例創(chuàng)建時通過class.newInstance();方法來進行創(chuàng)建踩衩。
LiveData:ViewModel可以結合LiveData使用從而達到自動把數據更新的效果,LiveData其實就是一個觀察者模式的實現贩汉,去觀察這個ViewModel驱富,當ViewMode有數據變化并告訴MutableLiveData時,LiveData調用onChanged();方法來告知觀察者匹舞。
31褐鸥、容器
List,Set,Map三者的區(qū)別:
- List(對付順序的好幫手): List接口存儲一組不唯一(可以有多個元素引用相同的對象),有序的對象
- Set(注重獨一無二的性質): 不允許重復的集合赐稽。不會有多個元素引用相同的對象叫榕。
- Map(用Key來搜索的專家): 使用鍵值對存儲浑侥。Map會維護與Key有關聯的值。兩個Key可以引用相同的對象晰绎,但Key不能重復寓落,典型的Key是String類型,但也可以是任何對象荞下。
集合框架底層數據結構總結:
List:
- Arraylist: Object數組
- Vector: Object數組
- LinkedList: 雙向鏈表(JDK1.6之前為循環(huán)鏈表伶选,JDK1.7取消了循環(huán))
Set
- HashSet(無序,唯一): 基于 HashMap 實現的锄弱,底層采用 HashMap 來保存元素
- LinkedHashSet: LinkedHashSet 繼承與 HashSet考蕾,并且其內部是通過 LinkedHashMap 來實現的。
- TreeSet(有序会宪,唯一): 紅黑樹(自平衡的排序二叉樹。)
Map
- HashMap:
JDK1.8之前HashMap由數組+鏈表組成的蚯窥,數組是HashMap的主體掸鹅,鏈表則是主要為了解決哈希沖突而存在的(“拉鏈法”解決沖突)。JDK1.8以后在解決哈希沖突時有了較大的變化拦赠,當鏈表長度大于閾值(默認為8)時巍沙,將鏈表轉化為紅黑樹,以減少搜索時間 - LinkedHashMap: LinkedHashMap 繼承自 HashMap荷鼠,所以它的底層仍然是基于拉鏈式散列結構即由數組和鏈表或紅黑樹組成句携。另外,LinkedHashMap 在上面結構的基礎上允乐,增加了一條雙向鏈表矮嫉,使得上面的結構可以保持鍵值對的插入順序。同時通過對鏈表進行相應的操作牍疏,實現了訪問順序相關邏輯蠢笋。詳細可以查看:《LinkedHashMap 源碼詳細分析(JDK1.8)》
- Hashtable: 數組+鏈表組成的,數組是 HashMap 的主體鳞陨,鏈表則是主要為了解決哈希沖突而存在的
- TreeMap: 紅黑樹(自平衡的排序二叉樹)
HashMap 和 Hashtable 的區(qū)別
- 線程是否安全: HashMap 是非線程安全的昨寞,HashTable 是線程安全的;HashTable 內部的方法基本都經過synchronized 修飾(要保證線程安全的話就使用 ConcurrentHashMap)厦滤;
- 效率:因為線程安全的問題援岩,HashMap 要比 HashTable 效率高一點。另外掏导,HashTable 基本被淘汰享怀,不要在代碼中使用它;
- 對Null key 和Null value的支持:HashMap 中碘菜,null 可以作為鍵凹蜈,這樣的鍵只有一個,可以有一個或多個鍵所對應的值為 null仰坦。但是在 HashTable 中 put 進的鍵值只要有一個 null篙贸,直接拋出NullPointerException投队。
- 初始容量大小和每次擴充容量大小的不同 : ①創(chuàng)建時如果不指定容量初始值,Hashtable 默認的初始大小為11爵川,之后每次擴充敷鸦,容量變?yōu)樵瓉淼?n+1。HashMap 默認的初始化大小為16寝贡。之后每次擴充扒披,容量變?yōu)樵瓉淼?倍。②創(chuàng)建時如果給定了容量初始值兔甘,那么 Hashtable 會直接使用你給定的大小谎碍,而 HashMap 會將其擴充為2的冪次方大小(HashMap 中的tableSizeFor()方法保證)洞焙。也就是說 HashMap 總是使用2的冪作為哈希表的大小蟆淀。
- 底層數據結構: JDK1.8 以后的 HashMap 在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值(默認為8)時澡匪,將鏈表轉化為紅黑樹熔任,以減少搜索時間。Hashtable 沒有這樣的機制
四種線程安全的HashMap:
- HastTable 采用synchronized方法上加鎖唁情,使用阻塞同步疑苔,效率低。會將整個map加鎖
- collections.synchronizedMap(map) 采用synchronized方法上加鎖甸鸟,使用阻塞同步惦费,效率低兵迅。
- CopyOnWriteMap (讀寫分離思想)java本身并沒有提供CopyOnWriteMap,可以自己實現
- ConcurrentHashMap 采用鎖分段技術薪贫,減小鎖的粒度恍箭,效率高。ConcurrentHashMap默認將hash表分為16個桶瞧省。
== 與 equals
== : 它的作用是判斷兩個對象的地址是不是相等扯夭。即,判斷兩個對象是不是同一個對象(基本數據類型==比較的是值鞍匾,引用數據類型==比較的是內存地址)交洗。
equals() : 它的作用也是判斷兩個對象是否相等。但它一般有兩種使用情況:
- 情況1:類沒有覆蓋 equals() 方法橡淑。則通過 equals() 比較該類的兩個對象時构拳,等價于通過“==”比較這兩個對象。
- 情況2:類覆蓋了 equals() 方法梳码。一般隐圾,我們都覆蓋 equals() 方法來比較兩個對象的內容是否相等;若它們的內容相等掰茶,則返回 true (即,認為這兩個對象相等)蜜笤。
hashCode 與 equals
hashCode() 的作用就是獲取哈希碼濒蒋,也稱為散列碼;它實際上是返回一個int整數把兔。這個哈希碼的作用是確定該對象在哈希表中的索引位置。hashCode() 在散列表中才有用,在其它情況下沒用碳柱。在散列表中hashCode() 的作用是獲取對象的散列碼禀倔,進而確定該對象在散列表中的位置。
hashCode()與equals()的相關規(guī)定
- 如果兩個對象相等缕贡,則hashcode一定也是相同的
- 兩個對象相等,對兩個對象分別調用equals方法都返回true
- 兩個對象有相同的hashcode值翁授,它們也不一定是相等的
- 因此,equals 方法被覆蓋過晾咪,則 hashCode 方法也必須被覆蓋
- hashCode() 的默認行為是對堆上的對象產生獨特值收擦。如果沒有重寫 hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)
Java序列化中如果有些字段不想進行序列化谍倦,如何做
對于不想進行序列化的變量塞赂,使用transient關鍵字修飾。
synchronized 關鍵字和 volatile 關鍵字的區(qū)別
- volatile關鍵字是線程同步的輕量級實現昼蛀,所以volatile性能肯定比synchronized關鍵字要好宴猾。但是volatile關鍵字只能用于變量而synchronized關鍵字可以修飾方法以及代碼塊圆存。synchronized關鍵字在JavaSE1.6之后進行了主要包括為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖以及其它各種優(yōu)化之后執(zhí)行效率有了顯著提升,實際開發(fā)中使用 synchronized 關鍵字的場景還是更多一些仇哆。
- 多線程訪問volatile關鍵字不會發(fā)生阻塞沦辙,而synchronized關鍵字可能會發(fā)生阻塞
- volatile關鍵字能保證數據的可見性,但不能保證數據的原子性税产。synchronized關鍵字兩者都能保證怕轿。
- volatile關鍵字主要用于解決變量在多個線程之間的可見性,而 synchronized關鍵字解決的是多個線程之間訪問資源的同步性辟拷。
32撞羽、RecycleView緩存機制
RecycleView可能會出現復用錯亂問題,原因就是我們在請求網絡加載圖片的時候對RecycleView進行了快速滑動衫冻,導致前面復用的條目加載的圖片才從服務器返回回來诀紊,用這個進行了復用顯示在條目上。復用一般復用的都是ViewHolder隅俘,所以解決這個的方案就可以設置綁定復用的為一個唯一的標識邻奠,比如url的地址,而不是ViewHolder为居。
RecycleView四級緩存
mChangeScrap與 mAttachedScrap
mCachedViews
mViewCacheExtension
RecycledViewPool
復用機制
- 首先會從 mChangedScrap中進行查找
- 沒有找到再從mAttachedScrap中進行查找
- 再沒有的話就從緩存中查找mCachedViews
- 從開發(fā)者自定義的重用機制中查找
- 從回收池中查找碌宴。
1,2,3中如mChangedScrap都是一個ArrayList<ViewHolder>集合對象
2和3會根據不同的方法查找兩次,一個是根據positon查找(2中沒找到就找3)蒙畴,一個是根據id進行查找贰镣。
回收機制
回收的話首先會將ViewHolder放入mCachedViews中,其默認大小是2膳凝,如果滿了就會將最舊的(下標為0的位置)進行移除(先進先出)碑隆,放入回收池中。回收池默認大小是5蹬音,類似于hashmap的存儲方式(可以類比為數組存儲viewTpye,然后對應的鏈表存儲該ViewType類型的ViewHolder)上煤,會根據設置的ViewType的類型進行存儲≈回收池屬于先進后出劫狠,當放滿之后就不再進行處理了(都是同樣的ViewHolder,我的理解是把數據之類的都進行了清除牧抽,所以更新不更新就沒什么關系了)嘉熊。所以總緩存的ViewHolder的數量為5n+2,n為ViewType的數量。
ListView和RecycleView區(qū)別:
- ListView 只有兩層緩存扬舒,但是 RecyclerView有四層阐肤;
- ListView 緩存的單位是 view,而 RecyclerView 緩存的單位是 ViewHolder。
- ListView 需要自己在adapter中實現item動畫效果孕惜,而RecycleView內置許多動畫API(notifyItemChanged())
- ListView 本身無法實現局部刷新愧薛,需要在adapter中實現一個itemChanged()方法,在其中獲取到這個item的position(使用getFirstVisibilePositin())衫画,然后調用getView()方法刷新這個item毫炉。RecyclerView可以使用notifyItemChanged()實現局部刷新。
RecycleView復用錯亂解決方法
- 數據來源是同步的:
要給每個控件的狀態(tài)賦值一個新的值削罩,替換掉之前的狀態(tài)瞄勾。有if就要有else - 數據來源是異步的:
將imageView和請求的url使用setTag進行綁定。首先在沒加載圖片之前弥激,給ImageView
設置一個默認圖片进陡,然后通過setTag
方法,將 ImageView和圖片的url進行綁定微服,設置的時候再判斷一下趾疚,這個 imageview的tag和當時請求的url是不是一致的,如果是一致的以蕴,再保存糙麦。 - 多布局的,在使用的時候要用getItemViewType進行類型判斷
- 插入和刪除數據導致錯亂丛肮,在刷新完后要調用
adapter.notifyItemChanged(posiotn)
或者adapter.notifyItemRangeChanged(positionStart,itemCount)
刷新position位置赡磅。 - 當bindViewHolder沒有調用導致錯亂時,在給recycleview設置adapter之前調用adapter.setHasStableIds(true)宝与,然后在adapter里面重寫getItemId(int position)仆邓,返回position
32、開源框架
okHttp
為什么要用okHttp?
- 請求失敗自動重試主機的其它ip伴鳖,自動重定向
- 默認通過GZip壓縮數據
- 響應緩存,避免了重復請求網絡
- 支持http/2并允許對同一主機的所有請求共享一個套接字徙硅,不需要重復建立TCP連接
- 通過連接池減少請求延遲
使用方法:
// 1. 創(chuàng)建一個OkhttpClient
OkhttpClient client = new OkhttpClient ();
// 2. 創(chuàng)建一個Request
Request request = new Request.Builder()
.url(url)
.build();
// 3.創(chuàng)建一個call對象
Call call = client.newCall(request);
// 4.執(zhí)行同步請求,獲取結果
Response response = call.execute();
// 執(zhí)行異步請求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
調用流程
OkHttpClient&Builder.build() -> OkHttpClient -> Call[RealCall].execute() -> Dispatcher -> Interceptors -> Response
分發(fā)器
Dispatch類中維護了三個隊列和一個線程池,最大默認異步請求并發(fā)數是64榜聂,同一域名的最大請求數是5個
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
//異步請求使用的線程池
private @Nullable ExecutorService executorService;
Q: 如何決定將請求放入ready還是running?
A: 如果當前正在進行的異步請求數不小于64放入ready嗓蘑;如果小于64须肆,但是已經存在同一域名主機的請求5個放入ready!
Q: 從ready移動到running的條件是什么桩皿?
A: 每個請求執(zhí)行完成就會從running移除豌汇,同時進行第一步相同邏輯的判斷,決定是否移動泄隔!
Q: 分發(fā)器線程池的工作行為拒贱?
A:無等待,最大并發(fā)
線程池中核心線程池數為0,線程池不會緩存線程逻澳,如果沒有網絡請求了闸天,這個就不會進行緩存了。
使用的隊列為SynchronousQueue斜做,是容量大小為0的隊列苞氮。這個是希望獲得最大并發(fā)量,往隊列中添加元素都會是失敗的瓤逼,會創(chuàng)建線程去運行笼吟,不會進行等待。使用別的隊列可能會將后面的請求阻塞霸旗。
每執(zhí)行完一個請求后會調用分發(fā)器的finished
方法,異步請求會調用
promoteCalls
方法贷帮,重新調配請求(是否將等待隊列中的請求移動到運行隊列中交給線程池去運行)
異步請求調用newCall方法會創(chuàng)建一個RealCall對象,調用enqueue
方法會調用dispatch的enqueue方法client.dispatcher().enqueue(new AsyncCall(responseCallback));
定硝,里面會執(zhí)行調用promoteAndExecute
方法皿桑,里面會獲取到AsyncCall對象,這個是RealCall的內部類蔬啡,繼承自NamedRunnable(本身就是一個Runnable)诲侮。AsyncCall里面的executeOn
方法就會去調用線程池的executorService.execute(this);
方法,發(fā)起網絡請求箱蟆。execute中通過Response response = getResponseWithInterceptorChain();
獲得響應結果沟绪。
攔截器
攔截器的執(zhí)行流程:
五大攔截器
1、重試攔截器在交出(交給下一個攔截器)之前空猜,負責判斷用戶是否取消了請求绽慈;在獲得了結果之后,會根據響應碼判斷是否需要重定向辈毯,如果滿足條件那么就會重啟執(zhí)行所有攔截器坝疼。
2、橋接攔截器在交出之前谆沃,負責將HTTP協(xié)議必備的請求頭加入其中(如:Host)并添加一些默認的行為(如:GZIP壓縮)钝凶;在獲得了結果后,調用保存cookie接口并解析GZIP數據唁影。
3耕陷、緩存攔截器顧名思義,交出之前讀取并判斷是否使用緩存据沈;獲得結果后判斷是否緩存哟沫。
4、連接攔截器在交出之前锌介,負責找到或者新建一個連接嗜诀,并獲得對應的socket流猾警;在獲得結果后不進行額外的處理。
5裹虫、請求服務器攔截器進行真正的與服務器的通信肿嘲,向服務器發(fā)送數據,解析讀取的響應數據筑公。
重試攔截器
RetryAndFollowUpInterceptor
重試攔截器默認開啟雳窟,最大重定向次數為20。允許重試路由或者IO異常匣屡,但是如果是協(xié)議異撤饩龋或者證書異常就直接不進行重試了。
橋接攔截器
BridgeInterceptor
橋接攔截器在請求前會補全請求頭信息捣作,響應后使用GzipSource包裝便于解析
對用戶構建的Request
進行添加或者刪除相關頭部信息誉结,以轉化成能夠真正進行網絡請求的Request
緩存攔截器
CacheInterceptor
緩存攔截器會根據緩存策略CacheStrategy
去判斷是使用緩存還是去請求網絡(只緩存Get請求的響應)
詳細流程:
- 判斷緩存是否存在
- 如果是Https請求的緩存,但是緩存中沒有對應的握手信息券躁,那么緩存無效惩坑。
- 根據響應碼和響應頭判斷緩存是否可用
- 獲取用戶的請求配置,如果用戶指定了
Cache-Control:no-cache
(不適用緩存)的請求頭或者請求頭包含If-Modified-Since
或If-None-Match
(請求驗證)也拜,那么就不允許使用緩存以舒。 - 判斷資源是否不變,如果緩存的響應中包含
Cache-Control: immutable
代表響應內容將一直不會改變 - 判斷響應緩存的有效期慢哈,如果在有效期內就可以使用緩存蔓钟。(比較復雜,其中涉及按到緩存到現在存活的時間ageMillis,緩存新鮮度-有效時間freshMillis卵贱,緩存最小新鮮度minFreshMillis,緩存過期后仍有效時長:maxStaleMillis等判斷)
連接攔截器
ConnectInterceptor
滥沫,打開與目標服務器的連接,并執(zhí)行下一個攔截器键俱。
RealConnection
——封裝了Socket與一個Socket連接池
DNS兰绣、代理、SSL證書编振、服務器域名狭魂、端口完全相同則可復用連接
請求服務攔截器
CallServerInterceptor
,利用Exchange
發(fā)出請求到服務器并且解析響應生成Response
党觅。
Expect: 100-continue
請求頭(用于上傳大容量請求體或者需要驗證的情況)。代表了在發(fā)送請求體之前需要和服務器確定是否愿意接受客戶端發(fā)送的請求體斋泄。
首先調用exchange.writeRequestHeaders(request)
; 將請求頭寫入到緩存中(直到調用flushRequest()
才真正發(fā)送給服務器)杯瞻。
- 服務端允許則返回100,客戶端繼續(xù)發(fā)送請求體
- 服務端不允許炫掐,直接返回用戶
- 服務端忽略該請求頭魁莉,則會一直無法讀取應答,拋出超時異常
使用的設計模式
- 建造者模式: OkHttpClient和Request的build
- 門面模式: 由OkHttpClient統(tǒng)一暴露出來
- 責任鏈模式 :五大構造器使用責任鏈模式處理自身的邏輯
Retrofit
使用
- 定義一個Api接口
interface WanAndroidApi{
@GET("project/tree/json")
Call<ProjectBean> getProject();
}
- 使用Retorfit類生成Api接口實現
Retrofit retrofit = new Retrofit.Builder()//建造者模式
.baseUrl("https://www.wanandroid.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
WanAndroidApi wanAndroidApi = retrofit.create(WanAndroidApi.class);//代理實例
- 發(fā)送同步或者異步請求
Call<ProjectBean> call = wanAndroidApi.getProject();//獲取具體的某個業(yè)務
//同步請求
Response<ProjectBean> response = call.execute(); ProjectBean projectBean = response.body();
//異步請求
call.enqueue(new Callback<ProjectBean>() {
@Override
public void onResponse(final Call<ProjectBean> call, final Response<ProjectBean> response) {
Log.i("Steven","response: " + response.body());
}
@Override
public void onFailure(final Call<ProjectBean> call, final Throwable t) {}
});
注解分類
- 請求方法類:GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,HTTP
- 標記類:
- FromUrlEncoded: 表單請求,表示請求實體是一個Form表單旗唁,配合@Filed和@FileMap注解使用
- Multipart: 請求參數畦浓,表示請求實體是一個支持文件長傳的Form表單,需要配合@Part使用检疫,適用于文件上傳讶请。
- Streaming: 表示響應體的數據用流的方式返回,適用于返回的數據比較大的情況(下載大文件)
- 參數類:
- Headers 作用于方法屎媳,用于添加固定請求頭夺溢,可以同時添加多個。
- Header 用于添加不固定的請求頭烛谊,會更新已有請求頭
- Body 用于post請求發(fā)送非表單數據(如post方式傳遞json數據)
- Filed 用于post請求中表單字段(Filed和FiledMap需要結合FromUrlEncoded使用)
- FiledMap 接收鍵值對類型的參數
- Part Part和PartMap與Multipart注解結合使用,適合文件
上傳的情況 - PartMap 接收鍵值對類型參數
- HeaderMap 用于URL,添加請求頭
- Path 用于url中的占位符
- Query 用于Get中指定參數
- QueryMap 和Query使用類似风响,接收的是鍵值對參數
- Url 指定請求的路徑
解析
Retrofit
通過create()
方法,使用動態(tài)代理模式丹禀,在InvocationHandler
接口的實現方法invoke()
中調用loadServiceMethod()
方法状勤,通過反射獲取method的標注和參數,生成ServiceMethod
對象双泪,緩存在serviceMethodCache
中持搜。最終通過invoke()
調用動態(tài)代理類的實現方法。
Retrofit通過build()
方法攒读,將用戶設置的CallAdapter.Factory
添加到callAdapterFactories
集合中朵诫,然后調用platform.defaultCallAdapterFactories(callbackExecutor)
方法添加默認的CallAdapter
。創(chuàng)建converterFactories
集合薄扁,添加new BuiltInConverters()
轉換器剪返,再將用戶設置的Converter.Factory
添加進去,最后添加通過platform.defaultConverterFactories()
添加一個默認的轉換器邓梅。最終通過Retrofit的構造方法脱盲,創(chuàng)建Retrofit對象。
Platform
Retrofit使用靜態(tài)內部類Builder時日缨,構造方法會調用Platform.get()
方法钱反,通過findPlatform()
方法返回對應的平臺。同時在build()
方法中初始化了defaultCallAdpterFactor
工廠匣距。支持Android和Java8面哥。
ServiceMethod
通過接口映射的網絡請求對象,通過動態(tài)代理毅待,將自定義接口的標注轉換為該對象尚卫,將標注以及參數生成OkHttp所需要的Request對象。
Call
Retrofit定義網絡請求的接口尸红,包含execute吱涉,enqueue刹泄,cancel等方法。
OkHttpCall
OkHttp的Call的實現怎爵,通過createRawCall()
方法得到真正的okhttp3.Call對象(通過okhttp3.Call.Factory callFactory工廠調用newCall()
方法獲取)特石,用于進行實際的網絡請求。
CallAdapter.Factory
CallAdapter的靜態(tài)工廠鳖链,包含get的抽象方法姆蘸,用于生產CallAdapter對象
DefaultCallAdapterFactory
繼承自CallAdapter.Factory類的工廠,get方法使用匿名內部類實現CallAdapter撒轮,返回
ExecutorCallbackCall乞旦,實現了Call
ExecutorCallbackCall
采用靜態(tài)代理設計,內部變量delegate
實際為OkHttpCall题山,使用callbackExecutor實現回調在主線程中執(zhí)行
RxJava2CallAdapterFactory
Rxjava平臺的CallAdapter工廠兰粉,get方法返回RxJavaCall2Adapter對象
RxJava2CallAdapter
Rxjava平臺的設配器,返回Observable對象
Converter.Factory
數據解析器工廠顶瞳,用于生產Converter實例
GsonConverterFactory
數據解析工廠實例玖姑,返回了GsonResponseBodyConverter數據解析器
GsonResponseBodyConverter
Gson的數據解析器,將服務端返回的json對象通過輸出流轉換成對應的java模型
Response
Retrofit網絡請求響應的Response
使用的設計模式
- 建造者模式:
Retrofit
和ServiceMethod
對象的創(chuàng)建都使用Build模式慨菱,將復雜對象的創(chuàng)建和表示分離焰络。 - 外觀模式(門面模式):由
Retrofit
類進行暴露 - 動態(tài)代理模式:調用
Retrofit
的create()
方法時,進行動態(tài)代理監(jiān)聽符喝。通過反射解析method的標注和參數闪彼,生成ServiceMethod
對象 - 靜態(tài)代理模式:Android平臺默認的適配器
ExecutorCallbackCall
,具體的實現delegate
為OkHttpCall
协饲。 - 工廠模式:
Converter
和CallAdapter
的創(chuàng)建都采用了工廠模式進行創(chuàng)建畏腕。 - 適配器模式:
CallAdapter
的adapter,是的interface的返回對象可以動態(tài)擴展,增強了靈活性茉稠。
Glide
ActiveResources 活動資源描馅,記錄正在運行的資源,通過弱引用進行管理
內存緩存MemoryCache接口的實現LruResourceCache繼承自LruCache而线,使用Lru算法進行緩存铭污,會有一個有最大緩存值
LruCache或者DiskLruCache里面會存在LinkedHashMap,實際是通過該map進行管理的膀篮,內部有實現訪問排序嘹狞。
在Activity上添加一個空的Fragment來監(jiān)聽Activity的生命周期,不用和別的開源框架一樣在onDestroy方法中去移除監(jiān)聽誓竿。
調用getRequestManagerFragment()
方法通過事務提交刁绒,添加fragment,會使用Handler發(fā)送消息烤黍,目的是為了讓fragment馬上添加進去知市,不讓它處于排隊狀態(tài)。因為事務的提交也是會通過Handler發(fā)送消息的速蕊。
圖片加載流程:
- APP冷啟動首次加載嫂丙,會去判斷磁盤緩存中是否存在,存在的話會讀取磁盤緩存中的數據進行展示规哲,并且放入ActiveResources活動緩存中跟啤。不存在會進行外部加載(通過網絡請求或者加載本地圖片),加載成功后會保存到磁盤緩存中唉锌,再放入活動緩存中隅肥。
- 正常加載圖片會先從活動緩存中查找,如果沒有再從內存緩存中找袄简,再沒有會從此磁盤緩存中查找腥放,再沒有會從外部加載。內存緩存中存在绿语,從該緩存加載顯示并且移出放入活動緩存中秃症。磁盤緩存中存在,會從該緩存中加載顯示并復制到活動緩存中吕粹≈指蹋活動緩存中的圖片如果沒有使用了,就會放入內存緩存中匹耕。
使用了內存緩存為什么還要使用活動緩存聚请?
內存緩存是使用Lru算法管理的,有maxsize最大內存的限制稳其,如果超過這個限制驶赏,內部算法會進行回收。(不安全欢际,不穩(wěn)定)
活動緩存保存正在使用的圖片母市,如果不用了才會進行掃描回收。通過弱引用進行監(jiān)聽管理,沒有使用了就進行回收损趋,通過
Engine
的監(jiān)聽回調存入到內存緩沖中患久。存入 移除 非常快浑槽。
Glide的資源封裝使用到了引用計數的方式蒋失。
Java知識
抽象類和接口的區(qū)別
- 接口中的方法默認是public的,所有方法在接口中不能有實現(Java8開始接口方法可以有默認實現)桐玻,抽象類可以有非抽象的方法篙挽。
- 接口中的實例變量是final的,而抽象類中的不是镊靴。
- 一個類可以實現多個接口铣卡,但是只能繼承一個抽象類链韭。
- 一個類實現接口必須要實現其所有的方法,而抽象類只需要實現其抽象方法煮落。
注解
元注解
Java中一共有五種元注解:
-
@Retention
標識注解的存活時間- RetentionPolicy.SOURCE 只在源碼階段保留敞峭,在編譯器進行編譯時丟棄。
- RetentionPolicy.CLASS 只被保留到編譯進行的時候蝉仇,并不會加載到JVM中
- RetentionPolicy.RUNTIME 保留到程序運行的時候旋讹,會被加載進入到JVM中,程序運行時可以獲取到轿衔。
@Documented
與文檔相關 能夠將注解中的元素包含到JavaDoc中去@Target
注解可以應用的地方ElementType
枚舉中定義了很多種類绪颖,比如說可以用到類屑咳,接口莹菱,枚舉苟耻,成員變量,方法等裙秋。@Inherited
繼承(并不是注解本身可以繼承琅拌,注解本身不支持繼承) 如果一個超類被@Inherited注解過的注解進行注解的話,那么如果它的子類沒有被任何注解應用的話摘刑,那這個子類就繼承了超類的注解进宝。@Repeatable
可重復的 用于注解的值可以同時設置多個。
注解的屬性
注解的屬性也叫作成員變量枷恕,注解只有屬性党晋,沒有方法。
注解中屬性的類型只能是8種基本類型數據加枚舉徐块,注解以及它們的數組未玻,String類型。
屬性可以有默認值胡控,用default關鍵字修飾 屬性名稱后面必須加上小括號()
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.ANNOTATION_TYPE)
public @interface TestAnnotation1 {
String name() default "名稱";
int id();
TestAnnotation test_annotation();
TestAnnotation[] test_annotations();
TestEnum value();
}
注解的提取
通過反射獲取扳剿。
通過Class對象的isAnnotationPresent(注解名.class)
方法判斷是否應用了某個注解
再通過getAnnotation(注解名.class)
方法來獲取Annotation單個對象≈缂ぃ或者通過getAnnotations()獲取所有的注解庇绽。
Class<ExtendAbs> clazz = ExtendAbs.class;
boolean b = clazz.isAnnotationPresent(TestAnnotation.class);
if (b) {
TestAnnotation annotation = clazz.getAnnotation(TestAnnotation.class);
System.out.println("ExtendAbs.main:name = " +annotation.name());
System.out.println("ExtendAbs.main:id = " +annotation.id());
}
Annotation[] annotations = clazz.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
System.out.println(annotations[i]);
}