The previous article (also my first technical article in the brief book ^.^) covered the basic usage and simple encapsulation of the Android Three Musketeers. Some of the encapsulation was only scratched over, and some of the usage was left out.

0x00 First correction:

HTTPS and failed reconnection are supported by OkHttp by default and do not need to be set manually (it is set by default in okHttpClient. Builder), so the initialization of okHttpClient. Builder can be simplified as:

/ / create OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder()
        // Set timeout
        .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
        .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
        .writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)
        / / cookie management
        .cookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(App.getInstance())));Copy the code

0x01 Cookie Persistence Management

This part mainly refers to this article.

  • No persistence

    builder.cookieJar(new CookieJar() {
          private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
    
          @Override
          public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
              cookieStore.put(url, cookies);
          }
    
          @Override
          public List<Cookie> loadForRequest(HttpUrl url) {
              List<Cookie> cookies = cookieStore.get(url);
              returncookies ! =null ? cookies : newArrayList<Cookie>(); }});Copy the code

    This simple implementation, which requires re-login every time you restart the App, is not desirable.

  • With the persistent

    CookieHandler cookieHandler = new CookieManager(
          new PersistentCookieStore(context), CookiePolicy.ACCEPT_ALL);
    builder.cookieJar(new JavaNetCookieJar(cookieHandler));Copy the code

    Two classes appear: JavaNetCookieJar and PersistentCookieStore

    • JavaNetCookieJarOkHttp has already implemented this class for us, we need to introduce the following package:
      compile 'com. Squareup. Okhttp3: okhttp - urlconnection: 3.5.0'Copy the code
    • PersistentCookieStore is a class that implements Cookie persistence using SharedPreferences. See this article for the code implementation. Of course, if you want to implement persistence through a database, you can also encapsulate a similar class to do so.
  • A third party library that encapsulates Cookie persistence (recommended)

    ClearableCookieJar cookieJar = new PersistentCookieJar(
          new SetCookieCache(), new SharedPrefsCookiePersistor(context));
    builder.cookieJar(cookieJar);Copy the code

    The following package needs to be imported:

    compile 'com.github.franmontiel:PersistentCookieJar:v1.0.0Copy the code

0 x02. Interceptors

  • The difference between addInterceptor and addNetworkInterceptor is very clear. OkHttp provides an overview of addNetworkInterceptor and addNetworkInterceptor.





    Paste_Image.png

    The two interceptors are simply the difference in call timing. The early call time of applying interceptors is that the recursion of chain.proceed is earlier, and the corresponding recursive response is later. On the contrary, the network interceptor invokes the request request in a later time, completes the chain.proceed recursive call earlier, and obtains the response earlier. Simply put, the application interceptor is the upper layer, while the network interceptor is the lower layer, and all interceptors are a recursive call from shallow to deep. Specific or have to see the source code.

  • Http headers you can use this interceptor to add a global Header to a Request.

    /** * Created by XiaoFeng on 17/1/18. */
    public class HttpHeaderInterceptor implements Interceptor {
      @Override
      public Response intercept(Chain chain) throws IOException {
          Request original = chain.request();
          Request request = original.newBuilder()
                  .header("User-Agent"."Android, xxx")
                  .header("Accept"."application/json")
                  .header("Content-type"."application/json")
                  .method(original.method(), original.body())
                  .build();
          return chain.proceed(request); }}Copy the code
  • Public parameters mainly refer to this article

    / * * * insert public network request parameters, * < p > * Created by XiaoFengon 2017/1/25.* /public class CommonParamsInterceptor implements Interceptor {
     @Override
     public Response intercept(Chain chain) throws IOException {
         Request request = chain.request(a);if (request.method().equals("GET")) {
             HttpUrl httpUrl = request.url().newBuilder()
                     .addQueryParameter("version"."xxx")
                     .addQueryParameter("device"."Android")
                     .addQueryParameter("timestamp".String.valueOf(System.currentTimeMillis()))
                     .build();
             request = request.newBuilder().url(httpUrl).build();
         } else if (request.method().equals("POST")) {
             if (request.body() instanceof FormBody) {
                 FormBody.Builder bodyBuilder = new FormBody.Builder();
                 FormBody formBody = (FormBody) request.body();
    
                 for (int i = 0; i < formBody.size(); i++) {
                     bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
                 }
    
                 formBody = bodyBuilder
                         .addEncoded("version"."xxx")
                         .addEncoded("device"."Android")
                         .addEncoded("timestamp".String.valueOf(System.currentTimeMillis()))
                         .build();
    
                 request = request.newBuilder().post(formBody).build();
             }
         }
    
         return chain.proceed(request); }}Copy the code
  • Caching strategies

    /** * Created by XiaoFeng on 17/1/17. */
    public class HttpCacheInterceptor implements Interceptor {
     @Override
     public Response intercept(Chain chain) throws IOException {
         Request request = chain.request();
         // The local Cache is always used when there is no network
         if(! NetworkUtil.isNetworkConnected()) { request = request.newBuilder()
                     .cacheControl(CacheControl.FORCE_CACHE)
                     .build();
         }
    
         Response response = chain.proceed(request);
         if (NetworkUtil.isNetworkConnected()) {
             If there is a network, set the cache expiration time to 0 hours
             int maxAge = 0;
             response.newBuilder()
                     .header("Cache-Control"."public, max-age=" + maxAge)
                     .removeHeader("Pragma") // Clear the header, because the server will return some interference if it does not support it
                     .build();
         } else {
             // If there is no network, set the cache expiration timeout period to 4 weeks
             int maxStale = 60 * 60 * 24 * 28;
             response.newBuilder()
                     .header("Cache-Control"."public, only-if-cached, max-stale=" + maxStale)
                     .removeHeader("Pragma")
                     .build();
         }
         returnresponse; }}Copy the code
  • The debugger uses a debugger that is integrated into Chrome from Facebook, and requires the following two libraries:

    compile 'com. Facebook. Stetho: stetho: 1.4.1'
    compile 'com. Facebook. Stetho: stetho - okhttp3:1.4.1'Copy the code

    You can use it in your Application

    Stetho.initializeWithDefaults(this);Copy the code

    How to debug?

  • Open Chrome Browser
  • Address bar inputchrome://inspect
  • After entering the page, go to DevTools -> Devices -> Remote Target on the left, you can find the mobile device you are connected to, click on it and the debugging page will appear. Later, you can study by yourself, not only can debug network request, You can also view persistent files such as databases and SharePreference files in your phone without root.

