lru算法:最近最少使用
1.新數據插入到鏈表頭部;
2.每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;
3.當鏈表滿的時候,將鏈表尾部的數據丟棄。
?
自定義控件:
1.measure操作,主要用于計算視圖的大小,并通過setMeasuredDimension(width, height)保存計算結果。
2.layout操作,用于設置視圖在屏幕中顯示的位置
3.draw操作,draw操作利用前兩部得到的參數,將視圖顯示在屏幕上
rect可以直接獲取文字的寬高,獲取FontMetrics對象,也可以獲得top,bottom,ascent,descent,然后計算寬高
?
java集合類:
java集合類主要由兩個接口派生而出:Collection和Map,Map沒有繼承Collection接口.
?
list:有序的,可以重復的,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數組下標)來訪問List中的元素,List允許有相同的元素,查詢速度快。因為往list集合里插入或刪除數據時,會伴隨著后面數據的移動,所有插入刪除數據速度慢。
map:Map提供key到value的映射,鍵不能重復,值可以重復
set:無序的,不包含重復的元素,當試圖把兩個相同的對象加入一個Set中時,對象會調用equals方法比較兩個對象元素是否相同,相同則不會加入。
Queue(隊列):實現了一個FIFO(First?In?First?Out,先進先出)的機制。元素在隊列的尾部插入(入隊操作),并從隊列的頭部移出(出隊操作)。
ArrayList:
基于數組實現,ArrayList的容量默認為10,是一個動態數組,可以通過下標索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低,自動根據數據去申請內存,不是線程安全,如果需要同步,可以用 Collections的synchronizedList方法,實現List接口、底層使用數組保存所有元素,每次數組容量的增長大約是其原容量的1.5倍,線程安全需要更大的系統開銷,每次增加元素,都要將原來的元素拷貝到一個新的數組中,非常之耗時,也因此建議在事先能確定元素數量的情況下,才使用ArrayList,否則建議使用LinkedList,ArrayList中允許元素為null。
HashMap:
實現了map接口,map不允許有重復的key,不是有序的,允許key,value為空,一個數組加一個鏈表,是數組和鏈表的結合體,值根據key來分配,不是線程安全,內存超出自動申請2倍的內存,把原來的對象放入新的集合中,HashMap不支持線程的同步,即任一時刻可以有多個線程同時寫HashMap;可能會導致數據的不一致。如果需要同步,可以用 Collections的synchronizedMap方法,或者使用ConcurrentHashMap
LinkedList:
基于鏈表的數據結構,LinkedList也和ArrayList一樣實現了List接口,隨機訪問get和set,linkedlist需要移動指針,所以慢于arraylist,執行插入和刪除操作時比ArrayList更加高效,因為它是基于鏈表的。基于鏈表也決定了它在隨機訪問方面要比ArrayList遜色一點。
插入或者是刪除元素頻繁,或者壓根就不需要訪問元素的時候,選擇LinkedLis;
查詢比插入或者是刪除元素更加頻繁的時候,應該使用ArrayList;
HashSet:
實現了Set接口,它不允許集合中有重復的值,HashSet較HashMap來說較慢,因為map因為是使用唯一的鍵來獲取對象,HashSet查找某個對象時,首先用hashCode()方法計算出這個對象的Hash碼,然后再根據Hash碼到相應的存儲區域用equals()方法查找,從而提高了效率。允許元素為null值
HashSet類源碼中有一個HashMap類型的成員變量,其中的成員方法也是調用hashMap的成員方法,只不過存儲時,按照key-value對存入HashMap對象中,把值存在key中,Value統一為null。
有時候hashset需要重寫equals方法,假如存儲同學對象,對象包含同學的信息,equals()方法是根據對象的內存地址來判斷兩個對象是否相等的,由于兩次插入的同學的內存地址肯定不相同,所以判斷的結果是不相等,所以兩次都插入了。于是,我們需要覆寫equals()方法來判斷兩個對象是否是同一個對象,可以根據地址,類型,然后加上hashcode值(每個對象的code值都不一樣,所以需要重寫hashcode方法,返回對象的名字的hashcode值來判斷)來判斷。
數組:尋址容易,插入和刪除困難;
鏈表:尋址困難,插入和刪除容易;
哈希表:兩者結合.
鏈表是一種數據的存儲方式,保存的數據在內存中不連續的,用指針對數據進行訪問
隊列是一種數據結構,其特點是先進先出,后進后出
?
GC工作原理:
參考文章:http://blog.jobbole.com/80499/
http://blog.csdn.net/zhb123gggggg/article/details/40901003
年輕代分為3塊,年輕代總共有3塊空間,其中2塊為Survivor區一塊Eden區域,其中一個Survivor區必須保持空。如果數據存在于兩個Survivor區,或兩個都沒使用,你可以將這個情況作為系統錯誤的一個標志。當老年代數據滿時,基本上會執行一次GC。執行程序根據不同GC類型而變化,永久帶可以看做是方法區
- 絕大多數新創建的對象分配在Eden區。
- 在Eden區發生一次GC后,存活的對象移到其中一個Survivor區。
- 在Eden區發生一次GC后,對象是存放到Survivor區,這個Survivor區已經存在其他存活的對象。
- 一旦一個Survivor區已滿,存活的對象移動到另外一個Survivor區。然后之前那個空間已滿Survivor區將置為空,沒有任何數據。
- 經過重復多次這樣的步驟后依舊存活的對象將被移到老年代。
? 在jdk7中有5中垃圾回收集器
- Serial收集器(不能用于服務器端。這個收集器類型僅應用于單核CPU桌面電腦。使用serial收集器會顯著降低應用程序的性能)
- Parallel收集器
- Parallel Old收集器 (Parallel Compacting GC)收集器
- Concurrent Mark & Sweep GC ?(or “CMS”)收集器
- Garbage First (G1) 收集器
Serial 收集器 (-XX:+UseSerialGC)
在老年代的GC使用算法被稱為“標記-清除-整理”。
- 該算法的第一步是在老年代標記存活的對象。
- 從頭開始檢查堆內存空間,并且只留下依然幸存的對象(清除)。
- 最后一步,從頭開始,順序地填滿堆內存空間,將存活的對象連續存放在一起,這樣堆分成兩部分:一邊有存放的對象,一邊沒有對象(整理)。
serial收集器應用于小的存儲器和少量的CPU。
Parallel收集器(-XX:+UseParallelGC)
serial收集器只使用一個線程來處理的GC,而parallel收集器使用多線程并行處理GC,因此更快。當有足夠大的內存和大量芯數時,parallel收集器是有用的。它也被稱為“吞吐量優先垃圾收集器。”
Parallel Old 垃圾收集器(-XX:+UseParallelOldGC)
Parallel Old收集器是自JDK 5開始支持的。相比于parallel收集器,他們的唯一區別就是在老年代所執行的GC算法的不同。它執行三個步驟:標記-匯總-壓縮(mark – summary – compaction)。匯總步驟與清理的不同之處在于,其將依然幸存的對象分發到GC預先處理好的不同區域,算法相對清理來說略微復雜一點。
CMS GC (-XX:+UseConcMarkSweepGC)
CMS垃圾收集器比上面解釋的各種算法都要復雜很多。初始標記(initial mark) 比較簡單。這一步驟只是查找距離類加載器最近的幸存對象。所以停頓時間非常短。之后的并發標記步驟,所有被幸存對象引用的對象會被確認是否已經被追蹤檢查。這一步的不同之處在于,在標記的過程中,其他的線程依然在執行。在重新標記步驟會修正那些在并發標記步驟中,因新增或者刪除對象而導致變動的那部分標記記錄。最后,在并發清除步驟,垃圾收集器執行。垃圾收集器進行垃圾收集時,其他線程的依舊在工作。一旦采取了這種GC類型,由于垃圾回收導致的停頓時間會極其短暫。CMS 收集器也被稱為低延遲垃圾收集器。它經常被用在那些對于響應時間要求十分苛刻的應用上。
缺點就是它會比其他GC類型占用更多的內存和CPU,默認情況下不支持壓縮步驟
G1 GC
首先你要忘記你所理解的新生代和老年代。正如你在上圖所看到的,每個對象被分配到不同的網格中,隨后執行垃圾回收。當一個區域填滿之后,對象被轉移到另一個區域,并再執行一次垃圾回收。在這種垃圾回收算法中,不再有從新生代移動到老年代的三部曲。這個類型的垃圾收集算法是為了替代CMS 收集器而被創建的,因為CMS 收集器在長時間持續運行時會產生很多問題,G1最大的好處是他的性能,他比我們在上面討論過的任何一種GC都要快。
?
Viewpager:
禁止左右滑動,攔截器返回false,和onTouchEvent返回false,消費事件可以達到
切換閃爍問題,ViewPager.setCurrentItem(position,false);
public void setCurrentItem(int item, boolean smoothScroll) { // TODO Auto-generated method stub super.setCurrentItem(item, smoothScroll); } @Override public void setCurrentItem(int item) { // TODO Auto-generated method stub super.setCurrentItem(item, false);//表示切換的時候,不需要切換時間}
?
Android 事件分發機制:
參考鏈接:http://www.jianshu.com/p/e99b5e8bd67b ,很贊,寫的很詳細
ACTION_DOWN:
由Activity的dispatchTouchEvent做分發,如果沒有對方法重寫或者更改返回值,那么整個事件流向應該是從Activity---->ViewGroup--->View 從上往下調用dispatchTouchEvent方法,一直到葉子節點(View)的時候,再由View--->ViewGroup--->Activity從下往上調用onTouchEvent方法。
假如方法返回了true,那么事件終止,被消費了,沒有誰能收到這個事件了,如果返回false的話,事件都會回傳給父控件的onTouchEvent處理
對于onInterceptTouchEvent攔截器,默認返回false,如果返回true,那么事件不再分發,而是分發給自己的onTouchEvent去處理
View 沒有攔截器,默認實現(super)就是把事件分發給自己的onTouchEvent
對于ACTION_MOVE、ACTION_UP:
如果在dispatchTouchEvent和onTouchEvent返回false,事件都會正常分發,返回true消費了事件,所有事件都不會分發;
dispatchTouchEvent返回true,onTouchEvent返回false,所有分發傳遞終止,所有事件都不會分發;
dispatchTouchEvent返回false,onTouchEvent返回true,ACTION_DOWN 事件會分發下去,然后在view中從下往上onTouchEvent事件傳遞,ACTION_MOVE 和 ACTION_UP事件則會在onTouchEvent事件消費處的控件中直接把事件交給這個控件,而不會向下分發
?
synchronized和volatile的區別:
一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾之后,那么就具備了兩層語義:
1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。
2)禁止進行指令重排序。volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
1.volatile僅能使用在變量級別;
?? synchronized則可以使用在變量、方法、和類級別的
2.volatile僅能實現變量的修改可見性,并不能保證原子性;
?? synchronized則可以保證變量的修改可見性和原子性
3.volatile不會造成線程的阻塞;
?? synchronized可能會造成線程的阻塞。
4.volatile標記的變量不會被編譯器優化;
?? synchronized標記的變量可以被編譯器優化
?
多線程如何同步:
介紹:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等,每個JAVA對象都有且只有一個同步鎖,在任何時刻,最多只允許一個線程擁有這把鎖,我們可以給共享資源加一把鎖,這把鎖只有一把鑰匙。哪個線程獲取了這把鑰匙,才有權利訪問該共享資源,同步鎖不是加在共享資源上,而是加在訪問共享資源的代碼段上,訪問同一份共享資源的不同代碼段,應該加上同一個同步鎖;如果加的是不同的同步鎖,那么根本就起不到同步的作用,沒有任何意義
1:synchronized:
1、同步代碼塊:
synchronized(同一個數據){}?同一個數據:就是N條線程同時訪問一個數據。
2、同步方法:
public synchronized 數據返回類型方法名(){}
2:AsyncTask:
實際上,doInBackground()調用時機取決于Android版本,Android 1.6之前,任務是串行執行,只需要一個后來線程。從Android 1.6開始,線程池取代了單個的后臺線程,線程池允許并行執行多個任務,以提升性能
3:使用局部變量實現線程同步:
如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本
4:lock:
線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用Lock,如果A不釋放,可以使B在等待了足夠長的時間以后,中斷等待,而干別的事情
synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中
在資源競爭不是很激烈的情況下,Synchronized的性能要優于Lock,但是在資源競爭很激烈,就是并發量高的情況下,Synchronized的性能會下降幾十倍,但是Lock的性能能維持常態;
5:ThreadLocal: 參考博客:http://blog.csdn.net/lufeng20/article/details/24314381
同步機制僅提供一份變量,讓不同的線程排隊訪問,而ThreadLocal為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。


