callback

callback

前言

callback发展历程图

setInterval: 另类的callback实现

  • setInterval同级别的另外一个函数:setTimeout。

  • 设置n秒后,有一定时间延时的,2ms左右;

  • 最低时间为4ms,参考传送门

var d = new Date, count = 0, f, timer;
timer = setInterval(f = function (){
    if(new Date - d > 1000) {
        clearInterval(timer), console.log(count);
    }
    count++;
}, 0);
  • setTimeout中的错误使用try,catch不可捕获

try{
    setTimeout(function(){
        throw new Error("我不希望这个错误出现!")
    }, 1000);
} catch(e){
    console.log(e.message);
}

callback: 常用的javascript回调

  • 通常作为参数进行传递

function getData(callback) {
    $.ajax({
        url: '',
        success: resp => {
            callback(resp);
        }
    });
}
getData(resp => {
    // write your code here
});
  • 调用的时候,可以直接调用,还可以通过bind,call,apply指定当前作用域

function getData(callback) {
    $.ajax({
        url: '',
        success: resp => {
            callback(resp.data);
            callback.bind(null)(resp.data);
            callback.call(null, resp.data);
            callback.apply(null, resp.data);
        }
    });
}
getData((...resp) => {
    // write your code here
});

事件监听: 一般用作dom的事件绑定

let myEvents = new MyEvent();
myEvents.addEvents({
    once: () => {
        console.log('只会console一次');
        myEvents.removeEvent('once');
    },
    infinity: () => {
        console.log('每次点击,都会console');
    }
});

document.onclick = e => {
    myEvents.fireEvents(['once', 'infinity']);
}
  • 2.DOM自定义事件

let elImage = document.getElementById('image');
$(elImage).addEvent('click', e => {
    e = e || window.event;
    let target = e.target || e.srcElement;

    // 元素节点 为1; 元素属性 为2
    if (target.nodeType === 1) {
        console.log(`点击类型:${e.type}`);
        $(target).fireEvent('console');
    }
})

事件流图片

发布/订阅: 消息通讯

  • 1.实现一个消息发布

let subPub = new SubPub();
subPub.subscribe('getName', name => {
    console.log('your name is: ', name);
});
subPub.publish('getName', 'Tom');

1.观察者模式和发布/订阅的区别:<br/>
1.1.Observer模式要求希望接收到主题通知者的观察者必须订阅内容改变的事件<br/>
1.2.Subscribe/Publish模式使用了一个主题/事件通道,这个通道介于订阅者和发布者之间。该事件系统允许代码定义应用程序的特定事件,该事件可以传递自定义参数,自定义参数包含订阅者所需要的值。其目的是避免订阅者和发布者产生依赖关系。<br/>
from: 《Javascript设计模式》<br/>

  • 2.nodejs版本的消息发布、订阅

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
  console.log(a, b, this);
});
myEmitter.emit('event', 'a', 'b');
  • 2.1.ES6中import对于循环引用的处理问题

TODO: require引用?

  • 2.2.?commonJS中require是值的copy?,ES6中import是值的引用

  • 3.更高级的状态管理:redux,vuex

promise: 回调的代码组织的封装

1.promise A+规范: wikiplusA+翻译

2.promise的流程

promise图片

3.要点

  • 3.1.Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。

  • 3.2.then 方法可以被同一个 promise 调用多次。

  • 3.3.then 方法必须返回一个 promise。

4.一些问题

  • 4.1.下面四个使用promise语句的不同点在哪里?

doSomething().then(function () {
    return doSomethingElse();
}).then(finalHandler);

doSomething().then(function () {
    doSomethingElse();
}).then(finalHandler);

doSomething().then(doSomethingElse()).then(finalHandler);

doSomething().then(doSomethingElse).then(finalHandler);
  • 4.2.新手问题:

  • 4.2.1.callback方式使用promise

// anotherPromise依赖somePromise
// 不推荐
somePromise()
.then(data => {
  anotherPromise(data.id)
  .then(anotherData => {
    // write your code here
  })
  .catch(window.console.log.bind(window.console))
})
.catch(window.console.log.bind(window.console))

