Skip to content

Commit a7266be

Browse files
committedAug 21, 2016
Add the auto token refresh demo.
1 parent bd9f3fb commit a7266be

23 files changed

+878
-5
lines changed
 

‎app/build.gradle

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,26 @@ android {
2121
sourceSets.main {
2222
jniLibs.srcDirs = ['src/main/libs'] // <-- Set your folder here!
2323
}
24-
2524
}
2625

2726
dependencies {
2827
compile fileTree(dir: 'libs', include: ['*.jar'])
29-
testCompile 'junit:junit:4.12'
28+
29+
// view
3030
compile 'com.android.support:appcompat-v7:23.4.0'
31+
32+
3133
compile 'com.jakewharton:butterknife:8.2.1'
3234
apt 'com.jakewharton:butterknife-compiler:8.2.1'
35+
36+
compile 'com.squareup.retrofit2:retrofit:2.1.0'
37+
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
38+
compile 'com.squareup.okhttp3:okhttp:3.4.1'
39+
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
40+
compile 'com.google.code.gson:gson:2.5'
41+
compile 'io.reactivex:rxjava:1.1.9'
42+
compile 'io.reactivex:rxandroid:1.2.1'
43+
44+
// test
45+
testCompile 'junit:junit:4.12'
3346
}

‎app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.lighters.demos">
44

5+
<uses-permission android:name="android.permission.INTERNET"/>
6+
57
<application
68
android:allowBackup="true"
79
android:icon="@mipmap/ic_launcher"
@@ -21,6 +23,8 @@
2123
android:theme="@style/TranslucentTheme"/>
2224

2325
<activity android:name=".jni.JniTestActivity"/>
26+
27+
<activity android:name=".token.TokenTestActivity"/>
2428
</application>
2529

2630
</manifest>

‎app/src/main/java/com/lighters/demos/MainActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.lighters.demos.anim.FirstActivity;
77
import com.lighters.demos.app.base.BaseActivity;
88
import com.lighters.demos.jni.JniTestActivity;
9+
import com.lighters.demos.token.TokenTestActivity;
910

