- RxJava如何與Retrofit結合
- 相同格式的Http請求數據該如何封裝
- 相同格式的Http請求數據統一進行預處理
- 如何取消一個Http請求 -- 觀察者之間的對決,Oberver VS Subscriber
- 一個需要ProgressDialog的Subscriber該有的樣子
1.RxJava如何與Retrofit結合1.1 基本頁面
01
02
03
04
05
06
07
08
09
10
11
12
|
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.2.0' compile 'io.reactivex:rxjava:1.1.0' compile 'io.reactivex:rxandroid:1.1.0' compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' compile 'com.google.code.gson:gson:2.6.2' compile 'com.jakewharton:butterknife:7.0.1' } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<? xml
version = "1.0"
encoding = "utf-8" ?> < RelativeLayout
xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:paddingBottom = "@dimen/activity_vertical_margin" android:paddingLeft = "@dimen/activity_horizontal_margin" android:paddingRight = "@dimen/activity_horizontal_margin" android:paddingTop = "@dimen/activity_vertical_margin" tools:context = ".activity.MainActivity" > < Button android:id = "@+id/click_me_BN" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignParentBottom = "true" android:padding = "5dp" android:text = "點我" android:textSize = "16sp" /> < TextView android:id = "@+id/result_TV" android:layout_width = "match_parent" android:layout_height = "match_parent" android:layout_above = "@id/click_me_BN" android:text = "Hello World!" android:textSize = "16sp" /> </ RelativeLayout > |
01
02
03
04
05
06
07
08
09
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
|
package
com.queen.rxjavaretrofitdemo.activity; import
android.os.Bundle; import
android.support.v7.app.AppCompatActivity; import
android.widget.Button; import
android.widget.TextView; import
com.queen.rxjavaretrofitdemo.R; import
butterknife.Bind; import
butterknife.ButterKnife; import
butterknife.OnClick; public
class MainActivity extends
AppCompatActivity { @Bind (R.id.click_me_BN) Button clickMeBN; @Bind (R.id.result_TV) TextView resultTV; @Override protected
void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind( this ); } @OnClick (R.id.click_me_BN) public
void onClick() { getMovie(); } //進行網絡請求 private
void getMovie(){ } } |
1
|
< uses-permission
android:name = "android.permission.INTERNET" /> |
1.2 只用Retrofit
1
2
3
4
|
public
interface MovieService { @GET ( "top250" ) Call<MovieEntity> getTopMovie( @Query ( "start" )
int start,
@Query ( "count" )
int count); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//進行網絡請求 private
void getMovie(){ String baseUrl =
"https://api.douban.com/v2/movie/" ; Retrofit retrofit =
new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); MovieService movieService = retrofit.create(MovieService. class ); Call<MovieEntity> call = movieService.getTopMovie( 0 ,
10 ); call.enqueue( new
Callback<MovieEntity>() { @Override public
void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) { resultTV.setText(response.body().toString()); } @Override public
void onFailure(Call<MovieEntity> call, Throwable t) { resultTV.setText(t.getMessage()); } }); } |
1
|
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' |
1
2
3
4
5
|
Retrofit retrofit =
new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); |
1
2
3
4
|
public
interface MovieService { @GET ( "top250" ) Observable<MovieEntity> getTopMovie( @Query ( "start" )
int start,
@Query ( "count" )
int count); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
//進行網絡請求 private
void getMovie(){ String baseUrl =
"https://api.douban.com/v2/movie/" ; Retrofit retrofit =
new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); MovieService movieService = retrofit.create(MovieService. class ); movieService.getTopMovie( 0 ,
10 ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( new
Subscriber<MovieEntity>() { @Override public
void onCompleted() { Toast.makeText(MainActivity. this ,
"Get Top Movie Completed" , Toast.LENGTH_SHORT).show(); } @Override public
void onError(Throwable e) { resultTV.setText(e.getMessage()); } @Override public
void onNext(MovieEntity movieEntity) { resultTV.setText(movieEntity.toString()); } }); } |
1.4 將請求過程進行封裝選擇Tag -> step2
01
02
03
04
05
06
07
08
09
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
|
public
class HttpMethods { public
static final
String BASE_URL = "https://api.douban.com/v2/movie/" ; private
static final
int DEFAULT_TIMEOUT = 5 ; private
Retrofit retrofit; private
MovieService movieService; //構造方法私有 private
HttpMethods() { //手動創建一個OkHttpClient並設置超時時間 OkHttpClient.Builder httpClientBuilder =
new OkHttpClient.Builder(); httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); retrofit =
new Retrofit.Builder() .client(httpClientBuilder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); movieService = retrofit.create(MovieService. class ); } //在訪問HttpMethods時創建單例 private
static class
SingletonHolder{ private
static final
HttpMethods INSTANCE = new
HttpMethods(); } //獲取單例 public
static HttpMethods getInstance(){ return
SingletonHolder.INSTANCE; } /** * 用於獲取豆瓣電影Top250的數據 * @param subscriber 由調用者傳過來的觀察者對象 * @param start 起始位置 * @param count 獲取長度 */ public
void getTopMovie(Subscriber<MovieEntity> subscriber,
int start,
int count){ movieService.getTopMovie(start, count) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private
void getMovie(){ subscriber =
new Subscriber<MovieEntity>() { @Override public
void onCompleted() { Toast.makeText(MainActivity. this ,
"Get Top Movie Completed" , Toast.LENGTH_SHORT).show(); } @Override public
void onError(Throwable e) { resultTV.setText(e.getMessage()); } @Override public
void onNext(MovieEntity movieEntity) { resultTV.setText(movieEntity.toString()); } }; HttpMethods.getInstance().getTopMovie(subscriber,
0 , 10 ); } |
2.相同格式的Http請求數據該如何封裝選擇Tag -> step3
1
2
3
4
5
6
|
public
class HttpResult<T> { private
int resultCode; private
String resultMessage; private
T data; } |
01
02
03
04
05
06
07
08
09
10
11
|
public
class HttpResult<T> { //用來模仿resultCode和resultMessage private
int count; private
int start; private
int total; private
String title; //用來模仿Data private
T subjects; } |
3.相同格式的Http請求數據統一進行預處理
選擇Tag -> step4
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
/** * 用來統一處理Http的resultCode,並將HttpResult的Data部分剝離出來返回給subscriber * * @param <T> Subscriber真正需要的數據類型,也就是Data部分的數據類型 */ private
class HttpResultFunc<T> implements
Func1<HttpResult<T>, T>{ @Override public
T call(HttpResult<T> httpResult) { if
(httpResult.getResultCode() != 0 ) { throw
new ApiException(httpResult.getResultCode()); } return
httpResult.getData(); } } |
1
2
3
4
5
6
7
8
9
|
public
void getTopMovie(Subscriber<List<Subject>> subscriber,
int start,
int count){ movieService.getTopMovie(start, count) .map( new
HttpResultFunc<List<Subject>>()) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } |
1
2
3
|
HttpResult<User> //or HttpResult<List<Subject>> |
4.1 取消一個Http請求
01
02
03
04
05
06
07
08
09
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
|
static
final class CallOnSubscribe<T>
implements Observable.OnSubscribe<Response<T>> { private
final Call<T> originalCall; CallOnSubscribe(Call<T> originalCall) { this .originalCall = originalCall; } @Override
public void
call( final Subscriber<?
super Response<T>> subscriber) { // Since Call is a one-shot type, clone it for each new subscriber. final
Call<T> call = originalCall.clone(); // Attempt to cancel the call if it is still in-flight on unsubscription. subscriber.add(Subscriptions.create( new
Action0() { @Override
public void
call() { call.cancel(); } })); try
{ Response<T> response = call.execute(); if
(!subscriber.isUnsubscribed()) { subscriber.onNext(response); } }
catch (Throwable t) { Exceptions.throwIfFatal(t); if
(!subscriber.isUnsubscribed()) { subscriber.onError(t); } return ; } if
(!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } } |
1
2
3
|
public
static Subscription create( final
Action0 unsubscribe) { return
BooleanSubscription.create(unsubscribe); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
final Subscription subscribe( final
Observer<? super
T> observer) { if
(observer instanceof
Subscriber) { return
subscribe((Subscriber<? super
T>)observer); } return
subscribe( new
Subscriber<T>() { @Override public
void onCompleted() { observer.onCompleted(); } @Override public
void onError(Throwable e) { observer.onError(e); } @Override public
void onNext(T t) { observer.onNext(t); } }); } |
1
2
3
4
5
|
public
interface SubscriberOnNextListener<T> { void
onNext(T t); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
class ProgressSubscriber<T> extends
Subscriber<T> { private
SubscriberOnNextListener mSubscriberOnNextListener; private
Context context; public
ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) { this .mSubscriberOnNextListener = mSubscriberOnNextListener; this .context = context; } @Override public
void onStart() { } @Override public
void onCompleted() { Toast.makeText(context,
"Get Top Movie Completed" , Toast.LENGTH_SHORT).show(); } @Override public
void onError(Throwable e) { Toast.makeText(context,
"error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public
void onNext(T t) { mSubscriberOnNextListener.onNext(t); } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private
SubscriberOnNextListener getTopMovieOnNext; @Override protected
void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind( this ); getTopMovieOnNext =
new SubscriberOnNextListener<List<Subject>>() { @Override public
void onNext(List<Subject> subjects) { resultTV.setText(subjects.toString()); } }; } |
1
2
3
4
5
|
private
void getMovie(){ HttpMethods.getInstance().getTopMovie( new
ProgressSubscriber(getTopMovieOnNext, MainActivity. this ),
0 ,
10 ); } |
1
2
3
|
public
interface ProgressCancelListener { void
onCancelProgress(); } |
1
2
3
4
5
6
|
@Override public
void onCancelProgress() { if
(! this .isUnsubscribed()) { this .unsubscribe(); } } |
01
02
03
04
05
06
07
08
09
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
|
public
class ProgressDialogHandler extends
Handler { public
static final
int SHOW_PROGRESS_DIALOG = 1 ; public
static final
int DISMISS_PROGRESS_DIALOG = 2 ; private
ProgressDialog pd; private
Context context; private
boolean cancelable; private
ProgressCancelListener mProgressCancelListener; public
ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener, boolean
cancelable) { super (); this .context = context; this .mProgressCancelListener = mProgressCancelListener; this .cancelable = cancelable; } private
void initProgressDialog(){ if
(pd == null ) { pd =
new ProgressDialog(context); pd.setCancelable(cancelable); if
(cancelable) { pd.setOnCancelListener( new
DialogInterface.OnCancelListener() { @Override public
void onCancel(DialogInterface dialogInterface) { mProgressCancelListener.onCancelProgress(); } }); } if
(!pd.isShowing()) { pd.show(); } } } private
void dismissProgressDialog(){ if
(pd != null ) { pd.dismiss(); pd =
null ; } } @Override public
void handleMessage(Message msg) { switch
(msg.what) { case
SHOW_PROGRESS_DIALOG: initProgressDialog(); break ; case
DISMISS_PROGRESS_DIALOG: dismissProgressDialog(); break ; } } } |
01
02
03
04
05
06
07
08
09
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
|
public
class ProgressSubscriber<T> extends
Subscriber<T> implements
ProgressCancelListener{ private
SubscriberOnNextListener mSubscriberOnNextListener; private
ProgressDialogHandler mProgressDialogHandler; private
Context context; public
ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) { this .mSubscriberOnNextListener = mSubscriberOnNextListener; this .context = context; mProgressDialogHandler =
new ProgressDialogHandler(context,
this , true ); } private
void showProgressDialog(){ if
(mProgressDialogHandler != null ) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget(); } } private
void dismissProgressDialog(){ if
(mProgressDialogHandler != null ) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget(); mProgressDialogHandler =
null ; } } @Override public
void onStart() { showProgressDialog(); } @Override public
void onCompleted() { dismissProgressDialog(); Toast.makeText(context,
"Get Top Movie Completed" , Toast.LENGTH_SHORT).show(); } @Override public
void onError(Throwable e) { dismissProgressDialog(); Toast.makeText(context,
"error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public
void onNext(T t) { mSubscriberOnNextListener.onNext(t); } @Override public
void onCancelProgress() { if
(! this .isUnsubscribed()) { this .unsubscribe(); } } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
void getTopMovie(Subscriber<List<Subject>> subscriber,
int start,
int count){ //原來的樣子 // movieService.getTopMovie(start, count) // .map(new HttpResultFunc<List<Subject>>()) // .subscribeOn(Schedulers.io()) // .unsubscribeOn(Schedulers.io()) // .observeOn(AndroidSchedulers.mainThread()) // .subscribe(subscriber); //修改之後的樣子 Observable observable = movieService.getTopMovie(start, count) .map( new
HttpResultFunc<List<Subject>>()); toSubscribe(observable, subscriber); } //添加線程管理並訂閱 private
void toSubscribe(Observable o, Subscriber s){ o.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(s); } |
1
2
3
4
5
|
{ "resultCode": 0, "resultMessage": "成功", "data": {"user": {}, "orderArray": []} } |