RxAndroid and Retrofit 2.0

RxAndroid and Retrofit 2.0

关于Rx的学习我参考了这篇博客,是我在网上看到的博客中写的比较全的
http://blog.csdn.net/meegomeego/article/details/49155989/

Retrofit 2.0还是测试版, 主要是‘领导’说Retrofit 比okHttp 和 Volley要好,今天弄了下,例子是照retrofit官网敲的。
http://square.github.io/retrofit/

设计还在弄设计图,闲的没什么事,翻译一下retrofit的官网吧

Retrofit

A type-safe HTTP client for Android and Java(蛋疼,这句怎么翻译)

Introduction

Retrofit让你的http api 变成java接口

public interface GitHubService { 
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit会生成一个GitHubService接口的实现

Retrofit retrofit = new Retrofit.Builder() 
.baseUrl("https://api.github.com/") 
.build();

GitHubService service = retrofit.create(GitHubService.class);

每一个GitHubService的回调,都会make一个同步或者异步的http请求到远程的webserver

Call<List<Repo>> repos = service.listRepos("octocat");

使用注解去描述http请求

  • url参数支持替换和查询
  • 对象转换到请求体
  • 复合的请求体和文件上传

API Declaration

注解的方法和他的参数怎样处理一个请求

REQUEST METHOD

每一个方法必须有一个http的注解,他提供了一个请求的方法和一个相对的url。一共有5中注解GET, POST, PUT, DELETE, and HEAD。资源的相对url在注解中规定

@GET("users/list")

还可以指定url的查询参数

@GET("users/list?sort=desc")   

URL MANIPULATION

一个请求的url中的参数和blocks可以被动态的替换,这个blocks要被{}包围。这个参数要使用@Path注解,并且使用同一个字符

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

查寻参数也可以被添加

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

对于相对复杂的查询参数也可以使用map

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

REQUEST BODY

使用@Body注解可以指定一个对象作为HTTP请求体。

@POST("users/new")
Call<User> createUser(@Body User user);

这个对象也可以使用一个Retrofit的实例指定的一个转换器转换,如果没有添加这个转换器,就会使用RequestBody(没看懂)

FORM ENCODED AND MULTIPART

方法也可以用来生命发送表单编码和符合数据
表单编码的数据发送当@FormUrlEncoded存在的方法。每个键-值对注释@Field包含名称和对象提供的价值。

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

多部分请求时使用@Multipart存在的方法。部分使用@Part注释声明。

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description")
RequestBody description);

部分部件采用改造的转换器,也可以实现RequestBody处理自己的序列化。

HEADER MANIPULATION

可以使用@Headers注释的方法设置静态头。

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

注意,头不会互相覆盖。具有相同名称的所有头文件都包括在请求

END

后面的说的就不往上粘了,因为我也看不懂

在运行例子的时候遇见了一些问题,当时看了一些其他的博客,GitHubService 是作为参数回掉的,返回值是空,运行的过程提示GitHubService 的返回值不能使空了, 不知道是不是2.0和之前的差别
接下来就是在Rx中使用Retrofit了

按照官网的做法
创建了一个GitHubService,这个接口简单的写了两个方法,第一个方法在RX中使用了Retrofit,要注意的就是每个请求前面都是/

public interface GitHubService
 {
    @GET("/{users}/mobilesiri")
    Observable<Repo> getUser(@Path("users") String user);

    @GET("/users/mobilesiri")
    Call<Repo> getUser2();
}

这里没使用官网上给的api接口,用的是https://api.github.com/users/mobilesiri
官网上面的数据太多了,然后用JsonFromat生成了一个bean 叫 Repo.java

接着就是主要的内容了
baseUrl是请求的通用的部分,这里设定的就是

private static final String URI = "https://api.github.com";

addConverterFactory 这个是设置用Gson转换,也可以自定义, 但自定义这个后面再说吧
addCallAdapterFactory 这个在Rx中使用的话要添加
还有提个坑就是这个依赖,一定要对应,版本不对,网又不好,在这弄了半天

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

Rx也跟最新的版本就行了

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URI)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
GitHubService gitHubService = retrofit.create(GitHubService.class);
Call<Repo> repos2 = gitHubService.getUser2();

repos2.enqueue(new Callback<Repo>() {
                @Override
                public void onResponse(Response<Repo> response,
                    Retrofit retrofit) {
                }

                @Override
                public void onFailure(Throwable t) {
                }
            });

以上是没有用Rx的代码
下面就是在Rx中使用Retrofit的代码了,完成一次网络请求,从实现上来说还是很简单的