// 推荐
somePromise()
.then(data => {
  return anotherPromise(data.id).then(data, anotherData);
})
then((data, another) => {

})
.catch(window.console.log.bind(window.console))
// 不推荐
let promises = [new Promise(resolve => {
  let dataA = {
    name: 'dataA'
  };
  resolve(dataA);
}), new Promise(resolve => {
  let dataB = {
    name: 'dataB'
  };
  resolve(dataB);
})];
let keys = ['dataA', 'dataB']
let dataAll = {};
promises.forEach((promise, index) => {
  promise
  .then(data => {
    dataAll[keys[index]] = data;
  })
  .catch(e => {
    console.log('error: ', e);
  })
});
// 推荐
Promise
.all(promises)
.then(data => {
  // [dataA, dataB]
})
  • 4.2.3.忘记加catch

somePromise()
.then(() => {
  return anotherPromise();
})
.then(() => {
  return lastPromise();
})
// 没有业务错误需求,加上这句就方便调试
.catch(console.log.bind(console));
  • 4.2.3.不推荐使用deferred(历史包袱),两种方式改正

  • 4.2.3.1.使用第三方的库包装成promise,如angular的$q库:

$q.when(db.put(doc)).then(...)
  • 4.2.3.2.使用promise:

new Promise(function (resolve, reject) {
    fs.readFile('myfile.txt', function (err, file) {
        if (err) {
            return reject(err);
        }
        resolve(file);
    });
})
.then(...)
  • 4.2.4.不显示调用return

somePromise()
.then(() => {
  anotherPromise();
})
.then(data => {
  // data was undefined
})
  • 4.3.进阶错误

  • 4.3.1.不了解Promise.resolve()/Promise.reject();

  • 4.3.2.catch和then(null, reject => {})不完全相同: then中的rejectHandler不会捕获resolveHandler中的错误

// 1.then reject
somePromise().then(resolve => {
  throw new Error('error');
}, reject => {
  // catch nothing
})
// 2.catch: this type was recomended
somePromise()
.then(resolve => {
  throw new Error('error');
})
.catch(e => {
  // catch the error
})

// 3.the same as below:
somePromise()
.then(resolve => {
  throw new Error('error');
})
.then(null, e => {
  // catch the error
})
  • 4.3.3.promise vs promise factories: 一个接一个执行一系列的promise

function executeSequentially(promiseFactories) {
  var result = Promise.resolve();
  promiseFactories.forEach(function (promiseFactory) {
    result = result.then(promiseFactory);
  });
  return result;
}
// 使用promise工厂
function myPromiseFactory() {
  return somethingThatCreatesAPromise();
}
// 示例:
let promiseFactories = [];
promiseFactories.push(myPromiseFactory);
executeSequentially(promiseFactories);
  • 4.3.4.想要两个promise的结果

  • 4.3.4.1.原始代码

let getUserAndAccount = user => {
  return new Promise((resolve, reject) => {
    getUserAccountById(user.id)
    .then(userAccount => {
      resolve(user, userAccount);
    })
    .catch(reject);
  })
}
getUserByName('nolan')
.then(getUserAndAccount)
.then(function (user, userAccount) {
  console.log('user and userAccount: ', user, userAccount);
})
.cath(e => {
  console.log('error: ', e);
});
  • 4.3.4.2.简化后代码

let getUserAndAccount = user => getUserAccountById(user.id)
                                .then(userAccount => Promise.resolve(user, userAccount))
getUserByName('nolan')
.then(getUserAndAccount)
.then(function (user, userAccount) {
  console.log('user and userAccount: ', user, userAccount);
})
.cath(e => {
  console.log('error: ', e);
});
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
  console.log(result);
});

5.一些提议

  • 5.1.then方法内部相关:

  • 5.1.1.return一个promise对象。

  • 5.1.2.return一个同步值或者是undefined

  • 5.1.3.同步的throw一个错误

getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  return inMemoryCache[user.id] || getUserAccountById(user.id);    // returning a synchronous value or a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
  if (err) {
    let message = err.message;
    if (~message.indexOf('logged')) {
      // 已经登出的处理逻辑
    } else {
      // 其他的错误处理逻辑
    }
  }
});

6.一些Promise知识点

  • 6.1.Promise.all, Promise.race

  • 6.1.1.相同点: Promise.race和Promise.all都能接收一个数组

  • 6.1.2.不同点: Promise.race只要有一个reject或者resolve,就立即返回,Promise.all等待所有的resolve,reject,才会返回,如果有一个reject,那么all的结果也是reject的(所有的resolve,才会resolve)

Promise.all([new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('first');
  }, 1000);
}), Promise.reject(123), new Promise((resolve, reject) => {
  console.log('second');
  resolve();
})])
.then(data => {
  console.log('I am all data: ', data);
})
.catch(e => {
  console.log('error', e);
});
  • 6.1.3.使用场景: Promise.race可以在ajax网络超时判断使用

let timeout = 3e3;
Promise.race([new Promise((resolve, reject) => {
  $.ajax('url', resp => {
    console.log('ajax resp: ', resp);
  });
}), new Promise((resolve, reject) => {
  setTimeout(resolve, timeout);
})]);

6.2.Promise.resolve返回一个已经resolve的promise对象,reject同理

generator,yeild: 流程控制的新语法

1.generator的含义与定义: 异步操作的容器

function* gen(){
    let url = 'https://api.github.com/users/github';
    let result = yield fetch(url);
    console.log('result: ', result.bio);
}

let genUser = () => {
    let g = gen();
    let result = g.next();

    result.value.then(data => {
        let json = data.json();
        return json;
    }).then(data => {
        g.next(data);
    });
}
  • 1.1.函数可以暂停执行和恢复执行

  • 1.2.Generator 函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数

function* f() {
    console.log('执行了!')
}

var generator = f();

setTimeout(function () {
    generator.next()
}, 2000);
  • 1.3.函数体内外的数据交换和

function* gen(x){
  var y = yield x + 2;
  console.log('gen(): ', y, x);
  return y;
}
var g = gen(1);
var value = g.next();
console.log('value: ', value);
var value2 = g.next(12);
console.log('value2: ', value2);
  • 1.4.错误处理机制

function* gen(x){
  var y;
  try{
    y = yield x + 2;
    console.log('gen(): ', y, x);
  }catch(e){
    console.log(e);
  }
  return y;
}
var g = gen(1);
var value = g.next();
console.log('value: ', value);
var value2 = g.throw(new Error('error'));
  • 1.5.yield表达式只能用在 Generator 函数里面

function f(param) {
    let a = yield 3 * param;
}

2.Thunk函数的含义与定义: 可以在回调函数里,将执行权交还给 Generator 函数,生产环境推荐thunkify

var gen = function* (){
  var f1 = yield readFile('fileA');
  var f2 = yield readFile('fileB');
  // ...
  var fn = yield readFile('fileN');
};

run(gen);
  • 2.thunk函数介绍: 诞生于上个60年代

  • 2.1.1.传值调用

let f = (a, b) => b;

f(3 * x * x - 2 * x - 1, x);
  • 2.1.2.传名调用

let f = m => m * 2;

f(x + 5);

// 等同于

let thunk () => (x + 5);

let f = thunk => (thunk() * 2);
  • 2.1.3.thunkify源码:

