params,
53 | @Nullable JsonObject jsonParams) {
54 | mBuilder = createBuilder();
55 | mBuilder.method = method;
56 | mBuilder.url = url;
57 | mBuilder.params = params;
58 | mBuilder.jsonParams = jsonParams;
59 | }
60 |
61 | /**
62 | * 额外设置的header,出去登录相关的
63 | */
64 | public GlintHttp setHeader(@NonNull Headers.Builder headers) {
65 | mBuilder.headers = headers;
66 | return this;
67 | }
68 |
69 | /**
70 | * 增加cookie到头部信息
71 | *
72 | * @param cookie 登录返回的cookie
73 | */
74 | public GlintHttp addCookie(@NonNull String cookie) {
75 | mBuilder.cookie = cookie;
76 | return this;
77 | }
78 |
79 | /**
80 | * 设置是否使用通用签名,默认是
81 | *
82 | * @param signature 使用通用签名
83 | */
84 | public GlintHttp signature(boolean signature) {
85 | mBuilder.signature = signature;
86 | return this;
87 | }
88 |
89 | /**
90 | * 设置是否使用标准化序列化,默认否
91 | */
92 | public GlintHttp standardDeserialize(boolean standardDeserialize) {
93 | mBuilder.standardDeserialize = standardDeserialize;
94 | return this;
95 | }
96 |
97 | /**
98 | * 默认重试20次
99 | *
100 | * @param retryOnConnectionFailure 失败后重试,默认是true
101 | */
102 | public GlintHttp retryOnConnectionFailure(boolean retryOnConnectionFailure) {
103 | mBuilder.retryOnConnectionFailure = retryOnConnectionFailure;
104 | return this;
105 | }
106 |
107 | /**
108 | * 设置是否使用主线程
109 | *
110 | * @param mainThread 是否使用主线程
111 | */
112 | public GlintHttp mainThread(boolean mainThread) {
113 | mBuilder.mainThread = mainThread;
114 | return this;
115 | }
116 |
117 | /**
118 | * 结果不是Json
119 | */
120 | public GlintHttp notJson(boolean notJson) {
121 | mBuilder.notJson = notJson;
122 | return this;
123 | }
124 |
125 | /**
126 | * 设置其他配置
127 | *
128 | * @param otherBuilder 其他配置
129 | */
130 | public GlintHttp otherBuilder(@NonNull Bundle otherBuilder) {
131 | mBuilder.otherBuilder = otherBuilder;
132 | return this;
133 | }
134 |
135 | /**
136 | * 设置文件的协议
137 | *
138 | * @param mimeType 文件协议
139 | */
140 | public GlintHttp setMimeType(String mimeType) {
141 | mBuilder.mimeType = mimeType;
142 | return this;
143 | }
144 |
145 | /**
146 | * 使用自定义Module,可做高级操作
147 | *
148 | * @param module 自定义Module
149 | */
150 | public GlintHttp using(@NonNull BaseHttpModule module) {
151 | super.moduleUsing(module);
152 | return this;
153 | }
154 |
155 | /**
156 | * 重试策略
157 | *
158 | * 保存当前请求
159 | * 错误回调
160 | * 失败回调
161 | *
162 | * 重试次数
163 | * 重试间隔时间
164 | * 支持时间迭代
165 | */
166 | public GlintHttp retry(@NonNull GlintHttpRetry retry) {
167 | mBuilder.retry = retry;
168 | mBuilder.baseRetry = retry;
169 | return this;
170 | }
171 |
172 | /**
173 | * 已有智能生命周期取消网络请求,这个为额外设置的方法
174 | *
175 | * @param tag 请求标签,用于取消请求
176 | */
177 | public GlintHttp setTag(@NonNull String tag) {
178 | mBuilder.tag = tag.hashCode();
179 | return this;
180 | }
181 |
182 | public GlintHttp setTag(int tag) {
183 | mBuilder.tag = tag;
184 | return this;
185 | }
186 |
187 | /**
188 | * 自由的生命周期,不受该框架控制
189 | */
190 | public GlintHttp freeLife() {
191 | mBuilder.freeLife = true;
192 | return this;
193 | }
194 |
195 | /**
196 | * 设置缓存
197 | *
198 | * @param cacheTime 缓存时间,单位是秒,默认不缓存
199 | */
200 | public GlintHttp cache(int cacheTime) {
201 | mBuilder.cacheTime = cacheTime;
202 | return this;
203 | }
204 |
205 | /**
206 | * 取消请求
207 | */
208 | public synchronized void cancel() {
209 | GlintHttpDispatcher.getInstance().cancel(mBuilder.tag);
210 | }
211 |
212 | /**
213 | * 执行网络请求
214 | */
215 | public void execute() {
216 | if (!TextUtils.isEmpty(mBuilder.url)) {
217 | GlintRequestUtil.addHttpRequestTag(mBuilder);
218 | if (mBuilder.retry != null && mBuilder.baseRetry != null) {
219 | mBuilder.retry = null;
220 | mBuilder.baseRetry.onCreateRequest(this);
221 | }
222 | GlintHttpDispatcher.getInstance().executed(this);
223 | }
224 | }
225 |
226 | /**
227 | * 执行网络请求
228 | * v1.1.4 新增API,改变调用顺序,使用更加合理
229 | *
230 | * @param listener 回调
231 | */
232 | public void execute(@NonNull GlintHttpListener listener) {
233 | if (!TextUtils.isEmpty(mBuilder.url)) {
234 | mBuilder.listener = listener;
235 | GlintRequestUtil.addHttpRequestTag(mBuilder);
236 | if (mBuilder.retry != null && mBuilder.baseRetry != null) {
237 | mBuilder.retry = null;
238 | mBuilder.baseRetry.onCreateRequest(this);
239 | }
240 | GlintHttpDispatcher.getInstance().executed(this);
241 | }
242 | }
243 |
244 | /**
245 | * 执行同步网络请求,该方法需在子线程发起,否则会异常
246 | *
247 | * @param classOfT 泛型类
248 | */
249 | public GlintResultBean executeSync(@NonNull Class classOfT) throws Exception {
250 | return executeSync(GlintRequestUtil.getListenerType(classOfT));
251 | }
252 |
253 | /**
254 | * 执行同步网络请求,该方法需在子线程发起,否则会异常
255 | *
256 | * @param typeOfT 通用型。您可以通过使用{@link com.google.gson.reflect.TypeToken}这个类来获取
257 | */
258 | public GlintResultBean executeSync(@NonNull Type typeOfT) throws Exception {
259 | if (mBuilder.listener != null) {
260 | mBuilder.listener = null;
261 | }
262 | this.mTypeOfT = typeOfT;
263 | //noinspection unchecked
264 | return runSync();
265 | }
266 |
267 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpActivityLifecycleCallbacks.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 |
4 | import android.annotation.SuppressLint;
5 | import android.app.Activity;
6 | import android.app.ActivityManager;
7 | import android.app.Application;
8 | import android.content.Context;
9 | import android.os.Build;
10 | import android.os.Bundle;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 | import androidx.annotation.RequiresApi;
15 | import androidx.fragment.app.FragmentActivity;
16 |
17 | import com.ysbing.glint.util.UiKit;
18 | import com.ysbing.glint.util.UiStack;
19 |
20 | import java.lang.reflect.Field;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | /**
25 | * 将Activity的名称收集起来,然后当该Activity销毁的时候,及时做销毁
26 | *
27 | * @author ysbing
28 | */
29 | @RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
30 | public final class GlintHttpActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
31 | private final GilntHttpFragmentLifecycleCallbacks mFragmentLifecycleCallbacks = new GilntHttpFragmentLifecycleCallbacks();
32 | private ActivityManager mActivityManager;
33 | private Object mActivityThread;
34 | private Field mActivitiesField;
35 | private int mActivityCount;
36 |
37 | public GlintHttpActivityLifecycleCallbacks(Context context) {
38 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
39 | mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
40 | } else {
41 | try {
42 | @SuppressLint("PrivateApi") Class> activityThreadClass = Class.forName("android.app.ActivityThread");
43 | mActivityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
44 | mActivitiesField = activityThreadClass.getDeclaredField("mActivities");
45 | mActivitiesField.setAccessible(true);
46 | } catch (Exception ignored) {
47 | }
48 | }
49 | }
50 |
51 | @Override
52 | public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
53 | mActivityCount++;
54 | UiStack.getInstance().pushActivity(activity);
55 | GlintHttpDispatcher.getInstance().mHostActivityNameList.add(activity.getClass().getName());
56 | GlintHttpDispatcher.getInstance().mHashCodeList.add(activity.hashCode());
57 | if (activity instanceof FragmentActivity) {
58 | ((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifecycleCallbacks, true);
59 | }
60 | }
61 |
62 | @Override
63 | public void onActivityStarted(Activity activity) {
64 |
65 | }
66 |
67 | @Override
68 | public void onActivityResumed(Activity activity) {
69 |
70 | }
71 |
72 | @Override
73 | public void onActivityPaused(Activity activity) {
74 |
75 | }
76 |
77 | @Override
78 | public void onActivityStopped(Activity activity) {
79 |
80 | }
81 |
82 | @Override
83 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
84 |
85 | }
86 |
87 | @Override
88 | public void onActivityDestroyed(Activity activity) {
89 | UiStack.getInstance().popActivity(activity);
90 | if (activity instanceof FragmentActivity) {
91 | ((FragmentActivity) activity).getSupportFragmentManager().unregisterFragmentLifecycleCallbacks(mFragmentLifecycleCallbacks);
92 | }
93 | GlintHttpDispatcher.getInstance().mHostActivityNameList.remove(activity.getClass().getName());
94 | GlintHttpDispatcher.getInstance().mHashCodeList.remove(Integer.valueOf(activity.hashCode()));
95 | GlintHttpDispatcher.getInstance().cancelAtHashCode(activity.hashCode());
96 |
97 | List tasks;
98 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && (tasks = mActivityManager.getRunningTasks(1)) != null) {
99 | mActivityCount = tasks.size();
100 | if (tasks.size() == 1 && tasks.get(0).baseActivity.toString().contains("com.google.android")) {
101 | mActivityCount--;
102 | }
103 | } else {
104 | try {
105 | if (mActivitiesField != null && mActivityThread != null) {
106 | Map activities = (Map) mActivitiesField.get(mActivityThread);
107 | mActivityCount = activities.size();
108 | }
109 | } catch (Exception ignored) {
110 | } finally {
111 | mActivityCount--;
112 | }
113 | }
114 | if (mActivityCount == 0) {
115 | GlintHttpDispatcher.getInstance().cancelAll();
116 | UiStack.getInstance().popAllActivity();
117 | UiStack.getInstance().popAllFragment();
118 | UiKit.dispose();
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpBuilder.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.ysbing.glint.base.BaseHttpModule;
6 | import com.ysbing.glint.base.Glint;
7 | import com.ysbing.glint.base.GlintBaseBuilder;
8 |
9 | /**
10 | * 网络请求的参数配置
11 | *
12 | * @author ysbing
13 | */
14 | public final class GlintHttpBuilder extends GlintBaseBuilder implements Cloneable {
15 | /**
16 | * GET和POST请求方式
17 | */
18 | public int method;
19 | /**
20 | * 上层的监听回调
21 | */
22 | public GlintHttpListener listener;
23 | /**
24 | * 结果不是json
25 | */
26 | public boolean notJson = false;
27 | /**
28 | * 是否重试
29 | */
30 | public boolean retryOnConnectionFailure = true;
31 | /**
32 | * 自定义重试策略
33 | */
34 | public GlintHttpRetry retry;
35 | public GlintHttpRetry baseRetry;
36 | /**
37 | * 该请求所属的宿主,有Activity和Fragment
38 | * Fragment支持support的Fragment
39 | * 用于在生命周期时销毁请求
40 | */
41 | public int hostHashCode;
42 | /**
43 | * 自由的生命周期,不智能销毁请求
44 | */
45 | public boolean freeLife = false;
46 | /**
47 | * 缓存时间,单位是秒,默认不缓存
48 | */
49 | public int cacheTime;
50 |
51 | public GlintHttpBuilder(@NonNull Glint glint) {
52 | this(glint, true);
53 | }
54 |
55 | public GlintHttpBuilder(@NonNull Glint glint, boolean init) {
56 | super();
57 | this.mimeType = "application/x-www-form-urlencoded; charset=utf-8";
58 | if (init) {
59 | glint.configDefaultBuilder(this);
60 | }
61 | }
62 |
63 | @Override
64 | protected GlintHttpBuilder clone() throws CloneNotSupportedException {
65 | return (GlintHttpBuilder) super.clone();
66 | }
67 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpCache.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.io.IOException;
6 |
7 | import okhttp3.Interceptor;
8 | import okhttp3.Response;
9 |
10 | /**
11 | * 缓存拦截器
12 | */
13 | final class GlintHttpCache implements Interceptor {
14 | private final int mCacheTime;
15 |
16 | GlintHttpCache(int cacheTime) {
17 | this.mCacheTime = cacheTime;
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public Response intercept(@NonNull Chain chain) throws IOException {
23 | return chain.proceed(chain.request()).newBuilder()
24 | .removeHeader("Pragma")
25 | .header("Cache-Control", "public, max-age=" + mCacheTime)
26 | .build();
27 | }
28 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 | import androidx.collection.SparseArrayCompat;
4 |
5 | import java.util.ArrayDeque;
6 | import java.util.ArrayList;
7 | import java.util.Deque;
8 | import java.util.Iterator;
9 | import java.util.List;
10 | import java.util.concurrent.ExecutorService;
11 | import java.util.concurrent.SynchronousQueue;
12 | import java.util.concurrent.ThreadPoolExecutor;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | import okhttp3.internal.Util;
16 |
17 | public final class GlintHttpDispatcher {
18 |
19 | private static final int mMaxRequests = 32;
20 |
21 | /**
22 | * Executes calls. Created lazily.
23 | */
24 | private ExecutorService mExecutorService;
25 |
26 | /**
27 | * Ready async calls in the order they'll be run.
28 | */
29 | private final Deque> mReadyAsyncCalls = new ArrayDeque<>();
30 |
31 | /**
32 | * Running asynchronous calls. Includes canceled calls that haven't finished yet.
33 | */
34 | private final Deque> mRunningAsyncCalls = new ArrayDeque<>();
35 | private final SparseArrayCompat> mCallTags = new SparseArrayCompat<>();
36 | private final SparseArrayCompat> mTagActivityHashCode = new SparseArrayCompat<>();
37 | public final List mHostActivityNameList = new ArrayList<>();
38 | public final List mHostFragmentNameList = new ArrayList<>();
39 | final List mHashCodeList = new ArrayList<>();
40 |
41 | private static final class InstanceHolder {
42 | static final GlintHttpDispatcher mInstance = new GlintHttpDispatcher();
43 | }
44 |
45 | public static GlintHttpDispatcher getInstance() {
46 | return InstanceHolder.mInstance;
47 | }
48 |
49 | private GlintHttpDispatcher() {
50 | }
51 |
52 | private synchronized ExecutorService executorService() {
53 | if (mExecutorService == null) {
54 | mExecutorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
55 | new SynchronousQueue(), Util.threadFactory("Glint Dispatcher", false));
56 | }
57 | return mExecutorService;
58 | }
59 |
60 | public synchronized void executed(GlintHttpCore> call) {
61 | if (mRunningAsyncCalls.size() < mMaxRequests) {
62 | mRunningAsyncCalls.add(call);
63 | executorService().execute(call);
64 | } else {
65 | mReadyAsyncCalls.add(call);
66 | }
67 | mCallTags.put(call.mBuilder.tag, call);
68 |
69 | List tags = mTagActivityHashCode.get(call.mBuilder.hostHashCode);
70 | if (tags == null) {
71 | tags = new ArrayList<>();
72 | }
73 | if (call.mBuilder.tag > 0) {
74 | tags.add(call.mBuilder.tag);
75 | mTagActivityHashCode.put(call.mBuilder.hostHashCode, tags);
76 | }
77 | }
78 |
79 | public synchronized void cancel(String tag) {
80 | cancel(tag.hashCode());
81 | }
82 |
83 | public synchronized void cancel(int tag) {
84 | GlintHttpCore> call = mCallTags.get(tag);
85 | if (call != null) {
86 | mCallTags.remove(tag);
87 | List tags = mTagActivityHashCode.get(call.mBuilder.hostHashCode);
88 | if (tags != null) {
89 | tags.remove(Integer.valueOf(call.mBuilder.tag));
90 | }
91 | mReadyAsyncCalls.remove(call);
92 | mRunningAsyncCalls.remove(call);
93 | call.coreCancel();
94 | }
95 | }
96 |
97 | public synchronized void cancelAtHashCode(int hashCode) {
98 | List tags = mTagActivityHashCode.get(hashCode);
99 | if (tags != null) {
100 | mTagActivityHashCode.remove(hashCode);
101 | for (Integer tag : tags) {
102 | if (tag != null) {
103 | cancel(tag);
104 | }
105 | }
106 | }
107 | }
108 |
109 | public synchronized void cancelAll() {
110 | for (GlintHttpCore> call : mReadyAsyncCalls) {
111 | call.coreCancel();
112 | }
113 |
114 | for (GlintHttpCore> call : mRunningAsyncCalls) {
115 | call.coreCancel();
116 | }
117 | mReadyAsyncCalls.clear();
118 | mRunningAsyncCalls.clear();
119 | mCallTags.clear();
120 | mTagActivityHashCode.clear();
121 | mHostActivityNameList.clear();
122 | mHostFragmentNameList.clear();
123 | mHashCodeList.clear();
124 | }
125 |
126 | private void promoteCalls() {
127 | // Already running max capacity.
128 | if (mRunningAsyncCalls.size() >= mMaxRequests) {
129 | return;
130 | }
131 | // No ready calls to promote.
132 | if (mReadyAsyncCalls.isEmpty()) {
133 | return;
134 | }
135 |
136 | for (Iterator> i = mReadyAsyncCalls.iterator(); i.hasNext(); ) {
137 | GlintHttpCore> call = i.next();
138 | i.remove();
139 | mRunningAsyncCalls.add(call);
140 | executorService().execute(call);
141 | // Reached max capacity.
142 | if (mRunningAsyncCalls.size() >= mMaxRequests) {
143 | return;
144 | }
145 | }
146 | }
147 |
148 | /**
149 | * Used by {@code GlintHttpCore#run} to signal completion.
150 | */
151 | void finished(GlintHttpCore> call) {
152 | synchronized (this) {
153 | mCallTags.remove(call.mBuilder.tag);
154 | List tags = mTagActivityHashCode.get(call.mBuilder.hostHashCode);
155 | if (tags != null) {
156 | tags.remove(Integer.valueOf(call.mBuilder.tag));
157 | if (tags.size() == 0) {
158 | mTagActivityHashCode.remove(call.mBuilder.hostHashCode);
159 | }
160 | }
161 | finished(mRunningAsyncCalls, call);
162 | }
163 | }
164 |
165 | private void finished(Deque calls, T call) {
166 | calls.remove(call);
167 | promoteCalls();
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpListener.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 | import androidx.annotation.CallSuper;
4 | import androidx.annotation.NonNull;
5 |
6 | import com.ysbing.glint.base.GlintResultBean;
7 |
8 | /**
9 | * 请求监听类
10 | *
11 | * @author ysbing
12 | */
13 | public abstract class GlintHttpListener {
14 |
15 | /**
16 | * 请求之前,开始的回调
17 | */
18 | public void onStart() {
19 | }
20 |
21 | /**
22 | * @param resultBean 封装结果类
23 | */
24 | public void onResponse(@NonNull GlintResultBean resultBean) throws Throwable {
25 | }
26 |
27 | /**
28 | * @param result 经过反序列化后的实体类
29 | */
30 | public void onSuccess(@NonNull T result) throws Throwable {
31 | }
32 |
33 | /**
34 | * 当http状态码200,状态码非200的回调
35 | *
36 | * @param status 状态码
37 | * @param errMsg 对应的错误提示
38 | */
39 | public void onError(int status, @NonNull String errMsg) throws Throwable {
40 | }
41 |
42 | /**
43 | * 当网络请求出现错误的时候,或者非标准json解析错误时的回调
44 | *
45 | * @param error 错误
46 | */
47 | @CallSuper
48 | public void onFail(@NonNull Throwable error) {
49 | error.printStackTrace();
50 | }
51 |
52 | /**
53 | * 当出现onError或者onFail的时候,会同时响应该回调
54 | * 如果有公共处理的逻辑,可以在此回调进行
55 | */
56 | public void onErrorOrFail() {
57 | }
58 |
59 | /**
60 | * 取消该请求
61 | */
62 | public void onCancel() {
63 | }
64 |
65 | /**
66 | * 无论成功与否,都会响应该回调
67 | */
68 | public void onFinish() {
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/http/GlintHttpRetry.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.http;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | /**
6 | * 重试接口类
7 | *
8 | * @author ysbing
9 | */
10 | public interface GlintHttpRetry {
11 | /**
12 | * 创建请求
13 | *
14 | * @param request 将该请求保存起来,在重试的时候使用
15 | */
16 | void onCreateRequest(@NonNull GlintHttp request);
17 |
18 | /**
19 | * 请求失败的时候回调,一般是网络超时等原因
20 | *
21 | * @param error 错误对象
22 | * @return 是否继续传递
23 | */
24 | boolean retryOnFail(@NonNull Throwable error);
25 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocket.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | import android.content.Context;
5 | import android.text.TextUtils;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 |
10 | import com.ysbing.glint.socket.socketio.GlintSocketIOCore;
11 | import com.ysbing.glint.socket.socketio.Protocol;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Socket请求类,请求的入口
18 | *
19 | * @author ysbing
20 | */
21 | public class GlintSocket {
22 |
23 | /**
24 | * 连接成功事件
25 | */
26 | public static final String EVENT_CONNECT = "EVENT_CONNECT";
27 | /**
28 | * 正常断开连接事件
29 | */
30 | public static final String EVENT_DISCONNECT = "EVENT_DISCONNECT";
31 | /**
32 | * 异常断开事件,如网络中断
33 | */
34 | public static final String EVENT_ERROR = "EVENT_ERROR";
35 | public static final List ALL_EVENT = new ArrayList() {{
36 | add(EVENT_CONNECT);
37 | add(EVENT_DISCONNECT);
38 | add(EVENT_ERROR);
39 | }};
40 | /**
41 | * 网络错误
42 | */
43 | public static final int ERROR_NET = 0x1;
44 | /**
45 | * 异常错误
46 | */
47 | public static final int ERROR_EXCEPTION = 0x2;
48 |
49 | private final GlintSocketBuilder builder;
50 |
51 | /**
52 | * 如果有跨进程需求,就做初始化
53 | * 在单一进程使用的话,不需要初始化
54 | *
55 | * @param context 上下文对象
56 | */
57 | public static void init(@NonNull Context context) {
58 | GlintSocketDispatcher.getInstance().init(context.getApplicationContext());
59 | }
60 |
61 | /**
62 | * 删除所有,包括Socket的连接
63 | */
64 | public static void removeAll() {
65 | GlintSocketDispatcher.getInstance().removeAll();
66 | }
67 |
68 | /**
69 | * 发送一条socket消息
70 | */
71 | public static GlintSocket send(@NonNull String url, @NonNull String message) {
72 | return new GlintSocket(url, null, message, -1, GlintSocketBuilder.RequestType.SEND);
73 | }
74 |
75 | /**
76 | * 发送一条socket消息
77 | */
78 | public static GlintSocket send(@NonNull String url, @Nullable String cmdId, @NonNull String message) {
79 | return new GlintSocket(url, cmdId, message, -1, GlintSocketBuilder.RequestType.SEND);
80 | }
81 |
82 | /**
83 | * 发送一条柚子IO socket消息
84 | */
85 | public static GlintSocket sendIO(@NonNull String url, @NonNull String cmdId, @NonNull String message) {
86 | final int sendId = GlintSocketIOCore.sSendId.incrementAndGet();
87 | return new GlintSocket(url, cmdId, "3:::" + Protocol.encode(sendId, cmdId, message), sendId, GlintSocketBuilder.RequestType.IO_SEND);
88 | }
89 |
90 | /**
91 | * 设置推送监听
92 | */
93 | public static GlintSocket on(@NonNull String url, @NonNull String cmdId) {
94 | return new GlintSocket(url, cmdId, "", -1, GlintSocketBuilder.RequestType.PUSH_LISTENER);
95 | }
96 |
97 | /**
98 | * 设置柚子IO推送监听
99 | */
100 | public static GlintSocket onIO(@NonNull String url, @NonNull String cmdId) {
101 | return new GlintSocket(url, cmdId, "", -1, GlintSocketBuilder.RequestType.IO_PUSH_LISTENER);
102 | }
103 |
104 | /**
105 | * 移除推送监听
106 | */
107 | public static void off(@NonNull String url) {
108 | off(url, "");
109 | }
110 |
111 | public static void off(@NonNull String url, @Nullable String cmdId) {
112 | off(url, cmdId, 0);
113 | }
114 |
115 | public static void off(@NonNull String url, @Nullable String cmdId, int tag) {
116 | GlintSocketBuilder builder = new GlintSocketBuilder<>();
117 | builder.url = url;
118 | builder.cmdId = cmdId;
119 | builder.tag = tag;
120 | GlintSocketDispatcher.getInstance().removePushListener(builder);
121 | }
122 |
123 | public void off() {
124 | GlintSocketDispatcher.getInstance().removePushListener(builder);
125 | }
126 |
127 | /**
128 | * @param tag 请求标签,用于取消请求
129 | */
130 | public GlintSocket setTag(@NonNull String tag) {
131 | builder.tag = tag.hashCode();
132 | return this;
133 | }
134 |
135 | public GlintSocket setTag(int tag) {
136 | builder.tag = tag;
137 | return this;
138 | }
139 |
140 | /**
141 | * 使用自定义Module,可做高级操作
142 | *
143 | * @param module 自定义Module
144 | */
145 | public GlintSocket using(@NonNull SocketHttpModule module) {
146 | builder.customGlintModule = module;
147 | return this;
148 | }
149 |
150 | public GlintSocket(@NonNull String url, @Nullable String cmdId, @NonNull String params, int sendId, @NonNull GlintSocketBuilder.RequestType requestType) {
151 | builder = new GlintSocketBuilder<>();
152 | builder.url = url;
153 | builder.cmdId = cmdId;
154 | builder.params = params;
155 | builder.sendId = sendId;
156 | builder.requestType = requestType;
157 | }
158 |
159 | public void execute() {
160 | execute(null);
161 | }
162 |
163 | public void execute(@Nullable GlintSocketListener listener) {
164 | if (!TextUtils.isEmpty(builder.url)) {
165 | builder.listener = listener;
166 | if (builder.tag == 0) {
167 | builder.tag = System.identityHashCode(builder);
168 | }
169 | switch (builder.requestType) {
170 | case SEND:
171 | if (builder.sendId != -1) {
172 | builder.cmdId += GlintSocket.class.getSimpleName() + builder.sendId;
173 | }
174 | GlintSocketDispatcher.getInstance().send(builder);
175 | break;
176 | case IO_SEND:
177 | if (builder.sendId != -1) {
178 | builder.cmdId += GlintSocket.class.getSimpleName() + builder.sendId;
179 | }
180 | GlintSocketDispatcher.getInstance().sendIO(builder);
181 | break;
182 | case PUSH_LISTENER:
183 | GlintSocketDispatcher.getInstance().on(builder);
184 | break;
185 | case IO_PUSH_LISTENER:
186 | GlintSocketDispatcher.getInstance().onIO(builder);
187 | break;
188 | default:
189 | break;
190 | }
191 | }
192 | }
193 |
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketBuilder.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | /**
5 | * 重要类,使用时定义的任务
6 | *
7 | * @author ysbing
8 | */
9 | public final class GlintSocketBuilder {
10 | /**
11 | * 请求的地址
12 | */
13 | public String url;
14 | /**
15 | * 消息命令
16 | */
17 | public String cmdId;
18 | /**
19 | * 请求参数
20 | */
21 | public String params;
22 | /**
23 | * 发送消息的id,用于回调识别
24 | */
25 | public int sendId;
26 | /**
27 | * 上层的监听回调
28 | */
29 | public GlintSocketListener listener;
30 | /**
31 | * 请求类型,有发送消息和监听推送两种
32 | */
33 | public RequestType requestType;
34 | /**
35 | * 请求标签,用于取消请求
36 | */
37 | public int tag;
38 | /**
39 | * 自定义解析Module
40 | */
41 | public SocketHttpModule customGlintModule;
42 |
43 | public enum RequestType {
44 | SEND,
45 | IO_SEND,
46 | PUSH_LISTENER,
47 | IO_PUSH_LISTENER
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketBuilderStub.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket;
2 |
3 | import static com.ysbing.glint.util.GlintRequestUtil.sGson;
4 |
5 | import android.os.RemoteException;
6 |
7 | import com.google.gson.JsonElement;
8 | import com.google.gson.JsonParser;
9 | import com.ysbing.glint.util.GlintRequestUtil;
10 | import com.ysbing.glint.util.UiKit;
11 |
12 | import java.lang.reflect.Type;
13 |
14 | /**
15 | * 只做Google的Gson中的JsonObject
16 | * 该类可做参考,用于扩展更多的实现
17 | *
18 | * @author ysbing
19 | */
20 | public class GlintSocketBuilderStub extends GlintSocketBuilderWrapper.Stub {
21 |
22 | private final GlintSocketBuilder builder;
23 | private Type typeOfT;
24 |
25 | public GlintSocketBuilderStub(GlintSocketBuilder builder) {
26 | this.builder = builder;
27 | if (builder.listener != null) {
28 | typeOfT = GlintRequestUtil.getListenerType(builder.listener.getClass());
29 | }
30 | }
31 |
32 | @Override
33 | public String getUrl() {
34 | return builder.url;
35 | }
36 |
37 | @Override
38 | public String getCmdId() {
39 | return builder.cmdId;
40 | }
41 |
42 | @Override
43 | public String getParams() {
44 | return builder.params;
45 | }
46 |
47 | @Override
48 | public int getSendId() {
49 | return builder.sendId;
50 | }
51 |
52 | @Override
53 | public int getTag() {
54 | return builder.tag;
55 | }
56 |
57 | @Override
58 | public String getResponseCmdId(String response) throws RemoteException {
59 | if (builder.listener != null && builder.customGlintModule != null) {
60 | if (!typeOfT.equals(Void.class)) {
61 | JsonParser parser = new JsonParser();
62 | JsonElement jsonElement = parser.parse(response);
63 | return builder.customGlintModule.getCmdId(jsonElement, sGson, typeOfT);
64 | }
65 | }
66 | return "";
67 | }
68 |
69 | @Override
70 | public void onResponse(String response) throws RemoteException {
71 | if (builder.listener != null) {
72 | T t;
73 | if (!typeOfT.equals(Void.class)) {
74 | JsonElement jsonElement = JsonParser.parseString(response);
75 | if (builder.customGlintModule != null) {
76 | try {
77 | t = builder.customGlintModule.customDeserialize(jsonElement, sGson, typeOfT);
78 | onProcess(t);
79 | } catch (Exception e) {
80 | onError(e.getMessage());
81 | }
82 | } else {
83 | t = GlintRequestUtil.successDeserialize(sGson, jsonElement, typeOfT);
84 | onProcess(t);
85 | }
86 | }
87 | }
88 | }
89 |
90 | private void onProcess(final T t) {
91 | UiKit.runOnMainThreadAsync(new Runnable() {
92 | @Override
93 | public void run() {
94 | try {
95 | builder.listener.onProcess(t);
96 | } catch (Throwable e) {
97 | onError(e.getMessage());
98 | }
99 | }
100 | });
101 | }
102 |
103 | @Override
104 | public void onError(final String error) {
105 | if (builder.listener != null) {
106 | UiKit.runOnMainThreadAsync(new Runnable() {
107 | @Override
108 | public void run() {
109 | builder.listener.onError(error);
110 | }
111 | });
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketCore.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | import android.text.TextUtils;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 |
9 | import com.ysbing.glint.base.Glint;
10 | import com.ysbing.glint.socket.socketio.GlintSocketIOCallback;
11 | import com.ysbing.glint.socket.socketio.IOWebSocketTransport;
12 |
13 | import java.net.URI;
14 | import java.util.ArrayDeque;
15 | import java.util.Deque;
16 | import java.util.Map;
17 | import java.util.Objects;
18 | import java.util.Set;
19 | import java.util.concurrent.ConcurrentHashMap;
20 |
21 | import okhttp3.OkHttpClient;
22 | import okhttp3.Request;
23 | import okhttp3.Response;
24 | import okhttp3.WebSocket;
25 | import okhttp3.WebSocketListener;
26 |
27 | /**
28 | * WebSocket核心类
29 | *
30 | * @author ysbing
31 | * 创建于 2020/6/25
32 | */
33 | public class GlintSocketCore {
34 |
35 | private static final Glint GLINT = Glint.getsInstance();
36 | private static final OkHttpClient sClient;
37 |
38 | private WebSocket mWebSocket;
39 | private final URI mUrl;
40 | private final Deque mWaitMessages = new ArrayDeque<>();
41 | private final Map> mAsyncListeners = new ConcurrentHashMap<>();
42 | private boolean mConnecting = false;
43 | private boolean mConnected = false;
44 |
45 | static {
46 | if (GLINT != null) {
47 | sClient = GLINT.onOkHttpBuildCreate(Glint.GlintType.SOCKET, new OkHttpClient.Builder()).build();
48 | } else {
49 | sClient = new OkHttpClient.Builder().build();
50 | }
51 | }
52 |
53 | GlintSocketCore(@NonNull URI url) {
54 | this.mUrl = url;
55 | }
56 |
57 | synchronized void connect() {
58 | if (mConnecting || mConnected) {
59 | return;
60 | }
61 | mConnecting = true;
62 | IOWebSocketTransport.getUrl(mUrl, new GlintSocketIOCallback() {
63 | @Override
64 | public void onSocketUrl(@NonNull String socketUrl) {
65 | socketConnect(socketUrl);
66 | }
67 |
68 | @Override
69 | public void onError(@NonNull Throwable throwable) {
70 | mConnecting = false;
71 | Set keySet = mAsyncListeners.keySet();
72 | for (String s : keySet) {
73 | if (GlintSocket.ALL_EVENT.contains(s)) {
74 | continue;
75 | }
76 | GlintSocketListener listener = mAsyncListeners.get(s);
77 | if (listener != null) {
78 | listener.onError(throwable.toString());
79 | }
80 | }
81 | if (mAsyncListeners.containsKey(GlintSocket.EVENT_ERROR)) {
82 | GlintSocketListener listener = mAsyncListeners.get(GlintSocket.EVENT_ERROR);
83 | if (listener != null) {
84 | try {
85 | SocketInnerResultBean bean = new SocketInnerResultBean();
86 | bean.response = String.valueOf(GlintSocket.ERROR_NET);
87 | bean.msgType = 1;
88 | listener.onProcess(bean);
89 | } catch (Throwable ignored) {
90 | }
91 | }
92 | }
93 | }
94 | });
95 | }
96 |
97 | private void socketConnect(@NonNull String socketUrl) {
98 | if (mWebSocket != null) {
99 | mWebSocket.cancel();
100 | mWebSocket = null;
101 | }
102 | Request request = new Request.Builder().url(socketUrl).build();
103 | sClient.newWebSocket(request, getSocketListener());
104 | }
105 |
106 | private WebSocketListener getSocketListener() {
107 | return new WebSocketListener() {
108 | @Override
109 | public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
110 | super.onOpen(webSocket, response);
111 | mWebSocket = webSocket;
112 | mConnecting = false;
113 | mConnected = true;
114 | for (String waitMessage : mWaitMessages) {
115 | webSocket.send(waitMessage);
116 | }
117 | mWaitMessages.clear();
118 | if (mAsyncListeners.containsKey(GlintSocket.EVENT_CONNECT)) {
119 | GlintSocketListener listener = mAsyncListeners.get(GlintSocket.EVENT_CONNECT);
120 | if (listener != null) {
121 | try {
122 | SocketInnerResultBean bean = new SocketInnerResultBean();
123 | bean.response = "";
124 | bean.msgType = 1;
125 | listener.onProcess(bean);
126 | } catch (Throwable ignored) {
127 | }
128 | }
129 | }
130 | }
131 |
132 | @Override
133 | public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
134 | super.onMessage(webSocket, text);
135 | messagePush(text);
136 | }
137 |
138 | @Override
139 | public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
140 | super.onClosing(webSocket, code, reason);
141 | mConnected = false;
142 | }
143 |
144 | @Override
145 | public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
146 | super.onClosed(webSocket, code, reason);
147 | mConnected = false;
148 | if (mAsyncListeners.containsKey(GlintSocket.EVENT_DISCONNECT)) {
149 | GlintSocketListener listener = mAsyncListeners.get(GlintSocket.EVENT_DISCONNECT);
150 | if (listener != null) {
151 | try {
152 | SocketInnerResultBean bean = new SocketInnerResultBean();
153 | bean.response = "";
154 | bean.msgType = 1;
155 | listener.onProcess(bean);
156 | } catch (Throwable ignored) {
157 | }
158 | }
159 | }
160 | }
161 |
162 | @Override
163 | public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
164 | super.onFailure(webSocket, t, response);
165 | if (mAsyncListeners.containsKey(GlintSocket.EVENT_ERROR)) {
166 | GlintSocketListener listener = mAsyncListeners.get(GlintSocket.EVENT_ERROR);
167 | if (listener != null) {
168 | try {
169 | SocketInnerResultBean bean = new SocketInnerResultBean();
170 | bean.response = String.valueOf(GlintSocket.ERROR_EXCEPTION);
171 | bean.msgType = 1;
172 | listener.onProcess(bean);
173 | } catch (Throwable ignored) {
174 | }
175 | }
176 | }
177 | disconnect();
178 | }
179 | };
180 | }
181 |
182 | private void messagePush(String messageStr) {
183 | Set keySet = mAsyncListeners.keySet();
184 | for (String s : keySet) {
185 | GlintSocketListener listener = mAsyncListeners.get(s);
186 | if (listener != null) {
187 | try {
188 | SocketInnerResultBean bean = new SocketInnerResultBean();
189 | bean.response = messageStr;
190 | bean.msgType = 0;
191 | listener.onProcess(bean);
192 | } catch (Throwable e) {
193 | listener.onError(Objects.requireNonNull(e.getMessage()));
194 | }
195 | }
196 | }
197 | }
198 |
199 | synchronized void disconnect() {
200 | mConnected = false;
201 | mConnecting = false;
202 | mAsyncListeners.clear();
203 | if (mWebSocket != null) {
204 | mWebSocket.cancel();
205 | mWebSocket = null;
206 | }
207 | }
208 |
209 | boolean isConnected() {
210 | return mConnected;
211 | }
212 |
213 | void on(@NonNull String cmdId, @NonNull GlintSocketListener listener) {
214 | mAsyncListeners.put(cmdId, listener);
215 | }
216 |
217 | synchronized void send(@NonNull String message) {
218 | if (mWebSocket != null && isConnected()) {
219 | mWebSocket.send(message);
220 | } else {
221 | mWaitMessages.add(message);
222 | }
223 | }
224 |
225 | public synchronized void off(@Nullable String cmdId) {
226 | if (TextUtils.isEmpty(cmdId)) {
227 | disconnect();
228 | } else {
229 | for (String key : mAsyncListeners.keySet()) {
230 | if (key.contains(GlintSocket.class.getSimpleName()) && key.contains(cmdId)) {
231 | mAsyncListeners.remove(key);
232 | }
233 | }
234 | mAsyncListeners.remove(cmdId);
235 | }
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketDispatcher.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | import android.app.Service;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.os.IBinder;
10 | import android.os.RemoteException;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 | import java.util.concurrent.LinkedBlockingQueue;
17 |
18 | /**
19 | * Socket请求类,请求的入口
20 | *
21 | * @author ysbing
22 | */
23 | public class GlintSocketDispatcher implements ServiceConnection {
24 |
25 | private static GlintSocketDispatcher mInstance = null;
26 |
27 | private final LinkedBlockingQueue mQueue = new LinkedBlockingQueue<>();
28 | private final LinkedBlockingQueue mIOQueue = new LinkedBlockingQueue<>();
29 | private final List mSocketTaskBuilders = new ArrayList<>();
30 | private final GlintSocketServiceStub mSocketServiceStub;
31 |
32 | private GlintSocketService mService;
33 | private boolean mInit;
34 |
35 | private GlintSocketDispatcher() {
36 | Thread worker = new Worker();
37 | Thread workerIO = new WorkerIO();
38 | worker.start();
39 | workerIO.start();
40 | mSocketServiceStub = new GlintSocketServiceStub();
41 | }
42 |
43 | public static GlintSocketDispatcher getInstance() {
44 | if (mInstance == null) {
45 | synchronized (GlintSocketDispatcher.class) {
46 | if (mInstance == null) {
47 | mInstance = new GlintSocketDispatcher();
48 | }
49 | }
50 | }
51 | return mInstance;
52 | }
53 |
54 | /**
55 | * 如果有跨进程需求,就做初始化
56 | * 在单一进程使用的话,不需要初始化
57 | *
58 | * @param context 上下文对象
59 | */
60 | void init(Context context) {
61 | mInit = true;
62 | if (mService == null) {
63 | Intent i = new Intent(context, GlintSocketServiceNative.class);
64 | context.bindService(i, mInstance, Service.BIND_AUTO_CREATE);
65 | }
66 | }
67 |
68 | /**
69 | * 发送一条socket消息
70 | */
71 | void send(@NonNull GlintSocketBuilder builder) {
72 | mQueue.offer(new GlintSocketBuilderStub<>(builder));
73 | }
74 |
75 | /**
76 | * 发送一条柚子IO socket消息
77 | */
78 | void sendIO(@NonNull GlintSocketBuilder builder) {
79 | mIOQueue.offer(new GlintSocketBuilderStub<>(builder));
80 | }
81 |
82 | /**
83 | * 设置一个推送监听
84 | */
85 | public void on(@NonNull GlintSocketBuilder builder) {
86 | if (mService == null && mInit) {
87 | mSocketTaskBuilders.add(builder);
88 | return;
89 | }
90 | try {
91 | if (mService != null) {
92 | mService.connect(builder.url);
93 | } else {
94 | mSocketServiceStub.connect(builder.url);
95 | }
96 | } catch (RemoteException e) {
97 | e.printStackTrace();
98 | }
99 | try {
100 | if (mService != null) {
101 | mService.on(new GlintSocketBuilderStub<>(builder));
102 | } else {
103 | mSocketServiceStub.on(new GlintSocketBuilderStub<>(builder));
104 | }
105 | } catch (Exception e) {
106 | if (builder.listener != null) {
107 | builder.listener.onError(e.getMessage());
108 | }
109 | }
110 | }
111 |
112 | /**
113 | * 设置一个柚子IO推送监听
114 | */
115 | public void onIO(@NonNull GlintSocketBuilder builder) {
116 | if (mService == null && mInit) {
117 | mSocketTaskBuilders.add(builder);
118 | return;
119 | }
120 | try {
121 | if (mService != null) {
122 | mService.connectIO(builder.url);
123 | } else {
124 | mSocketServiceStub.connectIO(builder.url);
125 | }
126 | } catch (RemoteException e) {
127 | e.printStackTrace();
128 | }
129 | try {
130 | if (mService != null) {
131 | mService.onIO(new GlintSocketBuilderStub<>(builder));
132 | } else {
133 | mSocketServiceStub.onIO(new GlintSocketBuilderStub<>(builder));
134 | }
135 | } catch (Exception e) {
136 | if (builder.listener != null) {
137 | builder.listener.onError(e.getMessage());
138 | }
139 | }
140 | }
141 |
142 | /**
143 | * 移除发送队列
144 | */
145 | void removePushListener(GlintSocketBuilder builder) {
146 | try {
147 | if (mService != null) {
148 | mService.off(builder.url, builder.cmdId, builder.tag);
149 | } else {
150 | mSocketServiceStub.off(builder.url, builder.cmdId, builder.tag);
151 | }
152 | } catch (RemoteException e) {
153 | e.printStackTrace();
154 | }
155 | }
156 |
157 | /**
158 | * 删除所有,包括Socket的连接
159 | */
160 | void removeAll() {
161 | try {
162 | if (mService != null) {
163 | mService.offAll();
164 | } else {
165 | mSocketServiceStub.offAll();
166 | }
167 | } catch (RemoteException e) {
168 | e.printStackTrace();
169 | }
170 | }
171 |
172 |
173 | @Override
174 | public void onServiceConnected(ComponentName name, IBinder iBinder) {
175 | try {
176 | mService = GlintSocketService.Stub.asInterface(iBinder);
177 | for (GlintSocketBuilder taskBuilder : mSocketTaskBuilders) {
178 | onIO(taskBuilder);
179 | }
180 | } catch (Exception e) {
181 | mService = null;
182 | }
183 | }
184 |
185 | @Override
186 | public void onServiceDisconnected(ComponentName name) {
187 | mService = null;
188 | }
189 |
190 | private void continueProcessTaskWrappers() {
191 | try {
192 | GlintSocketBuilderWrapper taskWrapper = mQueue.take();
193 | if (mService != null) {
194 | if (taskWrapper == null) {
195 | return;
196 | }
197 | mService.send(taskWrapper);
198 | } else {
199 | if (taskWrapper == null) {
200 | return;
201 | }
202 | mSocketServiceStub.send(taskWrapper);
203 | }
204 | } catch (Exception ignored) {
205 | }
206 | }
207 |
208 | private void continueProcessTaskWrappersIO() {
209 | try {
210 | GlintSocketBuilderWrapper taskWrapper = mIOQueue.take();
211 | if (mService != null) {
212 | if (taskWrapper == null) {
213 | return;
214 | }
215 | mService.sendIO(taskWrapper);
216 | } else {
217 | if (taskWrapper == null) {
218 | return;
219 | }
220 | mSocketServiceStub.sendIO(taskWrapper);
221 | }
222 | } catch (Exception ignored) {
223 | }
224 | }
225 |
226 | private class Worker extends Thread {
227 | @Override
228 | public void run() {
229 | //noinspection InfiniteLoopStatement
230 | for (; ; ) {
231 | continueProcessTaskWrappers();
232 | try {
233 | Thread.sleep(50);
234 | } catch (InterruptedException e) {
235 | //
236 | }
237 | }
238 | }
239 | }
240 |
241 | private class WorkerIO extends Thread {
242 | @Override
243 | public void run() {
244 | //这样写可以防止代码注入
245 | //noinspection InfiniteLoopStatement
246 | for (; ; ) {
247 | continueProcessTaskWrappersIO();
248 | try {
249 | Thread.sleep(50);
250 | } catch (InterruptedException e) {
251 | //
252 | }
253 | }
254 | }
255 | }
256 |
257 | }
258 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketListener.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | /**
7 | * Socket监听器
8 | *
9 | * @author ysbing
10 | */
11 | public abstract class GlintSocketListener {
12 |
13 | public void onProcess(@NonNull T result) throws Throwable {
14 | }
15 |
16 | public void onError(@NonNull String error) {
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/GlintSocketServiceNative.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ysbing.glint.socket;
3 |
4 | import android.app.Service;
5 | import android.content.Intent;
6 | import android.os.IBinder;
7 | import android.os.RemoteException;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | /**
12 | * Service接口
13 | *
14 | * @author ysbing
15 | */
16 | public class GlintSocketServiceNative extends Service implements GlintSocketService {
17 |
18 | private GlintSocketServiceStub mStub;
19 |
20 | @Nullable
21 | @Override
22 | public IBinder onBind(Intent intent) {
23 | return mStub;
24 | }
25 |
26 | @Override
27 | public void onCreate() {
28 | super.onCreate();
29 | mStub = new GlintSocketServiceStub();
30 | }
31 |
32 | @Override
33 | public void connect(String url) throws RemoteException {
34 | mStub.connect(url);
35 | }
36 |
37 | @Override
38 | public void connectIO(String url) throws RemoteException {
39 | mStub.connectIO(url);
40 | }
41 |
42 | @Override
43 | public void send(GlintSocketBuilderWrapper builderWrapper) throws RemoteException {
44 | mStub.send(builderWrapper);
45 | }
46 |
47 | @Override
48 | public void sendIO(GlintSocketBuilderWrapper builderWrapper) throws RemoteException {
49 | mStub.sendIO(builderWrapper);
50 | }
51 |
52 | @Override
53 | public void on(GlintSocketBuilderWrapper builderWrapper) throws RemoteException {
54 | mStub.on(builderWrapper);
55 | }
56 |
57 | @Override
58 | public void onIO(GlintSocketBuilderWrapper builderWrapper) throws RemoteException {
59 | mStub.onIO(builderWrapper);
60 | }
61 |
62 | @Override
63 | public void off(String url, String cmdId, int tag) throws RemoteException {
64 | mStub.off(url, cmdId, tag);
65 | }
66 |
67 | @Override
68 | public void offAll() throws RemoteException {
69 | mStub.offAll();
70 | }
71 |
72 | @Override
73 | public IBinder asBinder() {
74 | return mStub;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/SocketHttpModule.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket;
2 |
3 |
4 | import static com.ysbing.glint.util.GlintRequestUtil.sGson;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.google.gson.Gson;
9 | import com.google.gson.JsonElement;
10 | import com.ysbing.glint.util.GlintRequestUtil;
11 |
12 | import java.lang.reflect.Type;
13 |
14 | /**
15 | * 自定义Socket网络模块
16 | *
17 | * @author ysbing
18 | */
19 | public abstract class SocketHttpModule {
20 |
21 | public String getCmdId(@NonNull JsonElement jsonEl, @NonNull Gson gson, @NonNull Type typeOfT) {
22 | return null;
23 | }
24 |
25 | public T customDeserialize(@NonNull JsonElement jsonEl, @NonNull Gson gson, @NonNull Type typeOfT) throws Exception {
26 | return GlintRequestUtil.successDeserialize(sGson, jsonEl, typeOfT);
27 | }
28 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/SocketInnerResultBean.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket;
2 |
3 | public class SocketInnerResultBean {
4 | /**
5 | * 服务端响应内容
6 | */
7 | public String response;
8 | /**
9 | * 消息类型,0是普通消息,1是事件消息
10 | */
11 | public int msgType;
12 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/socketio/GlintSocketIOCallback.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket.socketio;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | /**
7 | * 用户SocketIO的连接地址
8 | *
9 | * @author ysbing
10 | * 创建于 2018/3/25
11 | */
12 | public interface GlintSocketIOCallback {
13 | /**
14 | * 异步获取socket io地址
15 | *
16 | * @param socketUrl 经过拼接的socket地址
17 | */
18 | void onSocketUrl(@NonNull String socketUrl);
19 |
20 | /**
21 | * 错误回调
22 | *
23 | * @param throwable 未知错误
24 | */
25 | void onError(@NonNull Throwable throwable);
26 | }
27 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/socketio/IOMessage.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket.socketio;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | public class IOMessage {
7 | public static final int TYPE_DISCONNECT = 0;
8 | public static final int TYPE_CONNECT = 1;
9 | public static final int TYPE_HEARTBEAT = 2;
10 | public static final int TYPE_MESSAGE = 3;
11 | public static final int TYPE_JSON_MESSAGE = 4;
12 | public static final int TYPE_EVENT = 5;
13 | public static final int TYPE_ACK = 6;
14 | public static final int TYPE_ERROR = 7;
15 | public static final int TYPE_NOOP = 8;
16 | public static final int FIELD_TYPE = 0;
17 | public static final int FIELD_ID = 1;
18 | public static final int FIELD_ENDPOINT = 2;
19 | public static final int FIELD_DATA = 3;
20 | public static final int NUM_FIELDS = 4;
21 | private final String[] mFields;
22 | private int type;
23 |
24 | private IOMessage(int type, String id, String namespace, String data) {
25 | this.mFields = new String[4];
26 | this.type = type;
27 | this.mFields[1] = id;
28 | this.mFields[0] = "" + type;
29 | this.mFields[2] = namespace;
30 | this.mFields[3] = data;
31 | }
32 |
33 | public IOMessage(String message) {
34 | this.mFields = new String[4];
35 | String[] fields = message.split(":", 4);
36 |
37 | for (int i = 0; i < fields.length; ++i) {
38 | this.mFields[i] = fields[i];
39 | if (i == 0) {
40 | this.type = Integer.parseInt(fields[i]);
41 | }
42 | }
43 |
44 | }
45 |
46 | @NonNull
47 | @Override
48 | public String toString() {
49 | StringBuilder builder = new StringBuilder();
50 | for (String field : this.mFields) {
51 | builder.append(':');
52 | if (field != null) {
53 | builder.append(field);
54 | }
55 | }
56 | return builder.substring(1);
57 | }
58 |
59 | public int getType() {
60 | return this.type;
61 | }
62 |
63 | public String getId() {
64 | return this.mFields[1];
65 | }
66 |
67 | public void setId(String id) {
68 | this.mFields[1] = id;
69 | }
70 |
71 | public String getEndpoint() {
72 | return this.mFields[2];
73 | }
74 |
75 | public String getData() {
76 | return this.mFields[3];
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/socketio/IOWebSocketTransport.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket.socketio;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | import com.ysbing.glint.http.GlintHttp;
7 | import com.ysbing.glint.http.GlintHttpListener;
8 |
9 | import java.net.URI;
10 | import java.util.Arrays;
11 | import java.util.List;
12 | import java.util.regex.Pattern;
13 |
14 | /**
15 | * SocketIO的URL处理类
16 | *
17 | * @author ysbing
18 | * 创建于 2018/3/25
19 | */
20 | public class IOWebSocketTransport {
21 | private static final Pattern PATTERN_HTTP = Pattern.compile("^http");
22 |
23 | public static void getUrl(@NonNull final URI url, @NonNull final GlintSocketIOCallback callback) {
24 | if ("ws".equals(url.getScheme()) || "wss".equals(url.getScheme())) {
25 | callback.onSocketUrl(url.toString());
26 | } else if ("http".equals(url.getScheme()) || "https".equals(url.getScheme())) {
27 | GlintHttp.get(url + "/socket.io/1/").signature(false).notJson(true).mainThread(false).execute(new GlintHttpListener() {
28 | @Override
29 | public void onSuccess(@NonNull String result) throws Throwable {
30 | super.onSuccess(result);
31 | String[] data = result.split(":");
32 | String sessionId = data[0];
33 | List protocols = Arrays.asList(data[3].split(","));
34 | String socketUrl;
35 | if (protocols.contains("websocket")) {
36 | socketUrl = PATTERN_HTTP.matcher(url.toString()).replaceFirst("ws") + "/socket.io/1/" + "websocket" + "/" + sessionId;
37 | } else {
38 | if (!protocols.contains("xhr-polling")) {
39 | callback.onError(new RuntimeException("socket url is empty"));
40 | return;
41 | }
42 | socketUrl = url.toString() + "/socket.io/1/" + "xhr-polling" + "/" + sessionId;
43 |
44 | }
45 | callback.onSocketUrl(socketUrl);
46 | }
47 |
48 | @Override
49 | public void onFail(@NonNull Throwable error) {
50 | super.onFail(error);
51 | callback.onError(error);
52 | }
53 | });
54 | } else {
55 | callback.onError(new RuntimeException("Unknown url"));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/socket/socketio/Protocol.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.socket.socketio;
2 |
3 | public class Protocol {
4 | private final static int HEADER = 5;
5 |
6 | public static String encode(int id, String route, String msg) {
7 | if (route.length() > 255) {
8 | throw new RuntimeException("route max length is overflow.");
9 | }
10 | byte[] arr = new byte[HEADER + route.length()];
11 | int index = 0;
12 | arr[index++] = (byte) ((id >> 24) & 0xFF);
13 | arr[index++] = (byte) ((id >> 16) & 0xFF);
14 | arr[index++] = (byte) ((id >> 8) & 0xFF);
15 | arr[index++] = (byte) (id & 0xFF);
16 | arr[index++] = (byte) (route.length() & 0xFF);
17 |
18 | for (int i = 0; i < route.length(); i++) {
19 | arr[index++] = (byte) route.codePointAt(i);
20 | }
21 | return bt2Str(arr, arr.length) + msg;
22 | }
23 |
24 | private static String bt2Str(byte[] arr, int end) {
25 | StringBuilder buff = new StringBuilder();
26 | for (int i = 0; i < arr.length && i < end; i++) {
27 | buff.append(String.valueOf(Character.toChars((arr[i] + 256) % 256)));
28 | }
29 | return buff.toString();
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/upload/GlintUpload.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.upload;
2 |
3 | import android.os.Bundle;
4 | import android.text.TextUtils;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 |
9 | import com.ysbing.glint.base.BaseHttpModule;
10 |
11 | import java.io.File;
12 | import java.util.TreeMap;
13 |
14 | import okhttp3.Headers;
15 |
16 | /**
17 | * 上传请求的入口
18 | * 该类不可继承,如果有自定义功能需求,就继承{@link GlintUploadCore}进行扩展
19 | *
20 | * @author ysbing
21 | * 创建于 2018/1/16
22 | */
23 | public final class GlintUpload extends GlintUploadCore {
24 |
25 | public static GlintUpload upload(@NonNull String url, @NonNull File file) {
26 | return upload(url, file, new TreeMap());
27 | }
28 |
29 | public static GlintUpload upload(@NonNull String url, @NonNull File file, @NonNull String keyName) {
30 | return upload(url, file, keyName, new TreeMap());
31 | }
32 |
33 | public static GlintUpload upload(@NonNull String url, @NonNull File file, @NonNull TreeMap params) {
34 | return upload(url, file, file.getName(), params);
35 | }
36 |
37 | public static GlintUpload upload(@NonNull String url, @NonNull File file, @NonNull String keyName, @NonNull TreeMap params) {
38 | return new GlintUpload(url, file, null, keyName, params);
39 | }
40 |
41 | public static GlintUpload upload(@NonNull String url, @NonNull byte[] data, @NonNull String keyName) {
42 | return upload(url, data, keyName, new TreeMap());
43 | }
44 |
45 | public static GlintUpload upload(@NonNull String url, @NonNull byte[] data, @NonNull String keyName, @NonNull TreeMap params) {
46 | return new GlintUpload(url, null, data, keyName, params);
47 | }
48 |
49 |
50 | public GlintUpload(@NonNull String url, @Nullable File file, @Nullable byte[] data, @Nullable String keyName, @NonNull TreeMap params) {
51 | mBuilder = createBuilder();
52 | mBuilder.url = url;
53 | mBuilder.file = file;
54 | mBuilder.data = data;
55 | mBuilder.keyName = keyName;
56 | mBuilder.params = params;
57 | if (mBuilder.file != null && TextUtils.isEmpty(mBuilder.keyName)) {
58 | mBuilder.keyName = mBuilder.file.getName();
59 | } else if (this.mBuilder == null && TextUtils.isEmpty(mBuilder.keyName)) {
60 | throw new RuntimeException(mBuilder.url + ":keyName is null");
61 | }
62 | }
63 |
64 | /**
65 | * 额外设置的header,出去登录相关的
66 | */
67 | public GlintUpload setHeader(@NonNull Headers.Builder headers) {
68 | mBuilder.headers = headers;
69 | return this;
70 | }
71 |
72 | /**
73 | * 增加cookie到头部信息
74 | *
75 | * @param cookie 登录返回的cookie
76 | */
77 | public GlintUpload addCookie(@NonNull String cookie) {
78 | mBuilder.cookie = cookie;
79 | return this;
80 | }
81 |
82 | /**
83 | * 设置是否使用通用签名,默认是
84 | *
85 | * @param signature 使用通用签名
86 | */
87 | public GlintUpload signature(boolean signature) {
88 | mBuilder.signature = signature;
89 | return this;
90 | }
91 |
92 | /**
93 | * 设置是否使用标准化序列化,默认否
94 | */
95 | public GlintUpload standardDeserialize(boolean standardDeserialize) {
96 | mBuilder.standardDeserialize = standardDeserialize;
97 | return this;
98 | }
99 |
100 | /**
101 | * 设置是否使用主线程
102 | */
103 | public GlintUpload mainThread() {
104 | mBuilder.mainThread = true;
105 | return this;
106 | }
107 |
108 | /**
109 | * 结果不是Json
110 | */
111 | public GlintUpload notJson(boolean notJson) {
112 | mBuilder.notJson = notJson;
113 | return this;
114 | }
115 |
116 | /**
117 | * 设置文件的协议
118 | *
119 | * @param mimeType 文件协议
120 | */
121 | public GlintUpload setMimeType(String mimeType) {
122 | mBuilder.mimeType = mimeType;
123 | return this;
124 | }
125 |
126 | /**
127 | * 使用自定义Module,可做高级操作
128 | * 该方法将会重置builder,使用时需在第一个使用
129 | *
130 | * @param module 自定义Module
131 | */
132 | public GlintUpload using(@NonNull BaseHttpModule module) {
133 | super.moduleUsing(module);
134 | return this;
135 | }
136 |
137 |
138 | /**
139 | * 设置其他配置
140 | *
141 | * @param otherBuilder 其他配置
142 | */
143 | public GlintUpload otherBuilder(@NonNull Bundle otherBuilder) {
144 | mBuilder.otherBuilder = otherBuilder;
145 | return this;
146 | }
147 |
148 | /**
149 | * 上传中途取消
150 | */
151 | public synchronized void cancel() {
152 | GlintUploadDispatcher.getInstance().cancel(this);
153 | }
154 |
155 | /**
156 | * 执行网络请求
157 | */
158 | public void execute() {
159 | if (!TextUtils.isEmpty(mBuilder.url)) {
160 | GlintUploadDispatcher.getInstance().executed(this);
161 | }
162 | }
163 |
164 | public void execute(@NonNull GlintUploadListener listener) {
165 | if (!TextUtils.isEmpty(mBuilder.url)) {
166 | mBuilder.listener = listener;
167 | GlintUploadDispatcher.getInstance().executed(this);
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/upload/GlintUploadBuilder.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.upload;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.ysbing.glint.base.BaseHttpModule;
6 | import com.ysbing.glint.base.Glint;
7 | import com.ysbing.glint.base.GlintBaseBuilder;
8 |
9 | import java.io.File;
10 |
11 | /**
12 | * 上传的参数配置
13 | *
14 | * @author ysbing
15 | * 创建于 2018/1/16
16 | */
17 | public final class GlintUploadBuilder extends GlintBaseBuilder implements Cloneable {
18 |
19 | /**
20 | * 上层的监听回调
21 | */
22 | public GlintUploadListener listener;
23 | /**
24 | * 要上传的文件,与{@link #data}同时不为空时优先使用data
25 | */
26 | public File file;
27 | /**
28 | * 要上传的数据,与{@link #file}同时不为空时优先使用data
29 | */
30 | public byte[] data;
31 | /**
32 | * 要上传的文件名称,默认使用file.getName,
33 | * 如使用byte[]而不指定keyName,将抛出异常
34 | */
35 | public String keyName;
36 | /**
37 | * 结果不是json
38 | */
39 | public boolean notJson = false;
40 |
41 | public GlintUploadBuilder(@NonNull Glint glint) {
42 | this(glint, true);
43 | }
44 |
45 | public GlintUploadBuilder(@NonNull Glint glint, boolean init) {
46 | super();
47 | this.mimeType = "multipart/form-data;charset=utf-8";
48 | if (init) {
49 | glint.configDefaultBuilder(this);
50 | }
51 | }
52 |
53 | @Override
54 | protected GlintUploadBuilder clone() throws CloneNotSupportedException {
55 | return (GlintUploadBuilder) super.clone();
56 | }
57 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/upload/GlintUploadCountingRequestBody.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.upload;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.ysbing.glint.util.UiKit;
6 |
7 | import java.io.IOException;
8 |
9 | import okhttp3.MediaType;
10 | import okhttp3.RequestBody;
11 | import okio.Buffer;
12 | import okio.BufferedSink;
13 | import okio.ForwardingSink;
14 | import okio.Okio;
15 | import okio.Sink;
16 |
17 | /**
18 | * 上传读文件操作
19 | *
20 | * @author ysbing
21 | * 创建于 2018/1/16
22 | */
23 | public final class GlintUploadCountingRequestBody extends RequestBody {
24 |
25 | private final RequestBody body;
26 | private final GlintUploadListener> listener;
27 | private long lastRefreshTime;
28 | private long lastBytesWritten;
29 | private long speed;
30 |
31 | GlintUploadCountingRequestBody(RequestBody body, GlintUploadListener> listener) {
32 | this.body = body;
33 | this.listener = listener;
34 | }
35 |
36 | @Override
37 | public long contentLength() throws IOException {
38 | return body.contentLength();
39 | }
40 |
41 | @Override
42 | public MediaType contentType() {
43 | return body.contentType();
44 | }
45 |
46 | @Override
47 | public void writeTo(@NonNull BufferedSink sink) throws IOException {
48 | BufferedSink bufferedSink;
49 | CountingSink countingSink = new CountingSink(sink);
50 | bufferedSink = Okio.buffer(countingSink);
51 | body.writeTo(bufferedSink);
52 | bufferedSink.flush();
53 | }
54 |
55 | protected final class CountingSink extends ForwardingSink {
56 |
57 | private long bytesWritten = 0;
58 |
59 | CountingSink(Sink delegate) {
60 | super(delegate);
61 | }
62 |
63 | @Override
64 | public void write(@NonNull Buffer source, long byteCount) throws IOException {
65 | super.write(source, byteCount);
66 | synchronized (GlintUploadCountingRequestBody.class) {
67 | bytesWritten += byteCount;
68 | UiKit.runOnMainThreadAsync(new Runnable() {
69 | @Override
70 | public void run() {
71 | if (listener != null) {
72 | try {
73 | long time = System.currentTimeMillis();
74 | if (time > lastRefreshTime) {
75 | long tempSpeed = (bytesWritten - lastBytesWritten) * 1000 / (time - lastRefreshTime);
76 | if (tempSpeed > 0) {
77 | speed = tempSpeed;
78 | }
79 | }
80 | lastRefreshTime = time;
81 | lastBytesWritten = bytesWritten;
82 | listener.onProgress(bytesWritten, contentLength(), speed, (int) (bytesWritten * 100 / contentLength()));
83 | } catch (Exception e) {
84 | e.printStackTrace();
85 | listener.onFail(e);
86 | }
87 | }
88 | }
89 | });
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/upload/GlintUploadDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.upload;
2 |
3 | import java.util.ArrayDeque;
4 | import java.util.Deque;
5 | import java.util.Iterator;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.SynchronousQueue;
8 | import java.util.concurrent.ThreadPoolExecutor;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import okhttp3.internal.Util;
12 |
13 | /**
14 | * 下载的线程调度
15 | *
16 | * @author ysbing
17 | * 创建于 2018/1/16
18 | */
19 |
20 | public final class GlintUploadDispatcher {
21 |
22 | private static final int maxRequests = 5;
23 |
24 | /**
25 | * Executes calls. Created lazily.
26 | */
27 | private ExecutorService executorService;
28 |
29 | /**
30 | * Ready async calls in the order they'll be run.
31 | */
32 | private final Deque> readyAsyncCalls = new ArrayDeque<>();
33 |
34 | /**
35 | * Running asynchronous calls. Includes canceled calls that haven't finished yet.
36 | */
37 | private final Deque> runningAsyncCalls = new ArrayDeque<>();
38 |
39 | private static final class InstanceHolder {
40 | static final GlintUploadDispatcher mInstance = new GlintUploadDispatcher();
41 | }
42 |
43 | public static GlintUploadDispatcher getInstance() {
44 | return InstanceHolder.mInstance;
45 | }
46 |
47 | private GlintUploadDispatcher() {
48 | }
49 |
50 | private synchronized ExecutorService executorService() {
51 | if (executorService == null) {
52 | executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
53 | new SynchronousQueue(), Util.threadFactory("GlintUpload Dispatcher", false));
54 | }
55 | return executorService;
56 | }
57 |
58 | public synchronized void executed(GlintUploadCore> call) {
59 | if (runningAsyncCalls.size() < maxRequests) {
60 | runningAsyncCalls.add(call);
61 | executorService().execute(call);
62 | } else {
63 | readyAsyncCalls.add(call);
64 | }
65 | }
66 |
67 | public synchronized void cancel(GlintUploadCore> call) {
68 | if (call != null) {
69 | readyAsyncCalls.remove(call);
70 | runningAsyncCalls.remove(call);
71 | call.coreCancel();
72 | }
73 | }
74 |
75 | public synchronized void cancelAll() {
76 | for (GlintUploadCore> call : readyAsyncCalls) {
77 | call.coreCancel();
78 | }
79 |
80 | for (GlintUploadCore> call : runningAsyncCalls) {
81 | call.coreCancel();
82 | }
83 | readyAsyncCalls.clear();
84 | runningAsyncCalls.clear();
85 | }
86 |
87 | private void promoteCalls() {
88 | // Already running max capacity.
89 | if (runningAsyncCalls.size() >= maxRequests) {
90 | return;
91 | }
92 | // No ready calls to promote.
93 | if (readyAsyncCalls.isEmpty()) {
94 | return;
95 | }
96 |
97 | for (Iterator> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
98 | GlintUploadCore> call = i.next();
99 | i.remove();
100 | runningAsyncCalls.add(call);
101 | executorService().execute(call);
102 | // Reached max capacity.
103 | if (runningAsyncCalls.size() >= maxRequests) {
104 | return;
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * Used by {@code GlintUploadCore#run} to signal completion.
111 | */
112 | void finished(GlintUploadCore> call) {
113 | synchronized (this) {
114 | finished(runningAsyncCalls, call);
115 | }
116 | }
117 |
118 | private void finished(Deque calls, T call) {
119 | calls.remove(call);
120 | promoteCalls();
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/upload/GlintUploadListener.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.upload;
2 |
3 | import com.ysbing.glint.http.GlintHttpListener;
4 |
5 | /**
6 | * 上传监听回调
7 | *
8 | * @author ysbing
9 | * 创建于 2018/1/16
10 | */
11 | public abstract class GlintUploadListener extends GlintHttpListener {
12 | /**
13 | * @param bytesWritten 已读取的长度
14 | * @param contentLength 总长度
15 | * @param speed 速度,单位是每秒/字节
16 | * @param percent 百分比,最小是0,最大是100
17 | * @throws Exception
18 | */
19 | public void onProgress(long bytesWritten, long contentLength, long speed, int percent) throws Exception {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/ContextHelper.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Application;
5 | import android.content.Context;
6 |
7 | import java.lang.ref.WeakReference;
8 | import java.lang.reflect.Method;
9 |
10 | /**
11 | * 获取Context对象的助手
12 | *
13 | * @author ysbing
14 | */
15 | public class ContextHelper {
16 |
17 | private static WeakReference mApplicationWeak;
18 |
19 | public static Context getAppContext() {
20 | return getApplication().getApplicationContext();
21 | }
22 |
23 | public static Application getApplication() {
24 | if (mApplicationWeak == null || mApplicationWeak.get() == null) {
25 | synchronized (ContextHelper.class) {
26 | if (mApplicationWeak == null || mApplicationWeak.get() == null) {
27 | try {
28 | @SuppressLint("PrivateApi") Class> clazzActivityThread = Class.forName("android.app.ActivityThread");
29 | Method currentActivityThread = clazzActivityThread.getDeclaredMethod("currentActivityThread");
30 | Method getApplicationMethod = clazzActivityThread.getMethod("getApplication");
31 | Object activityThread = currentActivityThread.invoke(null);
32 | Application application = (Application) getApplicationMethod.invoke(activityThread);
33 | mApplicationWeak = new WeakReference<>(application);
34 | } catch (Exception e) {
35 | e.printStackTrace();
36 | }
37 | }
38 | }
39 | }
40 | return mApplicationWeak.get();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/Md5Util.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 | import okio.BufferedSource;
10 | import okio.ByteString;
11 | import okio.Okio;
12 |
13 | /**
14 | * MD5获取工具
15 | *
16 | * @author ysbing
17 | */
18 | public class Md5Util {
19 |
20 | /**
21 | * 获取字符串的MD5
22 | *
23 | * @param str 要计算的字符串
24 | * @return 该文件的MD5
25 | */
26 | public static String getMD5Str(@NonNull String str) {
27 | return ByteString.encodeUtf8(str).md5().hex();
28 | }
29 |
30 | /**
31 | * 获取字节数组的MD5
32 | *
33 | * @param bytes 要计算的字节数组
34 | * @return 该文件的MD5
35 | */
36 | public static String getMD5Str(byte[] bytes) {
37 | if (bytes == null || bytes.length == 0) {
38 | return "";
39 | }
40 | return ByteString.of(bytes).md5().hex();
41 | }
42 |
43 | /**
44 | * 获取单个文件的MD5值
45 | *
46 | * @param file 要计算的文件
47 | * @return 该文件的MD5
48 | */
49 | public static String getMD5Str(@NonNull File file) {
50 | if (!file.isFile()) {
51 | return "";
52 | }
53 | BufferedSource source = null;
54 | try {
55 | source = Okio.buffer(Okio.source(file));
56 | return source.readByteString().md5().hex();
57 | } catch (IOException e) {
58 | e.printStackTrace();
59 | } finally {
60 | try {
61 | if (source != null) {
62 | source.close();
63 | }
64 | } catch (IOException e) {
65 | e.printStackTrace();
66 | }
67 | }
68 | return "";
69 | }
70 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/UiKit.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 | /*
3 | * Copyright (C) 2014 Qiujuer
4 | * WebSite http://www.qiujuer.net
5 | * Created 11/24/2014
6 | * Changed 2015/11/21
7 | * Version 3.0.0
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | import android.os.Looper;
23 |
24 | /**
25 | * This is UI operation class
26 | * You can run thread on MainThread By Async and Sync
27 | *
28 | * You don't need initialize, but when you don't need run
29 | * You should call {@link #dispose()} operation for destruction.
30 | */
31 | final public class UiKit {
32 | private static volatile UiKitHandlerPoster mainPoster = null;
33 |
34 | private static UiKitHandlerPoster getMainPoster() {
35 | if (mainPoster == null) {
36 | synchronized (UiKit.class) {
37 | if (mainPoster == null) {
38 | // This time is 1000/60 (60fps)
39 | mainPoster = new UiKitHandlerPoster(Looper.getMainLooper());
40 | }
41 | }
42 | }
43 | return mainPoster;
44 | }
45 |
46 | /**
47 | * Asynchronously
48 | * The child thread asynchronous run relative to the main thread,
49 | * not blocking the child thread
50 | *
51 | * @param runnable Runnable Interface
52 | */
53 | public static void runOnMainThreadAsync(Runnable runnable) {
54 | if (Looper.myLooper() == Looper.getMainLooper()) {
55 | runnable.run();
56 | return;
57 | }
58 | getMainPoster().async(runnable);
59 | }
60 |
61 | /**
62 | * Synchronously
63 | * The child thread relative thread synchronization operation,
64 | * blocking the child thread,
65 | * thread for the main thread to complete
66 | *
67 | * @param runnable Runnable Interface
68 | */
69 | public static void runOnMainThreadSync(Runnable runnable) {
70 | if (Looper.myLooper() == Looper.getMainLooper()) {
71 | runnable.run();
72 | return;
73 | }
74 | UiKitSyncPost poster = new UiKitSyncPost(runnable);
75 | getMainPoster().sync(poster);
76 | poster.waitRun();
77 | }
78 |
79 | /**
80 | * Synchronously
81 | * The child thread relative thread synchronization operation,
82 | * blocking the child thread,
83 | * thread for the main thread to complete
84 | * But the child thread just wait for the waitTime long.
85 | *
86 | * @param runnable Runnable Interface
87 | * @param waitTime wait for the main thread run Time
88 | * @param cancel on the child thread cancel the runnable task
89 | */
90 | public static void runOnMainThreadSync(Runnable runnable, int waitTime, boolean cancel) {
91 | if (Looper.myLooper() == Looper.getMainLooper()) {
92 | runnable.run();
93 | return;
94 | }
95 | UiKitSyncPost poster = new UiKitSyncPost(runnable);
96 | getMainPoster().sync(poster);
97 | poster.waitRun(waitTime, cancel);
98 | }
99 |
100 | public static void dispose() {
101 | if (mainPoster != null) {
102 | mainPoster.dispose();
103 | mainPoster = null;
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/UiKitHandlerPoster.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 | /*
3 | * Copyright (C) 2014 Qiujuer
4 | * WebSite http://www.qiujuer.net
5 | * Created 11/24/2014
6 | * Changed 1/07/2016
7 | * Version 3.0.0
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | import android.os.Handler;
23 | import android.os.Looper;
24 | import android.os.Message;
25 | import android.os.SystemClock;
26 |
27 | import java.util.LinkedList;
28 | import java.util.NoSuchElementException;
29 | import java.util.Queue;
30 |
31 | /**
32 | * UiKit Handler Poster extends Handler
33 | *
34 | * In class have two queue with {@link #mAsyncPool,#mSyncPool}
35 | */
36 | final class UiKitHandlerPoster extends Handler {
37 | private static final int ASYNC = 0x1;
38 | private static final int SYNC = 0x2;
39 | private final Queue mAsyncPool;
40 | private final Queue mSyncPool;
41 | private final int mMaxMillisInsideHandleMessage;
42 | private boolean isAsyncActive;
43 | private boolean isSyncActive;
44 |
45 | /**
46 | * Init this
47 | * @param looper Handler Looper
48 | *
49 | */
50 | UiKitHandlerPoster(Looper looper) {
51 | super(looper);
52 | this.mMaxMillisInsideHandleMessage = 16;
53 | mAsyncPool = new LinkedList<>();
54 | mSyncPool = new LinkedList<>();
55 | }
56 |
57 | /**
58 | * Pool clear
59 | */
60 | void dispose() {
61 | this.removeCallbacksAndMessages(null);
62 | this.mAsyncPool.clear();
63 | this.mSyncPool.clear();
64 | }
65 |
66 |
67 | void async(Runnable runnable) {
68 | synchronized (mAsyncPool) {
69 | mAsyncPool.offer(runnable);
70 | if (!isAsyncActive) {
71 | isAsyncActive = true;
72 | if (!sendMessage(obtainMessage(ASYNC))) {
73 | throw new RuntimeException("Could not send handler message");
74 | }
75 | }
76 | }
77 | }
78 |
79 | void sync(UiKitSyncPost post) {
80 | synchronized (mSyncPool) {
81 | mSyncPool.offer(post);
82 | if (!isSyncActive) {
83 | isSyncActive = true;
84 | if (!sendMessage(obtainMessage(SYNC))) {
85 | throw new RuntimeException("Could not send handler message");
86 | }
87 | }
88 | }
89 | }
90 |
91 | /**
92 | * Run in main thread
93 | *
94 | * @param msg call messages
95 | */
96 | @Override
97 | public void handleMessage(Message msg) {
98 | switch (msg.what) {
99 | case ASYNC: {
100 | boolean rescheduled = false;
101 | try {
102 | long started = SystemClock.uptimeMillis();
103 | while (true) {
104 | Runnable runnable = poolAsyncPost();
105 | if (runnable == null) {
106 | synchronized (mAsyncPool) {
107 | // Check again, this time in synchronized
108 | runnable = poolAsyncPost();
109 | if (runnable == null) {
110 | isAsyncActive = false;
111 | return;
112 | }
113 | }
114 | }
115 | runnable.run();
116 | long timeInMethod = SystemClock.uptimeMillis() - started;
117 | if (timeInMethod >= mMaxMillisInsideHandleMessage) {
118 | if (!sendMessage(obtainMessage(ASYNC))) {
119 | throw new RuntimeException("Could not send handler message");
120 | }
121 | rescheduled = true;
122 | return;
123 | }
124 | }
125 | } finally {
126 | isAsyncActive = rescheduled;
127 | }
128 | }
129 | case SYNC: {
130 | boolean rescheduled = false;
131 | try {
132 | long started = SystemClock.uptimeMillis();
133 | while (true) {
134 | UiKitSyncPost post = poolSyncPost();
135 | if (post == null) {
136 | synchronized (mSyncPool) {
137 | // Check again, this time in synchronized
138 | post = poolSyncPost();
139 | if (post == null) {
140 | isSyncActive = false;
141 | return;
142 | }
143 | }
144 | }
145 | post.run();
146 | long timeInMethod = SystemClock.uptimeMillis() - started;
147 | if (timeInMethod >= mMaxMillisInsideHandleMessage) {
148 | if (!sendMessage(obtainMessage(SYNC))) {
149 | throw new RuntimeException("Could not send handler message");
150 | }
151 | rescheduled = true;
152 | return;
153 | }
154 | }
155 | } finally {
156 | isSyncActive = rescheduled;
157 | }
158 | }
159 | default:
160 | super.handleMessage(msg);
161 | break;
162 | }
163 | }
164 |
165 | private Runnable poolAsyncPost() {
166 | synchronized (mAsyncPool) {
167 | try {
168 | return mAsyncPool.poll();
169 | } catch (NoSuchElementException e) {
170 | e.printStackTrace();
171 | return null;
172 | }
173 | }
174 | }
175 |
176 | private UiKitSyncPost poolSyncPost() {
177 | synchronized (mSyncPool) {
178 | try {
179 | return mSyncPool.poll();
180 | } catch (NoSuchElementException e) {
181 | e.printStackTrace();
182 | return null;
183 | }
184 | }
185 | }
186 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/UiKitSyncPost.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 | /*
3 | * Copyright (C) 2014 Qiujuer
4 | * WebSite http://www.qiujuer.net
5 | * Created 11/24/2014
6 | * Changed 03/08/2015
7 | * Version 3.0.0
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | /**
23 | * UiKitSyncPost use to {@link UiKitHandlerPoster}
24 | *
25 | * See {@link UiKit}
26 | */
27 | final class UiKitSyncPost {
28 | private final Runnable mRunnable;
29 | private volatile boolean isEnd = false;
30 |
31 |
32 | UiKitSyncPost(Runnable runnable) {
33 | this.mRunnable = runnable;
34 | }
35 |
36 | /**
37 | * Run to doing something
38 | */
39 | public void run() {
40 | if (!isEnd) {
41 | synchronized (this) {
42 | if (!isEnd) {
43 | mRunnable.run();
44 | isEnd = true;
45 | try {
46 | this.notifyAll();
47 | } catch (Exception e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | /**
56 | * Wait to run end
57 | */
58 | public void waitRun() {
59 | if (!isEnd) {
60 | synchronized (this) {
61 | if (!isEnd) {
62 | try {
63 | this.wait();
64 | } catch (InterruptedException e) {
65 | e.printStackTrace();
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * Wait for a period of time to run end
74 | *
75 | * @param time wait time
76 | * @param cancel when wait end cancel the run
77 | */
78 | public void waitRun(int time, boolean cancel) {
79 | if (!isEnd) {
80 | synchronized (this) {
81 | if (!isEnd) {
82 | try {
83 | this.wait(time);
84 | } catch (InterruptedException e) {
85 | e.printStackTrace();
86 | } finally {
87 | if (!isEnd && cancel) {
88 | isEnd = true;
89 | }
90 | }
91 | }
92 | }
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/glint/src/main/java/com/ysbing/glint/util/UiStack.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.glint.util;
2 |
3 | import android.app.Activity;
4 |
5 | import androidx.fragment.app.Fragment;
6 |
7 | import java.lang.ref.SoftReference;
8 | import java.util.Stack;
9 |
10 | /**
11 | * @author ysbing
12 | * 创建于 2017/11/2
13 | */
14 | public class UiStack {
15 | /**
16 | * Activity堆栈
17 | */
18 | private static final Stack> activityStack = new Stack<>();
19 | private static final Stack> fragmentStack = new Stack<>();
20 | /**
21 | * 应用管理对象
22 | */
23 | private static final UiStack instance = new UiStack();
24 |
25 | private UiStack() {
26 | }
27 |
28 | /**
29 | * 单一实例
30 | *
31 | * @return 应用管理对象
32 | */
33 | public static UiStack getInstance() {
34 | return instance;
35 | }
36 |
37 | /**
38 | * 移除所有Activity
39 | */
40 | public void popAllActivity() {
41 | activityStack.clear();
42 | }
43 |
44 | /**
45 | * 移除所有Fragment
46 | */
47 | public void popAllFragment() {
48 | fragmentStack.clear();
49 | }
50 |
51 | /**
52 | * 移除指定Activity
53 | */
54 | public void popActivity(Activity activity) {
55 | int removeIndex = -1;
56 | for (int i = 0; i < activityStack.size(); i++) {
57 | SoftReference activitySoftReference = activityStack.get(i);
58 | if (activitySoftReference != null && activitySoftReference.get() != null && activitySoftReference.get() == activity) {
59 | removeIndex = i;
60 | break;
61 | }
62 | }
63 | if (removeIndex > -1) {
64 | activityStack.remove(removeIndex);
65 | }
66 | }
67 |
68 | /**
69 | * 移除指定Fragment
70 | */
71 | public void popFragment(Fragment fragment) {
72 | int removeIndex = -1;
73 | for (int i = 0; i < fragmentStack.size(); i++) {
74 | SoftReference fragmentSoftReference = fragmentStack.get(i);
75 | if (fragmentSoftReference != null && fragmentSoftReference.get() != null && fragmentSoftReference.get() == fragment) {
76 | removeIndex = i;
77 | break;
78 | }
79 | }
80 | if (removeIndex > -1) {
81 | fragmentStack.remove(removeIndex);
82 | }
83 | }
84 |
85 | /**
86 | * 添加Activity
87 | */
88 | public void pushActivity(Activity activity) {
89 | SoftReference activitySoftReference = new SoftReference<>(activity);
90 | activityStack.add(activitySoftReference);
91 | }
92 |
93 | /**
94 | * 添加Activity
95 | */
96 | public void pushFragment(Fragment fragment) {
97 | SoftReference fragmentSoftReference = new SoftReference<>(fragment);
98 | fragmentStack.add(fragmentSoftReference);
99 | }
100 |
101 |
102 | /**
103 | * 根据类名获取Activity对象
104 | *
105 | * @param className 类名
106 | * @return Activity对象
107 | */
108 | public Activity getActivity(String className) {
109 | Class> cls = null;
110 | try {
111 | cls = Class.forName(className);
112 | } catch (Exception ignored) {
113 | }
114 | if (cls != null) {
115 | for (int i = activityStack.size() - 1; i >= 0; i--) {
116 | if (activityStack.get(i).get() != null && activityStack.get(i).get().getClass().equals(cls)) {
117 | return activityStack.get(i).get();
118 | }
119 | }
120 | } else {
121 | return null;
122 | }
123 | return null;
124 | }
125 |
126 | /**
127 | * 根据类名获取Fragment对象
128 | *
129 | * @param className 类名
130 | * @return Fragment对象
131 | */
132 | public Fragment getFragment(String className) {
133 | Class> cls = null;
134 | try {
135 | cls = Class.forName(className);
136 | } catch (Exception ignored) {
137 | }
138 | if (cls != null) {
139 | for (int i = 0; i < fragmentStack.size(); i++) {
140 | if (fragmentStack.get(i).get() != null && fragmentStack.get(i).get().getClass().equals(cls)) {
141 | return fragmentStack.get(i).get();
142 | }
143 | }
144 | } else {
145 | return null;
146 | }
147 | return null;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 | GLINT_VERSION=1.1.6-SNAPSHOT
16 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/publish.gradle:
--------------------------------------------------------------------------------
1 | afterEvaluate {
2 | apply from: rootProject.file('gradle/publish_remote.gradle')
3 | if (project.plugins.hasPlugin("com.android.internal.library")) {
4 | apply from: rootProject.file('gradle/publish_local_android.gradle')
5 | } else {
6 | apply from: rootProject.file('gradle/publish_local_java.gradle')
7 | }
8 | }
--------------------------------------------------------------------------------
/gradle/publish_local_android.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 |
3 | sourceCompatibility = rootProject.ext.javaVersion
4 | targetCompatibility = rootProject.ext.javaVersion
5 |
6 | version rootProject.ext.VERSION_NAME
7 | group rootProject.ext.GROUP
8 |
9 | task androidJavadocs(type: Javadoc) {
10 | source = android.sourceSets.main.java.srcDirs
11 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
12 | android.libraryVariants.all { variant ->
13 | if (variant.name == 'release') {
14 | owner.classpath += variant.javaCompileProvider.get().classpath
15 | }
16 | }
17 | exclude '**/R.html', '**/R.*.html', '**/index.html'
18 | }
19 |
20 | task sourcesJar(type: Jar) {
21 | from android.sourceSets.main.java.srcDirs
22 | classifier = 'sources'
23 | }
24 |
25 | task javadocJar(type: Jar, dependsOn: androidJavadocs) {
26 | classifier = 'javadoc'
27 | from androidJavadocs.destinationDir
28 | }
29 |
30 | artifacts {
31 | archives javadocJar
32 | archives sourcesJar
33 | }
34 |
35 | publishing {
36 | publications {
37 | maven(MavenPublication) {
38 | from components.release
39 | groupId = group
40 | artifactId = POM_ARTIFACT_ID
41 | version = version
42 | }
43 | }
44 | }
45 |
46 | task buildAndPublishLocalMaven(dependsOn: ['build', 'publishResguardPublicationToMavenLocal']) {}
--------------------------------------------------------------------------------
/gradle/publish_local_java.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 |
3 | sourceCompatibility = rootProject.ext.javaVersion
4 | targetCompatibility = rootProject.ext.javaVersion
5 |
6 | version rootProject.ext.VERSION_NAME
7 | group rootProject.ext.GROUP
8 |
9 | task sourcesJar(type: Jar) {
10 | from sourceSets.main.java.srcDirs
11 | classifier = 'sources'
12 | }
13 |
14 | task javadocJar(type: Jar, dependsOn: javadoc) {
15 | classifier = 'javadoc'
16 | from javadoc.destinationDir
17 | }
18 |
19 | artifacts {
20 | archives javadocJar
21 | archives sourcesJar
22 | }
23 |
24 | publishing {
25 | publications {
26 | maven(MavenPublication) {
27 | from components.java
28 | groupId = group
29 | artifactId = POM_ARTIFACT_ID
30 | version = version
31 | }
32 | }
33 | }
34 |
35 | task buildAndPublishLocalMaven(dependsOn: ['build', 'publishResguardPublicationToMavenLocal']) {}
36 |
37 |
--------------------------------------------------------------------------------
/gradle/publish_remote.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | def getBintrayUser() {
5 | return hasProperty('BINTRAY_USER') ? BINTRAY_USER :
6 | readPropertyFromLocalProperties('BINTRAY_USER')
7 | }
8 |
9 | def getBintrayKey() {
10 | return hasProperty('BINTRAY_APIKEY') ? BINTRAY_APIKEY :
11 | readPropertyFromLocalProperties('BINTRAY_APIKEY')
12 | }
13 |
14 | def readPropertyFromLocalProperties(String key) {
15 | Properties properties = new Properties()
16 | try {
17 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
18 | } catch (Exception ignore) {
19 | }
20 | return properties.getProperty(key)
21 | }
22 |
23 | bintray {
24 | user = getBintrayUser()
25 | key = getBintrayKey()
26 | configurations = ['archives']
27 | publications = ['ResguardPub']
28 |
29 | pkg {
30 | repo = POM_NAME
31 | userOrg = BINTRAY_ORGANIZATION
32 | name = "${GROUP}:${POM_ARTIFACT_ID}"
33 | desc = POM_DESCRIPTION
34 | licenses = BINTRAY_LICENCE
35 | vcsUrl = POM_SCM_URL
36 | websiteUrl = POM_URL
37 | issueTrackerUrl = POM_ISSUE_URL
38 | publicDownloadNumbers = true
39 | publish = true
40 | dryRun = false
41 | }
42 | }
43 |
44 | task buildAndPublishRepo(dependsOn: ['build', 'uploadArchives']) {
45 | doLast {
46 | println "*published to repo: ${project.group}:${project.name}:${project.version}"
47 | }
48 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ysbing/Glint/d7ca0d2d1e81cfc50777b59153c64cf01094bcee/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue May 30 17:11:33 CST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## 一、简介
2 |
3 | Glint是Android实现基于OkHttp的Http标准协议框架,支持接口请求,文件下载,文件上传,WebSocket四大功能。
4 |
5 | ## 二、框架特性
6 | * 底层框架为OkHttp
7 | * 支持异步请求、支持同步请求
8 | * 支持文件上传
9 | * 支持文件下载,支持暂停和恢复,断点续传
10 | * 支持长连接,兼容柚子IO
11 | * 失败重试机制
12 | * 智能生命周期,避免离开Activity或Fragment后导致内存泄露
13 | * 自定义HttpModule,用于自定义解析请求结果和添加公共参数
14 | * 支持取消某个请求、取消所有请求
15 | * 入口好记,分别是GlintHttp,GlintDownload,GlintUpload,GlintSocket
16 |
17 | ## 三、安装
18 |
19 | 推荐使用 Maven:
20 | ``` gradle
21 | dependencies {
22 | implementation 'com.ysbing.glint:glint:1.1.4'
23 | }
24 | ```
25 |
26 | ## 四、HTTP请求
27 |
28 | ### 请求队列
29 | 最基本的请求:
30 |
31 | ``` java
32 | String url = "https://www.sojson.com/open/api/lunar/json.shtml?date=2017-05-27";
33 | GlintHttp.get(url).execute(new GlintHttpListener() {
34 | @Override
35 | public void onSuccess(@NonNull String result) throws Throwable {
36 | super.onSuccess(result);
37 | }
38 | });
39 | ```
40 |
41 | ### 高级自定义配置
42 |
43 | 新建一个类,继承BaseHttpModule,使用HttpModule有两种使用方式
44 |
45 | 第一种方式:在AndroidManifest文件中声明该Module作为该Application通用的Module,需注意的是,每一个Application只能拥有一个通用的Module。
46 |
47 | ``` xml
48 |
52 | ```
53 |
54 |
55 | 第二种方式:在单独请求的时候,使用using方法,将该Module传入。
56 |
57 | 如果以上两种方式都不使用的话,将采取标准的json解析到对应的实体类。
58 |
59 | 使用第一种方式的HttpModule类必须是public的,以便在GlintHttp延迟初始化时,可以通过反射将它们实例化。
60 |
61 | 带有配置的请求,使用using方法
62 |
63 | ``` java
64 | String url = "https://www.sojson.com/open/api/lunar/json.shtml";
65 | TreeMap params = new TreeMap<>();
66 | params.put("date", "2018-10-01");
67 | GlintHttp.get(url, params).using(MyHttpModule.get()).execute(new GlintHttpListener() {
68 | @Override
69 | public void onSuccess(@NonNull LunarBean result) throws Throwable {
70 | super.onSuccess(result);
71 | }
72 |
73 | @Override
74 | public void onFail(@NonNull Throwable error) {
75 | super.onFail(error);
76 | }
77 |
78 | @Override
79 | public void onFinish() {
80 | super.onFinish();
81 | }
82 | });
83 | ```
84 |
85 | 其他使用方法请查阅GlintHttp的公开方法。
86 |
87 | ### HTTP请求添加参数
88 | 参数使用TreeMap来装载数据,使用时,请勿传入null键
89 |
90 | ### OkHttp配置
91 | 复写BaseHttpModule里的onOkHttpBuildCreate方法
92 |
93 | ``` java
94 | @Override
95 | public OkHttpClient.Builder onOkHttpBuildCreate(@NonNull Glint.GlintType clientType, @NonNull OkHttpClient.Builder builder) {
96 | return builder.readTimeout(3000L, TimeUnit.MILLISECONDS)
97 | .writeTimeout(5000L, TimeUnit.MILLISECONDS);
98 | }
99 | ```
100 |
101 | ### GlintHttpModule配置
102 |
103 | ``` java
104 | public class MyHttpModule extends BaseHttpModule {
105 |
106 | public static MyHttpModule get() {
107 | return new MyHttpModule();
108 | }
109 |
110 | @Override
111 | public OkHttpClient.Builder onOkHttpBuildCreate(@NonNull Glint.GlintType clientType, @NonNull OkHttpClient.Builder builder) {
112 | return builder.readTimeout(3000L, TimeUnit.MILLISECONDS)
113 | .writeTimeout(5000L, TimeUnit.MILLISECONDS);
114 | }
115 |
116 | @Override
117 | public void configDefaultBuilder(@NonNull GlintBaseBuilder builder) {
118 | super.configDefaultBuilder(builder);
119 | }
120 |
121 | @Override
122 | public boolean getHeaders(@NonNull Map originalHeader) throws Exception {
123 | return super.getHeaders(originalHeader);
124 | }
125 |
126 | @Override
127 | public boolean getParams(@NonNull TreeMap originalParams) throws Exception {
128 | return super.getParams(originalParams);
129 | }
130 |
131 | @Override
132 | public boolean getParams(@NonNull TreeMap originalParams, @Nullable JsonObject originalJsonParams) throws Exception {
133 | return super.getParams(originalParams, originalJsonParams);
134 | }
135 |
136 | @Override
137 | public UrlResult getUrl(@NonNull String originalUrl) throws Exception {
138 | return super.getUrl(originalUrl);
139 | }
140 |
141 | @Override
142 | public boolean customDeserialize(@NonNull GlintResultBean result, @NonNull JsonObject jsonObj, @NonNull Gson gson, @NonNull Type typeOfT) throws Exception {
143 | JsonElement statusElement = jsonObj.get("status");
144 | JsonElement messageElement = jsonObj.get("message");
145 | JsonElement dataElement = jsonObj.get("data");
146 | // 如果为空,可能是标准的json,不用判断状态码,直接解析
147 | if (statusElement == null) {
148 | result.setRunStatus(Glint.ResultStatus.STATUS_NORMAL);
149 | result.setData(GlintRequestUtil.standardDeserialize(gson, jsonObj, typeOfT));
150 | } else {
151 | // status节点,这里判断出是否请求成功
152 | int status = statusElement.getAsInt();
153 | if (messageElement != null) {
154 | // message节点
155 | String message = messageElement.getAsString();
156 | result.setMessage(message);
157 | }
158 | result.setStatus(status);
159 | if (status == 200) {
160 | result.setRunStatus(Glint.ResultStatus.STATUS_SUCCESS);
161 | if (dataElement != null) {
162 | result.setData(GlintRequestUtil.successDeserialize(gson, dataElement, typeOfT));
163 | }
164 | } else {
165 | result.setRunStatus(Glint.ResultStatus.STATUS_ERROR);
166 | }
167 | }
168 | return false;
169 | }
170 | }
171 | ```
172 | 这样就全部配置完毕了,如果你不需要这个高级配置的话,可无需任何配置也可以正常使用,在这份配置中,onOkHttpBuildCreate方法在本进程只执行一次,其他方法在每个请求都会执行。
173 |
174 |
175 | ## 五、文件下载请求
176 |
177 | 下面是基本的使用方法
178 |
179 | ``` java
180 | GlintDownload.download("https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk", new File(getExternalCacheDir(), "mobileqq_android.apk")).execute(new GlintDownloadListener() {
181 | @Override
182 | public void onProgress(long bytesWritten, long contentLength, long speed, int percent) throws Exception {
183 | super.onProgress(bytesWritten, contentLength, speed, percent);
184 | }
185 |
186 | @Override
187 | public void onSuccess(@NonNull File result) throws Throwable {
188 | super.onSuccess(result);
189 | }
190 | });
191 | ```
192 | 更多高级用法可参阅源码,如下载过程支持可取消,可暂停和恢复。
193 |
194 | ## 六、文件上传请求
195 |
196 | 下面是基本的使用方法:
197 |
198 | ```
199 | GlintUpload.upload("https://www.qq.com/", new File(getExternalCacheDir(), "mobileqq_android.apk")).execute(new GlintUploadListener() {
200 | @Override
201 | public void onProgress(long bytesWritten, long contentLength, long speed, int percent) throws Throwable {
202 | super.onProgress(bytesWritten, contentLength, speed, percent);
203 | }
204 |
205 | @Override
206 | public void onSuccess(@NonNull String result) throws Throwable {
207 | super.onSuccess(result);
208 | }
209 | });
210 | ```
211 |
212 | ## 七、WebSocket请求
213 |
214 | ### 下面是发送一条消息的使用方法:
215 |
216 | ``` java
217 | GlintSocket.sendIO(url, "cmd", "我是消息").execute(new GlintSocketListener() {
218 | @Override
219 | public void onProcess(@NonNull String result) throws Throwable {
220 | super.onProcess(result);
221 | }
222 |
223 | @Override
224 | public void onError(@NonNull String error) {
225 | super.onError(error);
226 | }
227 | });
228 | ```
229 |
230 | 注意,GlintSocketRequest有send和sendIO和两个方法,如果连接的是WebSocket就使用send,连接的是柚子IO就使用sendIO
231 |
232 | ### 下面是设置一个事件监听的使用方法:
233 |
234 | ``` java
235 | GlintSocket.on("http://socket.test", "cmd").execute(new GlintSocketListener() {
236 | @Override
237 | public void onProcess(@NonNull String result) throws Throwable {
238 | super.onProcess(result);
239 | }
240 |
241 | @Override
242 | public void onError(@NonNull String error) {
243 | super.onError(error);
244 | }
245 | });
246 | ```
247 | 当不需要Socket的时候,记住要将该连接做释放处理,否则会导致内存泄露,释放的使用方法如下:
248 |
249 | ``` java
250 | GlintSocket.off("http://socket.test", "cmd");
251 | ```
252 |
253 | ## 八、实用工具类
254 |
255 | ### 轻松获取一个Contex对象获取Application对象:
256 |
257 | ``` java
258 | Context context = ContextHelper.getAppContext();
259 | Application application = ContextHelper.getApplication();
260 | ```
261 |
262 | ### 获取字符串或文件的MD5值:
263 |
264 | ``` java
265 | String strMd5 = Md5Util.getMD5Str("hello, world");
266 | String fileMd5 = Md5Util.getMD5Str(new File("demo.txt"));
267 | ```
268 |
269 | ### 从子线程快速切到主线程:
270 |
271 | ``` java
272 | UiKit.runOnMainThreadAsync(new Runnable() {
273 | @Override
274 | public void run() {
275 |
276 | }
277 | });
278 | UiKit.runOnMainThreadSync(new Runnable() {
279 | @Override
280 | public void run() {
281 |
282 | }
283 | });
284 | UiKit.runOnMainThreadSync(new Runnable() {
285 | @Override
286 | public void run() {
287 |
288 | }
289 | }, 1000, false);
290 | ```
--------------------------------------------------------------------------------
/samples/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/samples/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | //apply plugin: 'YRouter'
3 |
4 | android {
5 | compileSdk 34
6 | defaultConfig {
7 | applicationId "com.ysbing.samples.glint"
8 | minSdk 14
9 | compileSdk 34
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'androidx.appcompat:appcompat:1.6.1'
24 | implementation 'androidx.recyclerview:recyclerview:1.3.2'
25 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
26 | implementation 'com.ysbing:ypermission:1.1.0'
27 | implementation project(':glint')
28 | }
--------------------------------------------------------------------------------
/samples/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/samples/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
31 |
35 |
39 |
43 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.appcompat.app.AppCompatActivity;
11 | import androidx.recyclerview.widget.DividerItemDecoration;
12 | import androidx.recyclerview.widget.LinearLayoutManager;
13 | import androidx.recyclerview.widget.RecyclerView;
14 |
15 | import com.ysbing.samples.glint.download.DownloadRequestActivity;
16 | import com.ysbing.samples.glint.http.HttpModuleRequestActivity;
17 | import com.ysbing.samples.glint.http.HttpRequestActivity;
18 | import com.ysbing.samples.glint.upload.UploadRequestActivity;
19 | import com.ysbing.samples.glint.websocket.WebSocketRequestActivity;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | public class MainActivity extends AppCompatActivity {
25 |
26 | private static class MyViewHolder extends RecyclerView.ViewHolder {
27 | private MyViewHolder(@NonNull View itemView) {
28 | super(itemView);
29 | }
30 | }
31 |
32 | private static class MyData {
33 | private final String title;
34 | private final String des;
35 |
36 | private MyData(String title, String des) {
37 | this.title = title;
38 | this.des = des;
39 | }
40 | }
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_main);
46 | RecyclerView recyclerView = findViewById(R.id.recyclerView);
47 | recyclerView.setLayoutManager(new LinearLayoutManager(this));
48 | recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
49 | recyclerView.setAdapter(new RecyclerView.Adapter() {
50 | private final List data = new ArrayList() {{
51 | add(new MyData("普通请求", "请求一个HTML页面"));
52 | add(new MyData("复杂请求", "自定义Module,可对OkHttp加拦截器和自定义解析数据等配置"));
53 | add(new MyData("上传请求", "将本地文件上传到服务器"));
54 | add(new MyData("下载请求", "下载文件到本地"));
55 | add(new MyData("WebSocket请求", "监听Socket事件"));
56 | }};
57 |
58 | @NonNull
59 | @Override
60 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
61 | View itemView = LayoutInflater.from(MainActivity.this)
62 | .inflate(R.layout.item_list, viewGroup, false);
63 | return new MyViewHolder(itemView);
64 | }
65 |
66 | @Override
67 | public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, int i) {
68 | TextView titleView = myViewHolder.itemView.findViewById(R.id.tv_title);
69 | TextView contentView = myViewHolder.itemView.findViewById(R.id.tv_content);
70 | titleView.setText(data.get(i).title);
71 | contentView.setText(data.get(i).des);
72 | myViewHolder.itemView.setOnClickListener(v -> {
73 | switch (myViewHolder.getBindingAdapterPosition()) {
74 | case 0:
75 | HttpRequestActivity.startAction(MainActivity.this);
76 | break;
77 | case 1:
78 | HttpModuleRequestActivity.startAction(MainActivity.this);
79 | break;
80 | case 2:
81 | UploadRequestActivity.startAction(MainActivity.this);
82 | break;
83 | case 3:
84 | DownloadRequestActivity.startAction(MainActivity.this);
85 | break;
86 | case 4:
87 | WebSocketRequestActivity.startAction(MainActivity.this);
88 | break;
89 | default:
90 | break;
91 | }
92 | });
93 | }
94 |
95 | @Override
96 | public int getItemCount() {
97 | return data.size();
98 | }
99 | });
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.graphics.Color;
6 | import android.os.Bundle;
7 | import android.os.Handler;
8 | import android.view.Gravity;
9 | import android.widget.TextView;
10 |
11 | public class SplashActivity extends Activity {
12 | private final Handler mHandler = new Handler();
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | TextView textView = new TextView(this);
18 | textView.setText("Glint");
19 | textView.setTextColor(getResources().getColor(android.R.color.white));
20 | textView.setTextSize(50f);
21 | textView.setGravity(Gravity.CENTER);
22 | textView.setBackgroundColor(Color.parseColor("#70C360"));
23 | setContentView(textView);
24 | mHandler.postDelayed(() -> {
25 | startActivity(new Intent(SplashActivity.this, MainActivity.class));
26 | finish();
27 | }, 1000L);
28 | }
29 |
30 | @Override
31 | protected void onPause() {
32 | super.onPause();
33 | if (isFinishing()) {
34 | mHandler.removeCallbacksAndMessages(null);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/download/DownloadRequestActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.download;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.ProgressBar;
9 | import android.widget.TextView;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import androidx.appcompat.app.AppCompatActivity;
14 |
15 | import com.ysbing.glint.download.GlintDownload;
16 | import com.ysbing.glint.download.GlintDownloadListener;
17 | import com.ysbing.samples.glint.R;
18 |
19 | import java.io.File;
20 |
21 | import okhttp3.Response;
22 |
23 | /**
24 | * Created by chenzhujie on 2019/5/14
25 | */
26 | public class DownloadRequestActivity extends AppCompatActivity {
27 | private Button mButtonDownload;
28 | private TextView mTextViewStatus;
29 | private ProgressBar mProgressBar;
30 | private GlintDownload mGlintDownload;
31 | private boolean isDownload;
32 |
33 | public static void startAction(Activity activity) {
34 | Intent intent = new Intent(activity, DownloadRequestActivity.class);
35 | activity.startActivity(intent);
36 | }
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_download_request);
42 | mButtonDownload = findViewById(R.id.btn_download);
43 | mTextViewStatus = findViewById(R.id.tv_status);
44 | mProgressBar = findViewById(R.id.progress_bar);
45 | mButtonDownload.setOnClickListener(new View.OnClickListener() {
46 | @Override
47 | public void onClick(View v) {
48 | if (mGlintDownload != null) {
49 | if (isDownload) {
50 | mGlintDownload.pause();
51 | } else {
52 | mGlintDownload.resume();
53 | }
54 | } else {
55 | request();
56 | }
57 | }
58 | });
59 | }
60 |
61 | @Override
62 | protected void onPause() {
63 | super.onPause();
64 | if (mGlintDownload != null) {
65 | mGlintDownload.cancel();
66 | }
67 | }
68 |
69 | private void request() {
70 | String url = "https://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk";
71 | File savePath = getExternalFilesDir("download");
72 | if (savePath == null) {
73 | savePath = getFilesDir();
74 | }
75 | mGlintDownload = GlintDownload.download(url, savePath);
76 | mGlintDownload.execute(new GlintDownloadListener() {
77 | @Override
78 | public void onStart() {
79 | super.onStart();
80 | String str = "onStart()";
81 | mTextViewStatus.setText(str);
82 | isDownload = true;
83 | mButtonDownload.setText("暂停");
84 | }
85 |
86 | @Override
87 | public void onProgress(long bytesWritten, long contentLength, long speed, int percent) throws Exception {
88 | super.onProgress(bytesWritten, contentLength, speed, percent);
89 | String str = percent + "%,onProgress()";
90 | mTextViewStatus.setText(str);
91 | mProgressBar.setProgress(percent);
92 | }
93 |
94 | @Override
95 | public void onPause() {
96 | super.onPause();
97 | String str = "onPause()";
98 | mTextViewStatus.setText(str);
99 | mButtonDownload.setText("继续");
100 | isDownload = false;
101 | }
102 |
103 | @Override
104 | public void onResume() {
105 | super.onResume();
106 | String str = "onResume()";
107 | mTextViewStatus.setText(str);
108 | mButtonDownload.setText("暂停");
109 | isDownload = true;
110 | }
111 |
112 | @Override
113 | public void onCancel() {
114 | super.onCancel();
115 | String str = "onCancel()";
116 | mTextViewStatus.setText(str);
117 | mButtonDownload.setText("开始");
118 | isDownload = false;
119 | }
120 |
121 | @Override
122 | public void onDownloadFail(@NonNull Throwable error, @Nullable Response response) {
123 | super.onDownloadFail(error, response);
124 | String str = "onDownloadFail(),error:" + error;
125 | mTextViewStatus.setText(str);
126 | mButtonDownload.setText("继续");
127 | isDownload = false;
128 | }
129 |
130 | @Override
131 | public void onSuccess(@NonNull File result) throws Throwable {
132 | super.onSuccess(result);
133 | String str = "onSuccess(),file:" + result;
134 | mTextViewStatus.setText(str);
135 | }
136 |
137 | @Override
138 | public void onFinish() {
139 | super.onFinish();
140 | isDownload = false;
141 | mGlintDownload = null;
142 | mButtonDownload.setText("开始");
143 | }
144 | });
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/http/HttpModuleRequestActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.http;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.TextView;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.appcompat.app.AppCompatActivity;
12 |
13 | import com.ysbing.glint.base.GlintResultBean;
14 | import com.ysbing.glint.http.GlintHttp;
15 | import com.ysbing.glint.http.GlintHttpListener;
16 | import com.ysbing.samples.glint.R;
17 |
18 | import java.util.TreeMap;
19 |
20 | import okhttp3.Headers;
21 |
22 | /**
23 | * Created by chenzhujie on 2019/5/13
24 | */
25 | public class HttpModuleRequestActivity extends AppCompatActivity {
26 |
27 | private TextView mTextView;
28 |
29 | public static void startAction(Activity activity) {
30 | Intent intent = new Intent(activity, HttpModuleRequestActivity.class);
31 | activity.startActivity(intent);
32 | }
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_http_request);
38 | Button button = findViewById(R.id.button);
39 | mTextView = findViewById(R.id.content);
40 | button.setOnClickListener(new View.OnClickListener() {
41 | @Override
42 | public void onClick(View v) {
43 | request();
44 | }
45 | });
46 | }
47 |
48 | private void request() {
49 | String url = "https://www.sojson.com/open/api/lunar/json.shtml";
50 | TreeMap params = new TreeMap<>();
51 | params.put("date", "2019-05-01");
52 | Headers.Builder headers = new Headers.Builder();
53 | headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36");
54 | GlintHttp.get(url, params).setHeader(headers)
55 | .using(MyHttpModule.get()).execute(new GlintHttpListener() {
56 | @Override
57 | public void onStart() {
58 | super.onStart();
59 | String str = "->onStart()";
60 | mTextView.setText(str);
61 | }
62 |
63 | @Override
64 | public void onResponse(@NonNull GlintResultBean resultBean) throws Throwable {
65 | super.onResponse(resultBean);
66 | String str = mTextView.getText() + "\n\n->onResponse()," + resultBean.getResponseStr();
67 | mTextView.setText(str);
68 | }
69 |
70 | @Override
71 | public void onSuccess(@NonNull LunarBean result) throws Throwable {
72 | super.onSuccess(result);
73 | String str = mTextView.getText() + "\n\n->onSuccess()," + result;
74 | mTextView.setText(str);
75 | }
76 |
77 | @Override
78 | public void onFail(@NonNull Throwable error) {
79 | super.onFail(error);
80 | String str = mTextView.getText() + "\n\n->onFail()," + error.toString();
81 | mTextView.setText(str);
82 | }
83 |
84 | @Override
85 | public void onError(int status, @NonNull String errMsg) throws Throwable {
86 | super.onError(status, errMsg);
87 | String str = mTextView.getText() + "\n\n->onError(),status:" + status + ",errMsg:" + errMsg;
88 | mTextView.setText(str);
89 | }
90 |
91 | @Override
92 | public void onFinish() {
93 | super.onFinish();
94 | String str = mTextView.getText() + "\n\n->onFinish()";
95 | mTextView.setText(str);
96 | }
97 | });
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/http/HttpRequestActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.http;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.text.method.ScrollingMovementMethod;
7 | import android.view.View;
8 | import android.widget.Button;
9 | import android.widget.TextView;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.appcompat.app.AppCompatActivity;
13 |
14 | import com.ysbing.glint.http.GlintHttp;
15 | import com.ysbing.glint.http.GlintHttpListener;
16 | import com.ysbing.samples.glint.R;
17 |
18 | /**
19 | * Created by chenzhujie on 2019/5/14
20 | */
21 | public class HttpRequestActivity extends AppCompatActivity {
22 |
23 | private TextView mTextView;
24 |
25 | public static void startAction(Activity activity) {
26 | Intent intent = new Intent(activity, HttpRequestActivity.class);
27 | activity.startActivity(intent);
28 | }
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_http_request);
34 | Button button = findViewById(R.id.button);
35 | mTextView = findViewById(R.id.content);
36 | mTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
37 | button.setOnClickListener(new View.OnClickListener() {
38 | @Override
39 | public void onClick(View v) {
40 | request();
41 | }
42 | });
43 | }
44 |
45 | private void request() {
46 | String url = "https://www.baidu.com/";
47 | GlintHttp.get(url).notJson(true).execute(new GlintHttpListener() {
48 | @Override
49 | public void onSuccess(@NonNull String result) throws Throwable {
50 | super.onSuccess(result);
51 | mTextView.setText(result);
52 | }
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/http/LunarBean.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.http;
2 |
3 | import java.util.List;
4 |
5 | public class LunarBean {
6 | public int year;
7 | public int month;
8 | public int day;
9 | public int lunarYear;
10 | public int lunarMonth;
11 | public int lunarDay;
12 | public String cnyear;
13 | public String cnmonth;
14 | public String cnday;
15 | public String hyear;
16 | public String cyclicalYear;
17 | public String cyclicalMonth;
18 | public String cyclicalDay;
19 | public String suit;
20 | public String taboo;
21 | public String animal;
22 | public String week;
23 | public List festivalList;
24 | public int maxDayInMonth;
25 | public boolean leap;
26 | public String lunarYearString;
27 | public boolean bigMonth;
28 | }
29 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/http/MyHttpModule.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.http;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.google.gson.Gson;
7 | import com.google.gson.JsonElement;
8 | import com.google.gson.JsonObject;
9 | import com.ysbing.glint.base.BaseHttpModule;
10 | import com.ysbing.glint.base.Glint;
11 | import com.ysbing.glint.base.GlintBaseBuilder;
12 | import com.ysbing.glint.base.GlintResultBean;
13 | import com.ysbing.glint.util.GlintRequestUtil;
14 |
15 | import java.lang.reflect.Type;
16 | import java.util.TreeMap;
17 | import java.util.concurrent.TimeUnit;
18 |
19 | import okhttp3.Headers;
20 | import okhttp3.OkHttpClient;
21 |
22 | public class MyHttpModule extends BaseHttpModule {
23 |
24 | public static MyHttpModule get() {
25 | return new MyHttpModule();
26 | }
27 |
28 | @Override
29 | public OkHttpClient.Builder onOkHttpBuildCreate(@NonNull Glint.GlintType clientType, @NonNull OkHttpClient.Builder builder) {
30 | return builder.readTimeout(3000L, TimeUnit.MILLISECONDS)
31 | .writeTimeout(5000L, TimeUnit.MILLISECONDS);
32 | }
33 |
34 | @Override
35 | public void configDefaultBuilder(
36 | @NonNull GlintBaseBuilder builder) {
37 | super.configDefaultBuilder(builder);
38 | }
39 |
40 | @Override
41 | public boolean getHeaders(@NonNull Headers.Builder originalHeader) throws Exception {
42 | return super.getHeaders(originalHeader);
43 | }
44 |
45 | @Override
46 | public boolean getParams(@NonNull TreeMap originalParams) throws Exception {
47 | return super.getParams(originalParams);
48 | }
49 |
50 | @Override
51 | public boolean getParams(@NonNull TreeMap originalParams, @Nullable JsonObject originalJsonParams) throws Exception {
52 | return super.getParams(originalParams, originalJsonParams);
53 | }
54 |
55 | @Override
56 | public UrlResult getUrl(@NonNull String originalUrl) throws Exception {
57 | return super.getUrl(originalUrl);
58 | }
59 |
60 | @Override
61 | public boolean customDeserialize(@NonNull GlintResultBean result,
62 | @NonNull JsonElement jsonEl, @NonNull Gson gson,
63 | @NonNull Type typeOfT) throws Exception {
64 | JsonObject jsonObj = jsonEl.getAsJsonObject();
65 | JsonElement statusElement = jsonObj.get("status");
66 | JsonElement messageElement = jsonObj.get("message");
67 | JsonElement dataElement = jsonObj.get("data");
68 | // 如果为空,可能是标准的json,不用判断状态码,直接解析
69 | if (statusElement == null) {
70 | result.setRunStatus(Glint.ResultStatus.STATUS_ERROR);
71 | result.setMessage(GlintRequestUtil.errorDeserialize(jsonObj));
72 | } else {
73 | // status节点,这里判断出是否请求成功
74 | int status = statusElement.getAsInt();
75 | if (messageElement != null) {
76 | // message节点
77 | String message = messageElement.getAsString();
78 | result.setMessage(message);
79 | }
80 | result.setStatus(status);
81 | if (status == 200) {
82 | result.setRunStatus(Glint.ResultStatus.STATUS_SUCCESS);
83 | if (dataElement != null) {
84 | result.setData(
85 | GlintRequestUtil.successDeserialize(gson, dataElement, typeOfT));
86 | }
87 | } else {
88 | result.setRunStatus(Glint.ResultStatus.STATUS_ERROR);
89 | }
90 | }
91 | return false;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/upload/ContentUriUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007-2008 OpenIntents.org
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.ysbing.samples.glint.upload;
18 |
19 | import android.content.ContentUris;
20 | import android.content.Context;
21 | import android.database.Cursor;
22 | import android.net.Uri;
23 | import android.os.Build;
24 | import android.os.Environment;
25 | import android.provider.DocumentsContract;
26 | import android.provider.MediaStore;
27 |
28 | public class ContentUriUtil {
29 | /**
30 | * Get a file path from a Uri. This will get the the path for Storage Access
31 | * Framework Documents, as well as the _data field for the MediaStore and
32 | * other file-based ContentProviders.
33 | *
34 | * @param context The context.
35 | * @param uri The Uri to query.
36 | * @author paulburke
37 | */
38 | public static String getPath(final Context context, final Uri uri) {
39 | // DocumentProvider
40 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
41 | && DocumentsContract.isDocumentUri(context, uri)) {
42 | // ExternalStorageProvider
43 | if (isExternalStorageDocument(uri)) {
44 | final String docId = DocumentsContract.getDocumentId(uri);
45 | final String[] split = docId.split(":");
46 | final String type = split[0];
47 |
48 | if ("primary".equalsIgnoreCase(type)) {
49 | return Environment.getExternalStorageDirectory() + "/" + split[1];
50 | }
51 |
52 | // TODO handle non-primary volumes
53 | }
54 | // DownloadsProvider
55 | else if (isDownloadsDocument(uri)) {
56 |
57 | final String id = DocumentsContract.getDocumentId(uri);
58 | final Uri contentUri = ContentUris.withAppendedId(
59 | Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
60 |
61 | return getDataColumn(context, contentUri, null, null);
62 | }
63 | // MediaProvider
64 | else if (isMediaDocument(uri)) {
65 | final String docId = DocumentsContract.getDocumentId(uri);
66 | final String[] split = docId.split(":");
67 | final String type = split[0];
68 |
69 | Uri contentUri = null;
70 | if ("image".equals(type)) {
71 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
72 | } else if ("video".equals(type)) {
73 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
74 | } else if ("audio".equals(type)) {
75 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
76 | }
77 |
78 | final String selection = "_id=?";
79 | final String[] selectionArgs = new String[]{
80 | split[1]
81 | };
82 |
83 | return getDataColumn(context, contentUri, selection, selectionArgs);
84 | }
85 | }
86 | // MediaStore (and general)
87 | else if ("content".equalsIgnoreCase(uri.getScheme())) {
88 | return getDataColumn(context, uri, null, null);
89 | }
90 | // File
91 | else if ("file".equalsIgnoreCase(uri.getScheme())) {
92 | return uri.getPath();
93 | }
94 |
95 | return null;
96 | }
97 |
98 | /**
99 | * Get the value of the data column for this Uri. This is useful for
100 | * MediaStore Uris, and other file-based ContentProviders.
101 | *
102 | * @param context The context.
103 | * @param uri The Uri to query.
104 | * @param selection (Optional) Filter used in the query.
105 | * @param selectionArgs (Optional) Selection arguments used in the query.
106 | * @return The value of the _data column, which is typically a file path.
107 | */
108 | public static String getDataColumn(Context context, Uri uri, String selection,
109 | String[] selectionArgs) {
110 |
111 | Cursor cursor = null;
112 | final String column = "_data";
113 | final String[] projection = {
114 | column
115 | };
116 |
117 | try {
118 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
119 | null);
120 | if (cursor != null && cursor.moveToFirst()) {
121 | final int column_index = cursor.getColumnIndexOrThrow(column);
122 | return cursor.getString(column_index);
123 | }
124 | } finally {
125 | if (cursor != null) {
126 | cursor.close();
127 | }
128 | }
129 | return null;
130 | }
131 |
132 |
133 | /**
134 | * @param uri The Uri to check.
135 | * @return Whether the Uri authority is ExternalStorageProvider.
136 | */
137 | public static boolean isExternalStorageDocument(Uri uri) {
138 | return "com.android.externalstorage.documents".equals(uri.getAuthority());
139 | }
140 |
141 | /**
142 | * @param uri The Uri to check.
143 | * @return Whether the Uri authority is DownloadsProvider.
144 | */
145 | public static boolean isDownloadsDocument(Uri uri) {
146 | return "com.android.providers.downloads.documents".equals(uri.getAuthority());
147 | }
148 |
149 | /**
150 | * @param uri The Uri to check.
151 | * @return Whether the Uri authority is MediaProvider.
152 | */
153 | public static boolean isMediaDocument(Uri uri) {
154 | return "com.android.providers.media.documents".equals(uri.getAuthority());
155 | }
156 | }
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/upload/UploadRequestActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.upload;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.text.TextUtils;
9 | import android.widget.Button;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 | import android.widget.Toast;
13 |
14 | import androidx.activity.result.contract.ActivityResultContracts;
15 | import androidx.annotation.NonNull;
16 | import androidx.annotation.RequiresApi;
17 | import androidx.appcompat.app.AppCompatActivity;
18 |
19 | import com.ysbing.glint.upload.GlintUpload;
20 | import com.ysbing.glint.upload.GlintUploadListener;
21 | import com.ysbing.samples.glint.R;
22 | import com.ysbing.ypermission.PermissionManager;
23 |
24 | import java.io.File;
25 |
26 | /**
27 | * Created by chenzhujie on 2019/5/14
28 | */
29 | public class UploadRequestActivity extends AppCompatActivity {
30 |
31 | private TextView mTextViewStatus;
32 | private ImageView mImageView;
33 | private String path;
34 |
35 | public static void startAction(Activity activity) {
36 | Intent intent = new Intent(activity, UploadRequestActivity.class);
37 | activity.startActivity(intent);
38 | }
39 |
40 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_upload_request);
45 | Button buttonSelect = findViewById(R.id.btn_select);
46 | Button buttonUpload = findViewById(R.id.btn_upload);
47 | mTextViewStatus = findViewById(R.id.tv_status);
48 | mImageView = findViewById(R.id.iv_image);
49 | buttonSelect.setOnClickListener(v -> {
50 | String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
51 | PermissionManager.requestPermission(UploadRequestActivity.this, permissions, new PermissionManager.PermissionsListener() {
52 | @Override
53 | public void onPermissionGranted() {
54 | pickPhoto();
55 | }
56 | });
57 | });
58 | buttonUpload.setOnClickListener(v -> request());
59 | }
60 |
61 | /***
62 | * 从相册中选取图片
63 | */
64 | public void pickPhoto() {
65 | Intent intent = new Intent();
66 | intent.setType("image/*");
67 | intent.setAction(Intent.ACTION_GET_CONTENT);
68 | registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
69 | if (result.getResultCode() == Activity.RESULT_OK) {
70 | if (result.getData() != null && result.getData().getData() != null) {
71 | path = ContentUriUtil.getPath(UploadRequestActivity.this, result.getData().getData());
72 | mImageView.setImageURI(result.getData().getData());
73 | }
74 | }
75 | }).launch(Intent.createChooser(intent, "Choose Image..."));
76 | }
77 |
78 | private void request() {
79 | if (TextUtils.isEmpty(path)) {
80 | Toast.makeText(this, "请选择文件", Toast.LENGTH_SHORT).show();
81 | } else {
82 | String url = "http://google.com/upload.do";
83 | GlintUpload.upload(url, new File(path)).execute(new GlintUploadListener() {
84 | @Override
85 | public void onStart() {
86 | super.onStart();
87 | String str = "onStart()";
88 | mTextViewStatus.setText(str);
89 | }
90 |
91 | @Override
92 | public void onProgress(long bytesWritten, long contentLength, long speed, int percent) throws Exception {
93 | super.onProgress(bytesWritten, contentLength, speed, percent);
94 | String str = "onProgress(),percent:" + percent + "%";
95 | mTextViewStatus.setText(str);
96 | }
97 |
98 | @Override
99 | public void onSuccess(@NonNull String result) throws Throwable {
100 | super.onSuccess(result);
101 | String str = "onSuccess()," + result;
102 | mTextViewStatus.setText(str);
103 | }
104 |
105 | @Override
106 | public void onFail(@NonNull Throwable error) {
107 | super.onFail(error);
108 | String str = "onFail()," + error;
109 | mTextViewStatus.setText(str);
110 | }
111 | });
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/websocket/MySocketHttpModule.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.websocket;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | import com.google.gson.Gson;
7 | import com.google.gson.JsonElement;
8 | import com.ysbing.glint.socket.SocketHttpModule;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | /**
13 | * 自定义网络模块
14 | *
15 | * @author ysbing
16 | */
17 | public class MySocketHttpModule extends SocketHttpModule {
18 | public static final String SOCKET_CMD_SEND = "MY_CMD";
19 |
20 | public static MySocketHttpModule get() {
21 | return new MySocketHttpModule();
22 | }
23 |
24 | @Override
25 | public String getCmdId(@NonNull JsonElement jsonEl, @NonNull Gson gson, @NonNull Type typeOfT) {
26 | return SOCKET_CMD_SEND;
27 | }
28 |
29 | @Override
30 | public T customDeserialize(@NonNull JsonElement jsonEl, @NonNull Gson gson, @NonNull Type typeOfT) throws Exception {
31 | return super.customDeserialize(jsonEl, gson, typeOfT);
32 | }
33 | }
--------------------------------------------------------------------------------
/samples/src/main/java/com/ysbing/samples/glint/websocket/WebSocketRequestActivity.java:
--------------------------------------------------------------------------------
1 | package com.ysbing.samples.glint.websocket;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.text.method.ScrollingMovementMethod;
7 | import android.view.View;
8 | import android.widget.Button;
9 | import android.widget.TextView;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.appcompat.app.AppCompatActivity;
13 |
14 | import com.ysbing.glint.socket.GlintSocket;
15 | import com.ysbing.glint.socket.GlintSocketListener;
16 | import com.ysbing.samples.glint.R;
17 |
18 | import java.util.concurrent.atomic.AtomicInteger;
19 |
20 | /**
21 | * Created by chenzhujie on 2019/5/14
22 | */
23 | public class WebSocketRequestActivity extends AppCompatActivity {
24 | private static final String SOCKET_URL = "ws://echo.websocket.org";
25 | private final AtomicInteger msgId = new AtomicInteger();
26 | private TextView mTextView;
27 | private String text = "";
28 |
29 | public static void startAction(Activity activity) {
30 | Intent intent = new Intent(activity, WebSocketRequestActivity.class);
31 | activity.startActivity(intent);
32 | }
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_websocket_request);
38 | Button buttonOn = findViewById(R.id.btn_on);
39 | Button buttonSend = findViewById(R.id.btn_send);
40 | mTextView = findViewById(R.id.et_content);
41 | mTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
42 | buttonOn.setOnClickListener(new View.OnClickListener() {
43 | @Override
44 | public void onClick(View v) {
45 | GlintSocket.off(SOCKET_URL, GlintSocket.EVENT_CONNECT);
46 | GlintSocket.off(SOCKET_URL, MySocketHttpModule.SOCKET_CMD_SEND);
47 | socketOn();
48 | }
49 | });
50 | buttonSend.setOnClickListener(new View.OnClickListener() {
51 | @Override
52 | public void onClick(View v) {
53 | socketSend();
54 | }
55 | });
56 | }
57 |
58 | @Override
59 | protected void onStop() {
60 | super.onStop();
61 | if (isFinishing()) {
62 | GlintSocket.off(SOCKET_URL);
63 | }
64 | }
65 |
66 | private void socketOn() {
67 | GlintSocket.on(SOCKET_URL, GlintSocket.EVENT_CONNECT).execute(new GlintSocketListener() {
68 | @Override
69 | public void onProcess(@NonNull String result) throws Throwable {
70 | super.onProcess(result);
71 | text = "socketOn,EVENT_CONNECT\n\n" + text;
72 | updateEditText();
73 | }
74 |
75 | @Override
76 | public void onError(@NonNull String error) {
77 | super.onError(error);
78 | text = "socketOn,EVENT_CONNECT,error:" + error + "\n\n" + text;
79 | updateEditText();
80 | }
81 | });
82 | GlintSocket.on(SOCKET_URL, MySocketHttpModule.SOCKET_CMD_SEND)
83 | .using(MySocketHttpModule.get()).execute(new GlintSocketListener() {
84 | @Override
85 | public void onProcess(@NonNull String result) throws Throwable {
86 | super.onProcess(result);
87 | text = "socketOn," + MySocketHttpModule.SOCKET_CMD_SEND + ",result:" + result + "\n\n" + text;
88 | updateEditText();
89 | }
90 |
91 | @Override
92 | public void onError(@NonNull String error) {
93 | super.onError(error);
94 | text = "socketOn," + MySocketHttpModule.SOCKET_CMD_SEND + ",error:" + error + "\n\n" + text;
95 | updateEditText();
96 | }
97 | });
98 | }
99 |
100 | private void socketSend() {
101 | GlintSocket.send(SOCKET_URL, "我是消息" + msgId.incrementAndGet()).execute();
102 | }
103 |
104 | private void updateEditText() {
105 | mTextView.setText(text);
106 | }
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/samples/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_download_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
33 |
34 |
45 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_http_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_upload_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
31 |
32 |
40 |
41 |
52 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_websocket_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
31 |
32 |
43 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/item_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
31 |
32 |
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ysbing/Glint/d7ca0d2d1e81cfc50777b59153c64cf01094bcee/samples/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ysbing/Glint/d7ca0d2d1e81cfc50777b59153c64cf01094bcee/samples/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/samples/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Glint
3 |
4 |
--------------------------------------------------------------------------------
/samples/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':samples', ':glint'
2 |
--------------------------------------------------------------------------------