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。

相关文章
相关标签/搜索