gitHubService.getUser("users").subscribeOn(Schedulers.io())
             .observeOn(AndroidSchedulers.mainThread())
             .subscribe(new Action1<Repo>() {
                  @Override
                  public void call(Repo repo) {
                }
            });

subscribeOn 指定的是observe运行线程;observeOn是subscribe的运行线程,呃……
当然subscribe中new 一个 Subscriber去实现onCompleted onError onNext
以上就是在Rx中使用Retrofit做网络请求的基本方法了

——————————————————————————————--

RxAndroid

Observable (被观察者)
Subscribers (订阅者)

Observable 我理解成了事件的发送者,Subscribers为事件的处理,这样方便理解吧……默背十遍,记住这两个概念,省得记混。
下面就照网上的教程敲一个小例子吧!!!

一、尝试

  • 导入rx所需要的包
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
  • 大概的思路是创建一个消息的发送者和接收者,然后将他们链接起来,和大象一样,也是分三步:
    第一个例子啥也不说了,直接上代码
package tiny.rxandrioddemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import rx.Observable;
import rx.Subscriber;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private Observable<String> mObservable;
    private Subscriber<String> mSubscriber;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        createObservable();
        createSubscriber();
        bindEvent();
    }

    private void bindEvent() {
        mObservable.subscribe(mSubscriber);
    }

    private void createSubscriber() {
        mSubscriber = new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable throwable) {
                    }

                    @Override
                    public void onNext(String s) {
                        Log.d(TAG, "onNext: " + s);
                    }
                };
    }

    private void createObservable() {
        mObservable = Observable.create(new Observable.OnSubscribe<String>() {
                    @Override
                    public void call(Subscriber<?super String> subscriber) {
                        subscriber.onNext("haha");
                        subscriber.onCompleted();
                    }
                });
    }
}

二、继续尝试

看到了rx中有from just ……这些东西, 看看这个是干嘛的吧, 之后一顿百度:
rx提供了一些快捷创建事件队列的方式,就上上面一行中提到的just和from了

  • just(T...): 将传入的参数依次发送出来。
  • from(T[])/ from(Iterable<? extends T>): 将传入的数组或 Iterable拆分成具体对象后,依次发送出来。
    依然还是先上代码了,上面的方法简化成下边这样
private void createObservable() {
    mObservable = Observable.just("ha1", "ha2", "ha3");
}

程序调用了三次doNext 一次 onComleted


Paste_Image.png

再将上面的代码改成这样, 也是ok的

private void createObservable(){  
List<String> eventList = new ArrayList<>();    
eventList.add("haha1"); 
eventList.add("haha2");    
eventList.add("haha3");   
mObservable = Observable.from(eventList);
}

三、呃……还是尝试

和上面一样,事件的发送者简化了,那么还想简化一下事件的处理,之后发现了actionx 这个方法,那么就改一下createSubscriber()

private void createSubscriber(){
    mSubscriberAction = new Action1<String>(){ 
        @Override
        public void call(String s){
            Log.d(TAG, "call: " + s);
        }
    };
}

下面是输出结果

Paste_Image.png

三、迷之变幻

  • map(): 事件对象的直接变换,可以将一个对象转变成另一个对象。它是 RxJava 最常用的变换
  • flatMap():这个事件比较复杂,就举个网上的例子来说明吧
    参考了博客http://blog.csdn.net/daditao/article/details/50606228
    首先假设这么一种需求:假设有一个数据结构『学生』,现在需要打印出一组学生的名字。实现方式很简单:
Paste_Image.png

很简单。那么再假设:如果要打印出每个学生所需要修的所有课程的名称呢?(需求的区别在于,每个学生只有一个名字,但却有多个课程。)首先可以这样实现:

Paste_Image.png

我哟啊是不想用for循环来输出也可以这么写

Paste_Image.png

这样就简单了
具体详细的可以看上面链接中的博客,就比较全面了

  • Funcx:在变换中,使用了Funcx这个方法,这个方法同Actionx一样理解,但是Funcx一些列的方法是带有返回值的

四、线程切换

写代码测试之后,发现RxAndroid 中可以使用observeOn 方法来切换线程,但是还没有摸清subscribeOn方法是干什么用的。
之前以为observeOn是设置Observable的运行线程;而subscribeOn是规定了subscribe的线程

总结:初学到这了,基本上可以拿Rxandroid写一个例子,在写例子的时候在慢慢学习Rx吧

大概一个月之前看了看Retrofit进行网络接口, 这把手里的这个应用的网络层改成Retrofit,但是公司的应用,请求的时候都要改header和post,弄了一下,随后就又开始加班了(艹)。之前打的都快忘了,还是趁着空闲的时间先记下来吧。

package tiny.rxandrioddemo.mkmy.net.net;

import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import tiny.rxandrioddemo.App;

