Android經常使用庫源碼解析

圖片加載框架比較

共同優勢html

  1. 都對多級緩存、線程池、緩存算法作了處理
  2. 自適應程度高,根據系統性能初始化緩存配置、系統信息變動後動態調整策略。好比根據 CPU 核數肯定最大併發數,根據可用內存肯定內存緩存大小,網絡狀態變化時調整最大併發數等。
  3. 支持多種數據源支持多種數據源,網絡、本地、資源、Assets 等

不一樣點java

  1. Picasso所能實現的功能,Glide都能作,無非是所需的設置不一樣。可是Picasso體積比起Glide小太多。
  2. Glide 不只是一個圖片緩存,它支持 Gif、WebP、縮略圖。Glide 支持加載 Gif 動態圖,而 Picasso 不支持該特性
  3. Fresco在5.0如下的內存優化很是好,代價就是體積也很是的大,按體積算Fresco>Glide>Picasso
  4. UIL能夠算是老牌最火的圖片加載庫了,該做者在項目中說明已經中止了對該項目的維護。這就意味着之後任何的 bug 都不會修復,任何的新特性都不會再繼續開發,因此毫無疑問 UIL 不推薦在項目中使用了。

圖片框架的緩存android

  1. MemorycCache圖片內存緩存。默認使用了 LRU 算法。 
  2. DiskCache圖片磁盤緩存,默認使用LruDiskCache算法,在緩存滿時刪除最近最少使用的圖片

glide源碼
通常看源碼先看他的使用方法,經過使用的方法看對應的代碼。
Glide.with(MainActivity.this).load(url).into(headerImage);git

  • 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 (頻繁增刪、不須要排序)中,而且把最近最少使用的對象在緩存值達到預設定值以前從內存中移除。 github

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協議、以流的形式進行上傳和下載、配置超時時間、IPv六、以及鏈接池等功能。
  • 在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請求,解析響應結果,寫入緩存,並回調主線程。json

爲何說Volley適合數據量小,通訊頻繁的網絡操做
volley中爲了提升請求處理的速度,採用了ByteArrayPool進行內存中的數據存儲的,若是下載大量的數據,這個存儲空間就會溢出,因此不適合大量的數據,可是因爲他的這個存儲空間是內存中分配的,當存儲的時候優是從ByteArrayPool中取出一塊已經分配的內存區域, 沒必要每次存數據都要進行內存分配,而是先查找緩衝池中有無適合的內存區域,若是有,直接拿來用,從而減小內存分配的次數 ,因此他比較適合據量小,通訊量大網絡數據交互狀況。api

Retrofit原理緩存

  • Retrofit 2.0底層依賴OkHttp實現,也就是說Retrofit本質上就是對OkHttp的更進一步封裝,還支持Rxjava。Retrofit和其它Http庫最大區別在於經過大範圍使用註解簡化Http請求(請求方式、請求參數)。
  • 網絡請求的工做本質上是OkHttp完成,而 Retrofit 僅負責網絡請求接口的封裝。
  • Retrofit主要是在create方法中採用動態代理模式實現接口方法
    ,這個過程構建了一個ServiceMethod對象。(擴展apiservice的功能)
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
  • loadServiceMethod(method)方法:解析註解獲取請求方式,參數類型和參數註解拼接請求的連接,當一切都準備好以後會把數據添加到Retrofit的RequestBuilder中。而後當咱們主動發起網絡請求的時候會調用okhttp發起網絡請求,okhttp的配置包括請求方式,URL等。
    new OkHttpCall<>(requestFactory, args, callFactory,responseConverter)

本身寫網絡請求框架服務器

  • volley,okHttp等,這類優秀的框架其底層的實現大部分也是基於系統的 線程池 和 httpClient 或 HttpUrlConnection的網絡請求類框架,Android中是不能在主線程中(又稱UI線程)進行網絡操做的,那麼框架中必不可少地要使用到子線程,可使用簡單的 Thread + Runnable + Handler或者重量級點的AsyncTask。
  • 處理好併發操做,一個應用中每每要進行多線程操做,而Java虛擬機對於一個線程的內存分配大約在1M左右,具體多少要看它執行的任務而定。全部就要使用線程池,例如newFixdThreadPool 能夠控制併發數量,且在整個APP運行過程當中有幾個常駐線程在,避免使用時反覆地new,退出時再銷燬,而 newCacheThreadPool 則會在任務完成後,自動回收線程,它會幫你釋放線程內存,也就不會有常駐線程。
  • 還要注意使接口分離,下降耦合,並且接口可以咱們帶來很大的方便。