function thunkify(fn){
  return function(){
    let args = Array.prototype.slice.call(arguments);
    let ctx = this;

    return function(done){
      // 检查机制: 确保回调函数只运行一次
      let called;

      args.push(function(){
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
        fn.apply(ctx, args);
      } catch (err) {
        done(err);
      }
    }
  }
};
  • 2.1.4.thunk与generator结合:

let fs = require('fs');
let thunkify = require('thunkify');
let readFile = thunkify(fs.readFile);

let gen = function* (){
  let r1 = yield readFile('/etc/fstab');
  console.log(r1.toString());
  let r2 = yield readFile('/etc/shells');
  console.log(r2.toString());
};
  • 2.1.5.手动执行:

let g = gen();

let r1 = g.next();
r1.value(function(err, data){
  if (err) throw err;
  let r2 = g.next(data);
  r2.value(function(err, data){
    if (err) throw err;
    g.next(data);
  });
});
  • 2.1.6.简化封装:

function run(fn) {
  let gen = fn();

  function next(err, data) {
    let result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

run(gen);

3.co函数库的含义与定义: Generator 函数的执行器, yield后必须是thunk/promise函数

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

var co = require('co');
co(gen);
  • 3.1.协程与事件循环: 控制流的主动让出和恢复

  • 3.1.1.提出时间: 1963; 提出人: Melvin Conway

  • 3.1.2.历程: 进程->线程->用户态线程->协程

  • 3.1.3.名词释义:

  • 3.1.3.1.进程: 代码,被代码控制的资源(内存,I/O,文件)两大基本元素等组成的实体,两大特性[掌控资源,可以被调度]

  • 3.1.3.2.线程: 程在进程内部,处理并发的逻辑,拥有独立的栈,却共享线程的资源

  • 3.1.3.3.用户态线程: 线程切换的时候,进程需要为了管理而切换到内核态,处理状态转换(性能消耗严重)

  • 3.1.4.没火的原因: 命令式编程(自顶向下开发,子历程作为唯一控制结构)、函数式编程[意气之争]

  • 3.1.5.关系: 子历程是没有使用yield的协程。Donald Ervin Knuth(wiki)/Donald Ervin Knuth(baike): 子历程是协程的一种特例

  • 3.2.使用co, yield后面放的必须是thunk/promise函数

async,await: generator的语法糖

async的含义与定义

let getData = () => {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: 'json/test.json',
            method: 'GET',
            success: function (resp) {
                // data = resp.data;
                resolve(resp);
            },
            error: function (error) {
                reject(error);
            }
        });
    });
}

async function initView(){
    try {
        let resp = await getData();
        console.log(resp);
    } catch (e) {
        console.error(e);
    }
}
initView();

async的一些问题

1.同时触发:

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

最后的一些问题与思考

1.从异步操作上,async是最后演化的结果,callback是就不用了、还是应该尽量避免?

参考资料