1011
public class MainActivity extends BaseActivity {
1112

@@ -33,4 +34,9 @@ void gotoAnimScreen() {
3334
void gotoJniTest() {
3435
startActivity(new Intent(this, JniTestActivity.class));
3536
}
37+
38+
@OnClick(R.id.btn_token_test)
39+
void gotoTokenTest() {
40+
startActivity(new Intent(this, TokenTestActivity.class));
41+
}
3642
}

‎app/src/main/java/com/lighters/demos/app/base/BaseApplication.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.lighters.demos.app.base;
1818

1919
import android.app.Application;
20+
import android.content.Context;
2021

2122
/**
2223
* Created by david on 16/7/27.
@@ -25,8 +26,16 @@
2526
*/
2627
public class BaseApplication extends Application {
2728

29+
private static Context mContext;
30+
2831
@Override
2932
public void onCreate() {
3033
super.onCreate();
34+
mContext = this;
35+
}
36+
37+
38+
public static Context getContext(){
39+
return mContext;
3140
}
3241
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token;
18+
19+
import android.os.Bundle;
20+
import android.text.TextUtils;
21+
import android.view.View;
22+
import butterknife.OnClick;
23+
import com.lighters.demos.R;
24+
import com.lighters.demos.app.base.BaseActivity;
25+
import com.lighters.demos.token.http.GlobalToken;
26+
import com.lighters.demos.token.http.RetrofitUtil;
27+
import com.lighters.demos.token.http.api.IApiService;
28+
import com.lighters.demos.token.http.api.ResultModel;
29+
import com.lighters.demos.token.http.api.TokenModel;
30+
import rx.Subscriber;
31+
import rx.android.schedulers.AndroidSchedulers;
32+
import rx.schedulers.Schedulers;
33+
34+
/**
35+
* Created by david on 16/8/21.
36+
* Email: huangdiv5@gmail.com
37+
* GitHub: https://github.com/alighters
38+
*/
39+
public class TokenTestActivity extends BaseActivity {
40+
@Override
41+
protected int getLayoutId() {
42+
return R.layout.activity_token_test;
43+
}
44+
45+
@Override
46+
protected void setView(Bundle savedInstanceState) {
47+
48+
}
49+
50+
@Override
51+
protected void initData() {
52+
}
53+
54+
@OnClick(R.id.btn_token_get)
55+
public void onGetTokenClick(View v) {
56+
RetrofitUtil.getInstance()
57+
.get(IApiService.class)
58+
.getToken()
59+
.subscribeOn(Schedulers.io())
60+
.observeOn(AndroidSchedulers.mainThread())
61+
.subscribe(new Subscriber<TokenModel>() {
62+
@Override
63+
public void onCompleted() {
64+
65+
}
66+
67+
@Override
68+
public void onError(Throwable e) {
69+
70+
}
71+
72+
@Override
73+
public void onNext(TokenModel model) {
74+
if (model != null && !TextUtils.isEmpty(model.token)) {
75+
GlobalToken.updateToken(model.token);
76+
}
77+
}
78+
});
79+
}
80+
81+
@OnClick(R.id.btn_request)
82+
public void onRequestClick(View v) {
83+
for (int i = 0; i < 5; i++) {
84+
RetrofitUtil.getInstance()
85+
.getProxy(IApiService.class)
86+
.getResult(GlobalToken.getToken())
87+
.subscribeOn(Schedulers.io())
88+
.observeOn(AndroidSchedulers.mainThread())
89+
.subscribe(new Subscriber<ResultModel>() {
90+
@Override
91+
public void onCompleted() {
92+
93+
}
94+
95+
@Override
96+
public void onError(Throwable e) {
97+
98+
}
99+
100+
@Override
101+
public void onNext(ResultModel model) {
102+
103+
}
104+
});
105+
}
106+
}
107+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http;
18+
19+
/**
20+
* Created by david on 16/8/21.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class GlobalToken {
25+
private static String sToken;
26+
27+
public static synchronized void updateToken(String token) {
28+
sToken = token;
29+
}
30+
31+
public static String getToken() {
32+
return sToken;
33+
}
34+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http;
18+
19+
import com.lighters.demos.token.http.converter.GsonConverterFactory;
20+
import com.lighters.demos.token.http.proxy.ProxyHandler;
21+
import java.lang.reflect.Proxy;
22+
import okhttp3.OkHttpClient;
23+
import okhttp3.logging.HttpLoggingInterceptor;
24+
import retrofit2.Retrofit;
25+
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
26+
27+
/**
28+
* Created by david on 16/8/19.
29+
* Email: huangdiv5@gmail.com
30+
* GitHub: https://github.com/alighters
31+
*/
32+
public class RetrofitUtil {
33+
34+
public static final String API = "http://192.168.56.1:8888/";
35+
36+
private static Retrofit sRetrofit;
37+
private static RetrofitUtil instance;
38+
39+
private final static Object mRetrofitLock = new Object();
40+
41+
private static Retrofit getRetrofit() {
42+
if (sRetrofit == null) {
43+
synchronized (mRetrofitLock) {
44+
if (sRetrofit == null) {
45+
OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder();
46+
47+
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
48+
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
49+
clientBuilder.addInterceptor(httpLoggingInterceptor);
50+
sRetrofit = new Retrofit.Builder().client(clientBuilder.build())
51+
.baseUrl(API)
52+
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
53+
.addConverterFactory(GsonConverterFactory.create())
54+
.build();
55+
}
56+
}
57+
}
58+
return sRetrofit;
59+
}
60+
61+
public static RetrofitUtil getInstance() {
62+
if (instance == null) {
63+
synchronized (RetrofitUtil.class) {
64+
if (instance == null) {
65+
instance = new RetrofitUtil();
66+
}
67+
}
68+
}
69+
return instance;
70+
}
71+
72+
public <T> T get(Class<T> tClass) {
73+
return getRetrofit().create(tClass);
74+
}
75+
76+
@SuppressWarnings("unchecked")
77+
public <T> T getProxy(Class<T> tClass) {
78+
T t = getRetrofit().create(tClass);
79+
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class<?>[] { tClass }, new ProxyHandler(t));
80+
}
81+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.api;
18+
19+
import com.google.gson.annotations.SerializedName;
20+
21+
/**
22+
* Created by david on 16/8/21.
23+
* Email: huangdiv5@gmail.com
24+
* GitHub: https://github.com/alighters
25+
*/
26+
public class ApiModel<T> {
27+
28+
public boolean success;
29+
@SerializedName("error_code") public int errorCode;
30+
31+
public T data;
32+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.api;
18+
19+
/**
20+
* Created by david on 16/8/21.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class ErrorCode {
25+
public static final int TOKEN_NOT_EXIST = 1000;
26+
public static final int TOKEN_INVALID = 1001;
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.api;
18+
19+
import retrofit2.http.GET;
20+
import retrofit2.http.Query;
21+
import rx.Observable;
22+
23+
/**
24+
* Created by david on 16/8/20.
25+
* Email: huangdiv5@gmail.com
26+
* GitHub: https://github.com/alighters
27+
*/
28+
public interface IApiService {
29+
30+
@GET("get_token")
31+
Observable<TokenModel> getToken();
32+
33+
@GET("refresh_token")
34+
Observable<TokenModel> refreshToken();
35+
36+
@GET("request")
37+
Observable<ResultModel> getResult(@Query("token") String token);
38+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.api;
18+
19+
/**
20+
* Created by david on 16/8/20.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class ResultModel {
25+
26+
/**
27+
* result : false
28+
*/
29+
30+
public boolean result;
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.api;
18+
19+
/**
20+
* Created by david on 16/8/20.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class TokenModel {
25+
26+
public String token;
27+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (C) 2015 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.converter;
18+
19+
import com.google.gson.Gson;
20+
import com.google.gson.TypeAdapter;
21+
import com.google.gson.reflect.TypeToken;
22+
import com.lighters.demos.token.http.api.ApiModel;
23+
import java.lang.annotation.Annotation;
24+
import java.lang.reflect.ParameterizedType;
25+
import java.lang.reflect.Type;
26+
import okhttp3.RequestBody;
27+
import okhttp3.ResponseBody;
28+
import retrofit2.Converter;
29+
import retrofit2.Retrofit;
30+
31+
/**
32+
* A {@linkplain Converter.Factory converter} which uses Gson for JSON.
33+
* <p>
34+
* Because Gson is so flexible in the types it supports, this converter assumes that it can handle
35+
* all types. If you are mixing JSON serialization with something else (such as protocol buffers),
36+
* you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
37+
* last to allow the other converters a chance to see their types.
38+
*/
39+
public final class GsonConverterFactory extends Converter.Factory {
40+
/**
41+
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
42+
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
43+
*/
44+
public static GsonConverterFactory create() {
45+
return create(new Gson());
46+
}
47+
48+
/**
49+
* Create an instance using {@code gson} for conversion. Encoding to JSON and
50+
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
51+
*/
52+
public static GsonConverterFactory create(Gson gson) {
53+
return new GsonConverterFactory(gson);
54+
}
55+
56+
private final Gson gson;
57+
58+
private GsonConverterFactory(Gson gson) {
59+
if (gson == null) throw new NullPointerException("gson == null");
60+
this.gson = gson;
61+
}
62+
63+
@Override
64+
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
65+
Type newType = new ParameterizedType() {
66+
@Override
67+
public Type[] getActualTypeArguments() {
68+
return new Type[] { type };
69+
}
70+
71+
@Override
72+
public Type getOwnerType() {
73+
return null;
74+
}
75+
76+
@Override
77+
public Type getRawType() {
78+
return ApiModel.class;
79+
}
80+
};
81+
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(newType));
82+
return new GsonResponseBodyConverter<>(adapter);
83+
}
84+
85+
@Override
86+
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
87+
Annotation[] methodAnnotations, Retrofit retrofit) {
88+
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
89+
return new GsonRequestBodyConverter<>(gson, adapter);
90+
}
91+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (C) 2015 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.converter;
18+
19+
import com.google.gson.Gson;
20+
import com.google.gson.TypeAdapter;
21+
import com.google.gson.stream.JsonWriter;
22+
import java.io.IOException;
23+
import java.io.OutputStreamWriter;
24+
import java.io.Writer;
25+
import java.nio.charset.Charset;
26+
import okhttp3.MediaType;
27+
import okhttp3.RequestBody;
28+
import okio.Buffer;
29+
import retrofit2.Converter;
30+
31+
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
32+
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
33+
private static final Charset UTF_8 = Charset.forName("UTF-8");
34+
35+
private final Gson gson;
36+
private final TypeAdapter<T> adapter;
37+
38+
GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
39+
this.gson = gson;
40+
this.adapter = adapter;
41+
}
42+
43+
@Override
44+
public RequestBody convert(T value) throws IOException {
45+
Buffer buffer = new Buffer();
46+
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
47+
JsonWriter jsonWriter = gson.newJsonWriter(writer);
48+
adapter.write(jsonWriter, value);
49+
jsonWriter.close();
50+
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
51+
}
52+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (C) 2015 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.converter;
18+
19+
import com.google.gson.TypeAdapter;
20+
import com.lighters.demos.token.http.api.ApiModel;
21+
import com.lighters.demos.token.http.api.ErrorCode;
22+
import com.lighters.demos.token.http.exception.TokenInvalidException;
23+
import com.lighters.demos.token.http.exception.TokenNotExistException;
24+
import java.io.IOException;
25+
import okhttp3.ResponseBody;
26+
import retrofit2.Converter;
27+
28+
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {
29+
30+
private final TypeAdapter<T> adapter;
31+
32+
GsonResponseBodyConverter(TypeAdapter<T> adapter) {
33+
this.adapter = adapter;
34+
}
35+
36+
@Override
37+
public Object convert(ResponseBody value) throws IOException {
38+
try {
39+
ApiModel apiModel = (ApiModel) adapter.fromJson(value.charStream());
40+
if (apiModel.errorCode == ErrorCode.TOKEN_NOT_EXIST) {
41+
throw new TokenNotExistException();
42+
} else if (apiModel.errorCode == ErrorCode.TOKEN_INVALID) {
43+
throw new TokenInvalidException();
44+
} else if (!apiModel.success) {
45+
// TODO: 16/8/21 handle the other error.
46+
return null;
47+
} else if (apiModel.success) {
48+
return apiModel.data;
49+
}
50+
} finally {
51+
value.close();
52+
}
53+
return null;
54+
}
55+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.exception;
18+
19+
/**
20+
* Created by david on 16/8/21.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class TokenInvalidException extends RuntimeException {
25+
26+
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.exception;
18+
19+
/**
20+
* Created by david on 16/8/21.
21+
* Email: huangdiv5@gmail.com
22+
* GitHub: https://github.com/alighters
23+
*/
24+
public class TokenNotExistException extends RuntimeException {
25+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (C) 2016 david.wei (lighters)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lighters.demos.token.http.proxy;
18+
19+
import android.text.TextUtils;
20+
import android.util.Log;
21+
import android.widget.Toast;
22+
import com.lighters.demos.app.base.BaseApplication;
23+
import com.lighters.demos.token.http.GlobalToken;
24+
import com.lighters.demos.token.http.RetrofitUtil;
25+
import com.lighters.demos.token.http.api.IApiService;
26+
import com.lighters.demos.token.http.api.TokenModel;
27+
import com.lighters.demos.token.http.exception.TokenInvalidException;
28+
import com.lighters.demos.token.http.exception.TokenNotExistException;
29+
import java.lang.annotation.Annotation;
30+
import java.lang.reflect.InvocationHandler;
31+
import java.lang.reflect.InvocationTargetException;
32+
import java.lang.reflect.Method;
33+
import java.util.Date;
34+
import retrofit2.http.Query;
35+
import rx.Observable;
36+
import rx.Subscriber;
37+
import rx.functions.Func1;
38+
39+
/**
40+
* Created by david on 16/8/21.
41+
* Email: huangdiv5@gmail.com
42+
* GitHub: https://github.com/alighters
43+
*/
44+
public class ProxyHandler implements InvocationHandler {
45+
46+
private final static String TAG = "Token_Proxy";
47+
48+
private final static String TOKEN = "token";
49+
50+
private final static int REFRESH_TOKEN_VALID_TIME = 30;
51+
private static long tokenChangedTime = 0;
52+
private Throwable mRefreshTokenError = null;
53+
private boolean mIsTokenNeedRefresh;
54+
55+
private Object mProxyObject;
56+
57+
public ProxyHandler(Object proxyObject) {
58+
mProxyObject = proxyObject;
59+
}
60+
61+
@Override
62+
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
63+
return Observable.just(null).flatMap(new Func1<Object, Observable<?>>() {
64+
@Override
65+
public Observable<?> call(Object o) {
66+
try {
67+
try {
68+
if (mIsTokenNeedRefresh) {
69+
updateMethodToken(method, args);
70+
}
71+
return (Observable<?>) method.invoke(mProxyObject, args);
72+
} catch (InvocationTargetException e) {
73+
e.printStackTrace();
74+
}
75+
} catch (IllegalAccessException e) {
76+
e.printStackTrace();
77+
}
78+
return null;
79+
}
80+
}).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
81+
@Override
82+
public Observable<?> call(Observable<? extends Throwable> observable) {
83+
return observable.flatMap(new Func1<Throwable, Observable<?>>() {
84+
@Override
85+
public Observable<?> call(Throwable throwable) {
86+
if (throwable instanceof TokenInvalidException) {
87+
return refreshTokenWhenTokenInvalid();
88+
} else if (throwable instanceof TokenNotExistException) {
89+
Toast.makeText(BaseApplication.getContext(), "Token is not existed!!", Toast.LENGTH_SHORT).show();
90+
return Observable.error(throwable);
91+
}
92+
return Observable.error(throwable);
93+
}
94+
});
95+
}
96+
});
97+
}
98+
99+
/**
100+
* Refresh the token when the current token is invalid.
101+
*
102+
* @return Observable
103+
*/
104+
private Observable<?> refreshTokenWhenTokenInvalid() {
105+
synchronized (ProxyHandler.class) {
106+
// Have refreshed the token successfully in the valid time.
107+
if (new Date().getTime() - tokenChangedTime < REFRESH_TOKEN_VALID_TIME) {
108+
mIsTokenNeedRefresh = true;
109+
return Observable.just(true);
110+
} else {
111+
// call the refresh token api.
112+
RetrofitUtil.getInstance().get(IApiService.class).refreshToken().subscribe(new Subscriber<TokenModel>() {
113+
@Override
114+
public void onCompleted() {
115+
116+
}
117+
118+
@Override
119+
public void onError(Throwable e) {
120+
mRefreshTokenError = e;
121+
}
122+
123+
@Override
124+
public void onNext(TokenModel model) {
125+
if (model != null) {
126+
mIsTokenNeedRefresh = true;
127+
tokenChangedTime = new Date().getTime();
128+
GlobalToken.updateToken(model.token);
129+
Log.d(TAG, "Refresh token success, time = " + tokenChangedTime);
130+
}
131+
}
132+
});
133+
if (mRefreshTokenError != null) {
134+
return Observable.error(mRefreshTokenError);
135+
} else {
136+
return Observable.just(true);
137+
}
138+
}
139+
}
140+
}
141+
142+
/**
143+
* Update the token of the args in the method.
144+
*/
145+
private void updateMethodToken(Method method, Object[] args) {
146+
if (mIsTokenNeedRefresh && !TextUtils.isEmpty(GlobalToken.getToken())) {
147+
Annotation[][] annotationsArray = method.getParameterAnnotations();
148+
Annotation[] annotations;
149+
if (annotationsArray != null && annotationsArray.length > 0) {
150+
for (int i = 0; i < annotationsArray.length; i++) {
151+
annotations = annotationsArray[i];
152+
for (Annotation annotation : annotations) {
153+
if (annotation instanceof Query) {
154+
if (TOKEN.equals(((Query) annotation).value())) {
155+
args[i] = GlobalToken.getToken();
156+
}
157+
}
158+
}
159+
}
160+
}
161+
mIsTokenNeedRefresh = false;
162+
}
163+
}
164+
}

‎app/src/main/res/layout/activity_main.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,12 @@
2626
android:gravity="center"
2727
android:text="@string/jni_test"
2828
android:textSize="@dimen/font_size_16"/>
29+
30+
<Button
31+
android:id="@+id/btn_token_test"
32+
android:layout_width="match_parent"
33+
android:layout_height="@dimen/item_normal_height"
34+
android:gravity="center"
35+
android:text="@string/token_test"
36+
android:textSize="@dimen/font_size_16"/>
2937
</LinearLayout>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2016 david.wei (lighters)
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent"
21+
android:orientation="vertical"
22+
android:padding="@dimen/activity_horizontal_margin">
23+
24+
<Button
25+
android:id="@+id/btn_token_get"
26+
android:layout_width="match_parent"
27+
android:layout_height="wrap_content"
28+
android:text="@string/get_token"/>
29+
30+
<Button
31+
android:id="@+id/btn_request"
32+
android:layout_width="match_parent"
33+
android:layout_height="wrap_content"
34+
android:text="@string/request_api"/>
35+
36+
37+
</LinearLayout>

‎app/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,9 @@
33

44
<string name="anim_screen_compat">Screen Anim</string>
55
<string name="jni_test">Jni Test</string>
6+
7+
<!-- token test -->
8+
<string name="token_test">Token Test</string>
9+
<string name="get_token">Get the token</string>
10+
<string name="request_api">Request the api</string>
611
</resources>

‎build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ buildscript {
55
jcenter()
66
}
77
dependencies {
8-
classpath 'com.android.tools.build:gradle:2.1.2'
8+
classpath 'com.android.tools.build:gradle:2.1.3'
99
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
1010

1111
// NOTE: Do not place your application dependencies here; they belong
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Mon Dec 28 10:00:20 PST 2015
1+
#Fri Aug 19 21:37:02 CST 2016
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

0 commit comments

Comments
 (0)
Please sign in to comment.