public class TestNum { // ①通過匿名內部類覆蓋ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() { public Integer initialValue() { return 0; } }; // ②獲取下一個序列值 public int getNextNum() { seqNum.set(seqNum.get() + 1); return seqNum.get(); } public static void main(String[] args) { TestNum sn = new TestNum(); // ③ 3個線程共享sn,各自產生序列號 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread { private TestNum sn; public TestClient(TestNum sn) { this.sn = sn; } public void run() { for (int i = 0; i < 3; i++) { // ④每個線程打出3個序列值 System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn[" + sn.getNextNum() + "]"); } } } } 輸出結果 thread[Thread-0] --> sn[1] thread[Thread-1] --> sn[1] thread[Thread-2] --> sn[1] thread[Thread-1] --> sn[2] thread[Thread-0] --> sn[2] thread[Thread-1] --> sn[3] thread[Thread-2] --> sn[2] thread[Thread-0] --> sn[3] thread[Thread-2] --> sn[3]
?
前面幾種同步方式都是在底層實現的線程同步,但是我們在實際開發當中,應當盡量遠離底層結構
如何避免死鎖:
一個通用的經驗法則是:當幾個線程都要訪問共享資源A、B、C?時,保證每個線程都按照同樣的順序去訪問他們。
?
線程間通信:
1:Handle:
handler:線程,創建了looper,looper里面擁有message queue,handler有兩個工作,一是發送任務或者消息;二是處理消息或者執行任務,handler既能發送message,也能發送runnbale。換句話說,message queue不只是裝message的queue(其實是一個單鏈表),而且還能裝runnable,創建handler前,必須先創建looper。handler發送消息到message queue,所以構造一個handler的時候必須知道message queue,才能確定把消息發送到哪里,而message queue是由looper來管理的,message的callback就是runnable。因此,分辨一個message是不是runnable,其實只要看message的callback是否為空,如果為空,就是普通的message,否則,就是一個runnbale,不產生消息也能發送消息,比如sendEmptyMessage,消息的處理時機還是由handler來決定,handler.sendMessage,把message放到message queue的尾部排隊,looper從前往后一個一個取消息。
handler.sendMessageAtFrontOfQueue,把message放到message queue的頭部,消息可以馬上被處理。
handler.sendMessageAtTime,不馬上發送消息到message queue,而是在指定的時間點發送。
handler.sendMessageDelayed,不馬上發送消息到message queue,而是在指定的時延后再發送。
要知道message應該發送到哪個handler,必須先創建looper,handler不是獨立存在的,一個handler,一定有一個專屬的線程,一個消息隊列,和一個looper與之關聯
handler引用context,loop引用handler,而當activity被finished后,延時發送的消息會繼續在消息隊列中存活,直到被處理,這個消息又持有activity的handler,因此activity不會被回收,從而造成內存泄漏,解決方案如下
@Override public void onDestroy() { // If null, all callbacks and messages will be removed. mHandler.removeCallbacksAndMessages(null); }
?
優缺點:
1. Handler用法簡單明了,可以將多個異步任務更新UI的代碼放在一起,清晰明了 2. 處理單個異步任務代碼略顯多
2:runOnUiThread
3:AsyncTask:
execute(觸發異步任務的執行)-->onPreExecute(前者被調用后立即執行)-->doInBackground(前者被調用后立即執行,用于執行較為費時的操作)-->onProgressUpdate(更新操作)-->onPostExecute(當后臺操作結束時)
實際上,doInBackground()調用時機取決于Android版本,只需要一個后來線程。線程池允許并行執行多個任務,以提升性能,AsyncTask對象的創建和execute方法必須在主線程中調用,一個AsyncTask實例只能調用一次execute方法,AsyncTask執行execute方法時在Android1.6之前串行;Android1.6之后并行;Android3.0后串行,如果想要并行的也可以,自定義執行器
?
進程:參考博客:?http://blog.csdn.net/wuseyukui/article/details/48004687
一個應用程序就是一個獨立的進程(應用運行在一個獨立的環境中,可以避免其他應用程序/進程的干擾)。一般來說,當我們啟動一個應用程序時,系統會創建一個進程,并為這個進程創建一個主線程(UI線程),然后就可以運行MainActivity了,如果內存不足,可又有其它為用戶提供更緊急服務的進程需要更多內存,Android可能會決定關閉一個進程。在此進程中運行著的應用程序組件也會因此被銷毀。當需要再次工作時,會為這些組件重新創建一個進程。
一.前臺進程(Foreground process)?
前臺進程是用戶當前做的事所必須的進程,如果滿足下面各種情況中的一種,一個進程被認為是在前臺:
-
進程持有一個正在與用戶交互的Activity。
-
進程持有一個Service,這個Service處于這幾種狀態:①Service與用戶正在交互的Activity綁定。②Service是在前臺運行的,即它調用了 startForeground()。③Service正在執行它的生命周期回調函數(onCreate(), onStart(), or onDestroy())。
-
進程持有一個BroadcastReceiver,這個BroadcastReceiver正在執行它的 onReceive() 方法。
殺死前臺進程需要用戶交互,因為前臺進程的優先級是最高的。
二.可見進程(Visible process)?
如果一個進程不含有任何前臺的組件,但仍可被用戶在屏幕上所見。當滿足如下任一條件時,進程被認為是可見的:
-
進程持有一個Activity,這個Activity不在前臺,但是仍然被用戶可見(處于onPause()調用后又沒有調用onStop()的狀態,比如,前臺的activity打開了一個對話框,這樣activity就會在其后可見)。
-
進程持有一個Service,這個Service和一個可見的(或者前臺的)Activity綁定。
可見的進程也被認為是很重要的,一般不會被銷毀,除非是為了保證所有前臺進程的運行而不得不殺死可見進程的時候。
三.服務進程 (Service process)?
如果一個進程中運行著一個service,這個service是通過 startService() 開啟的,并且不屬于上面兩種較高優先級的情況,這個進程就是一個服務進程。
盡管服務進程沒有和用戶可以看到的東西綁定,但是它們一般在做的事情是用戶關心的,比如后臺播放音樂,后臺下載數據等。所以系統會盡量維持它們的運行,除非系統內存不足以維持前臺進程和可見進程的運行需要。
四.后臺進程 (Background process)?
如果進程不屬于上面三種情況,但是進程持有一個用戶不可見的activity(activity的onStop()被調用,但是onDestroy()沒有調用的狀態),就認為進程是一個后臺進程。
后臺進程不直接影響用戶體驗,系統會為了前臺進程、可見進程、服務進程而任意殺死后臺進程。
通常會有很多個后臺進程存在,它們會被保存在一個LRU (least recently used)列表中,這樣就可以確保用戶最近使用的activity最后被銷毀,即最先銷毀時間最遠的activity。
五.空進程?
如果一個進程不包含任何活躍的應用組件,則認為是空進程。?
例如:一個進程當中已經沒有數據在運行了,但是內存當中還為這個應用駐留了一個進程空間。?
保存這種進程的唯一理由是為了緩存的需要,為了加快下次要啟動這個進程中的組件時的啟動時間。系統為了平衡進程緩存和底層內核緩存的資源,經常會殺死空進程。
?
IPC(Inter-Process Communication,進程間通信):
linux進程給每個應用程序都分配了一個獨立的userid,userid對應一個Linux用戶,如果多個app簽名一樣就會起沖突,兩個apk使用相同的userID,這樣它們就可以看到對方的文件,配置文件設置相同的sharedUserId,然后可以通過 createPackageContext("com.android.test", CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY);來獲取對方的context對象,有了該對象就可以獲取對方應用的資源文件了,還能跳轉activity(前提是需要在調用的應用里聲明exported=”true”屬性)


//第一個應用程序為的menifest文件代碼如下: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.demo_A" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.android.test"> //第二個應用程序的menifest文件代碼如下: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.demo_B" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.android.test">
1.Messenger(handler方式):
Messenger是一種輕量級的IPC方案,它對AIDL進行封裝,所以使用起來非常的方便,當然AIDL通信的底層實現也是對Binder的封裝,如果由于某些原因被系統殺死回收,連接就會斷開。
在onBind時返回Messenger的binder。雙方用Messenger來發送數據,用Handler來處理數據。Messenger處理數據依靠Handler,所以是串行的,也就是說,Handler接到多個message時,就要排隊依次處理。
注意使用sharedUserId+android:process


Messenger messenger = null; private static class MessengerHandler extends Handler{@Overridepublic void handleMessage(Message msg) {switch (msg.what){case 1:L.i("i receive '" + msg.getData().getString("message")+"'");Messenger client = msg.replyTo;//回應客戶端if (client != null){Message reply = Message.obtain();Bundle message = new Bundle();message.putString("message", "i have received your message");L.i("i have received your message");reply.setData(message);try {client.send(reply);} catch (RemoteException e) {e.printStackTrace();}}break;case 2:L.i("i receive '" + msg.getData().getString("message")+"'");L.i("client has disconnect this connection, bye~");break;default:break;}} }@Override public IBinder onBind(Intent intent) {return messenger.getBinder(); }@Override public void onCreate() {super.onCreate();messenger = new Messenger(new MessengerHandler()); }


private Button connect_handler; private TextView tv_handler; private Button connect_binder; private TextView tv_binder;private ServiceConnection serviceConnection; private Messenger serverMessenger; private Messenger messenger;private boolean hasBindService = false;@Override public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.client_handler);connect_handler = (Button) findViewById(R.id.connect_handler);connect_handler.setOnClickListener(this);tv_handler = (TextView) findViewById(R.id.tv_handler);connect_binder = (Button) findViewById(R.id.connect_binder);connect_binder.setOnClickListener(this);tv_binder = (TextView) findViewById(R.id.tv_binder);serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//綁定服務后實例化Messenger對象serverMessenger = new Messenger(service);communicate();}@Overridepublic void onServiceDisconnected(ComponentName name) {serverMessenger = null;}};messenger = new Messenger(new Handler(){@Overridepublic void handleMessage(Message msg) {L.i("i have received '" + msg.getData().getString("message") + "'");Message message = Message.obtain();Bundle bundle = new Bundle();bundle.putString("message", "OK, bye bye~");message.setData(bundle);L.i("i have send '" + message.getData().getString("message") + "'");message.what = 2;if (serverMessenger != null){try {serverMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}}}); }private void communicate(){SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");Message message = Message.obtain();Bundle msg = new Bundle();msg.putString("message", "i have send handler a message at " + simpleDateFormat.format(System.currentTimeMillis()));message.setData(msg);L.i("i have send '" + message.getData().getString("message") + "'");message.what = 1;message.replyTo = messenger;if (serverMessenger != null){try {serverMessenger.send(message);//發送} catch (RemoteException e) {e.printStackTrace();}} }@Override public void onClick(View v) {switch (v.getId()){case R.id.connect_handler:if (!hasBindService) {Intent intent = new Intent();intent.setClassName("com.android.messenger_a", "com.android.messenger_a.ServerWithHandler");bindService(intent, serviceConnection, BIND_AUTO_CREATE);hasBindService = true;}else{if (serverMessenger == null){return;}communicate();}break;case R.id.connect_binder:startActivity(new Intent(this, ClientForBinder.class));break;} }@Override protected void onDestroy() {super.onDestroy();if (serverMessenger != null)unbindService(serviceConnection); }
? 2.ContentProvider(底層同樣是Binder實現):
不只是數據庫,還可以操作文件,對象等,android:authorities 是ContentProvider的唯一標識,通過這個屬性外部應用就可以訪問我們的provider


<providerandroid:authorities="com.android.StudentProvider"android:name="com.android.contentprovider.StudentProvider"android:permission="com.android.CONTENTPROVIDER_READPERMISSSION"android:process=":provider"/>
3:Socket:
學過計算機網絡的對Socket不陌生,所以不需要詳細講述。只需要注意,Android不允許在主線程中請求網絡,而且請求網絡必須要注意聲明相應的permission。然后,在服務器中定義ServerSocket來監聽端口,客戶端使用Socket來請求端口,連通后就可以進行通信
TCP:
當對網絡通訊質量有要求的時候,比如:整個數據要準確無誤的傳遞給對方,這往往用于一些要求可靠的應用,比如HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。 在日常生活中,常見使用TCP協議的應用如下: 瀏覽器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件傳輸?
TCP的優點: 可靠,穩定 TCP的可靠體現在TCP在傳遞數據之前,會有三次握手來建立連接,而且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完后,還會斷開連接用來節約系統資源。
TCP的缺點: 慢,效率低,占用系統資源高,易被攻擊 TCP在傳遞數據之前,要先建連接,這會消耗時間,而且在數據傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,而且要在每臺設備上維護所有的傳輸連接,事實上,每個連接都會占用系統的CPU、內存等硬件資源。 而且,因為TCP有確認機制、三次握手機制,這些也導致TCP容易被人利用,實現DOS、DDOS、CC等攻擊。
UDP:
當對網絡通訊質量要求不高的時候,要求網絡通訊速度能盡量的快,這時就可以使用UDP。 比如,日常生活中,常見使用UDP協議的應用如下: QQ語音 QQ視頻 TFTP
UDP的優點: 快,比TCP稍安全 UDP沒有TCP的握手、確認、窗口、重傳、擁塞控制等機制,UDP是一個無狀態的傳輸協議,所以它在傳遞數據時非常快。沒有TCP的這些機制,UDP較TCP被攻擊者利用的漏洞就要少一些。但UDP也是無法避免攻擊的,比如:UDP Flood攻擊
UDP的缺點: 不可靠,不穩定 因為UDP沒有TCP那些可靠的機制,在數據傳遞時,如果網絡質量不好,就會很容易丟包。
區別:
1、TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發送數據之前不需要建立連接
2、TCP提供可靠的服務。也就是說,通過TCP連接傳送的數據,無差錯,不丟失,不重復,且按序到達;UDP盡最大努力交付,即不保證可靠交付
3、TCP面向字節流,實際上是TCP把數據看成一連串無結構的字節流;UDP是面向報文的UDP沒有擁塞控制,因此網絡出現擁塞不會使源主機的發送速率降低(對實時應用很有用,如IP電話,實時視頻會議等)
4、每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
5、TCP首部開銷20字節;UDP的首部開銷小,只有8個字節
6、TCP的邏輯通信信道是全雙工的可靠信道,UDP則是不可靠信道
4:AIDL(Android Interface definition language):
AIDL通過定義服務端暴露的接口,以提供給客戶端來調用,AIDL使服務器可以并行處理,而Messenger封裝了AIDL之后只能串行運行,所以Messenger一般用作消息傳遞。通過編寫aidl文件來設計想要暴露的接口,編譯后會自動生成響應的java文件,服務器將接口的具體實現寫在Stub中,用iBinder對象傳遞給客戶端,客戶端bindService的時候,用asInterface的形式將iBinder還原成接口,再調用其中的方法。
- AIDL文件中,并不是所有的數據類型都是可以使用的,它支持的數據類型有:
- 基本數據類型(int,long,char,boolean,double等)
- String和CharSequence
- List:只支持ArrayList,而且list中的元素也必須是 AIDL 支持的類型
- Map:只支持HashMap,里面的key和value也必須是AIDL支持的類型
- Parceable:所有實現了 Parceable 接口的對象
- AIDL:所有的 AIDL 接口本身也可以在 AIDL 文件中使用,所以 IBinder 類型也是支持的。
?
Binder :
Binder是Android系統進程間通信(IPC)方式之一,Linux則是通過管道(Pipe)、信號(Signal)和跟蹤(Trace)、插口(Socket)、報文隊列(Message)、共享內存(Share Memory)等來通訊
- Binder Client 只需要知道自己要使用的 Binder 的名字及其在 ServiceManager 中的引用即可獲取該 Binder 的引用,得到引用后就可以像普通方法調用一樣調用 Binder 實體的方法,實現在用戶空間中;
- Binder Server 在生成一個 IBinder 實體時會為其綁定一個名稱并傳遞給 Binder Driver,Binder Driver 會在內核空間中創建相應的 Binder 實體節點和節點引用,并將引用傳遞給 ServiceManager。ServiceManager 會將該 Binder 的名字和引用插入一張數據表中,這樣 Binder Client 就能夠獲取該 Binder 實體的引用,并調用上面的方法,實現在用戶空間中;
- ServiceManager 相當于 DNS服務器,負責映射 Binder 名稱及其引用,其本質同樣是一個標準的 Binder Server,實現在用戶空間中;
- Binder Driver 則相當于一個路由器,實現在內核空間中。
?
?
類裝載器:
(1) 裝載:查找和導入(加載類的二進制數據)Class文件;
(2) 鏈接:把類的二進制數據合并到JRE中;
(a)校驗:檢查載入Class文件數據的正確性;
(b)準備:給類的靜態變量分配存儲空間;
(c)解析:將符號引用轉成直接引用;
(3) 初始化:JVM負責對類進行初始化,對類的靜態變量,靜態代碼塊執行初始化操作,包括字段的賦值
自底向上順序檢查類是否已經加載,自頂向下順序加載類
雙親委派機制:(類在系統創建的時候會有一個flag標簽屬性標識著)
1、當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,首先判斷該類型是否已經被加載,沒有就把類加載請求委派給父類加載器ExtClassLoader去完成。
2、當ExtClassLoader加載一個class時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。
3、如果BootStrapClassLoader加載失敗(例如在$JAVA_HOME/jre/lib里未查找到該class),會使用ExtClassLoader來嘗試加載;
4、若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果AppClassLoader也加載失敗,則會報出異常ClassNotFoundException。
類從被加載到虛擬機內存中開始,到卸載出內存為止,它的整個生命周期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中準備、驗證、解析3個部分統稱為連接(Linking)。
雙親委派模型意義:
-系統類防止內存中出現多份同樣的字節碼
-保證Java程序安全穩定運行
自定義類加載器:
通常情況下,我們都是直接使用系統類加載器。但是,有的時候,我們也需要自定義類加載器。比如應用是通過網絡來傳輸 Java 類的字節碼,為保證安全性,這些字節碼經過了加密處理,這時系統類加載器就無法對其進行加載,這樣則需要自定義類加載器來實現。自定義類加載器一般都是繼承自 ClassLoader 類,從上面對 loadClass 方法來分析來看,我們只需要重寫 findClass 方法即可,最好不要重寫loadClass方法,因為這樣容易破壞雙親委托模式
可見性限制:下層的加載器能夠看到上層加載器中的類,反之則不行,也就是是說委托只能從下到上。
不允許卸載類:類加載器可以加載一個類,但是它不能卸載一個類。但是類加載器可以被刪除或者被創建。
在Android中ClassLoader主要有兩個直接子類,叫做 BaseDexClassLoader 和 SecureClassLoader 。而前者有兩個直接子類是 PathClassLoader 和 DexClassLoader,現在很流行的插件化/熱補丁,其實都是通過DexClassLoader來實現的。具體思路是: 創建一個DexClassLoader,通過反射將前者的DexPathList跟系統的PathClassLoader中的DexPathList合并,就可以實現優先加載我們自己的新類,從而替換舊類中的邏輯了。
?
JVM工作流程:
Loading:文章前面介紹的類加載,將文件系統中的Class文件載入到JVM內存(運行數據區域)
當運行一個JVM示例時,系統將分配給它一塊內存區域(這塊內存區域的大小可以設置的),這一內存區域由JVM自己來管理。從這一塊內存中分出一塊用來存儲一些運行數據,例如創建的對象,傳遞給方法的參數,局部變量,返回值等等。分出來的這一塊就稱為運行數據區域。運行數據區域可以劃分為6大塊:Java棧、程序計數寄存器(PC寄存器)、本地方法棧(Native Method Stack)、Java堆、方法區域、運行常量池(Runtime Constant Pool)
Verifying:檢查載入的類文件是否符合Java規范和虛擬機規范。
Preparing:為這個類分配所需要的內存,確定這個類的屬性、方法等所需的數據結構。
Resolving:將該類常量池中的符號引用都改變為直接引用。(不是很理解)
Initialing:初始化類的局部變量,為靜態域賦值,同時執行靜態初始化塊。
堆中存放的是程序創建的對象或者實例。這個區域對JVM的性能影響很大。垃圾回收機制處理的正是這一塊內存區域
靜態區:編譯時就能確定
棧:運行的時候分配,棧式存儲分配按照先進后出的原則進行分配
堆:在編譯時或運行時,無法確定存儲要求的數據結構的內存分配
5.0
1:Material Design
融合卡片式,立體式的設計風格,強調層次感,動畫,陰影等元素
2:新增art虛擬機
Dalvik:Android4.4及以前使用的都是Dalvik虛擬機,我們知道Apk在打包的過程中會先將java等源碼通過javac編譯成.class文件,但是我們的Dalvik虛擬機只會執行.dex文件,這個時候dx會將.class文件轉換成Dalvik虛擬機執行的.dex文件。Dalvik虛擬機在啟動的時候會先將.dex文件轉換成快速運行的機器碼,又因為65535這個問題,導致我們在應用冷啟動的時候有一個合包的過程,最后導致的一個結果就是我們的app啟動慢,這就是Dalvik虛擬機的JIT特性(Just In Time)
ART:ART虛擬機是在Android5.0才開始使用的Android虛擬機,ART虛擬機必須要兼容Dalvik虛擬機的特性,但是ART有一個很好的特性AOT(ahead of time),這個特性就是我們在安裝APK的時候就將dex直接處理成可直接供ART虛擬機使用的機器碼,ART虛擬機將.dex文件轉換成可直接運行的.oat文件,ART虛擬機天生支持多dex,所以也不會有一個合包的過程,所以ART虛擬機會很大的提升APP冷啟動速度,缺點是APP安裝速度慢,APK占用空間大,因為在APK安裝的時候要生成可運行.oat文件
3:水波紋動畫,CardView,RecyclerView,ToolBar
升級修改:1.當前使用 Ringtone、MediaPlayer 或 Vibrator 類向通知中添加聲音和振動,則移除此代碼,以便系統可以在“優先”模式中正確顯示通知。取而代之的是,使用 Notification.Builder 方法添加聲音和振動
2.Context.bindService() 方法現在需要顯式 Intent,如果提供隱式 intent,將引發異常。為確保應用的安全性,請使用顯式 intent 啟動或綁定 Service,且不要為服務聲明 intent 過濾器
3.默認情況下,系統會阻止混合內容和第三方 Cookie。要允許混合內容和第三方 Cookie,請分別使用 setMixedContentMode() 和 setAcceptThirdPartyCookies() 方法
6.0
1:運行時權限(運行時權限提供給用戶關于應用所需權限更多的相關上下文和可視性,這也讓開發者幫助用戶更好的理解:為什么應用需要所請求的權限,授權將有什么樣的好處,拒絕將有何種不便)
安裝時權限模型(Android5.1以及更早):用戶在應用安裝和更新時,對危險權限授權。但是OEM和運行商預裝的應用將自動預授權。
運行時權限(Android6.0及以后):用戶在應用運行時,對應用授予危險權限。由應用決定何時去申請權限(例如,在應用啟動時或者用戶訪問某個特性時),但必須容許用戶來授予或者拒絕應用對特定權限組的訪問。OEM和運營商可以預裝應用,但是不能對權限進行預授權。
2:指紋識別
之前都是各個廠商自行開發的并沒有系統底層的支持,Android 6.0則在系統層面加入指紋識別,能提供原生指紋識別API,這不但降低了廠商開發指紋識別模塊的成本,最重要的是原生指紋識別將會大大提升安卓手機的指紋識別支付安全性
升級修改:1.在運行時檢查和請求權限。要確定您的應用是否已被授予權限,請調用新增的 checkSelfPermission() 方法
2.Android 6.0 版移除了對 Apache HTTP 客戶端的支持。如果您的應用使用該客戶端,并以 Android 2.3(API 級別 9)或更高版本為目標平臺,請改用 HttpURLConnection 類。此 API 效率更高,因為它可以通過透明壓縮和響應緩存減少網絡使用,并可最大限度降低耗電量,如果要繼續使用可以添加依賴庫實現
3.相機服務變更,相機服務中共享資源的訪問模式已從之前的“先到先得”訪問模式更改為高優先級進程優先的訪問模式
7.0
1:多屏多任務
按住其中一個卡片,然后向上拖動至頂部即可開啟分屏多任務,支持上下分欄和左右分欄,允許拖動中間的分割線調整兩個APP所占的比例
2:通知消息回籠
串行是各數據位按順序進行,只有前面的程序段執行完了才會執行后面的,這樣大量的浪費cpu資源,因為等待時不能做其它事情,并行是多個程序段同時執行,同時執行不是指某個時刻同時執行,除非有多個CPU,而是CPU能把時間分給不同的程序段,一個程序等待時就把資源分配給另外的程序,提高資源利用
3:添加了 Just in Time (JIT) 編譯器,對 ART 進行代碼分析,讓它可以在應用運行時持續提升 Android 應用的性能。 JIT 編譯器對 Android 運行組件當前的 Ahead of Time (AOT) 編譯器進行了補充,有助于提升運行時性能,節省存儲空間,加快應用更新和系統更新速度
4:VR支持,一個新的跨平臺圖形計算庫—Vulkan(Vulkan 是 3D 圖形和渲染的一項開放標準)
升級修改:1.為了提高私有文件的安全性,面向 Android 7.0 或更高版本的應用私有目錄被限制訪問 (0700)。此設置可防止私有文件的元數據泄漏,如嘗試傳遞 file:// URI 會觸發 FileUriExposedException。分享私有文件內容的推薦方法是使用 FileProvider
?
class A {
static{System.out.println("A");}//靜態域
}
靜態域是指靜態域在聲明時被賦值
?
?
Context的數量等于Activity的個數 + Service的個數 + 1,這個1為Application
?
四大組件:
默認情況下,同一個應用程序內的所有組件都是運行在同一個進程中的,大部分應用程序也不會去改變它。不過,如果需要指定某個特定組件所屬的進程,則可以利用manifest?文件來達到目的,manifest文件中的每種組件元素——<activity>、?<service>、?<receiver>和<provider>——都支持定義android:process屬性,用于指定組件運行的進程。<application>元素也支持android:process屬性,用于指定所有組件的默認進程。
activity 顯示界面(顯示的界面都是繼承activity完成的)
service 服務(后臺運行的,service是運行在主線程上的,可以理解為沒有界面的activity)
參考博客:http://www.jianshu.com/p/4c798c91a613
一個原則是Service的onCreate的方法只會被調用一次,就是你無論多少次的 startService 又 bindService,Service只被創建一次,onBind(...)函數是Service基類中的唯一抽象方法,子類都必須重寫實現
startService: onCreate -> onStart -> onDestroy
通過startService啟動后,service會一直無限期運行下去,只有外部調用了stopService()或stopSelf()方法時,該Service才會停止運行并銷毀
bindService:onCreate -> onBind -> onUnbind -> onDestroyed
bindService可以通過IBinder接口獲取Service實例,從而實現在client端直接調用Service中的方法以實現靈活交互,這在通過startService方法啟動中是無法實現的,
Broadcast Receiver 廣播(做廣播,廣播是運行在主線程上的,通知時候用到)
Content Provider 數據通信(數據之間通信,同個程序間數據,或者是不同程序間通信)
?
常用加密:
參考博客:?http://www.cnblogs.com/whoislcj/p/5885006.html
視頻加密:
全部加密耗時長,選擇將文件的前面多少個字節進行加密,這樣播放器就無法識別這個文件的編碼,就無法播放了
des:DES是一種對稱加密算法,就是加密和解密使用相同密鑰的算法。DES加密算法出自IBM的研究,
后來被美國政府正式采用,之后開始廣泛流傳,但是近些年使用越來越少,因為DES使用56位密鑰,以現代計算能力,
24小時內即可被破解。
aes:安全性要高于DES,AES的出現本身就是為了取代DES的,AES具有比DES更好的安全性、效率、靈活性,所以對稱加密優先采用AES。
rsa:公鑰密碼算法,使用長度可以變化的密鑰。RSA是第一個既能用于數據加密也能用于數字簽名的算法,由于RSA算法進行的都是大數計算,使得RSA最快的情況也比DES慢上倍,這是RSA最大的缺陷,所以通常只能用于加密少量數據或者加密密鑰,登陸,支付等接口采用rsa非對稱加密
md5:單向加密算法,是不可逆的一種的加密方式,一般用于驗證一致性,數字簽名,安全訪問認證,網上有關MD5解密的網站數不勝數,破解機制采用窮舉法,就是我們平時說的跑字典,可以多次加密,或者加鹽的方式(隨機生成一串字符串作為鹽,然后進行MD5加密,md5+key),一般密碼和下載的文件會加密md5
加密后最終都會對加密后的二進制數據進行Base64編碼,起到一種二次加密的效果,其實呢Base64從嚴格意義上來說的話不是一種加密算法,而是一種編碼算法
在計算機中任何數據都是按ascii碼存儲的,而ascii碼的128~255之間的值是不可見字符。而在網絡上交換數據時,比如說從A地傳到B地,往往要經過多個路由設備,由于不同的設備對字符的處理方式有一些不同,這樣那些不可見字符就有可能被處理錯誤,這是不利于傳輸的。所以就先把數據先做一個Base64編碼,統統變成可見字符,這樣出錯的可能性就大降低了。
HTTPS一般使用的加密與HASH算法如下:?
非對稱加密算法:RSA,DSA/DSS?
對稱加密算法:AES,RC4,3DES?
HASH算法:MD5,SHA1,SHA256
?
https:
https協議需要到ca申請證書,一般免費證書很少,需要交費。http是超文本傳輸協議,信息是明文傳輸,https 則是具有安全性的ssl加密傳輸協議,http和https使用的是完全不同的連接方式用的端口也不一樣,前者是80,后者是443。http的連接很簡單,是無狀態的HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸,HTTPS協議是在HTTP協議的基礎上,添加了SSL/TLS握手以及數據加密傳輸,也屬于應用層協議。
SSL/TLS協議的基本思路是采用公鑰加密法,客戶端先向服務器端索要公鑰,然后用公鑰加密信息,服務器收到密文后,用自己的私鑰解密,這就是非對稱加密。
?
xmpp:
是一種以 XML 為基礎的開放式實時通信協議,是經由互聯網工程工作小組( IETF)通過的互聯網標準。
優點:
1.開源,容易了解,
2.分布式,XMPP以TCP傳遞XML數據流,沒有中央主服務器。任何人都可以運行自己的XMPP服務器,使個人及組織能夠掌控他們的實時傳訊體驗
3.安全,任何XMPP協議的服務器可以獨立于公眾XMPP網絡
4.基于XML 建立起來的應用具有良好的語義完整性和擴展性,比如將XMPP 綁定到HTTP 而不是TCP,主要用于不能夠持久的維持與服務器TCP 連接的設備
缺點:
1.XMPP協議的方式被編碼為一個單一的長的XML文件,因此無法提供修改二進制數據,Base64是網絡上最常見的用于傳輸8Bit字節代碼的編碼方式之一
服務端一般采用Openfire,Openfire為java開源項目,采用開放的 XMPP 協議,使用 Socket 通訊TCP方式進行通信,單臺服務器可支持上萬并發用戶,搭建分布式云服務器可輕松提供大量并發用戶,還提供了接口,可以自己開發插件。
域名是一個主要的ID,并且是JID中唯一必須的元素。(一個純粹的域名也是一個合法的JID)
?
websocket:
?websocket協議是近些年隨html5發展而誕生的,主要用于解決web服務器與客戶端無法雙向交互的問題。如今已經被W3C收入標準協議。


import de.tavendo.autobahn.WebSocketConnection; import de.tavendo.autobahn.WebSocketException; import de.tavendo.autobahn.WebSocketHandler; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String wsurl = "ws://111.11.11.111:8080/websocketServer"; private static final String TAG = "MainActivity"; private WebSocketConnection mConnect = new WebSocketConnection(); private EditText mContent; private Button mSend; private TextView mText; private EditText mUserName; private EditText mToSb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindObject(); connect(); } /** * 綁定控件 */ private void bindObject() { mContent = (EditText) findViewById(R.id.et_content); mSend = (Button) findViewById(R.id.btn_send); mText = (TextView) findViewById(R.id.tv_test); mUserName = (EditText) findViewById(R.id.et_username); mToSb = (EditText) findViewById(R.id.et_to); mSend.setOnClickListener(this); } /** * websocket連接,接收服務器消息 */ private void connect() { Log.i(TAG, "ws connect...."); try { mConnect.connect(wsurl, new WebSocketHandler() { @Override public void onOpen() { Log.i(TAG, "Status:Connect to " + wsurl); sendUsername(); } @Override public void onTextMessage(String payload) { Log.i(TAG, payload); mText.setText(payload != null ? payload : ""); // mConnect.sendTextMessage("I am android client"); } @Override public void onClose(int code, String reason) { Log.i(TAG, "Connection lost.."); } }); } catch (WebSocketException e) { e.printStackTrace(); } } /** * 發送用戶名給服務器 */ private void sendUsername() { String user = mUserName.getText().toString(); if (user != null && user.length() != 0) mConnect.sendTextMessage(user); else Toast.makeText(getApplicationContext(), "不能為空", Toast.LENGTH_SHORT).show(); } /** * 發送消息 * * @param msg */ private void sendMessage(String msg) { if (mConnect.isConnected()) { mConnect.sendTextMessage(msg); } else { Log.i(TAG, "no connection!!"); } } @Override protected void onDestroy() { super.onDestroy(); mConnect.disconnect(); } @Override public void onClick(View view) { if (view == mSend) { String content = mToSb.getText().toString() + "@" + mContent.getText().toString(); if (content != null && content.length() != 0) sendMessage(content); else Toast.makeText(getApplicationContext(), "不能為空", Toast.LENGTH_SHORT).show(); } } }
?
?http三次握手四次揮手:(參考博客 --?http://uule.iteye.com/blog/2213562)
(1)第一次握手:Client將標志位SYN置為1,隨機產生一個值seq=J,并將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
(2)第二次握手:Server收到數據包后由標志位SYN=1知道Client請求建立連接,Server將標志位SYN和ACK都置為1,ack (number )=J+1,隨機產生一個值seq=K,并將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
(3)第三次握手:Client收到確認后,檢查ack是否為J+1,ACK是否為1,如果正確則將標志位ACK置為1,ack=K+1,并將該數據包發送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨后Client與Server之間可以開始傳輸數據了。
? SYN攻擊:
? 在三次握手過程中,Server發送SYN-ACK之后,收到Client的ACK之前的TCP連接稱為半連接(half-open connect),此時Server處于SYN_RCVD狀態,當收到ACK后,Server轉入ESTABLISHED狀態。SYN攻擊就是Client在短時間內偽造大量不存在的IP地址,并向Server不斷地發送SYN包,Server回復確認包,并等待Client的確認,由于源地址是不存在的,因此,Server需要不斷重發直至超時,這些偽造的SYN包將長時間占用未連接隊列,導致正常的SYN請求因為隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱瘓。SYN攻擊時一種典型的DDOS攻擊,檢測SYN攻擊的方式非常簡單,即當Server上有大量半連接狀態且源IP地址是隨機的,則可以斷定遭到SYN攻擊了,使用如下命令可以讓之現行:
? #netstat -nap | grep SYN_RECV
四次揮手:
(1)第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
(2)第二次揮手:Server收到FIN后,發送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN占用一個序號),Server進入CLOSE_WAIT狀態。
(3)第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
(4)第四次揮手:Client收到FIN后,Client進入TIME_WAIT狀態,接著發送一個ACK給Server,確認序號為收到序號+1,Server進入CLOSED狀態,完成四次揮手。
服務端在LISTEN狀態下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發送給客戶端。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方后,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。
? socket和http的區別:
TCP協議對應于傳輸層,而HTTP協議對應于應用層,從本質上來說,二者沒有可比性。Http協議是建立在TCP協議基礎之上的,當瀏覽器需要從服務器獲取網頁數據的時候,會發出一次Http請求。Http會通過TCP建立起一個到服務器的連接通道,當本次請求需要的數據完畢后,Http會立即將TCP連接斷開,這個過程是很短的。所以Http連接是一種短連接,是一種無狀態的連接。Http就是在每次請求完成后就把TCP連接關了,所以是短連接。
TCP/IP和其他的協議在最初OSI模型中的位置
?
網絡訪問框架:
volley:
請求網絡,緩存隊列沒有就緩存在隊列,然后添加到請求隊列請求網絡,發送線程請求,創建一條緩存線程和四條網絡請求線程(默認)并運行。
retrofit:
使用注意:一般我們下載文件使用okhttp去下載


Call<ResponseBody> call = userBiz.downloadTest(); call.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response){//還是要處理io操作,也就是說你在這里還要另外開線程操作InputStream is = response.body().byteStream();//save file }@Overridepublic void onFailure(Call<ResponseBody> call, Throwable t){} });
?
洪洋的一篇博客:http://blog.csdn.net/lmj623565791/article/details/51304204
使用動態代理模式把API當作一個動態代理對象利用java的proxy類完成創建實例,然后解析注解,構建okhttpclient對象,如果不傳入默認直接new一個對象,然后交給okhttp去請求網絡,返回解析數據


public <T> T create(final Class<T> service) {return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {@Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {});}
版本差異:Retrofit1有默認的轉換器gson,Retrofit2沒有,需要定義?
Retrofit1需要添加okhttp的依賴,Retrofit2中自動添加了,是必須的
baseurl問題:可以直接用url注解傳遞全地址解決
用攔截器 Interceptor 來實現,請求經過攔截器,可以通過攔截器的回調事件做修改重置操作


public class RetrofitHelper {private static final String BASE_URL_USER = "https://www.111.com/";private static final String BASE_URL_PAY = "https://www.222.com/";private static final long TIME_OUT = 5000;private RetrofitService retrofitService;public static RetrofitHelper getInstance() {return SingleHolder.INSTANCE;}private static class SingleHolder {private static final RetrofitHelper INSTANCE = new RetrofitHelper();}private RetrofitHelper() {OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)//添加應用攔截器.addInterceptor(new Interceptor() {@Overridepublic Response intercept(Chain chain) throws IOException {//獲取requestRequest request = chain.request();//獲取request的創建者builderRequest.Builder builder = request.newBuilder();//從request中獲取headers,通過給定的鍵url_nameList<string> headerValues = request.headers("url_name");if (headerValues != null && headerValues.size() > 0) {//如果有這個header,先將配置的header刪除,因此header僅用作app和okhttp之間使用 builder.removeHeader(HttpConfig.HEADER_KEY);//匹配獲得新的BaseUrlString headerValue = headerValues.get(0);HttpUrl newBaseUrl = null;if ("user".equals(headerValue)) {newBaseUrl = HttpUrl.parse(BASE_URL_USER);} else if ("pay".equals(headerValue)) {newBaseUrl = HttpUrl.parse(BASE_URL_PAY);} else{newBaseUrl = oldHttpUrl;}//從request中獲取原有的HttpUrl實例oldHttpUrlHttpUrl oldHttpUrl = request.url(); //重建新的HttpUrl,修改需要修改的url部分HttpUrl newFullUrl = oldHttpUrl.newBuilder().scheme(newBaseUrl.scheme()).host(newBaseUrl.host()).port(newBaseUrl.port()).build();//重建這個request,通過builder.url(newFullUrl).build();//然后返回一個response至此結束修改return chain.proceed(builder.url(newFullUrl).build());} else {return chain.proceed(request);}}}).build();Retrofit retrofit = new Retrofit.Builder().client(okHttpClient)//創建retrofit時的baseUrl可以不需擔心、隨意指定了 .baseUrl(BASE_URL_USER).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();retrofitService = retrofit.create(RetrofitService.class);}}
?
?
okhttp:
參考博客:http://www.jianshu.com/p/27c1554b7fee
整體采用采用門面模式(OkHttpClient)+建造者模式,http連接使用了橋接模式(將HttpEngine和HttpConnection連接起來),Platform(Android和JdkWithJettyBootPlatform兩個平臺的適應)采用了動態代理模式,intercept攔截器使用了責任鏈設計模式
通過OkHttpClient
構建的Request,不過真正請求執行的是?
RealCall(OkHttpClient client, Request originalRequest)
方法,
,然后在RealCall中進行異步或同步任務,返回結果,這個結果會經過攔截器interceptor過程,攔截后返回結果
,各個攔截器會對
respone
進行一些處理,最后會傳到RealCall
類中通過execute
來得到response
OkHttp使用Okio來大大簡化數據的訪問與存儲,Okio是一個增強 java.io 和 java.nio的庫,支持SPDY(通過復用僅僅一條(或幾條)TCP連接,在客戶端與服務器間發送幾十個請求或回應)
?
Glide:
流程:Glide 收到加載及顯示資源的任務,創建 Request 并將它交給RequestManager(任務管理器),Request 啟動 Engine(數據獲取引擎) 去數據源獲取資源(通過 Fetcher(數據獲取器) ),獲取到后 Transformation(圖片處理) 處理后交給 Target(目標)。相對于UniversalImageLoader,Picasso,它還支持video,Gif,SVG格式,支持縮略圖請求,旨在打造更好的列表圖片滑動體驗。Glide有生命周期的概念(主要是對請求進行pause,resume,clear),而且其生命周期與Activity/Fragment的生命周期綁定,支持Volley,OkHttp
Imageloader:
傳入地址,把地址作為key,存入鏈表,鏈表采用lru算法,先進后出,判斷是否有緩存(網絡,磁盤,本地三級緩存),沒有就去請求網絡下載圖片,返回資源
RxJava:
通過觀察者模式來實現的異步操作的庫,代碼簡潔,而且隨著代碼邏輯復雜度增加依然保持簡潔,核心功能之一是事件和對象的變換,比如map,將string對象轉換成bitmap對象返回,flatmap,把也是變換,但是卻是返回observable對象
源碼解析:
訂閱流程:
注意:RxJava的onComplete();與onError(t);只有一個會被執行,通過傳入的parent(也就是執行分發數據的)調用其dispose方法來終止
1、Observable通過調用create創建一個Observable(觀察者)對象
2、調用create時需要傳入一個ObservableOnSubscribe類型的實例參數
3、最終實例參數作為ObservableCreate構造函數的參數傳入,subscribe(訂閱者)訂閱,開始執行事件分發流程?onSubscribe > onNext > onComplete
線程切換:
subscribeOn切換線程,利用裝飾者模式往中間插入包裝類實現線程的切換,(線程狀態會被存入一個叫scheduler調度器的對象變量中),具體點說就是利用裝飾者模式在中間包裝了Observable和Observer,,通過中間增加一個Observable和Observer來實現線程的切換,裝飾者模式的使用貫穿了RxJava2的各處
常用操作符:
zip:把多個事件整合在一起執行
From :將其它的對象或數據結構轉換為Observable
tolist:將一個Observable轉換為一個List
subscribeOn(Schedulers.newThread())//子線程執行
observerOn(AndroidSchedulers.mainThread())//取回結果返回主線程
doOnCancel:取消訂閱
一般用CompositeSubscription來收集Subscription統一取消訂閱
1.0和2.0的區別:
背壓的概念:流速控制的一種策略,指在異步場景中,背觀察者發送事件速度遠快于觀察者的處理速度的情況下,告訴上游的被觀察者降低發送速度的策略,背壓的前提是異步環境,背觀察者和觀察者處于不同環境中,而因為工作量不一樣,事件處理速度也不一樣,這會出現兩種情況,一種是被觀察者速度慢些,觀察者很快,此時觀察者等待被觀察者,沒有問題,第二種情況是被觀察者速度快,觀察者處理慢,處理不過來導致事件堆積,最終擠爆內存,程序奔潰,拋出MissingBackpressureException異常,背壓也因此得出,背壓是響應式拉取,觀察者主動從被觀察者那去拉取數據,被觀察者被動等待通知再發送數據,觀察者可以根據自身情況按需求拉取數據,而不是被動接受,也就相當于告訴上游的被觀察者把速度慢下來,最終達到背壓的策略
Hot and Cold Observables:
Hot指創建后不管是否訂閱就發送事件的Observable,Cold指訂閱之后才開始發送事件的Observable取消訂閱放到了內部的方法中實現了,function增加了throws exception,這樣就不用try catch了
Dialog使用了建造者模式
activity和bind使用了動態代理模式
?
反射:
Class.forName():將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static塊;
ClassLoader.loadClass():只干一件事情,就是將.class文件加載到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。
?
service彈出dialog在.show() 調用之前需要添加dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)),清單文件添加權限:
android.permission.SYSTEM_ALERT_WINDOW
?
框架模式:
MVP:
Model:處理數據 View:顯示數據 Presenter:M層和V層進行通信,M層在獲取到數據之后,把它交給P,P層在交給View層
工作原理:presenter層充當了橋梁的作用,用于操作view層發出的事件傳遞到presenter層中,presenter層去操作model層,并且將數據返回給view層,整個過程中view層和model層完全沒有聯系。
缺陷:使用了接口的方式去連接view層和presenter層,這樣就導致了一個問題,如果你有一個邏輯很復雜的頁面,你的接口會有很多,十幾二十個都不足為奇。想象一個app中有很多個這樣復雜的頁面,維護接口的成本就會非常的大。
View不直接與Model交互,而是通過與Presenter交互來與Model間接交互。?
Presenter與View的交互是通過接口來進行的。?
通常View與Presenter是一對一的,但復雜的View可能綁定多個Presenter來處理邏輯。


//Model層抽象接口 public interface IInfoModel { //從數據提供者獲取數據方法 InfoBean getInfo(); //存入數據提供者方法 void setInfo(InfoBean info); } //View層的抽象接口 public interface IInfoView { //給UI顯示數據的方法 void setInfo(InfoBean info); //從UI取數據的方法 InfoBean getInfo(); } //Presenter public class Presenter { private IInfoModel infoModel; private IInfoView infoView; public Presenter(IInfoView infoView) { this.infoView = infoView; infoModel = new InfoModelImpl(); } //供UI調運 public void saveInfo(InfoBean bean) { infoModel.setInfo(bean); } //供UI調運 public void getInfo() { //通過調用IInfoView的方法來更新顯示,設計模式運用 //類似回調監聽處理 infoView.setInfo(infoModel.getInfo()); } } public class MainActivity extends ActionBarActivity implements IInfoView, View.OnClickListener{ private EditText inputId, inputName, inputAddr; private Button saveBtn, loadBtn; private TextView infoTxt; private Presenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); } private void initData() { presenter = new Presenter(this); inputId = (EditText) findViewById(R.id.id_input); inputName = (EditText) findViewById(R.id.name_input); inputAddr = (EditText) findViewById(R.id.addr_input); saveBtn = (Button) findViewById(R.id.input_confirm); loadBtn = (Button) findViewById(R.id.get_confirm); infoTxt = (TextView) findViewById(R.id.show); saveBtn.setOnClickListener(this); loadBtn.setOnClickListener(this); } @Override public void setInfo(InfoBean info) { StringBuilder builder = new StringBuilder(""); builder.append(info.getId()); builder.append("\n"); builder.append(info.getName()); builder.append("\n"); builder.append(info.getAddress()); infoTxt.setText(builder.toString()); } @Override public InfoBean getInfo() { InfoBean info = new InfoBean(); info.setId(Integer.parseInt(inputId.getText().toString())); info.setName(inputName.getText().toString()); info.setAddress(inputAddr.getText().toString()); return info; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.input_confirm: presenter.saveInfo(getInfo()); break; case R.id.get_confirm: presenter.getInfo(); break; } } }
?
?
MVC:
Model:獲取處理數據 View:XML界面顯示 Contronller:Activity,通過接口通信來協同 View(視圖)和Model(模型)工作,起到了兩者之間的通信作用
工作原理:當用戶出發事件的時候,view層會發送指令到controller層,接著controller去通知model層更新數據,model層更新完數據以后直接顯示在view層上
缺陷:view層和model層是相互可知的,這意味著兩層之間存在耦合,耦合對于一個大型程序來說是非常致命的,因為這表示開發,測試,維護都需要花大量的精力。
? ? View可以與Model直接交互。?
Controller是基于行為的,并且可以被多個View共享。?
可以負責決定顯示哪個View。
?


public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener { private WeatherModel weatherModel; private Dialog loadingDialog; private EditText cityNOInput; private TextView city; private TextView cityNO; private TextView temp; private TextView wd; private TextView ws; private TextView sd; private TextView wse; private TextView time; private TextView njd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); weatherModel = new WeatherModelImpl(); initView(); } /** * 初始化View */ private void initView() { cityNOInput = findView(R.id.et_city_no); city = findView(R.id.tv_city); cityNO = findView(R.id.tv_city_no); temp = findView(R.id.tv_temp); wd = findView(R.id.tv_WD); ws = findView(R.id.tv_WS); sd = findView(R.id.tv_SD); wse = findView(R.id.tv_WSE); time = findView(R.id.tv_time); njd = findView(R.id.tv_njd); findView(R.id.btn_go).setOnClickListener(this); loadingDialog = new ProgressDialog(this); loadingDialog.setTitle(加載天氣中...); } /** * 顯示結果 * * @param weather */ public void displayResult(Weather weather) { WeatherInfo weatherInfo = weather.getWeatherinfo(); city.setText(weatherInfo.getCity()); cityNO.setText(weatherInfo.getCityid()); temp.setText(weatherInfo.getTemp()); wd.setText(weatherInfo.getWD()); ws.setText(weatherInfo.getWS()); sd.setText(weatherInfo.getSD()); wse.setText(weatherInfo.getWSE()); time.setText(weatherInfo.getTime()); njd.setText(weatherInfo.getNjd()); } /** * 隱藏進度對話框 */ public void hideLoadingDialog() { loadingDialog.dismiss(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_go: loadingDialog.show(); weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); break; } } @Override public void onSuccess(Weather weather) { hideLoadingDialog(); displayResult(weather); } @Override public void onError() { hideLoadingDialog(); Toast.makeText(this, 獲取天氣信息失敗, Toast.LENGTH_SHORT).show(); } private <t extends="" view=""> T findView(int id) { return (T) findViewById(id); } }


public interface WeatherModel { void getWeather(String cityNumber, OnWeatherListener listener); } public class WeatherModelImpl implements WeatherModel { @Override public void getWeather(String cityNumber, final OnWeatherListener listener) { /*數據層操作*/ VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, Weather.class, new Response.Listener<weather>() { @Override public void onResponse(Weather weather) { if (weather != null) { listener.onSuccess(weather); } else { listener.onError(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { listener.onError(); } }); } }
?
MVVM:
MVVM最早是由微軟提出的
Model:數據模型層。包含業務邏輯和校驗邏輯 View:屏幕上顯示的UI界面(layout、views) ViewModel:View和Model之間的鏈接橋梁,處理視圖邏輯
缺陷:data binding框架解決了數據綁定的問題,但是view層還是會過重
它和MVP的區別貌似不大,只不過是presenter層換成了viewmodel層,還有一點就是view層和viewmodel層是相互綁定的關系,這意味著當你更新viewmodel層的數據的時候,view層會相應的變動ui
view和viewmodel相互綁定在一起,viewmodel的改變會同步到view層,從而view層作出響應
?
?
?
?
設計模式:
1:裝飾者模式:
動態地給一個對象添加一些額外的職責,在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責


/**Component 被裝飾對象的基類*/ public interface Person {void eat(); } /**ConcreteComponent 具體被裝飾對象*/ public class Man implements Person {public void eat() {System.out.println("吃放");} }/**Decorator 裝飾者*/ public abstract class Decorator implements Person {protected Person person;//把原功能對象添加進來public void setPerson(Person person) {this.person = person;}public void eat() {person.eat();} } /**ConcreteDectrator 具體裝飾者*/ public class ManDecoratorA extends Decorator {public void eat() {super.eat();//不影響原功能 reEat();System.out.println("ManDecoratorA類");}public void reEat() {//添加擴展方法System.out.println("再吃一頓飯");} }public class Test {public static void main(String[] args) {Man man = new Man();ManDecoratorA md1 = new ManDecoratorA();...md1.setPerson(man);md1.eat();} }
2:單例模式:
懶漢式,線程不安全,多線程下不能正常工作


public class Singleton {private volatile static Singleton instance; //聲明成 volatileprivate Singleton (){}public static Singleton getSingleton() {if (instance == null) { synchronized (Singleton.class) {if (instance == null) { instance = new Singleton();}}}return instance;} }
?
懶漢式,線程安全,不高效,每次調用都會


public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance; }
?
雙重檢驗鎖
1,給 instance 分配內存
2,調用 Singleton 的構造函數來初始化成員變量
3,將instance對象指向分配的內存空間(執行完這步 instance 就為非 null 了)
但是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯,所以我們還需要將 instance 變量聲明成 volatile ,使用 volatile 的主要原因是其另一個特性:禁止指令重排序優化。也就是說,在 volatile 變量的賦值操作后面會有一個內存屏障(生成的匯編代碼上),讀操作不會被重排序到內存屏障之前。比如上面的例子,取操作必須在執行完 1-2-3 之后或者 1-3-2 之后,不存在執行到 1-3 然后取到值的情況
但是特別注意在 Java 5 以前的版本使用了 volatile 的雙檢鎖還是有問題的。其原因是 java 5 以前的 JMM (Java 內存模型)是存在缺陷的,即時將變量聲明成 volatile 也不能完全避免重排序,主要是 volatile 變量前后的代碼仍然存在重排序問題。這個 volatile 屏蔽重排序的問題在 Java 5 中才得以修復,所以在這之后才可以放心使用volatile


public class Singleton {private volatile static Singleton instance; //聲明成 volatileprivate Singleton (){}public static Singleton getSingleton() {if (instance == null) { synchronized (Singleton.class) {if (instance == null) { instance = new Singleton();}}}return instance; }}
?
餓漢式 static final field
單例會在加載類后一開始就被初始化,即使客戶端沒有調用 getInstance()方法。


public class Singleton{//類加載時就初始化private static final Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;} }
?
靜態內部類 static nested class,《Effective Java》上所推薦的,SingletonHolder 是私有的,


public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
? 3:代理模式:(我們不希望或是不能直接訪問對象 A,而是通過訪問一個中介對象 B,由 B 去訪問 A 達成目的,這種方式我們就稱為代理)
根據程序運行前代理類是否已經存在,可以將代理分為靜態代理和動態代理。
使用場景:當無法或者不想直接訪問某個對象或者是訪問某個對象存在困難時,可以通過一個代理對象來間接地訪問,為了保證客戶端使用的透明性,委托對象與代理對象需要實現相同的接口
靜態代理模式:


//一般會繼承同一父類或實現同一接口 //委托類 class ClassA {public void getMethod1() {}; } //代理類 public class ClassB {private ClassA a;public ClassB(ClassA a) {this.a = a;}public void getMethod1() {a.getMethod1();};}
動態代理模式:(動態代理其實就是通過反射來生成一個代理)
在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過使用這個類和接口就可以生成動態代理對象。JDK提供的代理只能針對接口做代理
實現步驟:新建委托類? -? 實現InvocationHandler接口,這是負責連接代理類和委托類的中間類必須實現的接口? -??通過Proxy類新建代理類對象
?


public interface User {public void add();}public class UserImp implements User {@Overridepublic void add() {System.out.println("添加功能");}}public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {method.invoke(target, args); //執行被代理target對象的方法return null;}}public void add() {UserImp ui = new UserImp();MyInvocationHandler m = new MyInvocationHandler(ui);User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);u.add(); //權限校驗 添加功能 日志記錄 }
當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用,在調用前后我們也可以添加一些自己的操作
4:門面模式:
將一些復雜的小系統整合起來,為用戶的使用提供一個簡單的接口,統一一個高層接口給用戶使用,門面模式要求一個子系統的外部與其內部的通信必須通過一個統一的門面(Facade)對象進行。


public interface LetterWriter { //寫信的內容 public void writeContent(String content); //寫信的地址 public void writeAddress(String address); //將信放入信封 public void inMailer(); //寄信 public void sendMail(); } public class LetterWriterImpl implements LetterWriter { @Override public void inMailer() { // TODO Auto-generated method stub System.out.println("放入信封"); } @Override public void sendMail() { // TODO Auto-generated method stub System.out.println("發信"); } @Override public void writeAddress(String address) { // TODO Auto-generated method stub System.out.println("寫地址:"+address); } @Override public void writeContent(String content) { // TODO Auto-generated method stub System.out.println("寫內容:"+content); } } //那么此時如果一個用戶想發信:非常簡單 //office.Send("Hello","Good"); public class PostOffice { private LetterWriter writer=new LetterWriterImpl(); //發送郵件 public void Send(String address,String content) { writer.writeContent(content); writer.writeAddress(address); writer.inMailer(); writer.sendMail(); } }
5:責任鏈設計模式:
責任鏈就是從一個起點發起請求,然后沿著任務鏈依次傳遞給每一個節點上的對象,直到有一個節點處理這個請求為止。比如員工要申請一筆資金,會先向組長申請,額度如果在組長的范圍內,組長就批了,組長權限不夠就向主管申請,主管如果也額度不夠就向經理申請。這就形成了個責任鏈。普通員工,就是那個申請的發起者并不需要知道到底最后是誰審批的,他只要拿到錢就行了。這樣就造成了請求者和處理者的解耦。


//抽象的領導--Handler public abstract class Leader {protected Leader nextLeader;public final void handleRequest(int money){if (money<=getLimit()){handle(money);}else {if (nextLeader!=null){nextLeader.handleRequest(money);}else {System.out.println(money+"沒人能批準");}}}public abstract int getLimit();public abstract void handle(int money); } //三個具體的領導--ConcreteHandler public class GroupLeader extends Leader {@Overridepublic int getLimit() {return 5000;}@Overridepublic void handle(int money) {System.out.println(money+"由組長批準");} }public class Director extends Leader {@Overridepublic int getLimit() {return 10000;}@Overridepublic void handle(int money) {System.out.println(money+"由主管批準");} }public class Manager extends Leader {@Overridepublic int getLimit() {return 20000;}@Overridepublic void handle(int money) {System.out.println(money+"由經理批準");} }//員工申請 public class A {public static void main(String[] args) {Leader groupLeader = new GroupLeader();Leader director = new Director();Leader manager = new Manager();groupLeader.nextLeader = director;director.nextLeader = manager;groupLeader.handleRequest(5000);groupLeader.handleRequest(9000);groupLeader.handleRequest(12000);groupLeader.handleRequest(30000);} }
運行結果:
?
Handler:抽象處理者角色,聲明一個處理請求的方法,并保持對下一個處理節點Handler對象的引用。
ConcreteHandler: 具體的處理者,對請求進行處理,如果不處理就講請求轉發給下一個節點上的處理對象。
?
優點
- 降低耦合度,便于拓展,提高代碼靈活性。
- 責任鏈對象互相鏈接,只用想頭部發起請求。
缺點
- 如果責任鏈太長,或者每條鏈判斷處理的時間太長會影響性能。特別是遞歸循環的時候。
- 請求不一定能得到處理,可能會沒有對象處理。
7:工廠模式:
簡單工廠:
由一個工廠對象決定創建出哪一種產品類的實例。


public interface ICar{void GetCar();}public enum CarType{SportCarType = 0,JeepCarType = 1,HatchbackCarType = 2}/// <summary>/// 具體產品類: 跑車/// </summary>public class SportCar : ICar{public void GetCar(){Console.WriteLine("場務把跑車交給范·迪塞爾");}}/// <summary>/// 具體產品類: 越野車/// </summary>public class JeepCar : ICar{public void GetCar(){Console.WriteLine("場務把越野車交給范·迪塞爾");}}/// <summary>/// 具體產品類: 兩箱車/// </summary>public class HatchbackCar : ICar{public void GetCar(){Console.WriteLine("場務把兩箱車交給范·迪塞爾");}}public class Factory{public ICar GetCar(CarType carType){switch (carType){case CarType.SportCarType:return new SportCar();case CarType.JeepCarType:return new JeepCar();case CarType.HatchbackCarType:return new HatchbackCar();default:throw new Exception("愛上一匹野馬,可我的家里沒有草原. 你走吧!");}}}class Program{static void Main(string[] args){ICar car;try{Factory factory = new Factory();Console.WriteLine("范·迪塞爾下一場戲開跑車。");car = factory.GetCar(CarType.SportCarType);car.GetCar();}catch (Exception ex){Console.WriteLine(ex.Message);}}}
- 優點:簡單工廠模式能夠根據外界給定的信息,決定究竟應該創建哪個具體類的對象。明確區分了各自的職責和權力,有利于整個軟件體系結構的優化。
- 缺點:很明顯工廠類集中了所有實例的創建邏輯,容易違反GRASPR的高內聚的責任分配原則
抽象工廠:
抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產品的具體的情況下,創建多個產品族中的產品對象。
抽象工廠模式符合了六大原則中的開閉原則(對于擴展是開放的,對于修改是關閉的,這意味著模塊的行為是可以擴展的)、里氏代換原則(所有引用基類的地方必須能透明地使用其子類的對象)等等
- 優點:
- 抽象工廠模式隔離了具體類的生產,使得客戶并不需要知道什么被創建。
- 當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。
- 增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。
- 缺點:增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類
?


//大致是在簡單工廠基礎上把創建實例這一塊抽象了出來/// 抽象工廠類public abstract class Factorys{/// 抽象方法public abstract Car CreateRedCar();public abstract Car CreateBlueCar();}
?
out of memory:
內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用
內存泄露 memory leak,是指程序在申請內存后,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積后果很嚴重,無論多少內存,遲早會被占光。
memory leak會最終會導致out of memory!
內存泄漏檢測庫:leak canary


public class ExampleApplication extends Application {private RefWatcher refWatcher;@Override public void onCreate() {super.onCreate();refWatcher = LeakCanary.install(this);} }//activity也一樣 public class BaseFragment extends Fragment {@Override public void onDestroy() {super.onDestroy();RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());refWatcher.watch(this);} }
原理分析:
RefWatcher.watch()
會以監控對象來創建一個KeyedWeakReference
弱引用對象- 在
AndroidWatchExecutor
的后臺線程里,來檢查弱引用已經被清除了,如果沒被清除,則執行一次 GC - 如果弱引用對象仍然沒有被清除,說明內存泄漏了,系統就導出 hprof 文件,保存在 app 的文件系統目錄下
?
JS交互:
Android調用JS:


<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Carson_Ho</title><script>// Android需要調用的方法 function callJS(){alert("Android調用了JS的callJS方法");}</script></head> </html>public class MainActivity extends AppCompatActivity {WebView mWebView;Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView =(WebView) findViewById(R.id.webview);WebSettings webSettings = mWebView.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先載入JS代碼// 格式規定為:file:///android_asset/文件名.htmlmWebView.loadUrl("file:///android_asset/javascript.html");button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 必須另開線程進行JS方法調用(否則無法調用)mWebView.post(new Runnable() {@Overridepublic void run() {// 注意調用的JS方法名要對應上// 調用javascript的callJS()方法mWebView.loadUrl("javascript:callJS()");}});}});// 由于設置了彈窗檢驗調用結果,所以需要支持js對話框// webview只是載體,內容的渲染需要使用webviewChromClient類去實現// 通過設置WebChromeClient對象處理JavaScript的對話框//設置響應js 的Alert()函數mWebView.setWebChromeClient(new WebChromeClient() {@Overridepublic boolean onJsAlert(WebView view, String url, String message, final JsResult result) {AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);b.setTitle("Alert");b.setMessage(message);b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {result.confirm();}});b.setCancelable(false);b.create().show();return true;}});} }mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此處為 js 返回的結果 }}); }// Android版本變量 final int version = Build.VERSION.SDK_INT; // 因為該方法在 Android 4.4 版本才可使用,所以使用時需進行版本判斷 if (version < 18) {mWebView.loadUrl("javascript:callJS()"); } else {mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此處為 js 返回的結果 }}); }
JS調用Android:
方法一:


// 繼承自Object類 public class AndroidtoJs extends Object {// 定義JS需要調用的方法// 被JS調用的方法必須加入@JavascriptInterface注解 @JavascriptInterfacepublic void hello(String msg) {System.out.println("JS調用了Android的hello方法");} }<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Carson</title> <script>function callAndroid(){// 由于對象映射,所以調用test對象等于調用Android映射的對象test.hello("js調用了android中的hello方法");}</script></head><body>//點擊按鈕則調用callAndroid函數<button type="button" id="button1" οnclick="callAndroid()"></button></body> </html>public class MainActivity extends AppCompatActivity {WebView mWebView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = (WebView) findViewById(R.id.webview);WebSettings webSettings = mWebView.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 通過addJavascriptInterface()將Java對象映射到JS對象//參數1:Javascript對象名//參數2:Java對象名mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS類對象映射到js的test對象// 加載JS代碼// 格式規定為:file:///android_asset/文件名.htmlmWebView.loadUrl("file:///android_asset/javascript.html");
方法二:


<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Carson_Ho</title><script>function callAndroid(){/*約定的url協議為:js://webview?arg1=111&arg2=222*/document.location = "js://webview?arg1=111&arg2=222";}</script> </head><!-- 點擊按鈕則調用callAndroid()方法 --><body><button type="button" id="button1" οnclick="callAndroid()">點擊調用Android代碼</button></body> </html>public class MainActivity extends AppCompatActivity {WebView mWebView; // Button button; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = (WebView) findViewById(R.id.webview);WebSettings webSettings = mWebView.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 步驟1:加載JS代碼// 格式規定為:file:///android_asset/文件名.htmlmWebView.loadUrl("file:///android_asset/javascript.html");// 復寫WebViewClient類的shouldOverrideUrlLoading方法 mWebView.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {// 步驟2:根據協議的參數,判斷是否是所需要的url// 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的) Uri uri = Uri.parse(url); // 如果url的協議 = 預先約定的 js 協議// 就解析往下解析參數if ( uri.getScheme().equals("js")) {// 如果 authority = 預先約定協議里的 webview,即代表都符合約定的協議// 所以攔截url,下面JS開始調用Android需要的方法if (uri.getAuthority().equals("webview")) {// 步驟3:// 執行JS所需要調用的邏輯System.out.println("js調用了Android的方法");// 可以在協議上帶有參數并傳遞到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();}return true;}return super.shouldOverrideUrlLoading(view, url);}});}}


<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Carson_Ho</title><script>function callAndroid(){/*約定的url協議為:js://webview?arg1=111&arg2=222*/document.location = "js://webview?arg1=111&arg2=222";}</script> </head><!-- 點擊按鈕則調用callAndroid()方法 --><body><button type="button" id="button1" οnclick="callAndroid()">點擊調用Android代碼</button></body> </html>public class MainActivity extends AppCompatActivity {WebView mWebView; // Button button; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = (WebView) findViewById(R.id.webview);WebSettings webSettings = mWebView.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 步驟1:加載JS代碼// 格式規定為:file:///android_asset/文件名.htmlmWebView.loadUrl("file:///android_asset/javascript.html");// 復寫WebViewClient類的shouldOverrideUrlLoading方法 mWebView.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {// 步驟2:根據協議的參數,判斷是否是所需要的url// 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的) Uri uri = Uri.parse(url); // 如果url的協議 = 預先約定的 js 協議// 就解析往下解析參數if ( uri.getScheme().equals("js")) {// 如果 authority = 預先約定協議里的 webview,即代表都符合約定的協議// 所以攔截url,下面JS開始調用Android需要的方法if (uri.getAuthority().equals("webview")) {// 步驟3:// 執行JS所需要調用的邏輯System.out.println("js調用了Android的方法");// 可以在協議上帶有參數并傳遞到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();}return true;}return super.shouldOverrideUrlLoading(view, url);}});}}


<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Carson_Ho</title><script>function clickprompt(){// 調用prompt()var result=prompt("js://demo?arg1=111&arg2=222");alert("demo " + result); }</script> </head><!-- 點擊按鈕則調用clickprompt() --><body><button type="button" id="button1" οnclick="clickprompt()">點擊調用Android代碼</button></body> </html>public class MainActivity extends AppCompatActivity {WebView mWebView; // Button button; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = (WebView) findViewById(R.id.webview);WebSettings webSettings = mWebView.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先加載JS代碼// 格式規定為:file:///android_asset/文件名.htmlmWebView.loadUrl("file:///android_asset/javascript.html");mWebView.setWebChromeClient(new WebChromeClient() {// 攔截輸入框(原理同方式2)// 參數message:代表promt()的內容(不是url)// 參數result:代表輸入框的返回值 @Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {// 根據協議的參數,判斷是否是所需要的url(原理同方式2)// 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的) Uri uri = Uri.parse(message);// 如果url的協議 = 預先約定的 js 協議// 就解析往下解析參數if ( uri.getScheme().equals("js")) {// 如果 authority = 預先約定協議里的 webview,即代表都符合約定的協議// 所以攔截url,下面JS開始調用Android需要的方法if (uri.getAuthority().equals("webview")) {//// 執行JS所需要調用的邏輯System.out.println("js調用了Android的方法");// 可以在協議上帶有參數并傳遞到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();//參數result:代表消息框的返回值(輸入值)result.confirm("js調用了Android的方法成功啦");}return true;}return super.onJsPrompt(view, url, message, defaultValue, result);}// 通過alert()和confirm()攔截的原理相同,此處不作過多講述// 攔截JS的警告框 @Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {return super.onJsAlert(view, url, message, result);}// 攔截JS的確認框 @Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) {return super.onJsConfirm(view, url, message, result);}});}}
?
activity啟動黑屏白屏處理:
參考博客:http://www.cnblogs.com/netcorner/p/4883069.html? 和??http://www.jianshu.com/p/0026a01c6811


//1、設置背景圖Theme<style name="Theme.AppStartLoad" parent="android:Theme"> <item name="android:windowBackground">@drawable/ipod_bg</item> <item name="android:windowNoTitle">true</item> </style>//2、設置透明Theme<style name="Theme.AppStartLoadTranslucent" parent="android:Theme"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> </style>
第一種Theme就是設置一張背景圖。當程序啟動時,首先顯示這張背景圖,避免出現黑屏。(程序啟動快,界面先顯示背景圖,然后再刷新其他界面控件。給人刷新不同步感覺)
第二種Theme是把樣式設置為透明,程序啟動后不會黑屏而是整個透明了,等到界面初始化完才一次性顯示出來。(給人程序啟動慢感覺,界面一次性刷出來,刷新同步)
還有方法也就是禁止此功能的出現,不過不建議這么做,android增加此功能就是為了提升用戶體驗
給Preview Window設置的背景圖如果不做處理,圖片就會一直存在于內存中,所以,當我們進入到歡迎頁的時候,不要忘了把背景圖設置為空
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {//將window的背景圖設置為空getWindow().setBackgroundDrawable(null);
getWindow().setBackgroundDrawableResource(android.R.color.white);super.onCreate(savedInstanceState);}
?
android抽象布局:
1.<include />標簽能夠重用布局文件
2.<merge/>刪減多余的層級,多用于替換FrameLayout或者當一個布局包含另一個時
3.<ViewStub />需要時加載,是一個不可見的,大小為0的View。
1)ViewStub只能Inflate一次,之后ViewStub對象會被置為空。按句話說,某個被ViewStub指定的布局被Inflate后,就不會夠再通過ViewStub來控制它了。
2) ViewStub只能用來Inflate一個布局文件,而不是某個具體的View,當然也可以把View寫在某個布局文件中。
<ViewStub android:id="@+id/viewstub_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_marginTop="10dip" android:layout="@layout/viewstub_demo_text_layout"/>
ViewStub stub = (ViewStub) findViewById(R.id.viewstub_text); stub.inflate(); TextView text = (TextView) findViewById(R.id.viewstub_demo_textview); text.setText("test");
?
Fragment:
Fragment生命周期分析:
1. 當一個fragment被創建的時候,它會經歷以下狀態.
- onAttach()--當fragment被加入到activity時調用(在這個方法中可以獲得所在的activity,不過此方法已經標記為過時)
- onCreate()
- onCreateView()
- onActivityCreated()--當activity的onCreated()方法返回后
2. 當這個fragment對用戶可見的時候,它會經歷以下狀態。
- onStart()
- onResume()
3. 當這個fragment進入“后臺模式”的時候,它會經歷以下狀態。
- onPause()
- onStop()
4. 當這個fragment被銷毀了(或者持有它的activity被銷毀了),它會經歷以下狀態。
- onPause()
- onStop()
- onDestroyView()--fragment中的視圖被移除
- onDestroy()
- onDetach()--fragment和activity分離的時候
?
addToBackStack(null)表示加入返回棧,這樣你點擊返回鍵的時候就會回退到上一個fragment了
activity和fragment之間:
1.可以通過fragment對象直接調用fragment里面的方法
2.通過接口回調傳遞回activity
3.getActivity直接強制轉換activity獲取activity的數據
4.通過構造方法傳遞參數
5.第三方庫
fragment和fragment之間傳遞數據:
1.
// 在Fragment1中創建Fragment2的實例
Fragment2 fragment2 = new Fragment2();
Bundle bundle = new Bundle();
// 傳遞實體類,需要實體類實現Serializable接口
bundle.putSerializable("key_entity", value_Entity);
//設置數據
fragment2.setArgument(bundle);
//調用上面的方法由 fragment1 跳轉到 fragment2
showFragment(Fragment1.this, fragment2);
//在fragment2中onCreateView通過getArgument.getSerializable("key_entity");直接獲取數據
2.通過接口回調
3.通過構造方法傳遞
4.在onActivityCreated方法里通過getActivity().findViewById獲取view刷新(不建議此方法刷新)
?
四大啟動模式:?
standard(默認):每次重新創建實例
singletop:如果在任務的棧頂正好存在該Activity的實例, 就重用該實例,調用其onNewIntent方法,否者就會創建新的實例并放入棧頂(即使棧中已經存在該Activity實例,只要不在棧頂,都會創建實例)。
singletask:若Activity不存在,則會在當前task創建一個新的實例,若存在,則會把task中在其之上的其它Activity destory掉并調用它的onNewIntent方法。
singleinstance:只有一個實例,并且這個實例獨立運行在一個task中,這個task只有這個實例,不允許有別的Activity存在。
singletask傳值問題:
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); }
?
內存優化:
? 圖片壓縮:
照片壓縮,并且保證圖片不失真
1.獲取設備的寬高,根據寬高比例壓縮
2.在判斷大小進行質量壓縮
回收圖片資源
處理一些占用內存大而且聲明周期較長的對象時候,可以盡量應用軟引用和弱引用技術
避免創建不必要的對象
避免經常訪問的類中提供get和set字段的訪問
使用實體類比接口好,比如:Map map1 = new HashMap();??HashMap map2 = new HashMap();,后者能滿足需求盡量使用后者
避免使用枚舉類
內存和磁盤的緩存
?
引用類型:
引用分為四種級別,這四種級別由高到低依次為:強引用>軟引用>弱引用>虛引用。
強引用(strong reference)
如:Object object=new Object(),object就是一個強引用了。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。
軟引用(SoftReference)
只有內存不夠時才回收,常用于緩存;當內存達到一個閥值,GC就會去回收它;
弱引用(WeakReference)???
弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它 所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。?
虛引用(PhantomReference)???
虛引用,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。
? 如果只是想避免OutOfMemory異常的發生,則可以使用軟引用。如果對于應用的性能更在意,想盡快回收一些占用內存比較大的對象,則可以使用弱引用。
?
netcraft:查詢公司服務器系統
1.團隊有多少人,現在負責什么,我來了之后,要做什么。
2.公司的背景是什么,之前做過哪些項目。
3.現有的主要技術框架是什么,開發流程是什么,項目做到了什么程度