public class RetrofitUtil
{
    private static final String TAG = "RetrofitUtil";
    private static final Converter.Factory gsonConverterFactory = GsonConverterFactory.create();
    private static final CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create();
    public static final MediaType JSON
            = MediaType.parse("application/json; charset=utf-8");


    public static Retrofit getRetrofit(String baseUrl)
    {
        return new Retrofit.Builder()
                .client(okHttpClientConfig())
                .baseUrl(baseUrl)
                .addConverterFactory(gsonConverterFactory)
                .addCallAdapterFactory(rxJavaCallAdapterFactory)
                .build();
    }

    private static OkHttpClient okHttpClientConfig()
    {
        return new OkHttpClient.Builder()
                .addInterceptor(new LogInterceptor())
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();
    }

    public void cancel()
    {
    }


    private static class LogInterceptor implements Interceptor
    {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException
        {

            Request originalRequest = chain.request();

            Request.Builder originalRequestBuilder = chain.request().newBuilder();


            //  headers
            String userAgent = System.getProperty("http.agent");
            userAgent = App.getInstance().getChannelName() +
                    "/" + App.getInstance().getVersionName()
                    + " " + userAgent;
            Log.d(TAG, "intercept userAgent : " + userAgent);
            originalRequestBuilder.addHeader("User-Agent", userAgent);

            String session = App.getInstance().getSp("PHPSESSID");
            Log.d(TAG, "intercept session : " + session);
            if (!TextUtils.isEmpty(session))
            {
                //originalRequestBuilder.addHeader("Cookie", "PHPSESSID=ndi4uke0r1thf9m1taimv078u3");
            }
            Log.d(TAG, "intercept body: " + (originalRequest.body() instanceof FormBody));
            if (originalRequest.body() instanceof FormBody)
            {
                FormBody originalBody = (FormBody) originalRequest.body();
                FormBody.Builder newFormBody = new FormBody.Builder();

                Log.d(TAG, "intercept POST sizer: " + originalBody.size());
                for (int i = 0; i < originalBody.size(); i++)
                {
                    Log.d(TAG, "intercept fied: " + originalBody.encodedName(i));
                    if (!originalBody.encodedName(i).equals("nu"))
                        newFormBody.addEncoded(originalBody.encodedName(i), originalBody.encodedValue(i));
                }
                String[] values = App.getInstance().getNetInfo();
                if (!TextUtils.isEmpty(values[0]) && !TextUtils.isEmpty(values[1]))
                {
                    newFormBody.add("sig", values[0]);
                    Log.d(TAG, "intercept sig: " + values[0]);
                    newFormBody.add("timestamp", values[1]);
                    Log.d(TAG, "intercept timestamp: " + values[1]);
                }

                originalRequestBuilder.method(originalRequest.method(), newFormBody.build());
            }


            Request req = originalRequestBuilder.build();
            Log.d(TAG, "intercept: x: " + req.headers().toString());
            Log.d(TAG, "intercept: method: " + req.method());
            Log.d(TAG, "intercept: url: " + req.url());
            Log.d(TAG, "intercept: body: " + req.body());

            okhttp3.Response response = chain.proceed(req);

            if (response.body() != null)
            {
                try
                {
                    String mHeader = response.headers().toString();
                    Log.i(TAG, "intercept: header1: " + mHeader);
                    Pattern pattern = Pattern.compile("Set-Cookie.*?;");
                    Matcher m = pattern.matcher(mHeader);
                    String cookieFromResponse = "";
                    if (m.find())
                    {
                        cookieFromResponse = m.group();
                    }
                    Log.i(TAG, "intercept: header2: " + cookieFromResponse);
                    // 去掉cookie末尾的分号
                    // cookieFromResponse = cookieFromResponse.substring(11, cookieFromResponse.length() - 1);
                    App.getInstance().setSp("PHPSESSID", cookieFromResponse);
                } catch (Exception e)
                {
                    e.printStackTrace();
                }

                return response;
            }
            else
            {
                return response;
            }
        }
    }
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,790评论 25 707
  • 安卓开发领域中,很多重要的问题都有很好的开源解决方案,例如Square公司提供网络请求 OkHttp , Retr...
    aaron688阅读 1,905评论 1 20
  • 简介 刚接触Retrofit的时候,就写了一篇简单的使用介绍:Retrofit 2.0基本使用方法,算是对Retr...
    Whyn阅读 2,838评论 4 24
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 题记:我想看沙漠下暴雨,想看大海亲吻鲨鱼,想看黄昏追逐黎明,想看未来的你。 一直很难忘记的场景:正值长途汽车夜里停...
    散落星尘阅读 416评论 2 5