0x03.fastJSON Parsing library encapsulation

Many of the articles on RetroFit on the web use the default Gson library for the results returned from network requests, which is fine for most people, but for those who are more demanding on performance, the FastJson library is still used for parsing. Here’s how to replace the default Gson library with the FastJson library.

First, the default setting of the Gson library looks like this:

Retrofit retrofit = new Retrofit.Builder().client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL).build(a);Copy the code

Using the FastJson library to replace it looks like this:

Retrofit retrofit = new Retrofit.Builder().client(builder.build())
                .addConverterFactory(FastJsonConvertFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL).build(a);Copy the code

Doesn’t it look like that? Yeah, it just replaces the ConverterFactory.

As for the FastJsonConvertFactory implementation, in fact, is imitation of GsonConverterFactory source code to write, not complex.

There are three main classes:

  1. Factory class: FastJsonConvertFactory, which creates the Request and Response conversion classes respectively.

    /**
    *
    * Created by XiaoFeng on 2017/1/17.
    */
    public class FastJsonConvertFactory extends Converter.Factory {
     public static FastJsonConvertFactory create() {
         return new FastJsonConvertFactory(a); }@Override
     public Converter<? .RequestBody> requestBodyConverter(Type type.Annotation[] parameterAnnotations.Annotation[] methodAnnotations.Retrofit retrofit) {
         return new FastJsonRequestConverter< > (); }@Override
     public Converter<ResponseBody, ?> responseBodyConverter(Type type.Annotation[] annotations.Retrofit retrofit) {
         return new FastJsonResponseConverter< > (type); }}Copy the code
  2. Request conversion class: FastJsonRequestConverter

    /**
    *
    * Created by XiaoFeng on 2017/1/17.
    */
    public class FastJsonRequestConverter<T> implements Converter<T.RequestBody> {
     private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
     private static final Charset UTF_8 = Charset.forName("UTF-8");
    
     @Override
     public RequestBody convert(T value) throws IOException {
         returnRequestBody.create(MEDIA_TYPE, JSON.toJSONBytes(value)); }}Copy the code
  3. The Response transformation class: FastJsonResponseConverter

    /**
    *
    * Created by XiaoFeng on 2017/1/17.
    */
    public class FastJsonResponseConverter<T> implements Converter<ResponseBody.T> {
     private final Type type;
    
     public FastJsonResponseConverter(Type type) {
         this.type = type;
     }
    
     @Override
     public T convert(ResponseBody value) throws IOException {
         BufferedSource buffer = Okio.buffer(value.source());
         String s = buffer.readUtf8();
         buffer.close();
         return JSON.parseObject(s, type); }}Copy the code

    If you want to switch to another third-party JSON parsing library, just follow this script.

0x04. Life cycle

In the last article, some students mentioned RxJava life cycle management to prevent memory leaks, which can be directly used by third-party libraries, see this article. It is generally sufficient to refer to the following two libraries:

compile 'com. Trello: rxlifecycle: 1.0'
compile 'com. Trello: rxlifecycle - components: 1.0'Copy the code

There are two ways to use it: 1. Automatically unsubscribe and use bindToLifecycle. You need to extend to base classes such as RxActivity or RxFragment.

