第一章:Android基石——四大組件
- 四大組件:
? Activity:負(fù)責(zé)UI元素的加載與頁(yè)面之間的跳轉(zhuǎn),相當(dāng)于一個(gè)頁(yè)面單元。
? Service:負(fù)責(zé)與UI無(wú)關(guān)的操作,如后臺(tái)耗時(shí)操作等。
? ContentProvider:負(fù)責(zé)存儲(chǔ)、共享數(shù)據(jù),使得數(shù)據(jù)在多個(gè)應(yīng)用之間共享。
? Broadcast:在各個(gè)應(yīng)用、組件之間進(jìn)行通信,簡(jiǎn)化Android開(kāi)發(fā)中的通信問(wèn)題。 - Activity構(gòu)成:Activity -> PhoneWindow -> DecorView -> title+content -> 自己的xml布局。(Decor布局:LinearLayou -> ViewStub、FrameLayout(TextView(title))、FrameLayout(content))
- Activity四種啟動(dòng)模式:
? standard:默認(rèn)模式,同一個(gè)任務(wù)棧中可以有多個(gè)實(shí)例。
? singleTop:如果已經(jīng)在棧頂,則不創(chuàng)建新的實(shí)例,回調(diào)onNewIntent()函數(shù),否則創(chuàng)建新的實(shí)例。
? singleTask:如果棧中已有該實(shí)例,則會(huì)將實(shí)例上方activity全部出棧,回調(diào)onNewIntent()函數(shù),多個(gè)棧則可以有有個(gè)實(shí)例。
? singleInstance:會(huì)啟動(dòng)一個(gè)單獨(dú)的task存儲(chǔ)該實(shí)例,并回調(diào)onNewIntent()函數(shù),多個(gè)應(yīng)用之間共享該棧保證只有一個(gè)實(shí)例。 - Service默認(rèn)在UI線程執(zhí)行,生命周期會(huì)隨著進(jìn)程而存在,進(jìn)程被殺掉則會(huì)停止(異常殺死會(huì)重建,僅重建Service),因此耗時(shí)操作需要在service里單開(kāi)線程執(zhí)行。
stopService()或stopSelf()可關(guān)閉service,startService()執(zhí)行多次只會(huì)有一個(gè)service實(shí)例。 - IntentService的
onHandleIntent()會(huì)單開(kāi)工作線程執(zhí)行操作,執(zhí)行完畢自動(dòng)進(jìn)行stopSelf()自我銷(xiāo)毀,適用于完成一些短期的耗時(shí)任務(wù)。 - 系統(tǒng)內(nèi)存不足時(shí)Service還是有可能會(huì)被回收,但前臺(tái)服務(wù)不會(huì)被回收,還可以開(kāi)啟一個(gè)通知欄展示。
startForeground(NOTIFY_ID,notification)設(shè)為前臺(tái)服務(wù)。 - AIDL概述:
? AIDL是一種接口描述語(yǔ)言,用于進(jìn)程間通信。編譯器會(huì)根據(jù)AIDL文件生成對(duì)應(yīng)的java類(lèi),通過(guò)預(yù)先定義的接口和Binder機(jī)制達(dá)到進(jìn)程間通信目的。
? 客戶端通過(guò)調(diào)用bindService()來(lái)與遠(yuǎn)程服務(wù)建立一個(gè)連接,該鏈接建立時(shí)會(huì)返回一個(gè)IBinder對(duì)象,這是遠(yuǎn)程服務(wù)端Binder的BinderProxy,客戶端通過(guò)Stub.asInterface()將該BinderProxy包裝成本地的Proxy,遠(yuǎn)程BinderProxy作為本地Proxy的mRemote字段,通過(guò)mRemote執(zhí)行遠(yuǎn)程函數(shù)的調(diào)用(transact),遠(yuǎn)程的Stub的onTransact()方法會(huì)被回調(diào)來(lái)執(zhí)行邏輯(遠(yuǎn)程調(diào)客戶端也是一樣,雙向的)。
? 遠(yuǎn)程service需設(shè)置export="true"和process=":remote"。(類(lèi)似socket通信的過(guò)程) - 四種廣播類(lèi)型:
? 普通廣播:消息傳遞效率高,但所有receivers接收的順序不穩(wěn)定,不可中斷廣播,直到?jīng)]有接收器接受為止。(全局類(lèi)型,只要注冊(cè)了且過(guò)濾條件相同,都能收到)
? 有序廣播:通過(guò)sendOrderdBroadcast()來(lái)發(fā)送,接收器可設(shè)置android:priority優(yōu)先級(jí),通過(guò)setResult()和getResult()實(shí)現(xiàn)往下傳遞廣播,通過(guò)abortBroadcast()可中斷廣播。
? 本地廣播:使用LocalBroadcastManager.getInstance().sendBroadcast()發(fā)送,只有本進(jìn)程可以收到廣播,為了程序的安全性,在不需要其他進(jìn)程接收廣播時(shí)建議使用本地廣播。
? Sticky廣播:使用sendStickBroadcast()實(shí)現(xiàn),需要BROADCAST_STICKY權(quán)限,廣播會(huì)一直滯留,只要有新的接收器被注冊(cè)就會(huì)被接收(重復(fù)只會(huì)保留最后一條廣播),可以通過(guò)removeStickyBroadcast()移除廣播。 - ContentProvider統(tǒng)一了數(shù)據(jù)訪問(wèn)方式,是對(duì)SQliteOpenHelper的進(jìn)一步封裝,通過(guò)Uri來(lái)映射要操作的表,提供統(tǒng)一API接口,實(shí)現(xiàn)應(yīng)用間數(shù)據(jù)共享。Manifest里要設(shè)置
authoritys="com.xxx.xxx"唯一標(biāo)識(shí)。 - Uri表示絕對(duì)路徑,格式為:
schema + authority + path + id。 - *表示匹配任意長(zhǎng)度的任意字符,#表示匹配任意長(zhǎng)度的數(shù)字。
第二章:UI——View與動(dòng)畫(huà)
- ListView通過(guò)Adapter模式、觀察者模式、Item復(fù)用機(jī)制實(shí)現(xiàn)了高效的列表顯示。其內(nèi)部有個(gè)
AdapterDataSetObserver,setAdapter()的時(shí)候?qū)⑵鋵?shí)例化并設(shè)置到adapter中,當(dāng)adapter.notrifyDataSetChanged()時(shí)會(huì)通知觀察者,listView內(nèi)部的observer便接收變更通知并控制listView來(lái)requestLayout()重新布局、繪制等。 - RecyclerView對(duì)ListView的Adapter再次封裝,將判斷是否有緩存的邏輯封裝到了RecyclerView內(nèi)部;另外通過(guò)橋接的方式將布局職責(zé)抽離出去由LayoutManager來(lái)控制,使得更加靈活;還可以用ItemDecotation為Item添加裝飾等。這些都是將各種職責(zé)抽象為接口,并提供一些默認(rèn)實(shí)現(xiàn),相互注入,由接口實(shí)現(xiàn)來(lái)完成各項(xiàng)職責(zé),因此用戶也能夠自己實(shí)現(xiàn)接口完成一些自定義的需求。
- 三種自定義控件類(lèi)型:繼承自View完全自定義、繼承自現(xiàn)有控件實(shí)現(xiàn)特殊效果、繼承自ViewGroup實(shí)現(xiàn)布局組合。
- 自定義控件核心三步:
測(cè)量(onMeasure) -> 布局(onLayout) -> 繪制(onDraw)。 - MeasureSpec是一個(gè)32位的值,有specSize和specMode共同組成,高2位表示Mode,低30位表示Size。
- 三種SpecMode類(lèi)型:EXACTLY(固定高度和match_parent)、AT_MOST(小于最大高度wrap_content)、UNSPECIFIED(無(wú)限制任意大小)。
- MeasureSpec的測(cè)量:
? 父View的MeasureSpec由父View的LayoutParams和窗口的長(zhǎng)寬size決定。
? 子View的MeasureSpec是由父View的Spec和子View的自己的LayoutParams共同決定的(主要還是看子View的params,如果是固定值dp,則一定是childSize+EXACTLY;如果是wrap,則一定是parentSize+AT_MOST;如果是match就再結(jié)合父Spec來(lái)決定)。 - Canvas的
save()和restore()要配對(duì)使用,restore()可以比save()少,不能多,否則會(huì)報(bào)錯(cuò),想想也是這樣。 - Scroller介紹:
? Scoller本身是計(jì)算輔助類(lèi),只計(jì)算滾動(dòng)位置,不會(huì)改變布局參數(shù)。它封裝了滾動(dòng)時(shí)間、要滾動(dòng)的目標(biāo)x和y軸以及每個(gè)時(shí)間點(diǎn)需要滾動(dòng)到的x和y位置等信息,用戶可以通過(guò)getCurX()和getCurY()來(lái)獲取當(dāng)前時(shí)刻View應(yīng)該滾動(dòng)到的位置,然后使用View自身的scrollTo()或scrollBy()方法來(lái)真正實(shí)現(xiàn)滾動(dòng)。
? 執(zhí)行scroll.startScroll()來(lái)開(kāi)啟滑動(dòng),然后復(fù)寫(xiě)View的computeScroll()方法,該方法會(huì)在View被繪制時(shí)回調(diào),判斷scroll.computeScrollOffset(),返回true表示滾動(dòng)未完成,就繼續(xù)重繪postInvalidate()即可。 - Android四種動(dòng)畫(huà):
? 幀動(dòng)畫(huà):在anim目錄下創(chuàng)建,animation-list節(jié)點(diǎn),設(shè)置duration和drawable即可;直接設(shè)置為view的background,然后getBackground()轉(zhuǎn)為AnimationDrawable,通過(guò)animation.start()開(kāi)啟動(dòng)畫(huà)。注意圖片不能太大避免內(nèi)存溢出。
? 補(bǔ)間動(dòng)畫(huà):在anim目錄下創(chuàng)建,alpha/scale/translate/rotate/set五種類(lèi)型,浮點(diǎn)數(shù)/num%/num%p分別表示相對(duì)原坐標(biāo)/相對(duì)自身/相對(duì)父布局移動(dòng)距離;通過(guò)view.startAnimation(anim)來(lái)開(kāi)啟動(dòng)畫(huà)。
? 屬性動(dòng)畫(huà):在animator目錄下創(chuàng)建,在一段時(shí)間內(nèi)不斷修改某個(gè)屬性值,ValueAnimator封裝了時(shí)間和節(jié)點(diǎn)值(只負(fù)責(zé)計(jì)算和監(jiān)聽(tīng),可用于計(jì)時(shí)器實(shí)現(xiàn));ObjectAnimation封裝了作用的目標(biāo)View、作用的屬性等(有默認(rèn)屬性,也可以包裝自己的屬性和setter效果,自己實(shí)現(xiàn)屬性設(shè)置)。
? VectorDrawabl矢量動(dòng)畫(huà):5.0以上,實(shí)現(xiàn)略復(fù)雜,用法詳見(jiàn)博客。 - 屬性動(dòng)畫(huà)添加變化值監(jiān)聽(tīng)addUpdateListener、添加動(dòng)畫(huà)節(jié)點(diǎn)監(jiān)聽(tīng)setListener。
- ObjectAnimator初始時(shí)設(shè)置目標(biāo)對(duì)象、目標(biāo)屬性、時(shí)長(zhǎng)等值,內(nèi)部會(huì)通過(guò)計(jì)算出各個(gè)時(shí)間屬性應(yīng)該得到的值,并通過(guò)目標(biāo)屬性的
setter方法進(jìn)行更新,如果該屬性沒(méi)有setter方法則通過(guò)反射形式更新目標(biāo)值(它只負(fù)責(zé)更新屬性域的值,自己要實(shí)現(xiàn)的就是更新后的變化效果)。 - TypeEvaluator和TimeInterpolator為類(lèi)型估值器和差值器,前者接口只有一個(gè)方法,就是一個(gè)計(jì)算接口,可以自己實(shí)現(xiàn)計(jì)算方法,返回每個(gè)節(jié)點(diǎn)的值(值的類(lèi)型也是泛型,可以自己定義);后者可以對(duì)前者計(jì)算的值再進(jìn)行一次轉(zhuǎn)化,實(shí)現(xiàn)非線性變化。都是接口,只定義計(jì)算規(guī)則,真正的計(jì)算由子類(lèi)或者實(shí)現(xiàn)類(lèi)實(shí)現(xiàn),可以自定義完了設(shè)置給ObjectAnimator,會(huì)根據(jù)自定義的規(guī)則得出計(jì)算值并設(shè)置給目標(biāo)對(duì)象。
- VelocityTracker、Scoller、ValueAnimator等實(shí)際上都是對(duì)總距離、時(shí)間、節(jié)點(diǎn)值等一系列屬性的計(jì)算類(lèi)封裝,屬于輔助類(lèi),可以幫助我們算出每個(gè)節(jié)點(diǎn)的值,至于如何使用這些值來(lái)進(jìn)行變換,自己實(shí)現(xiàn)即可。
第三章:App流暢度——多線程
- 每個(gè)Android應(yīng)用在啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè)線程,稱為主線程或者UI線程,Android所有默認(rèn)操作都在這個(gè)線程里執(zhí)行。主線程會(huì)關(guān)聯(lián)一個(gè)消息隊(duì)列,因此可以在主線程直接創(chuàng)建Handler,就是在主線程執(zhí)行的,循環(huán)消息是一個(gè)死循環(huán)保證主線程不會(huì)退出。
- Looper、MessageQueue和Handler的關(guān)系:
? 一個(gè)線程最多創(chuàng)建一個(gè)Looper和多個(gè)Handler,默認(rèn)情況下,消息隊(duì)列只有一個(gè),就是主線程消息隊(duì)列,因此Handler可以在主線程中直接創(chuàng)建,發(fā)的消息就在主線程MessageQueue獲取并調(diào)用Handler處理。
? Looper在存儲(chǔ)于ThreadLocal中,依附于一個(gè)線程,Looper創(chuàng)建并管理一個(gè)MessageQueue,Handler創(chuàng)建時(shí)會(huì)先判斷Looper是否存在,存在則將Looper的MessageQueue與自己關(guān)聯(lián),因此Handler創(chuàng)建依賴于先有Looper。
? 因此,Handler發(fā)送消息時(shí)是將自己作為Message的target發(fā)給MessageQueue,Looper循環(huán)消息隊(duì)列時(shí),拿到消息,調(diào)用Message的target(實(shí)際上就是handler)來(lái)dispatch分發(fā)Message;因此,可以創(chuàng)建多個(gè)Handler向同一個(gè)Looper里發(fā)消息。
? 由此可見(jiàn),如果在子線程直接創(chuàng)建Handler會(huì)報(bào)錯(cuò),要先創(chuàng)建Looper,再創(chuàng)建Handler,再Looper.loop()循環(huán)消息,才算一個(gè)完整的消息機(jī)制創(chuàng)建。且Handler不能訪問(wèn)其他線程的MessageQueue,也保證線程安全性。
? 其實(shí)也可以在Handler的構(gòu)造方法里直接傳入looper.messageQueue,它就只往該Looper的MessageQueue里發(fā)消息,這樣Handler構(gòu)造時(shí)就不會(huì)報(bào)錯(cuò)了,消息也是發(fā)給該線程執(zhí)行的。
? Handler構(gòu)造時(shí)也可以直接傳入一個(gè)Callback,它會(huì)作為mCallback,處理Message時(shí)使用callback.handleMessage(),不用重寫(xiě)Handler的handleMessage()方法了。
? Handler使用時(shí)可以sendMessage()然后處理handleMessage(),也可以直接post(runnable),其實(shí)最終都是創(chuàng)建了一個(gè)Message對(duì)象,在Handler處理時(shí)會(huì)判斷如果Message的Runnable不為空就直接調(diào)它的run()方法,否則就調(diào)到handleMessage()去了。
? 子線程要先創(chuàng)建Looper.prepare(),然后創(chuàng)建Handler,最后再Looper.loop()循環(huán)起來(lái),注意子線程不用時(shí)要退出Looper,否則會(huì)一直在阻塞中。 - 線程的wait、sleep、join和yield:
? wait():Object的方法,會(huì)使線程進(jìn)入該對(duì)象的等待池中,釋放該對(duì)象鎖,只能等待其他線程來(lái)調(diào)用Object的notify()和notifyAll()才能被喚醒,這幾個(gè)方法必須在synchronized方法塊中執(zhí)行(要先獲取鎖才能wait釋放鎖)。
? sleep():Thread的靜態(tài)方法,不會(huì)改變鎖狀態(tài),時(shí)間結(jié)束會(huì)自己?jiǎn)拘炎约?。因此如果直?code>sleep()就sleep了,如果在synchronized代碼塊中調(diào)用sleep(),會(huì)持有鎖sleep。
? join():Thread的實(shí)例方法,其他thread調(diào)用,插入當(dāng)前線程,阻塞當(dāng)前線程先執(zhí)行自己。
? yield():Thread的靜態(tài)方法,線程禮讓,當(dāng)前線程調(diào)用,阻塞自己先讓出時(shí)間片,至于誰(shuí)執(zhí)行不管,完了自己繼續(xù)執(zhí)行。 - Runnable、Callable、Future和FutureTask:
? Runnable:異步任務(wù)封裝類(lèi),無(wú)返回值,可以在Thread和線程池中使用,被用來(lái)提交異步任務(wù)到線程池threadPool.submit(runnable)。
? Callable:異步任務(wù)封裝類(lèi),有返回值,只能在線程池中使用,被用來(lái)提交異步任務(wù)到線程池threadPool.submit(callable)。
? Future:線程池的執(zhí)行返回結(jié)果管理類(lèi),線程一旦被啟動(dòng)就不好控制了,threadPool.submit()的返回Future,可以控制線程池的執(zhí)行,獲取執(zhí)行結(jié)果等。
? FutureTask:實(shí)現(xiàn)了Runnalbe和Future的接口,自己可封裝業(yè)務(wù)處理邏輯,然后threadPool.submit(futureTask)自己,不用依賴于submit的返回值,自己直接獲取并處理管理結(jié)果。 - 線程池的作用:重用存在的線程、有效控制最大并發(fā)數(shù)、提供定時(shí)、定期、單線程、并發(fā)數(shù)控制等。三種狀態(tài):運(yùn)行(創(chuàng)建后)、關(guān)閉(shutdown,不接受新任務(wù)但未執(zhí)行完的繼續(xù)執(zhí)行)、終止(執(zhí)行完所有任務(wù))。
- 線程池的幾個(gè)重要參數(shù):
? corePollSize、maximumPoolSize:核心線程數(shù)和最大線程數(shù),當(dāng)收到新任務(wù)時(shí),如果當(dāng)前線程數(shù)小于核線程數(shù)則直接創(chuàng)建新線程執(zhí)行;如果核心等于最大數(shù),則不會(huì)創(chuàng)建新線程了;如果大于核心線程數(shù)小于最大線程數(shù),則將任務(wù)加入阻塞隊(duì)列,如果阻塞隊(duì)列已滿,創(chuàng)建新線程執(zhí)行,如果最大線程和阻塞隊(duì)列都滿,調(diào)用拒絕策略。
? keepAliveTime:當(dāng)前線程數(shù)大于核心線程數(shù)小于最大線程數(shù)時(shí),空閑線程的存活時(shí)間。
? workQueue:阻塞任務(wù)隊(duì)列,線程大于核心線程時(shí)先加入任務(wù)隊(duì)列滿了再開(kāi)啟新線程,如果是無(wú)界隊(duì)列,則最大線程數(shù)和拒絕策略兩個(gè)參數(shù)無(wú)用。
? Handler:拒絕策略。
①AbortPolicy:拒絕新任務(wù)直接拋異常(默認(rèn)策略) 。
②CallerRunsPolicy:拒絕新任務(wù)加入,如果線程池未關(guān)閉,則使之直接在調(diào)用線程執(zhí)行。
③DiscardOldestPolicy刪除隊(duì)列最早任務(wù),不會(huì)拋異常。
④DiscardPolicy:直接拒絕加入新任務(wù),不會(huì)拋異常。 - 四種常用線程池:CachedThreadPool、FixedThreadPool、SingleThreadPool和ScheduleThreadPool。
- 幾種優(yōu)化策略:
? CopyOnWriteArrayList(并發(fā)List,讀的時(shí)候不同步,寫(xiě)的時(shí)候先復(fù)制一份寫(xiě)完再合并,占用雙倍內(nèi)存)。
? ConcurrentHashMap(并發(fā)Map,分段鎖HashMap,提高方法調(diào)用效率)。
? BlockingQueue(阻塞隊(duì)列,只是一種數(shù)據(jù)結(jié)構(gòu),內(nèi)部實(shí)現(xiàn)了鎖控制,使用put/take方法,會(huì)阻塞)。 - 幾種常用阻塞隊(duì)列:
add(e)、element()方法會(huì)拋異常;
offer(e)/offer(e,time,unit)、peek()/poll(time,unit)方法返回false或null;
put(e)、take()不會(huì)拋異常,是阻塞方法。
? ArrayBlockingQueue:數(shù)組實(shí)現(xiàn)、線程安全、有界的隊(duì)列,內(nèi)部通過(guò)互斥鎖保護(hù)競(jìng)爭(zhēng)資源,F(xiàn)IFO原則。
? LinkedBlockingQueue:?jiǎn)蜗蜴湵韺?shí)現(xiàn)的阻塞隊(duì)列,F(xiàn)IFO原則,吞吐量一般高于數(shù)組隊(duì)列。
? LinkedBlockingDeque:雙向鏈表實(shí)現(xiàn)的雙向并發(fā)阻塞隊(duì)列,支持FIFO和FILO,容量可選,默認(rèn)容量為MAX。
? ConcurrentLinkedQueue:基于鏈接節(jié)點(diǎn)的無(wú)界線程安全隊(duì)列,采用FIFO原則排序。 - 同步鎖機(jī)制synchronized:是一種粗略鎖,可作用于函數(shù)、對(duì)象、靜態(tài)函數(shù)和Class,作用于函數(shù)和this都是只鎖該對(duì)象(利用對(duì)象內(nèi)部lock),作用于靜態(tài)函數(shù)和Class鎖的是該類(lèi)下所有同步靜態(tài)方法。獲取鎖和釋放鎖都在同一代碼塊中,使用簡(jiǎn)單。只有在synchronized代碼塊中才能
wait()、notifyAll()等,因?yàn)橐全@取鎖,類(lèi)似只有lock.lock()里才能condation.await()、condation.singalAll()等。 - 顯示鎖ReentrantLock和Condition:更靈活和高效的鎖,可以實(shí)現(xiàn)條件阻塞,阻塞隊(duì)列就是使用該實(shí)現(xiàn)。但是使用不當(dāng)可能會(huì)死鎖,因?yàn)楂@取鎖和釋放鎖可以分開(kāi)寫(xiě),注意必須在
finally代碼塊中手動(dòng)釋放鎖,否則異常后就會(huì)死鎖。await()等方法必須在lock()以后執(zhí)行(先獲取鎖才能再釋放)。newCondition()函數(shù)可以獲取Lock上的一個(gè)條件,用于實(shí)現(xiàn)不同線程間的通信,解決Object對(duì)象鎖線程無(wú)法通信的問(wèn)題,每個(gè)條件await()之后,由其他條件來(lái)singalAll(),實(shí)現(xiàn)相互喚醒。 - 幾種同步概念:
? 信號(hào)量Semaphore:一個(gè)共享鎖,維護(hù)一個(gè)信號(hào)量許可集,線程可以向它acquire()許可,也可release()許可,可以控制線程許可集大小來(lái)控制同一時(shí)刻同步線程數(shù)的大小。
? 循環(huán)柵欄CyclicBarrier:允許一組線程相互等待,直到達(dá)到某一條件后可執(zhí)行一個(gè)特殊Runnable邏輯,完了其余線程再繼續(xù)執(zhí)行。
? 閉鎖CountDownLatch:await()阻塞當(dāng)前線程,先執(zhí)行其他線程然后countdown()置位-1,當(dāng)計(jì)數(shù)器為0繼續(xù)執(zhí)行當(dāng)前線程,只能用一次。 - 每個(gè)對(duì)象內(nèi)都有一個(gè)Lock鎖,每個(gè)對(duì)象是創(chuàng)建在當(dāng)前調(diào)用線程中,因此在對(duì)象里調(diào)用
lock.lock()或者在線程中調(diào)用object.wait()都能將當(dāng)前調(diào)用線程給阻塞掉。 - AsyncTask注意事項(xiàng):
? 異步任務(wù)實(shí)例必須在UI線程中創(chuàng)建(要利用主線程handler);
?excute()方法必須放在UI線程中調(diào)用(內(nèi)部會(huì)判斷mState狀態(tài));
? 不能在doInBackground()中更改UI組件信息(線程池中調(diào)用的);
? 一個(gè)實(shí)例只能執(zhí)行一次,第二次會(huì)拋異常(狀態(tài)標(biāo)志位已改變)。 - AsyncTask內(nèi)部有一個(gè)核心線程數(shù)為5,最大線程數(shù)128,阻塞隊(duì)列為10的線程池。3.0以下并發(fā)138個(gè)以上個(gè)任務(wù)會(huì)拋異常(線程池拒絕策略是默認(rèn)的AbortPolicy),3.0以上雖然還是線程池執(zhí)行,但是會(huì)用一個(gè)ArrayQueue阻塞隊(duì)列來(lái)提交任務(wù)到線程池執(zhí)行??刂迫蝿?wù)阻塞且一個(gè)一個(gè)提交(單線程執(zhí)行)。
- 通過(guò)兩個(gè)運(yùn)行在不同線程的Handler可實(shí)現(xiàn)簡(jiǎn)易的AsyncTask(一個(gè)UI線程的handler和一個(gè)子線程的handler)。
第四章:HTTP網(wǎng)絡(luò)請(qǐng)求
- Http是一種應(yīng)用層協(xié)議,下層通過(guò)TCP進(jìn)行可靠的數(shù)據(jù)傳輸,能夠保證數(shù)據(jù)的完整性、正確性,而TCP對(duì)數(shù)據(jù)傳輸控制的優(yōu)點(diǎn)也體現(xiàn)在Http上,保證數(shù)據(jù)傳輸?shù)耐掏铝亢托省?/li>
- 7種常用Http請(qǐng)求類(lèi)型:
? PUT:增。向服務(wù)器寫(xiě)入資源,使服務(wù)器用請(qǐng)求主體來(lái)創(chuàng)建一個(gè)以請(qǐng)求Url命名的新文檔,若已存在則替換掉。
? DELETE:刪。刪除服務(wù)器請(qǐng)求Url指定的資源,參數(shù)與GET一樣放在Url中。
? POST:改。向服務(wù)器傳輸數(shù)據(jù),通常用于提交Html表單數(shù)據(jù)。
? GET:查。獲取服務(wù)器中的某個(gè)資源,參數(shù)放在Url中。Url長(zhǎng)度不同瀏覽器限制不同,HTTP1.1之后長(zhǎng)度無(wú)限制。
? HEAD:與GET類(lèi)似,只返回響應(yīng)首部,用于判斷是請(qǐng)求否連通、資源是否存在等,HTTP/1.1規(guī)范必須實(shí)現(xiàn)HEAD方法。
? TRACE:HTTP會(huì)穿過(guò)一些防火墻、代理和網(wǎng)關(guān)等,會(huì)在行程的最后一站返回TRANCE響應(yīng),包括請(qǐng)求原始報(bào)文和最終響應(yīng)報(bào)文,用于查看請(qǐng)求最終被修改成了什么樣子。
? OPTIONS:請(qǐng)求Web服務(wù)器告知支持哪些功能。 - HTTP請(qǐng)求報(bào)文格式:
? 請(qǐng)求行:請(qǐng)求方法 (空格) URL (空格) HTTP版本 回車(chē)換行
? 請(qǐng)求頭:key:value 回車(chē)換行
? 空行: 回車(chē)換行
? 請(qǐng)求體:文本或二進(jìn)制數(shù)據(jù)。
-開(kāi)始標(biāo)志:--boundary
-內(nèi)容頭部:key:value
-空行:回車(chē)換行
-內(nèi)容主體:字符或二進(jìn)制
-結(jié)束標(biāo)志:--boundary-- - HTTP響應(yīng)報(bào)文格式:
? 狀態(tài)行:HTTP版本 (空格) 狀態(tài)碼 (空格) 結(jié)果短語(yǔ) 回車(chē)換行
? 響應(yīng)頭:key:value 回車(chē)換行
? 空行: 回車(chē)換行
? 響應(yīng)體:json字符串或html文檔 - 狀態(tài)碼:
? 100~199:請(qǐng)求已接受繼續(xù)處理。
? 200~299:請(qǐng)求成功。
? 300~399:重定向。
? 400~499:客戶端錯(cuò)誤。
? 500~599:服務(wù)器錯(cuò)誤。 - Http是基于TCP的應(yīng)用層協(xié)議,封裝了TCP的使用細(xì)節(jié),TCP連接是基于流的可靠連接(傳輸層),它為Http提供了一條可靠的傳輸通道。TCP的數(shù)據(jù)是通過(guò)IP分組的小數(shù)據(jù)塊來(lái)發(fā)送的,Http要發(fā)送過(guò)一條報(bào)文時(shí),會(huì)以流的形式將報(bào)文數(shù)據(jù)通過(guò)一條打開(kāi)的TCP連接按順序傳輸;TCP收到流數(shù)據(jù)之后,將數(shù)據(jù)分割成被稱作段的小數(shù)據(jù)塊,將段封裝在IP分組中進(jìn)行傳輸。
- Http協(xié)議棧(從低到高):物理層、數(shù)據(jù)鏈路層(網(wǎng)絡(luò)接口)、網(wǎng)絡(luò)層(IP)、傳輸層(TCP)、安全層(TSL或SSL)、應(yīng)用層(HTTP)。
- 對(duì)于文件上傳,我們需要做的時(shí)將文件數(shù)據(jù)按照Http報(bào)文格式輸出到Socket的輸出流中,這樣文件數(shù)據(jù)就作為Http報(bào)文中的一個(gè)參數(shù),通常我們?nèi)绫韱紊蟼鲿r(shí)Content-Type為
application/x-www-form-urlencoded,上傳文件時(shí)的報(bào)文Content-Type都設(shè)置為multipart/form-data格式,文件最好Base64編碼一下。 - 不管是不同文本還是文件參數(shù),都是構(gòu)造固定的Http報(bào)文格式,變化的只是Content-Type、Content-Transfer-Encoding以及參數(shù)格式,最終將這些參數(shù)輸入到Http請(qǐng)求的Socket輸出流中,通過(guò)底層的網(wǎng)絡(luò)協(xié)議將數(shù)據(jù)傳輸?shù)侥繕?biāo)主機(jī)上。
第五章:SQLite數(shù)據(jù)庫(kù)
- SQLite概述:
? SQLite是一個(gè)遵守ACID(原子性、一致性、隔離性和持久性)的關(guān)系型數(shù)據(jù)庫(kù),包含在一個(gè)很小的C程序中,被集成在用戶程序里,使用動(dòng)態(tài)的、弱類(lèi)型的SQL語(yǔ)法。
? SQLite引擎不是一個(gè)應(yīng)用程序與之通信的獨(dú)立進(jìn)程,其庫(kù)連接到程序中,成為應(yīng)用的一部分,這個(gè)庫(kù)可以被動(dòng)態(tài)鏈接,包括定義、表、索引以及數(shù)據(jù)本身,作為一個(gè)單獨(dú)的、可跨平臺(tái)使用的文件存儲(chǔ)在主機(jī)上。
? 它采用寫(xiě)數(shù)據(jù)時(shí)將整個(gè)數(shù)據(jù)文件加鎖的設(shè)計(jì),保證寫(xiě)操作串行進(jìn)行,讀操作可以多任務(wù)同時(shí)進(jìn)行。
? Android在Framework層封裝了一層Java接口供我們方便使用SQLite的功能。 - SQLite的局限:
①僅支持部分觸發(fā)器;
②alert table時(shí)只添加和重命名列,不能修改或刪除列(一般只能刪除+重新創(chuàng)建表實(shí)現(xiàn)升級(jí));
③弱類(lèi)型,不進(jìn)行類(lèi)型檢查,可以把字符串插入整數(shù)列中。 - SQLite模塊分為前端解析系統(tǒng)(詞法分析器、語(yǔ)法分析器和代碼生成器)和后端引擎(虛擬機(jī)VM、B/B++樹(shù)和頁(yè)面調(diào)度程序)。
- SQLite數(shù)據(jù)類(lèi)型只有5種:INTEGER(整型)、REAL(浮點(diǎn)型)、TEXT(字符型UTF-8/UTF-16等)、BLOB(完全按輸入存放的數(shù)據(jù)塊)、NULL(數(shù)值為空)。除了整型主鍵,任何列可存儲(chǔ)任何類(lèi)型,注意沒(méi)有Boolean值,可用Integer代替。
- SQLite在創(chuàng)建列和內(nèi)部存儲(chǔ)時(shí),會(huì)進(jìn)行近似轉(zhuǎn)換,最終轉(zhuǎn)化為那5中內(nèi)置類(lèi)型。一個(gè)text存入integer中,如果前15位可以順利轉(zhuǎn)換,則認(rèn)為是無(wú)損轉(zhuǎn)換,可以存入,否則就存入text中。
- 如果在一個(gè)表中需要將多個(gè)字段組合起來(lái)成為唯一,可以在SQL語(yǔ)句最后使用
unique(colum1,colum2)聲明,多主鍵也是一樣。 - 在SQLite中,一個(gè)表必須有一個(gè)主鍵。默認(rèn)情況下SQLite表中都含有一個(gè)內(nèi)置主鍵,它是一個(gè)64位的整型字段,稱為
rowid,自增。也可以定義自定義主鍵,使用autoincrement自增。 - SQLite從Android2.2版本以后支持外鍵,為了兼容以前程序,默認(rèn)沒(méi)有開(kāi)啟,需要自己在SQLOpenHelper中重寫(xiě)
onOpen()方法,db.execSQL("PRAGMA foreign_keys=ON;")開(kāi)啟。 - insert支持批量插入:
?insert into stu (name,age) select name,age from students;(先創(chuàng)建表在查詢插入,升級(jí)表時(shí)可以用)
?create table stu as select * from students;(只能導(dǎo)入基本數(shù)據(jù),字段約束不能導(dǎo)入,不常用) - 多表連接查詢就是內(nèi)連接(inner join),取交集;左外連接(left outer jion)、右外鏈接(right outer join)分別以左、右表全表數(shù)據(jù)為準(zhǔn),無(wú)字段值賦空值;全連接取兩張表全集,無(wú)值賦空值。但是SQLite只支持內(nèi)連接和左外連接。
eg:select * from table1 inner join table2 on table1.id = table2.id; - SQLite的alert表功能支持支修改表名和添加表字段兩個(gè)功能,不能刪除字段。表升級(jí)時(shí)可以先創(chuàng)建一個(gè)臨時(shí)新表,將舊表數(shù)據(jù)移植、刪除舊表,再將新表改名即可。
- 索引:
? 索引是用于加速查詢的結(jié)構(gòu)。默認(rèn)情況下查詢條件會(huì)掃描所有的行來(lái)找匹配的數(shù)據(jù),可為查詢頻率較高的列建立索引,索引能夠?yàn)橹付ǖ牧袆?chuàng)建一張索引表,每個(gè)索引指向特定的記錄,可以加快查詢速度。
? 索引可以加快查詢速度,卻會(huì)增加數(shù)據(jù)庫(kù)體積,且減慢insert、update和delete操作,因此需要權(quán)衡優(yōu)缺點(diǎn)。不能在較小的表、頻繁插入更新的表、含有大量NULL值的表上建立索引。
? 當(dāng)查詢條件是與索引字段判等時(shí),數(shù)據(jù)庫(kù)使用索引加速,但是如果有多個(gè)索引字段,那么出現(xiàn)第一個(gè)不是判等的邏輯之后,后續(xù)字段就不會(huì)使用索引了(僅判等邏輯會(huì)使用索引加速)。 - 視圖是動(dòng)態(tài)生成的虛擬表,不會(huì)被存儲(chǔ)到數(shù)據(jù)庫(kù)文件中,常用來(lái)將復(fù)雜的查詢語(yǔ)句的結(jié)果簡(jiǎn)化為一個(gè)視圖結(jié)果,下次直接執(zhí)行
select * from view即可。(存儲(chǔ)一個(gè)結(jié)果集,只能查詢不能修改刪除,但可以增加觸發(fā)器) - 觸發(fā)器語(yǔ)法:
create trigger name after insert of colums on table begin action.. end;。觸發(fā)器插入記錄時(shí)使用new,而刪除記錄時(shí)使用old。 - SQLite查詢語(yǔ)句時(shí),返回的數(shù)據(jù)類(lèi)型是Cursor,它提供了一個(gè)隨機(jī)讀寫(xiě)訪問(wèn)數(shù)據(jù)庫(kù)查詢結(jié)果集的接口,并不是線程安全的,因此多線程訪問(wèn)Cursor的時(shí)候要手動(dòng)進(jìn)行同步操作。Cursor中定義了一個(gè)光標(biāo),默認(rèn)指向第一個(gè)數(shù)據(jù)。
- SQLite默認(rèn)會(huì)為每個(gè)插入、更新操作創(chuàng)建一個(gè)事務(wù),每次插入、更新結(jié)束后立即提交。因此如果批量插入或更新,最好統(tǒng)一手動(dòng)開(kāi)啟事務(wù)和結(jié)束事務(wù)。注意不要忘記調(diào)用
endTransaction()結(jié)束事務(wù)。 - ActiveAndroid數(shù)據(jù)庫(kù)框架的表對(duì)象必須有一個(gè)無(wú)參的構(gòu)造函數(shù),每個(gè)表對(duì)象有內(nèi)置的id字段,因此自己的字段不能叫id。通過(guò)運(yùn)行時(shí)注解與反射來(lái)動(dòng)態(tài)生成語(yǔ)句執(zhí)行Model對(duì)象與SQL語(yǔ)句的轉(zhuǎn)換,效率不是很高。而GreenDAO和DBFlow等使用編譯時(shí)注解,提高了運(yùn)行效率。
- 注解實(shí)際上就是在源碼中附帶一些額外信息的載體,這些信息在編譯或運(yùn)行時(shí)期都可以從Class信息中讀取到,從而進(jìn)行一些額外操作和處理。
- SQLite是一個(gè)內(nèi)嵌式數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)服務(wù)器就寄宿在應(yīng)用程序內(nèi),與客戶端程序運(yùn)行在同一進(jìn)程,簡(jiǎn)化了數(shù)據(jù)庫(kù)管理,提高了執(zhí)行效率。它的鎖機(jī)制為粗粒度的表級(jí)鎖,最好不要多線程并發(fā)操作,可以將對(duì)數(shù)據(jù)庫(kù)的查詢Manager設(shè)為單例,使用一個(gè)單線程池執(zhí)行完了通過(guò)Handler回調(diào)給主線程。
- SQLite并發(fā):
? 同一時(shí)間內(nèi)打開(kāi)多個(gè)Database連接讀寫(xiě),其中一個(gè)會(huì)失敗。
? 當(dāng)有寫(xiě)操作,其他讀操作會(huì)被駁回。
? 當(dāng)有寫(xiě)操作,其他寫(xiě)操作會(huì)被駁回。
? 當(dāng)有開(kāi)啟事務(wù),事務(wù)提交之前,其他寫(xiě)操作會(huì)被駁回。
? 當(dāng)有開(kāi)啟事務(wù),事務(wù)提交之前,其他事務(wù)請(qǐng)求會(huì)被駁回。
? 當(dāng)有讀操作,其他寫(xiě)操作會(huì)被駁回。
? 讀操作之間能夠并發(fā)執(zhí)行。
總結(jié):?jiǎn)螌?xiě)多讀,保證全局只有一個(gè)連接,注意關(guān)閉連接時(shí)機(jī)。
SQLite擁有文件級(jí)別的鎖,多線程可以同時(shí)讀,但只有一個(gè)能寫(xiě)。多個(gè)線程瘋狂的訪問(wèn)數(shù)據(jù)庫(kù)應(yīng)該不會(huì)崩潰。但是創(chuàng)建多個(gè)連接去寫(xiě)數(shù)據(jù)庫(kù),就會(huì)有同步問(wèn)題。SQLiteHelper獲取的可讀和可讀寫(xiě)的數(shù)據(jù)庫(kù)實(shí)際都是同一個(gè)連接,因此全局創(chuàng)建SQLiteHelper對(duì)象單例可在Application里開(kāi)啟連接(database也可不用close,App關(guān)閉時(shí)會(huì)自動(dòng)釋放)。 - 在Application里保持SQLiteHelper同步和單例,保證全局只有一個(gè)Database連接,在應(yīng)用關(guān)閉時(shí)close掉Database。
- SQLite約束:NOT NULL、DEFAULT、UNIQUE、PRIMARY KEY、CHECK。
- UNION字句/運(yùn)算符用于合并兩個(gè)或多個(gè)SELECT語(yǔ)句結(jié)果,不返回重復(fù)的行,每個(gè)SELECT被選擇的列數(shù)必須是相同數(shù)目和數(shù)據(jù)類(lèi)型的,且保證有相同的順序。UNION ALL可以包含重的行。
- SQLite常用函數(shù):
count()、max()、min()、avg()、sum()、random()、abs()、upper()、lower()、length()、sqlite_version()等。
第六章:性能優(yōu)化
- < include/>標(biāo)簽:
? include標(biāo)簽使用中,include原布局可以設(shè)置寬、高、id等屬性,這些屬性和子控件都會(huì)直接附加到主布局中,在主布局中可以直接根據(jù)id獲取這些子布局中的控件。
? 同時(shí)在主布局中可以重設(shè)include的id,會(huì)覆蓋掉原布局id,這時(shí)候就要先獲取到原布局的View,再view.findViewById()找原布局中的子View。
? 如果要在主布局中要設(shè)置include的布局屬性,要重寫(xiě)layout_width和layout_height兩個(gè)屬性。 - <merge/>標(biāo)簽:
? merge標(biāo)簽與include結(jié)合使用,如果相鄰兩個(gè)布局重復(fù)則用merge標(biāo)簽優(yōu)化,只能作為根布局來(lái)用,布局直接附加到主布局中。
? 如果用它來(lái)inflate一個(gè)布局時(shí),要指定parent元素且attachToRoot參數(shù)要設(shè)為true(一定是被加到一個(gè)父布局中的)。 - <ViewStub/>:
? ViewStub用于布局延遲加載,只能inflate()或setVisiable()一次,ViewStub本身就不存在了,返回inflate后的布局 。
?layout_width或layout_height等一些屬性要加載ViewStub上,這些在它inflate后會(huì)傳給初始化布局。
? ViewStub有id和inflatedId兩個(gè)屬性,id作用于自身,inflatedId指被inflate()后得到的View的根視圖id,尋找子布局控件時(shí)要先有子布局View,可以通過(guò)inflatedId找到。
? ViewStub目前還不支持merge標(biāo)簽。 - RelativeLayout和LinearLayout的
layout_weight屬性通常會(huì)將視圖進(jìn)行兩次測(cè)量或以上,因此在ListView中避免使用這兩種屬性。 - 內(nèi)存優(yōu)化:
? 避免保留不必要的Service,可以使用IntentService來(lái)使Service處理完任務(wù)后自動(dòng)關(guān)閉。
?onTeimMemory()中可以監(jiān)聽(tīng)TRIM_MEMORY_UI_HIDDEN級(jí)別的回調(diào),當(dāng)UI已經(jīng)隱藏時(shí),釋放UI占用的資源,注意它和onStop()的區(qū)別。
? 在manifest下添加largHeap=true屬性可以聲明一個(gè)更大的heap空間。(不一定好使)
? 避免Bitmap的浪費(fèi),進(jìn)行像素壓縮以及及時(shí)回收Bitmap。
? 多使用SparseArray等優(yōu)化過(guò)的容器類(lèi),他們避免了對(duì)key與value的autobox自動(dòng)裝箱和拆箱的過(guò)程。
? 避免過(guò)多使用枚舉enums,通常枚舉的內(nèi)存消耗是static constants的2倍。
? 使用ProGuard提剔除不需要的代碼和資源,進(jìn)行代碼壓縮混淆。
? 對(duì)打包的pak進(jìn)行zipalign優(yōu)化。 - 內(nèi)存泄漏:垃圾回收機(jī)制只回收那些不可達(dá)的對(duì)象,如果一個(gè)本該被銷(xiāo)毀的對(duì)象被錯(cuò)誤的持有,那么就屬于內(nèi)存泄漏,該對(duì)象不會(huì)被及時(shí)回收。可用內(nèi)存越來(lái)越小時(shí)會(huì)引起大量的GC操作,雖然不會(huì)明顯影響App性能,但是會(huì)使App變慢,降低UI流暢度。
- LeakCanry通過(guò)ActivityLifecycleCallbacks監(jiān)聽(tīng)了Acivity的所有生命周期回調(diào),可以追蹤、檢測(cè)、輸入內(nèi)存泄露的信息,在通知欄和日志上進(jìn)行輸出。Memory Monitor可以檢測(cè)內(nèi)存實(shí)時(shí)使用情況,當(dāng)已分配內(nèi)存明顯降低說(shuō)明觸發(fā)了GC操作,如果短時(shí)間內(nèi)內(nèi)存急劇上升或者GC十分頻繁,這時(shí)候要注意是否有內(nèi)存泄漏風(fēng)險(xiǎn)。
- 打開(kāi)GPU可以查看過(guò)度繪制區(qū)域:通常需要解決過(guò)度繪制了2次以上的情況(設(shè)置了background才算繪制)。
? 無(wú)顏色:沒(méi)有過(guò)度繪制,只繪制了1次。
? 藍(lán)色 :過(guò)度繪制了1次,一個(gè)像素點(diǎn)被繪制了2次。
? 綠色 :過(guò)度繪制了2次,一個(gè)像素點(diǎn)被繪制了3次。
? 淺紅色:過(guò)度繪制了3次,一個(gè)像素點(diǎn)被繪制了4次。
? 深紅色:過(guò)度繪制了4次,一個(gè)像素點(diǎn)被繪制了5次。 - 數(shù)據(jù)采集和分析工具TraceView可以分析每個(gè)方法的調(diào)用次數(shù)、耗時(shí)、CPU占用率等信息,幫助我們優(yōu)化代碼。
第七章:代碼規(guī)范
- 常見(jiàn)規(guī)范:代碼縮進(jìn)、一句一行、空行分隔、函數(shù)排布、修飾詞順序、字段函數(shù)命名等。
- 如果一個(gè)類(lèi)的字段沒(méi)必要對(duì)外隱藏,直接設(shè)為
public即可,不需要setter和getter產(chǎn)生額外消耗。 - 重載父類(lèi)函數(shù)最好加上
@Override注解標(biāo)識(shí)。 - 操作符加上括號(hào)標(biāo)明優(yōu)先級(jí)、if-else等方法加上括號(hào)、switch函數(shù)加上
default分支等。
第八章:Git版本控制
- Git是分布式版本控制系統(tǒng),在客戶端將整個(gè)項(xiàng)目倉(cāng)庫(kù)完整的鏡像下來(lái),將變化的文件做快照保存在本地微型文件系統(tǒng)中,若無(wú)變化則只對(duì)上次的快照做一次連接;通過(guò)文件摘要判斷是否有過(guò)修改,因此只關(guān)心文件數(shù)據(jù)的整體是否發(fā)生變化而不關(guān)注文件內(nèi)容的具體差異。
- gitconfig的三種級(jí)別:每一種級(jí)別會(huì)覆蓋掉上層級(jí)別的設(shè)置,如果查看時(shí)有2項(xiàng)以上,說(shuō)明級(jí)別不同,取最后一個(gè)配置。
①/etc/gitconfig:系統(tǒng)中所有用戶的所有倉(cāng)庫(kù)普遍適用的配置,適用--system選項(xiàng)讀寫(xiě)的文件。
②~/.gitconfig:用戶目錄下的配置文件,適用于該用戶的所有倉(cāng)庫(kù),適用--global選項(xiàng)讀寫(xiě)的文件。
③項(xiàng)目下的./git/gitconfig:對(duì)該項(xiàng)目下配置有效。 - 基本命令:
? git rm --cached [-r] xxx:移除文件的追蹤(不刪除/刪除本地文件)
? git checkout -- xxx:還原工作區(qū)當(dāng)前已修改的文件
? git reset HEAD xxx:還原暫存區(qū)add以后的文件到工作區(qū)
? git reset --hard HEAD^ / xxx:還原版本庫(kù)commit以后的文件到上個(gè)版本/指定版本
? git clone remote_url:克隆遠(yuǎn)程項(xiàng)目到本地
? git remote add origin remote_url:先有本地倉(cāng)庫(kù),再與遠(yuǎn)程已有倉(cāng)庫(kù)關(guān)聯(lián)
? git fetch remote branch:下載遠(yuǎn)程倉(cāng)庫(kù)的變動(dòng)(只下載不合并)
? git pull remote branch:下載遠(yuǎn)程倉(cāng)庫(kù)的變動(dòng)(合并),第一次拉取實(shí)際就自動(dòng)關(guān)聯(lián)了分支,所以一般都先拉取一次
? git push -u origin master:推送master上的內(nèi)容到遠(yuǎn)程倉(cāng)庫(kù)(第一次推送 -u關(guān)聯(lián)分支)
? git remote rm remote_url:刪除遠(yuǎn)程倉(cāng)庫(kù)
? git push origin locan_branch:remote_branch:推送本地分支到遠(yuǎn)程,如果遠(yuǎn)程沒(méi)有,則新建一個(gè)分支
? git push origin :remote_branch:刪除上一步創(chuàng)建的那個(gè)遠(yuǎn)程分支 - GitHub生成SSH key命令:
ssh-keygen -t rsa -b 4096 -c "your_email@example.com"。使用SSH -t git@github.com來(lái)驗(yàn)證github的ssh key是否添加成功??赏ㄟ^(guò)SSH或Https進(jìn)行通信,一般選擇SSH,通過(guò)SSH Key訪問(wèn)不用每次都輸入密碼。 -
git remote add [branch_name] [branch_url]命令可以關(guān)聯(lián)本地倉(cāng)庫(kù)和一個(gè)遠(yuǎn)程倉(cāng)庫(kù),且一個(gè)本地方庫(kù)可以關(guān)聯(lián)多個(gè)遠(yuǎn)程倉(cāng)庫(kù)。因此,github的Fork和Pull Request模式就是先將原項(xiàng)目Fork到自己的github下,再在本地添加關(guān)聯(lián)自己Fork來(lái)的項(xiàng)目,并進(jìn)行修改、提交;最后在自己的Fork項(xiàng)目再添加關(guān)聯(lián)原項(xiàng)目(本地倉(cāng)庫(kù)串行關(guān)聯(lián)了兩個(gè)遠(yuǎn)程倉(cāng)庫(kù)),先將原項(xiàng)目pull一次到自己的Fork項(xiàng)目,解決沖突,再在自己Fork的項(xiàng)目上進(jìn)行pull request即可。
第九章:?jiǎn)卧獪y(cè)試
- 單元測(cè)試是由開(kāi)發(fā)人員編寫(xiě),用于檢測(cè)特定條件下目標(biāo)代碼正確性的代碼。是獨(dú)立的類(lèi),基于方法的細(xì)粒度的,具有回歸性,一勞永逸,將項(xiàng)目重構(gòu)后只要單元測(cè)試能跑通,基本代碼就沒(méi)什么問(wèn)題。
- Junit是Java的一套基本的單元測(cè)試框架:1.定義一個(gè)測(cè)試類(lèi)繼承自
TestCase.class。2.重寫(xiě)setUp()和tearDown()方法進(jìn)行必要的初始化邏輯和銷(xiāo)毀邏輯。3.自定義public的測(cè)試方法,以test開(kāi)頭,添加@Test注解。4.方法中可以添加各種基于方法測(cè)驗(yàn)證代碼。5.運(yùn)行測(cè)試類(lèi)即可。注意每個(gè)測(cè)試方法相互并不相關(guān),執(zhí)行順序也不一定,相互不要有依賴性。 - 斷言類(lèi)型:
assertEquals、assertTrue/False、assertNull/NotNull、assertSame/NotSame、failNotEquals、failSame/NotSame、fail(String)、fail等。 - 運(yùn)行多個(gè)測(cè)試類(lèi)的兩種方法:
①創(chuàng)建TestSuite類(lèi),測(cè)試類(lèi)用JUnit4TestAdapter包裝,調(diào)用suite.addTest(new JUnit4TestAdapter(AddTest.class))即可,返回suite實(shí)例。
②TestSuite類(lèi)上添加注解@RunWith(Suite.class)和@Suite.SuiteClasses(AddTest.calss)即可。 - 要測(cè)試的內(nèi)容:各種邊界條件、if-else的各種執(zhí)行路徑等。
- 當(dāng)某些關(guān)聯(lián)的模塊沒(méi)有開(kāi)發(fā)完畢時(shí),可以手動(dòng)Mock虛擬對(duì)象來(lái)提供測(cè)試依賴的實(shí)例(基于接口編程的方便性,可創(chuàng)建測(cè)試實(shí)現(xiàn)類(lèi)進(jìn)行替換即可)。
- Mockio庫(kù)是一套基于MIT協(xié)議的開(kāi)源Java測(cè)試框架,能夠隨時(shí)隨地自動(dòng)Mock對(duì)象、驗(yàn)證結(jié)果以給測(cè)試用例提前打樁。它在運(yùn)行時(shí)才去Mock對(duì)象,模擬測(cè)試對(duì)象的行為,消除了依賴,解耦性強(qiáng)。
? 需引入dexmaker-mockito-1.0.jar和dexmaker-1.0.jar兩個(gè)jar包。
? 常用功能都在org.mockito.Mockit.*類(lèi)的靜態(tài)方法里,測(cè)試類(lèi)中可以引入這個(gè)類(lèi)即可。
? Mock對(duì)象可以調(diào)用mock(xxx.class)也可以直接在成員變量上加上@Mock注解來(lái)自動(dòng)創(chuàng)建對(duì)象,注解方式要在setUp()方法中初始化Mock對(duì)象MockitoAnnotations.initMocks(this)。
?verify()函數(shù)可以驗(yàn)證對(duì)象的交互;when()...then()...可以為打測(cè)試樁,類(lèi)似debug中的Evaluate expression和Set Value,讓用例按照自己設(shè)置的值輸出;any()可以輸出任意值,方便我們測(cè)試。 - Android測(cè)試:
? Android平臺(tái)下的單元測(cè)試都是InstrumentationTestCase的子類(lèi),內(nèi)部封裝了Instrumentation對(duì)四大組件的測(cè)試操作以及一些基本操作,它繼承自Junit的TestCase,它有一些子類(lèi)進(jìn)行更詳細(xì)的測(cè)試使用。
? AndroidTestCase測(cè)試類(lèi)內(nèi)置了mContext對(duì)象,可以用來(lái)啟動(dòng)Activity、獲取資源、獲取系統(tǒng)服務(wù)等。
? ActivityInstrumentationTestCase/TestCase2、ServiceTestCase、ProviderTestCase2等測(cè)試類(lèi)可以測(cè)試四大組件,模擬點(diǎn)擊效果等。
? ActivityTestCase2中代碼不是在UI線程,涉及更新UI的操作需要runTestOnUiThread(),然后調(diào)用getInstrumentation().waitForIdleSync()執(zhí)行后續(xù)操作。 - 測(cè)試驅(qū)動(dòng)TDD開(kāi)發(fā)要求先寫(xiě)測(cè)試用例,再進(jìn)行開(kāi)發(fā),一步一步驗(yàn)證測(cè)試用例,這使得我們開(kāi)發(fā)的時(shí)候要多去解耦,抽出功能性代碼到單獨(dú)的類(lèi)中。
- 常用測(cè)試框架:Robolectric、Espresso、UI Automator等。
第十章:六大原則與設(shè)計(jì)模式
- 六大原則:
? 里氏替換原則:所有引用基類(lèi)、接口的地方可以透明的試用其子類(lèi)對(duì)象,反過(guò)來(lái)就不行。(多態(tài)的體現(xiàn),高層調(diào)用者持有調(diào)用對(duì)象的接口引用,可以拓展性的創(chuàng)建具體不同的子類(lèi)實(shí)例)
? 依賴導(dǎo)致原則:模塊間的依賴都是通過(guò)接口或抽象發(fā)生,實(shí)現(xiàn)類(lèi)之間不發(fā)生直接的依賴關(guān)系。(高層間相互持有對(duì)方的接口依賴,使子類(lèi)可替換性實(shí)現(xiàn)而調(diào)用者代碼不用變)
? 開(kāi)閉原則 :一個(gè)軟件的實(shí)體如類(lèi)、模塊和函數(shù)應(yīng)該對(duì)拓展開(kāi)放、對(duì)修改關(guān)閉。(策略模式,面向接口傳值,提供不同實(shí)現(xiàn)類(lèi)做具體解析處理)。以上三者都是"面向接口編程"
? 單一職責(zé)原則:一個(gè)類(lèi)只做一件事,類(lèi)中的函數(shù)必須是高度相關(guān)的。(功能性代碼分開(kāi)寫(xiě),通過(guò)接口來(lái)確認(rèn)功能函數(shù))
? 接口隔離原則:一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴應(yīng)該建立在一個(gè)最小接口上。(一個(gè)子類(lèi)可能實(shí)現(xiàn)多個(gè)接口,高層調(diào)用者只關(guān)心其某個(gè)單一職責(zé)接口,不必知道其他細(xì)節(jié)實(shí)現(xiàn))
? 迪米特原則 :一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有最少的了解。(一個(gè)類(lèi)應(yīng)該對(duì)自己需要耦合的類(lèi)知道的最少,或關(guān)聯(lián)最簡(jiǎn)接口,或只與最直接朋友通信而不管一些代理/helper的存在)。以上三者都是"單一職責(zé)" - 六大設(shè)計(jì)模式最終的幾個(gè)關(guān)鍵字:抽象、單一職責(zé)、最小化;高內(nèi)聚、低耦合。
- 反模式:與設(shè)計(jì)模式類(lèi)似,是一種文字記錄形式,描述了對(duì)某個(gè)問(wèn)題必然產(chǎn)生的消極后果的常見(jiàn)解決方案,包括形式、主要原因、典型癥狀、后果以及如何通過(guò)重構(gòu)解決問(wèn)題等。
- 設(shè)計(jì)模式總結(jié)了在特定問(wèn)題下的正確的解決方案,而反模式則是告訴你在特定問(wèn)題上的錯(cuò)誤的解決方案以及他們的原因、解決方案,最終通過(guò)正確的解決方案將項(xiàng)目拉回正軌。
第十一章:重構(gòu)
- 常見(jiàn)的重構(gòu)手法:
? 提取子函數(shù):將臃腫的函數(shù)代碼分離到具體的職責(zé)單一的子函數(shù)中,也方便其他業(yè)務(wù)邏輯調(diào)用。
? 上移到父類(lèi)函數(shù):如果某個(gè)函數(shù)在較多類(lèi)中共同調(diào)用,應(yīng)當(dāng)上移到統(tǒng)一的父類(lèi)中,可以通過(guò)參數(shù)不同定義不同實(shí)現(xiàn),必要時(shí)子類(lèi)也可以重寫(xiě)該函數(shù)。
? 下移函數(shù)到子類(lèi):有時(shí)候父類(lèi)與子類(lèi)中有些函數(shù)不一定全部用到,可以建立中間層基類(lèi)實(shí)現(xiàn)局部繼承。
? 封裝固定的調(diào)用邏輯:可以通過(guò)接口或父類(lèi)封裝規(guī)定的方法調(diào)用邏輯,至于具體實(shí)現(xiàn)可以不同,但調(diào)用邏輯要相同。接口只定義調(diào)用邏輯,父類(lèi)可以封裝好一套固定實(shí)現(xiàn)。
? 使用泛型去除重復(fù)邏輯:必要的時(shí)候使用泛型來(lái)定義父類(lèi),可以更好的抽象代碼,復(fù)用邏輯,減少代碼量。
? 使用對(duì)象避免過(guò)多的參數(shù):當(dāng)一個(gè)方法參數(shù)過(guò)多時(shí)應(yīng)當(dāng)考慮將參數(shù)封裝成一個(gè)對(duì)象來(lái)傳遞,這樣更加清晰且默認(rèn)的null字段不用顯示聲明了。
? 轉(zhuǎn)移函數(shù):根據(jù)業(yè)務(wù)邏輯將函數(shù)轉(zhuǎn)移到主要使用它的類(lèi)中,單一職責(zé)原則。
? 將類(lèi)型碼轉(zhuǎn)為狀態(tài)模式:當(dāng)使用過(guò)多條件時(shí),想添加一種新的條件時(shí),秩序建立一個(gè)新的子類(lèi),并在其中實(shí)現(xiàn)相應(yīng)的函數(shù),在適當(dāng)?shù)臅r(shí)機(jī)將該子類(lèi)注入到對(duì)象中即可(策略模式)。狀態(tài)父類(lèi)中定義了所有行為方法(非抽象);不用狀態(tài)的子類(lèi)選擇性的實(shí)現(xiàn)需要實(shí)現(xiàn)的方法,代理了行為類(lèi);行為類(lèi)中定義狀態(tài)父類(lèi),選擇性的注入子類(lèi)實(shí)例,行為方法完全調(diào)用不同子類(lèi)實(shí)例的代理方法實(shí)現(xiàn)(狀態(tài)模式)。
? NullObject模式:有時(shí)候一個(gè)類(lèi)中的某個(gè)對(duì)象,供其他類(lèi)調(diào)用時(shí)都要先判空,可以在該類(lèi)中定義一個(gè)private static final的默認(rèn)的null實(shí)現(xiàn)類(lèi),其他對(duì)象調(diào)用get時(shí)返回默認(rèn)null實(shí)現(xiàn)類(lèi)或真正的實(shí)現(xiàn)類(lèi),這樣其他類(lèi)只管調(diào)用拿到的對(duì)象即可,肯定不為空。
? 分解“胖”類(lèi)型:盡量保持類(lèi)的職責(zé)單一,將一個(gè)功能性的類(lèi)拆分成多個(gè)不同功能的類(lèi)組合,這些不同功能的類(lèi)也可以使用工廠方法實(shí)現(xiàn)可替換性。