package com.example.kucun2.entity.data;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.example.kucun2.entity.Information;
import com.example.kucun2.entity.User;
import com.example.kucun2.function.MyAppFunction;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import okhttp3.*;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
/**
* 重构版API客户端 - 更灵活的类型处理
* 主要改进:
* 1. 分离请求参数类型和响应类型
* 2. 支持多种请求方法(GET/POST/PUT/DELETE)
* 3. 支持表单和JSON两种请求格式
* 4. 自动推导响应类型
* 5. 统一的请求执行流程
*/
public class ApiClient {
private static final Gson gson = GsonFactory.createGson();
private static final String TAG = "ApiClient";
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private static final int MAX_RETRY = 3;
private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
// ====================== 核心请求方法 ======================
/**
* 执行API请求(核心方法)
* @param request 构建好的OkHttp请求
* @param responseType 期望的响应类型
* @param callback 回调接口
* @param <R> 响应数据类型
*/
public static <R> void executeRequest(Request request,
Type responseType,
ApiCallback<R> callback) {
OkHttpClient client = MyAppFunction.getClient();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleFailure(call, e, callback, 0);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResponse(response, responseType, callback);
}
});
}
// ====================== 请求构建方法 ======================
/**
* 构建JSON请求
* @param url API地址
* @param method 请求方法("POST", "PUT", "DELETE")
* @param requestData 请求数据对象
* @return 构建好的Request对象
*/
public static Request buildJsonRequest(String url, String method, Object requestData) {
String jsonRequest = ReflectionJsonUtils.toJson(requestData);
Log.d(TAG, method + " URL: " + url);
Log.d(TAG, "请求数据: " + jsonRequest);
RequestBody body = RequestBody.create(JSON, jsonRequest);
return new Request.Builder()
.url(url)
.method(method, body)
.build();
}
/**
* 构建表单请求
* @param url API地址
* @param method 请求方法
* @param formData 表单数据
* @return 构建好的Request对象
*/
public static Request buildFormRequest(String url, String method, Map<String, String> formData) {
FormBody.Builder builder = new FormBody.Builder();
for (Map.Entry<String, String> entry : formData.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
Log.d(TAG, method + " URL: " + url);
Log.d(TAG, "表单数据: " + formData);
return new Request.Builder()
.url(url)
.method(method, builder.build())
.build();
}
// ====================== 响应处理方法 ======================
private static <R> void handleResponse(Response response,
Type responseType,
ApiCallback<R> callback) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) {
String error = "HTTP " + response.code() + ": " + response.message();
Log.e(TAG, error);
notifyError(callback, response.code(), error);
return;
}
String jsonResponse = responseBody.string();
Log.d(TAG, "服务器响应: " + jsonResponse);
// 解析服务端的Information包装
Information<R> wrapper = gson.fromJson(jsonResponse, responseType);
if (wrapper != null && wrapper.getStatus() == 200) {
notifySuccess(callback, wrapper.getData());
} else {
String errorMsg = wrapper != null ?
"服务端错误: " + wrapper.getStatus() + " - " + wrapper.getText() :
"无效的响应格式";
Log.e(TAG, errorMsg);
notifyError(callback,
wrapper != null ? wrapper.getStatus() : -1,
errorMsg);
}
} catch (Exception e) {
Log.e(TAG, "响应处理异常: " + e.getMessage());
notifyError(callback, -2, "数据处理异常: " + e.getMessage());
}
}
// ====================== 失败处理与重试 ======================
private static <R> void handleFailure(Call call,
IOException e,
ApiCallback<R> callback,
int retryCount) {
if (retryCount < MAX_RETRY) {
Log.w(TAG, "请求失败,第" + (retryCount + 1) + "次重试: " + e.getMessage());
MAIN_HANDLER.postDelayed(() -> {
call.clone().enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleFailure(call, e, callback, retryCount + 1);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResponse(response, getResponseType(callback), callback);
}
});
}, 2000);
} else {
Log.e(TAG, "最终请求失败: " + e.getMessage());
notifyError(callback, -1, "网络请求失败: " + e.getMessage());
}
}
// ====================== 类型处理工具 ======================
/**
* 获取响应类型(通过回调接口的泛型参数)
*/
private static <R> Type getResponseType(ApiCallback<R> callback) {
if (callback == null) {
return new TypeToken<Information<Object>>(){}.getType();
}
// 尝试获取泛型类型
Type[] genericInterfaces = callback.getClass().getGenericInterfaces();
for (Type type : genericInterfaces) {
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
if (pType.getRawType().equals(ApiCallback.class)) {
Type dataType = pType.getActualTypeArguments()[0];
return TypeToken.getParameterized(Information.class, dataType).getType();
}
}
}
// 默认返回Object类型
Log.w(TAG, "无法确定响应类型,使用默认Object类型");
return new TypeToken<Information<Object>>(){}.getType();
}
// ====================== 回调通知方法 ======================
private static <R> void notifySuccess(ApiCallback<R> callback, R data) {
if (callback != null) {
MAIN_HANDLER.post(() -> callback.onSuccess(data));
}
}
private static <R> void notifyError(ApiCallback<R> callback, int code, String error) {
if (callback != null) {
MAIN_HANDLER.post(() -> callback.onError(code, error));
}
}
// ====================== 专用API方法 ======================
/**
* 执行JSON API请求
* @param url API地址
* @param method 请求方法
* @param requestData 请求数据
* @param callback 回调接口
* @param <T> 请求数据类型
* @param <R> 响应数据类型
*/
public static <T, R> void jsonRequest(String url,
String method,
T requestData,
ApiCallback<R> callback) {
Request request = buildJsonRequest(url, method, requestData);
executeRequest(request, getResponseType(callback), callback);
}
/**
* 执行表单API请求
* @param url API地址
* @param method 请求方法
* @param formData 表单数据
* @param callback 回调接口
* @param <R> 响应数据类型
*/
public static <R> void formRequest(String url,
String method,
Map<String, String> formData,
ApiCallback<R> callback) {
Request request = buildFormRequest(url, method, formData);
executeRequest(request, getResponseType(callback), callback);
}
// ====================== 便捷方法 ======================
public static <T, R> void postJson(String url, T data, ApiCallback<R> callback) {
jsonRequest(url, "POST", data, callback);
}
public static <T, R> void putJson(String url, T data, ApiCallback<R> callback) {
jsonRequest(url, "PUT", data, callback);
}
public static <T, R> void deleteJson(String url, T data, ApiCallback<R> callback) {
jsonRequest(url, "DELETE", data, callback);
}
public static <R> void get(String url, ApiCallback<R> callback) {
Request request = new Request.Builder().url(url).get().build();
executeRequest(request, getResponseType(callback), callback);
}
// ====================== 登录专用方法 ======================
public static void login(String username, String password, LoginCallback callback) {
String url = MyAppFunction.getApiUrl("url_login");
Log.d(TAG, "login: " + url);
formRequest(url, "POST", Map.of(
"andy", username,
"pass", password
), new ApiCallback<User>() {
@Override
public void onSuccess(User user) {
if (callback != null) callback.onSuccess(user);
}
@Override
public void onError(int statusCode, String error) {
if (callback != null) callback.onFailure(error);
}
});
}
// ====================== 回调接口定义 ======================
public interface ApiCallback<T> {
void onSuccess(T data);
void onError(int statusCode, String error);
}
public interface LoginCallback {
void onSuccess(User user);
void onFailure(String error);
}
}
package com.example.kucun2.entity.data;
import android.util.Log;
import com.example.kucun2.function.MyAppFunction;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 可同步实体基类
* 提供实体到服务端的同步功能,包含自动重试机制和线程管理
*/
public abstract class SynchronizableEntity implements EntityClassGrassrootsid {
private static final String TAG = "SynchronizableEntity";
// 双重缓存结构:外层缓存类名,内层缓存字段名->Field对象
private static final Map<Class<?>, Map<String, Field>> CLASS_FIELD_CACHE = new HashMap<>();
// 网络请求线程池(静态共享)
private static final ExecutorService NETWORK_EXECUTOR = Executors.newFixedThreadPool(4);
/**
* 获取指定类型操作的端点URL
*
* @param type 操作类型(如"create", "update", "delete"等)
* @return 完整的端点URL路径
*
* @apiNote 该方法通过资源键名拼接规则查找对应的URL资源
* @example 对于Product类的create操作,查找键为:"url_create_product"
*/
public String getEndpoint(String type) {
// 构建资源键名:url_操作类型_类名小写
String key = "url_" + type + "_" + this.getClass().getSimpleName().toLowerCase();
return MyAppFunction.getApiUrl(key);
}
//================ 核心同步方法 ================//
/**
* 同步实体到服务端(公开接口)
*
* @param type 操作类型(如"add", "update","select","detect","all"等)
* @param callback 同步结果回调接口
*
* @implNote 内部调用私有方法实现带重试机制的同步
* @see #sync(String, SyncCallback, int)
*/
public void sync(String type, SyncCallback callback) {
sync(type, callback, 0); // 初始重试次数为0
}
private <T extends SynchronizableEntity> ApiClient.ApiCallback<T> getApiCallback(String type,SyncCallback callback,int retryCount,T thistype){
return new ApiClient.ApiCallback<T>() {
@Override
public void onSuccess(T responseData) {
handleSyncSuccess(responseData, callback);
}
@Override
public void onError(int statusCode, String error) {
Log.d(TAG, "onError: "+thistype);
handleSyncError(type, statusCode, error, callback, retryCount);
}
};
}
private <T extends SynchronizableEntity> ApiClient.ApiCallback<T> getApiCallbackList(String type,SyncCallback callback,int retryCount,T thistype){
return new ApiClient.ApiCallback<T>() {
@Override
public void onSuccess(T responseData) {
handleSyncSuccess(responseData, callback);
}
@Override
public void onError(int statusCode, String error) {
handleSyncError(type, statusCode, error, callback, retryCount);
}
};
}
/**
* 带重试机制的同步实现(私有方法)
*
* @param type 操作类型
* @param callback 同步结果回调
* @param retryCount 当前重试次数
*
* @implSpec 1. 构建完整端点URL
* 2. 通过线程池提交网络请求
* 3. 处理成功/失败回调
*/
private void sync(String type, SyncCallback callback, int retryCount) {
// 构建完整端点URL
String endpoint =getEndpoint(type);
Log.d(TAG, "同步端点: " + endpoint + ", 重试次数: " + retryCount);
// 提交到线程池执行网络请求
NETWORK_EXECUTOR.execute(() -> {
ApiClient.postJson(endpoint, this, getApiCallback(type,callback,retryCount,this));
});
}
/**
* 处理同步成功结果
*
* @param responseData 服务端返回的实体数据
* @param callback 同步结果回调
*
* @implNote 1. 更新实体ID
* 2. 记录成功日志
* 3. 触发成功回调
*/
private void handleSyncSuccess(SynchronizableEntity responseData, SyncCallback callback) {
// 更新实体ID(如果服务端返回了新ID)
if (responseData != null) {
setId(responseData.getId());
Log.i(TAG, "同步成功, 新ID: " + responseData.getId());
}
// 触发成功回调
if (callback != null) callback.onSyncSuccess(this);
}
/**
* 处理同步错误(含重试逻辑)
*
* @param type 操作类型
* @param statusCode HTTP状态码(-1表示网络错误)
* @param error 错误信息
* @param callback 同步结果回调
* @param retryCount 当前重试次数
*
* @implSpec 1. 判断错误是否可重试(网络错误或5xx服务错误)
* 2. 满足条件时进行指数退避重试
* 3. 达到最大重试次数后触发失败回调
*
* @algorithm 使用指数退避算法:延迟时间 = 1000ms * 2^重试次数
*/
private void handleSyncError(String type, int statusCode, String error,
SyncCallback callback, int retryCount) {
Log.e(TAG, "同步失败: " + error + ", 状态码: " + statusCode);
// 判断是否可重试(网络错误或服务端5xx错误)
boolean canRetry = statusCode == -1 || (statusCode >= 500 && statusCode < 600);
// 满足重试条件(可重试错误且未达到最大重试次数)
if (canRetry && retryCount < 3) {
// 计算指数退避延迟时间
long delay = (long) (1000 * Math.pow(2, retryCount));
Log.w(TAG, "将在 " + delay + "ms 后重试");
try {
// 当前线程休眠指定时间
Thread.sleep(delay);
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
}
// 递归调用进行重试(重试次数+1)
sync(type, callback, retryCount + 1);
} else {
// 不可重试或达到最大重试次数,触发失败回调
if (callback != null) {
String finalError = "同步失败: " + error;
if (canRetry) finalError += " (重试失败)";
callback.onSyncFailure(finalError);
}
}
}
// 线程安全的对象复制方法
public void updateFrom(Object source) {
if ( source == null) return;
Class<?> targetClass = this.getClass();
Class<?> sourceClass = source.getClass();
// 获取或创建字段缓存
Map<String, Field> targetFields = getOrCreateFieldCache(targetClass);
Map<String, Field> sourceFields = getOrCreateFieldCache(sourceClass);
// 遍历源对象字段
for (Map.Entry<String, Field> entry : sourceFields.entrySet()) {
String fieldName = entry.getKey();
Field sourceField = entry.getValue();
// 查找目标对象对应字段
Field targetField = targetFields.get(fieldName);
if (targetField != null) {
// 类型兼容性检查
if (isCompatibleTypes(sourceField.getType(), targetField.getType())) {
try {
// 复制字段值
Object value = sourceField.get(source);
targetField.set(this, value);
} catch (IllegalAccessException e) {
// 处理异常,记录日志
}
}
}
}
}
// 获取或创建类的字段缓存
private static Map<String, Field> getOrCreateFieldCache(Class<?> clazz) {
// 双重检查锁确保线程安全
if (!CLASS_FIELD_CACHE.containsKey(clazz)) {
synchronized (clazz) {
if (!CLASS_FIELD_CACHE.containsKey(clazz)) {
Map<String, Field> fieldMap = new HashMap<>();
// 递归获取所有字段(包括父类)
for (Class<?> current = clazz; current != null; current = current.getSuperclass()) {
for (Field field : current.getDeclaredFields()) {
field.setAccessible(true); // 突破访问限制
fieldMap.put(field.getName(), field);
}
}
CLASS_FIELD_CACHE.put(clazz, fieldMap);
}
}
}
return CLASS_FIELD_CACHE.get(clazz);
}
// 类型兼容性检查(支持自动装箱/拆箱)
private static boolean isCompatibleTypes(Class<?> sourceType, Class<?> targetType) {
// 处理基本类型和包装类的兼容性
if (sourceType.isPrimitive()) {
sourceType = primitiveToWrapper(sourceType);
}
if (targetType.isPrimitive()) {
targetType = primitiveToWrapper(targetType);
}
return targetType.isAssignableFrom(sourceType);
}
// 基本类型转包装类
private static Class<?> primitiveToWrapper(Class<?> primitiveType) {
if (boolean.class.equals(primitiveType)) return Boolean.class;
if (byte.class.equals(primitiveType)) return Byte.class;
if (char.class.equals(primitiveType)) return Character.class;
if (double.class.equals(primitiveType)) return Double.class;
if (float.class.equals(primitiveType)) return Float.class;
if (int.class.equals(primitiveType)) return Integer.class;
if (long.class.equals(primitiveType)) return Long.class;
if (short.class.equals(primitiveType)) return Short.class;
if (void.class.equals(primitiveType)) return Void.class;
return primitiveType;
}
//================ 回调接口 ================//
/**
* 同步操作回调接口
*
* @implNote 使用方需实现此接口处理同步结果
*/
public interface SyncCallback {
/**
* 同步成功回调
*
* @param entity 同步后的实体对象(已更新ID)
*/
void onSyncSuccess(SynchronizableEntity entity);
/**
* 同步失败回调
*
* @param error 失败原因描述
*/
void onSyncFailure(String error);
}
}
package com.example.kucun2.DataPreserver;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.example.kucun2.entity.*;
import com.example.kucun2.entity.Information;
import com.example.kucun2.entity.data.*;
import com.example.kucun2.function.MyAppFunction;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
/**
* 重构后的数据加载器:使用ApiClient处理网络请求
*/
public class DataLoader {
private static final String TAG = "DataLoader";
private final DataStore dataStore;
private final DataAssociator dataAssociator;
// private final DataPreserver dataPreserver;
public DataLoader(DataStore dataStore, DataAssociator dataAssociator
) {
this.dataStore = dataStore;
this.dataAssociator = dataAssociator;
// this.dataPreserver = dataPreserver;
}
public void loadAllData(Context context, LoadDataCallback callback) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("必须在主线程调用Data.loadAllData");
}
// dataPreserver.ensurePreservedObjects();
String url = MyAppFunction.getApiUrl("url_all");
Log.d(TAG, "开始加载数据, URL: " + url);
// 使用ApiClient的get方法发送请求
ApiClient.get(url, new ApiClient.ApiCallback<DataLoader.AllDataResponse>() {
@Override
public void onSuccess(AllDataResponse allData) {
Log.d(TAG, "数据加载成功");
parseAndAssignData(allData, callback);
}
@Override
public void onError(int statusCode, String error) {
Log.e(TAG, "数据加载失败: " + error + ", 状态码: " + statusCode);
new Handler(Looper.getMainLooper()).post(callback::onFailure);
}
});
}
private void parseAndAssignData(AllDataResponse allData, LoadDataCallback callback) {
try {
mergeAllLists(allData);
dataAssociator.automaticAssociation();
callback.onSuccess();
} catch (Exception e) {
Log.e(TAG, "数据处理异常: " + e.getMessage());
callback.onFailure();
}
}
private void mergeAllLists(AllDataResponse allData) {
mergeList(dataStore.bancais, allData.bancais);
mergeList(dataStore.caizhis, allData.caizhis);
mergeList(dataStore.mupis, allData.mupis);
mergeList(dataStore.chanpins, allData.chanpins);
mergeList(dataStore.chanpin_zujians, allData.chanpin_zujians);
mergeList(dataStore.dingdans, allData.dingdans);
mergeList(dataStore.dingdan_chanpins, allData.dingdan_chanpins);
mergeList(dataStore.dingdan_bancais, allData.dingdan_bancais);
mergeList(dataStore.kucuns, allData.kucuns);
mergeList(dataStore.zujians, allData.zujians);
mergeList(dataStore.users, allData.users);
mergeList(dataStore.jinhuos, allData.jinhuos);
}
private <T extends SynchronizableEntity> void mergeList(
SynchronizedList<T> targetList,
List<T> newList) {
if (newList == null) return;
targetList.mergeList(newList);
}
public interface LoadDataCallback {
void onSuccess();
void onFailure();
}
public static class AllDataResponse {
public List<Bancai> bancais;
public List<Caizhi> caizhis;
public List<Mupi> mupis;
public List<Chanpin> chanpins;
public List<Chanpin_Zujian> chanpin_zujians;
public List<Dingdan> dingdans;
public List<Dingdan_Chanpin> dingdan_chanpins;
public List<Dingdan_bancai> dingdan_bancais;
public List<Kucun> kucuns;
public List<Zujian> zujians;
public List<User> users;
public List<Jinhuo> jinhuos;
}
}
package com.example.kucun2.ui.login;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.kucun2.DataPreserver.Data;
import com.example.kucun2.MainActivity;
import com.example.kucun2.R;
import com.example.kucun2.entity.User;
import com.example.kucun2.entity.data.ApiClient;
public class LoginActivity extends AppCompatActivity {
private EditText etUsername, etPassword;
private CheckBox cbRemember, cbAutoLogin;
private Button btnLogin;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
// 初始化视图
etUsername = findViewById(R.id.et_username);
etPassword = findViewById(R.id.et_password);
cbRemember = findViewById(R.id.cb_remember);
cbAutoLogin = findViewById(R.id.cb_auto_login);
btnLogin = findViewById(R.id.btn_login);
progressBar = findViewById(R.id.progress_bar);
// 设置登录按钮点击事件
btnLogin.setOnClickListener(v -> attemptLogin());
}
private void attemptLogin() {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
// 显示进度条,禁用登录按钮
progressBar.setVisibility(View.VISIBLE);
btnLogin.setEnabled(false);
// 调用网络登录
ApiClient.login(username, password, new ApiClient.LoginCallback() {
@Override
public void onSuccess(User user) {
runOnUiThread(() -> {
progressBar.setVisibility(View.GONE);
btnLogin.setEnabled(true);
// 保存登录状态
saveLoginPreferences(username, password);
// 设置当前用户
Data.setCurrentUser(user);
// 跳转到主界面
startMainActivity();
});
}
@Override
public void onFailure(String error) {
runOnUiThread(() -> {
progressBar.setVisibility(View.GONE);
btnLogin.setEnabled(true);
Toast.makeText(LoginActivity.this, "登录失败: " + error, Toast.LENGTH_SHORT).show();
});
}
});
}
private void saveLoginPreferences(String username, String password) {
// 在实际应用中,应使用加密方式存储密码
getSharedPreferences("login_prefs", MODE_PRIVATE).edit()
.putString("username", username)
.putString("password", password)
.putBoolean("remember", cbRemember.isChecked())
.putBoolean("auto_login", cbAutoLogin.isChecked())
.apply();
}
private void startMainActivity() {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish(); // 关闭登录界面
}
}
------------------------登录和获取初始数据的时候一点问题没有,只有通过基类传递时出错了------------------------------------ java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.kucun2.entity.data.SynchronizableEntity
at com.example.kucun2.entity.data.SynchronizableEntity$1.onSuccess(SynchronizableEntity.java:56)
最新发布