Android -- 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接口

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

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

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder() 
.baseUrl("https://api.github.com/")
.build();

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

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

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

使用注解去描述http请求

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

API Declaration

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

REQUEST METHOD

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

1
@GET("users/list")

还可以指定url的查询参数

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

URL MANIPULATION

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

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

查寻参数也可以被添加

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

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

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

REQUEST BODY

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

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

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

FORM ENCODED AND MULTIPART

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

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

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

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

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

HEADER MANIPULATION

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

1
2
3
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

1
2
3
@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,要注意的就是每个请求前面都是/

1
2
3
4
5
6
7
8
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 叫

1
2
3

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

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

1
2
3
4
5
6
7
addConverterFactory 这个是设置用Gson转换,也可以自定义, 但自定义这个后面再说吧
addCallAdapterFactory 这个在Rx中使用的话要添加
还有提个坑就是这个依赖,一定要对应,版本不对,网又不好,在这弄了半天
```gradle
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也跟最新的版本就行了

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URI)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

1
2
3
4
5
6
7
8
9
10
11
12
13
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的代码了,完成一次网络请求,从实现上来说还是很简单的

1
2
3
4
5
6
7
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去实现

```onError``` ```onNext```
1
2
3
4
5
6
7
8
9
10
11
12
13
以上就是在Rx中使用Retrofit做网络请求的基本方法了

——————————————————————————————--
# RxAndroid
##### Observable (被观察者)
##### Subscribers (订阅者)
Observable 我理解成了事件的发送者,Subscribers为事件的处理,这样方便理解吧……默背十遍,记住这两个概念,省得记混。
下面就照网上的教程敲一个小例子吧!!!
### 一、尝试
* 导入rx所需要的包
```android
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'

  • 大概的思路是创建一个消息的发送者和接收者,然后将他们链接起来,和大象一样,也是分三步:
    第一个例子啥也不说了,直接上代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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拆分成具体对象后,依次发送出来。
    依然还是先上代码了,上面的方法简化成下边这样
    1
    2
    3
    private void createObservable() {
    mObservable = Observable.just("ha1", "ha2", "ha3");
    }

程序调用了三次doNext 一次 onComleted
Paste_Image.png
再将上面的代码改成这样, 也是ok的

1
2
3
4
5
6
7
private void createObservable(){  
List<String> eventList = new ArrayList<>();
eventList.add("haha1");
eventList.add("haha2");
eventList.add("haha3");
mObservable = Observable.from(eventList);
}

三、呃……还是尝试

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

1
2
3
4
5
6
7
8
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,弄了一下,随后就又开始加班了(艹)。之前打的都快忘了,还是趁着空闲的时间先记下来吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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;
}
}
}
}