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在注解中规定
还可以指定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(没看懂)
方法也可以用来生命发送表单编码和符合数据
表单编码的数据发送当@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处理自己的序列化。
可以使用@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

再将上面的代码改成这样, 也是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); } }; }
|
下面是输出结果

三、迷之变幻

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

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

这样就简单了
具体详细的可以看上面链接中的博客,就比较全面了
- Funcx:在变换中,使用了Funcx这个方法,这个方法同Actionx一样理解,但是Funcx一些列的方法是带有返回值的
四、线程切换
写代码测试之后,发现RxAndroid 中可以使用observeOn 方法来切换线程,但是还没有摸清subscribeOn方法是干什么用的。
之前以为observeOn是设置Observable的运行线程;而subscribeOn是规定了subscribe的线程
总结:初学到这了,基本上可以拿Rxandroid写一个例子,在写例子的时候在慢慢学习Rx吧
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();
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)) { } 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); App.getInstance().setSp("PHPSESSID", cookieFromResponse); } catch (Exception e) { e.printStackTrace(); }
return response; } else { return response; } } } }
|