扩展阅读

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.getStringResource(“string”, key); } //================ 核心同步方法 ================// /** 同步实体到服务端(公开接口) @param type 操作类型(如"create", "update"等) @param callback 同步结果回调接口 @implNote 内部调用私有方法实现带重试机制的同步 @see #sync(String, SyncCallback, int) */ public void sync(String type, SyncCallback callback) { sync(type, callback, 0); // 初始重试次数为0 } /** 带重试机制的同步实现(私有方法) @param type 操作类型 @param callback 同步结果回调 @param retryCount 当前重试次数 @implSpec 1. 构建完整端点URL 通过线程池提交网络请求 处理成功/失败回调 */ private void sync(String type, SyncCallback callback, int retryCount) { // 构建完整端点URL String baseUrl = MyAppFunction.getStringResource(“string”, “url”); String endpoint = baseUrl + getEndpoint(type); Log.d(TAG, "同步端点: " + endpoint + ", 重试次数: " + retryCount); // 提交到线程池执行网络请求 NETWORK_EXECUTOR.execute(() -> { ApiClient.postJson(endpoint, this, new ApiClient.ApiCallback() { @Override public void onSuccess(SynchronizableEntity responseData) { handleSyncSuccess(responseData, callback); } @Override public void onError(int statusCode, String error) { handleSyncError(type, statusCode, error, callback, retryCount); } }); }); } /** 处理同步成功结果 @param responseData 服务端返回的实体数据 @param callback 同步结果回调 @implNote 1. 更新实体ID 记录成功日志 触发成功回调 */ 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服务错误) 满足条件时进行指数退避重试 达到最大重试次数后触发失败回调 @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.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客户端 - 更灵活的类型处理 主要改进: 分离请求参数类型和响应类型 支持多种请求方法(GET/POST/PUT/DELETE) 支持表单和JSON两种请求格式 自动推导响应类型 统一的请求执行流程 */ 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 响应数据类型 */ public static void executeRequest(Request request, Type responseType, ApiCallback 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 void handleResponse(Response response, Type responseType, ApiCallback 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 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 void handleFailure(Call call, IOException e, ApiCallback 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 Type getResponseType(ApiCallback callback) { if (callback == null) { return new TypeToken(){}.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(){}.getType(); } // ====================== 回调通知方法 ====================== private static void notifySuccess(ApiCallback callback, R data) { if (callback != null) { MAIN_HANDLER.post(() -> callback.onSuccess(data)); } } private static void notifyError(ApiCallback 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 请求数据类型 @param 响应数据类型 */ public static <T, R> void jsonRequest(String url, String method, T requestData, ApiCallback callback) { Request request = buildJsonRequest(url, method, requestData); executeRequest(request, getResponseType(callback), callback); } /** 执行表单API请求 @param url API地址 @param method 请求方法 @param formData 表单数据 @param callback 回调接口 @param 响应数据类型 */ public static void formRequest(String url, String method, Map<String, String> formData, ApiCallback callback) { Request request = buildFormRequest(url, method, formData); executeRequest(request, getResponseType(callback), callback); } // ====================== 便捷方法 ====================== public static <T, R> void postJson(String url, T data, ApiCallback callback) { jsonRequest(url, “POST”, data, callback); } public static <T, R> void putJson(String url, T data, ApiCallback callback) { jsonRequest(url, “PUT”, data, callback); } public static <T, R> void deleteJson(String url, T data, ApiCallback callback) { jsonRequest(url, “DELETE”, data, callback); } public static void get(String url, ApiCallback 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() { @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 { 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.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客户端 - 更灵活的类型处理 主要改进: 分离请求参数类型和响应类型 支持多种请求方法(GET/POST/PUT/DELETE) 支持表单和JSON两种请求格式 自动推导响应类型 统一的请求执行流程 */ 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 响应数据类型 */ public static void executeRequest(Request request, Type responseType, ApiCallback 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 void handleResponse(Response response, Type responseType, ApiCallback 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 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 void handleFailure(Call call, IOException e, ApiCallback 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 Type getResponseType(ApiCallback callback) { if (callback == null) { return new TypeToken(){}.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(){}.getType(); } // ====================== 回调通知方法 ====================== private static void notifySuccess(ApiCallback callback, R data) { if (callback != null) { MAIN_HANDLER.post(() -> callback.onSuccess(data)); } } private static void notifyError(ApiCallback 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 请求数据类型 @param 响应数据类型 */ public static <T, R> void jsonRequest(String url, String method, T requestData, ApiCallback callback) { Request request = buildJsonRequest(url, method, requestData); executeRequest(request, getResponseType(callback), callback); } /** 执行表单API请求 @param url API地址 @param method 请求方法 @param formData 表单数据 @param callback 回调接口 @param 响应数据类型 */ public static void formRequest(String url, String method, Map<String, String> formData, ApiCallback callback) { Request request = buildFormRequest(url, method, formData); executeRequest(request, getResponseType(callback), callback); } // ====================== 便捷方法 ====================== public static <T, R> void postJson(String url, T data, ApiCallback callback) { jsonRequest(url, “POST”, data, callback); } public static <T, R> void putJson(String url, T data, ApiCallback callback) { jsonRequest(url, “PUT”, data, callback); } public static <T, R> void deleteJson(String url, T data, ApiCallback callback) { jsonRequest(url, “DELETE”, data, callback); } public static void get(String url, ApiCallback 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() { @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 { void onSuccess(T data); void onError(int statusCode, String error); } public interface LoginCallback { void onSuccess(User user); void onFailure(String error); } } SynchronizableEntity根据ApiClient构建数据请求,创建和查询方法返回的数据类型不同,并且期待返回的类型是基类但是返回的数据是实体类的全部字段很多字段就没有了吧
最新发布
06-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值