okhttp源碼

  • 在構造器中利用建造者模式來構建 OkHttpClient 的對象,OkHttpClient 的構造器中主要是默認的配置。例如:
    1. dispatcher :調度器,⽤於調度多線程發起⽹絡請求,有總請求數和單主機總請求數的控制(64,5)
    2. List protocols :⽀持的應⽤層協議,即 HTTP/1.一、HTTP/2 等
    3. Cache cache :Cache 存儲的配置。默認是沒有,若是須要⽤,得⾃⼰配置出 Cache 存儲的⽂件位置以及存儲空間上限。
    4. boolean followRedirects :遇到重定向的要求是,是否⾃動follow。
    5. 鏈接、讀取、寫入超時
  • 在newCall(Request request) (request是請求參數和URL)的時候,實際上是裏面建立了一個 RealCall 的對象,裏面有execute() 方法。裏面getResponseWithInterceptorChain() ,添加了不少Interceptor,並返回 Response 對象的。
  • RealCall.enqueue() 被調⽤的時候⼤同⼩異,區別在於enqueue() 會使⽤ Dispatcher 的線程池來把請求放在後臺線程進⾏,但實質上使⽤的一樣也是getResponseWithInterceptorChain() ⽅法。
  • getResponseWithInterceptorChain() ⽅法作的事:把全部配置好的Interceptor 放在⼀個 List ⾥,而後做爲參數,建立⼀個RealInterceptorChain 對象,並調用chain.proceed(request) 來
    發起請求和獲取響應。
  • Interceptor 是 OkHttp 最核心的一個東西,它負責攔截請求進行一些額外的處理。Interceptor有:
    1. RetryAndFollowlnterceptor:負責失敗重試、重定向
    2. Bridgelnterceptor負責向服務器發送請求數據,例如頭消息、cookie等等
    3. Cachelnterceptor: 讀取緩存、更新緩存
    4. Connectlnterceptor:負責和服務器創建鏈接的。在這⾥,OkHttp 會建立出⽹絡請求所須要的 TCP 鏈接(若是是 HTTP),或者是建⽴在 TCP 鏈接之上的 TLS 鏈接(若是是 HTTPS),而且會建立出對應的 HttpCodec 對象(⽤於編碼解碼 HTTP 請求)
    5. Networklnterceptor:從服務器讀取響應數據
  • 每個功能都只是一個 Interceptor,它們再鏈接成一個 Interceptor.Chain,環環相扣,最終完成一次網絡請求。
  • 而後是開發者使⽤ addNetworkInterceptor(Interceptor) 所設置的,它們的⾏爲邏輯和使⽤ addInterceptor(Interceptor) 建立的⼀樣,但因爲位置不一樣,因此這⾥建立的 Interceptor 會看到每一個請求和響應的數據(包括重定向以及重試的⼀些中間請求和響應),而且看到的是完整原始數據,⽽不是沒有加 Content-Length 的請求數據,或者 Body尚未被 gzip 解壓的響應數據。多數狀況,這個⽅法不須要被使⽤;
  • 同步請求經過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<RealConnection> 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);
    }
  }

自定義一個攔截器

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原理解析

  1. lifecycleCallbacks監聽Activity的onDestroy方法,正常狀況下activity在onDestroy後須要當即被回收,onActivityDestroyed方法最終會調用RefWatcher.watch方法:
  2. 經過將Activity包裝到WeakReference(弱引用)中,被WeakReference包裝過的Activity對象若是被回收,該WeakReference引用會被放到ReferenceQueue中,經過監測ReferenceQueue裏面的內容就能檢查到Activity是否可以被回收
  3. 若是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<Subscription> ),eventType是咱們方法參數的Class,Subscription中則保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了執行改方法所需的一切),而後post的時候,根據post傳入的參數,去找到匹配的方法,反射調用。數據傳遞是經過handler。

相關文章
相關標籤/搜索