@Override
protected void onStart(a) {
    super.onStart();
    // Using automatic unsubscription, this should determine that the correct time to
    // unsubscribe is onStop (the opposite of onStart).
    Observable.interval(1, TimeUnit.SECONDS)
            .doOnUnsubscribe(new Action0() {
                @Override
                public void call(a) {
                    Log.i(TAG, "Unsubscribing subscription from onStart()"); }})// Since bindToLifecycle is called on onStart, it is automatically unsubscribed on onStop
            .compose(this.<Long>bindToLifecycle())
            .subscribe(new Action1<Long>() {
                @Override
                public void call(Long num) {
                    Log.i(TAG, "Started in onStart(), running until in onStop(): "+ num); }}); }Copy the code

The auto-unsubscribe rule can be seen in the core function below, which means that the call to bindToLifecycle will automatically unsubscribe when the corresponding end-of-life function is called within the same lifecycle.

private static final Func1<ActivityEvent, ActivityEvent> ACTIVITY_LIFECYCLE =
        new Func1<ActivityEvent, ActivityEvent>() {
            @Override
            public ActivityEvent call(ActivityEvent lastEvent) {
                switch (lastEvent) {
                    case CREATE:
                        return ActivityEvent.DESTROY;
                    case START:
                        return ActivityEvent.STOP;
                    case RESUME:
                        return ActivityEvent.PAUSE;
                    case PAUSE:
                        return ActivityEvent.STOP;
                    case STOP:
                        return ActivityEvent.DESTROY;
                    case DESTROY:
                        throw new OutsideLifecycleException("Cannot bind to Activity lifecycle when outside of it.");
                    default:
                        throw new UnsupportedOperationException("Binding to " + lastEvent + " not yet implemented"); }}};Copy the code

2. Manually unsubscribe using bindUntilEvent. You need to extend to base classes such as RxActivity or RxFragment.

@Override
protected void onResume(a) {
    super.onResume();
    // `this.<Long>` is necessary if you're compiling on JDK7 or below.
    // If you're using JDK8+, then you can safely remove it.
    Observable.interval(1, TimeUnit.SECONDS)
            .doOnUnsubscribe(new Action0() {
                @Override
                public void call(a) {
                    Log.i(TAG, "Unsubscribing subscription from onResume()"); }})// Manually set unsubscribe on Activity onDestroy
            .compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY))
            .subscribe(new Action1<Long>() {
                @Override
                public void call(Long num) {
                    Log.i(TAG, "Started in onResume(), running until in onDestroy(): "+ num); }}); }Copy the code

RxActivity/RxFragment can cause problems if there is already a base class that needs to be inherited. Don’t panic, though, we can create our own base class, as RxActivity does.

public abstract class RxActivity extends Activity implements LifecycleProvider<activityevent> {
    private final BehaviorSubject<activityevent> lifecycleSubject = BehaviorSubject.create();

    public RxActivity(a) {}@NonNull
    @CheckResult
    public final Observable<activityevent> lifecycle(a) {
        return this.lifecycleSubject.asObservable();
    }

    @NonNull
    @CheckResult
    public final <t> LifecycleTransformer<t> bindUntilEvent(@NonNull ActivityEvent event) {
        return RxLifecycle.bindUntilEvent(this.lifecycleSubject, event);
    }

    @NonNull
    @CheckResult
    public final <t> LifecycleTransformer<t> bindToLifecycle(a) {
        return RxLifecycleAndroid.bindActivity(this.lifecycleSubject);
    }

    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.lifecycleSubject.onNext(ActivityEvent.CREATE);
    }

    @CallSuper
    protected void onStart(a) {
        super.onStart();
        this.lifecycleSubject.onNext(ActivityEvent.START);
    }

    @CallSuper
    protected void onResume(a) {
        super.onResume();
        this.lifecycleSubject.onNext(ActivityEvent.RESUME);
    }

    @CallSuper
    protected void onPause(a) {
        this.lifecycleSubject.onNext(ActivityEvent.PAUSE);
        super.onPause();
    }

    @CallSuper
    protected void onStop(a) {
        this.lifecycleSubject.onNext(ActivityEvent.STOP);
        super.onStop();
    }

    @CallSuper
    protected void onDestroy(a) {
        this.lifecycleSubject.onNext(ActivityEvent.DESTROY);
        super.onDestroy(); }}Copy the code

I suddenly found that writing articles is really a good way to comb knowledge and learn by myself. It is many times more useful than reading a lot of technical articles without purpose. It is highly recommended that competent students should try to write their own technical blog. ^ ^

Reference: juejin. Cn/post / 684490… Gist.github.com/franmontiel… Juejin. Cn/post / 684490… www.codexiu.cn/android/blo… androidxx.ren/forum.php? M… Juejin. Cn/post / 684490…