Android常用庫源碼解析
圖片加載框架比較
共同優點
都對多級緩存、線程池、緩存算法做了處理
自適應程度高,根據系統性能初始化緩存配置、系統信息變更後動態調整策略。比如根據 CPU 核數確定最大併發數,根據可用內存確定內存緩存大小,網絡狀態變化時調整最大併發數等。
支持多種數據源支持多種數據源,網絡、本地、資源、Assets 等
不同點
Picasso所能實現的功能,Glide都能做,無非是所需的設置不同。但是Picasso體積比起Glide小太多。
Glide 不僅是一個圖片緩存,它支持 Gif、WebP、縮略圖。Glide 支持加載 Gif 動態圖,而 Picasso 不支持該特性
Fresco在5.0以下的內存優化非常好,代價就是體積也非常的大,按體積算Fresco>Glide>Picasso
UIL可以算是老牌最火的圖片加載庫了,該作者在項目中說明已經停止了對該項目的維護。這就意味着以後任何的 bug 都不會修復,任何的新特性都不會再繼續開發,所以毫無疑問 UIL 不推薦在項目中使用了。
圖片框架的緩存
MemorycCache圖片內存緩存。默認使用了 LRU 算法。
DiskCache圖片磁盤緩存,默認使用LruDiskCache算法,在緩存滿時刪除最近最少使用的圖片
glide源碼
一般看源碼先看他的使用方法,通過使用的方法看對應的代碼。
Glide.with(MainActivity.this).load(url).into(headerImage);
with方法把context傳進去,返回GlideBuilder的對應,在這裏做一些初始化操作,比如構建線程池(包括sourceExecutor ,diskCacheExecutor ),緩存大小和緩存器,默認的連接監聽工廠(connectivityMonitorFactory ),Engine對象和RequestManagerRetriever 對象等等。
load(URL)方法沒做什麼事情,主要就是把URL傳進去,獲取RequestBuilder對象。
主要的操作都在into方法裏(在這裏會取lru緩存還是本地緩存,還是沒有,告訴RequestBuilder)。RequestBuilder的into方法裏開啓了線程池進行加載資源。網絡請求是通過url打開連接,返回一個HttpURLConnection對象,進行網絡請求的。加載得資源後轉換到主線程並進行回調(沒注意看這個)。設置給imageview
glide爲什麼有lru還會內存溢出。因爲直接把整個大圖片的整個內存加載進去了。對於大圖可以下載下來,asdrawale來加載,drawable更省內存,Drawable應該不屬於常駐內存的對象,不然的話,不可能不會出現OOM的~~
Glide內部處理了網絡圖片加載的錯位或者閃爍(tag)。
public Request getRequest() {
//本質還是getTag
Object tag = getTag();
Request request = null;
if (tag != null) {
if (tag instanceof Request) {
request = (Request) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view
Glide is targeting");
}
}
return request;
}
@Override
public void setRequest(Request request) {
//本質是setTag
setTag(request);
}
對圖片加載用到了LruCache(最少最近使用)算法
他會把內存控制在一定大小內,超過最大值時會自動回收,這個最大值可以自己定,一個太小的緩存空間,有可能造成圖片頻繁地被釋放和重新加載,這並沒有好處。而一個太大的緩存空間,則有可能還是會引起 java.lang.OutOfMemory 的異常。一般使用最大可用內存的1/8作爲緩存的大小。LruCache的主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap (頻繁增刪、不需要排序)中,並且把最近最少使用的對象在緩存值達到預設定值之前從內存中移除。
public class BitmapCache implements ImageCache {
private LruCache<String, Bitmap> mCache;
public BitmapCache() {
int maxSize = 10 * 1024 * 1024;
mCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
}
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
網絡框架比較
常用網絡庫使用方法
public interface netApi {
@GET("repos/{owner}/{repo}/contributors")
Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
volleyStringRequest();
volleyJsonRequest();
retrofitHttpRequest();
try {
okhttpAsyGet();
OkHttpSyncGet();
} catch (Exception e) {
e.printStackTrace();
}
}
//volley第一步
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
private void volleyStringRequest() {
//volley第二步
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
//volley第三步
mQueue.add(stringRequest);
}
private void volleyJsonRequest() {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.sina.com/sports/101010100.html", null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("TAG", response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
mQueue.add(jsonObjectRequest);
}
//okhttp第一步
private final OkHttpClient client = new OkHttpClient();
public void okhttpAsyGet() throws Exception {
//okhttp第二步
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
//okhttp第三步
okhttp3.Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
}
public void OkHttpSyncGet() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, okhttp3.Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());//只能獲取一次,可以用string保存
}
});
}
public void retrofitHttpRequest() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
netApi repo = retrofit.create(netApi.class);
retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("userName", "path");
call.enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
//response
}
@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {
}
});
}
}
String post(String url, String json) throws IOException {
RequestBody formBody = new FormEncodingBuilder()
.add("platform", "android")
.add("name", "bug")
.add("subject", "XXXXXXXXXXXXXXX")
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
HttpURLConnection和HttpClient。這兩種方式都支持HTTPS協議、以流的形式進行上傳和下載、配置超時時間、IPv6、以及連接池等功能。
在Android 2.2版本之前,HttpClient是最好的選擇。因爲HttpURLConnection有一些bug。比如說對一個可讀的InputStream調用close()方法時,就有可能會導致連接池失效了。那麼我們通常的解決辦法就是直接禁用掉連接池的功能。
在Android 2.3版本及以後,HttpClientHttpURLConnection則是最佳的選擇,HttpURLConnection的API提供的比較簡單,可以更加容易地去使用和擴展它。而且速度快、節省電量。
OkHttp 處理了很多網絡問題:自動重連、會從很多常用的連接問題中自動恢復。如果您的服務器配置了多個IP地址,當第一個IP連接失敗的時候,OkHttp會自動嘗試下一個IP。OkHttp還處理了代理服務器問題和SSL握手失敗問題。
volley的設計目標就是非常適合數據量小,通信量大的客戶端,而對於大數據量的網絡操作,比如說下載文件等,Volley的表現就會非常糟糕。Volley停止了更新,而OkHttp得到了官方的認可,並在不斷優化。因此我最終替換爲了OkHttp
volley原理
主線程中調用RequestQueue的add()方法來添加一條網絡請求,這條請求會先被加入到緩存隊列當中,如果發現可以找到相應的緩存結果就直接讀取緩存並解析,然後回調給主線程。如果在緩存中沒有找到結果,則將這條請求加入到網絡請求隊列中,然後處理髮送HTTP請求,解析響應結果,寫入緩存,並回調主線程。
爲什麼說Volley適合數據量小,通信頻繁的網絡操作
volley中爲了提高請求處理的速度,採用了ByteArrayPool進行內存中的數據存儲的,如果下載大量的數據,這個存儲空間就會溢出,所以不適合大量的數據,但是由於他的這個存儲空間是內存中分配的,當存儲的時候優是從ByteArrayPool中取出一塊已經分配的內存區域, 不必每次存數據都要進行內存分配,而是先查找緩衝池中有無適合的內存區域,如果有,直接拿來用,從而減少內存分配的次數 ,所以他比較適合據量小,通信量大網絡數據交互情況。
Retrofit原理
Retrofit 2.0底層依賴OkHttp實現,也就是說Retrofit本質上就是對OkHttp的更進一步封裝,還支持Rxjava。Retrofit和其它Http庫最大區別在於通過大範圍使用註解簡化Http請求。Retrofit使用註解來描述HTTP請求(請求方式、請求參數)。
內部也是調用okhttp的網絡請求方式
Retrofit主要是在create方法中採用動態代理模式實現接口方法,這個過程構建了一個ServiceMethod對象,根據方法註解獲取請求方式,參數類型和參數註解拼接請求的鏈接,當一切都準備好之後會把數據添加到Retrofit的RequestBuilder中。然後當我們主動發起網絡請求的時候會調用okhttp發起網絡請求,okhttp的配置包括請求方式,URL等在Retrofit的RequestBuilder的build()方法中實現,併發起真正的網絡請求。
網絡請求的工作本質上是OkHttp完成,而 Retrofit 僅負責網絡請求接口的封裝。
自己寫網絡請求框架
volley,okHttp等,這類優秀的框架其底層的實現大部分也是基於系統的 線程池 和 httpClient 或 HttpUrlConnection的網絡請求類框架,Android中是不能在主線程中(又稱UI線程)進行網絡操作的,那麼框架中必不可少地要使用到子線程,可以使用簡單的 Thread + Runnable + Handler或者重量級點的AsyncTask。
處理好併發操作,一個應用中往往要進行多線程操作,而Java虛擬機對於一個線程的內存分配大約在1M左右,具體多少要看它執行的任務而定。所有就要使用線程池,例如newFixdThreadPool 可以控制併發數量,且在整個APP運行過程中有幾個常駐線程在,避免使用時反覆地new,退出時再銷燬,而 newCacheThreadPool 則會在任務完成後,自動回收線程,它會幫你釋放線程內存,也就不會有常駐線程。
還要注意使接口分離,降低耦合,而且接口能夠我們帶來很大的方便。
okhttp源碼
在構造器中利用建造者模式來構建 OkHttpClient 的對象,OkHttpClient 的構造器中主要是默認的配置。在newCall(Request request) (request是請求參數和URL)的時候,其實是裏面創建了一個 RealCall 的對象,裏面有execute() 方法。裏面有getResponseWithInterceptorChain() ,添加了很多Interceptor,並返回 Response 對象的。
Interceptor 是 OkHttp 最核心的一個東西,它負責攔截請求進行一些額外的處理(例如 設置cookie),Interceptor有負責失敗重試、重定向的(RetryAndFollowlnterceptor)、讀取緩存、更新緩存的(Cachelnterceptor)、負責和服務器建立連接的(Connectlnterceptor)、負責向服務器發送請求數據(Bridgelnterceptor)、從服務器讀取響應數據的(Networklnterceptor)。
每一個功能都只是一個 Interceptor,它們再連接成一個 Interceptor.Chain,環環相扣,最終完成一次網絡請求。
根據響應碼判斷是否是重定向(3開頭3xx)。RetryAndFollowUpInterceptor:如果不需要重定向,那麼 followUp 爲空,會釋放資源,返回 response。 若爲重定向就銷燬舊連接,創建新連接,將重定向操作得到的新請求設置給 request。
同步請求通過Call.execute()直接返回當前的Response,而異步請求會把當前的請求Call.enqueue添加(AsyncCall)到請求隊列中,並通過回調(Callback)的方式來獲取最後結果。
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
複用機制:Http中添加了一種KeepAlive機制,當數據傳輸完畢後仍然保持連接,等待下一次請求時直接複用該連接。
ConnectionPool :取到的話複用,沒有取到放到連接池中。
ConnectionPool關鍵代碼:
OkHttp 默認最大併發數 64,單域名最大併發 5,爲了實現請求的併發,Dispatcher 配置了一個線程池,
//線程池,核心線程數爲0,最大線程數爲最大整數,線程空閒存活時間60s,//SynchronousQueue 直接提交策略
private static final Executor executor = new ThreadPoolExecutor(0,
Integer.MAX_VALUE , 60L , TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
//空閒連接的最大連接數
private final int maxIdleConnections;
//保持連接的週期
private final long keepAliveDurationNs;
//雙端隊列,存放具體的連接
private final Deque connections = new ArrayDeque<>();
//用於記錄連接失敗的route
final RouteDatabase routeDatabase = new RouteDatabase();
//構造函數//從這裏可以知道,空閒連接的最大連接數爲5,保持連接的週期是5分鐘
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
同步
Dispatcher會在同步執行任務隊列中記錄當前被執行過得任務Call,同時在當前線程中去執行Call的getResponseWithInterceptorChain()方法,直接獲取當前的返回數據Response;
異步
Dispatcher內部實現了懶加載無邊界限制的線程池方式,同時該線程池採用了SynchronousQueue這種阻塞隊列。異步執行是通過Call.enqueue(Callback responseCallback)來執行,在Dispatcher中添加一個封裝了Callback的Call的匿名內部類Runnable來執行當前的Call。這裏一定要注意的地方這個AsyncCall是Call的匿名內部類。AsyncCall的execute方法仍然會回調到Call的getResponseWithInterceptorChain方法來完成請求,同時將返回數據或者狀態通過Callback來完成。
使用緩存,有多種不同的緩存方式
自定義一個攔截器
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
其他庫
LeakCanary原理解析
lifecycleCallbacks監聽Activity的onDestroy方法,正常情況下activity在onDestroy後需要立即被回收,onActivityDestroyed方法最終會調用RefWatcher.watch方法:
通過將Activity包裝到WeakReference(弱引用)中,被WeakReference包裝過的Activity對象如果被回收,該WeakReference引用會被放到ReferenceQueue中,通過監測ReferenceQueue裏面的內容就能檢查到Activity是否能夠被回收
如果Activity沒有被回收,調用GcTigger.runGc方法運行GC,如果這時候還沒有被回收,那就說明Activity可能已經泄露。
Evenbus是做什麼的?和RXjava有什麼區別?
採用EventBus作爲事件管理,可以跨線程,跨組件通信。 以前我們做組件間的消息分發更新,一般會採用觀察者模式,或者接口數據回調的相關方式。但是這樣的做法雖然可以解決問題,但是組件之間的耦合比較嚴重,而且代碼也不易閱讀和相關維護。爲了解決這樣的問題我們可以使用消息總線EventBus框架。
EventBus是一款針對Android優化的發佈/訂閱事件總線。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,線程之間傳遞消息.優點是開銷小,代碼更優雅。以及將發送者和接收者解耦。
RxJava要比EventBus的應用更廣泛,RxJava裏面幾乎可以做任何事情。做異步、網絡的數據處理,寫出來的代碼比較優雅。
黏性事件
簡單講,就是在發送事件之後再訂閱該事件也能收到該事件,跟黏性廣播類似,但是它只能收到最新的一次消息,比如說在未訂閱之前已經發送了多條黏性消息了,然後再訂閱只能收到最近的一條消息。
EventBus源碼
register(this)就是去當前類,遍歷所有的方法,找到onEvent開頭的然後進行存儲(把匹配的方法最終保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList ),eventType是我們方法參數的Class,Subscription中則保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了執行改方法所需的一切),然後post的時候,根據post傳入的參數,去找到匹配的方法,反射調用。數據傳遞是通過handler。
原文地址https://www.cnblogs.com/sixrain/p/11369003.html