NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
## 1.UnifyStorage说明 此库主要用来解决下面几个问题: **1).存储对象以及对象集合数据到本地数据库 2).存储基本数据类型的数据或者对象,对象集合类型的数据为key-value形式(可类比sharepreference) 3).网络中下载的大json解析到内存会出现内存爆炸,这里提供直接json到数据库的存储的一种选择 4).mock网络访问直接返回本地的json对应的对象或者对象集合(目前提供retrofit网络访问的mock方式,之后这部分会集成到framework)** > 同时此库提供统一的维护点和相同的对外方式,减少了业务系统后期的维护成本和扩展成本。 [TOC] ## 2.集成 UnifyStorage综合考虑各种移动端数据库性能以及可干预程度,最终采用realm作为存储数据库,但是这部分是对用户无感知的。更多一些知识可以参看下面文档: 数据库性能对比分析可以点击此网页进行查看: [https://www.aliyun.com/jiaocheng/1354383.html](https://www.aliyun.com/jiaocheng/1354383.html) 如果想了解更多关于realm的知识可以查看我们上一篇文章:https://www.kancloud.cn/sharkchao/sharkchao/858754 官方关于realm的技术文档:https://realm.io/docs/java/latest/ ### 2.1集成说明 > JDK version 7.0 及以上 > Android API 9 以上 (Android 2.3及以上) #### 1)添加项目依赖(目前还未上传至仓库) 1.在app目录下的build.gradle添加如下项目依赖 ``` implementation project(':unifystorage_core')//存储库 implementation project(':unifystorage_mock')//mock网络访问的库 ``` 同时需要在此build.gradle里面添加插件: ``` apply plugin: 'realm-android' ``` 2.在项目目录下的build.gradle添加如下: ``` classpath "io.realm:realm-gradle-plugin:5.8.0" ``` 这个是realm数据库的要求。 #### 2)在application中的onCreate()方法中进行初始化 ``` UStorage.initialize(this); ``` #### 3)定义需要进行的操作,这个地方以数据库查询为例 ``` public interface ApiDataBase { @DB(table = User.class) @FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age") DbResult<User> findUser(String name, int age, String sex); } ``` > 完整的使用类如下: > public interface ApiDataBase { > > @DB(table = User.class) > @SAVE > DbResult saveUser(User user); > > @DB(table = User.class) > @SAVE(type = Constants.JSON_OBJECT) > DbResult saveUsersByJsonObject(String jsonObject); > > @DB(table = User.class) > @SAVE(type = Constants.JSON_ARRAY) > DbResult saveUsersByJsonArray(String jsonArray); > > @DB(table = User.class) > @SAVEORUPDATE(type = Constants.JSON_ARRAY) > DbResult saveOrUpdateUsersByJsonArray(String jsonArray); > > @DB(table = User.class) > @SAVEORUPDATE(type = Constants.JSON_OBJECT) > DbResult saveOrUpdateUsersByJsonObject(String jsonObject); > > @DB(table = User.class) > @SAVE > DbResult saveUsersByList(List<User> user); > > @DB(table = User.class) > @SAVE > DbResult saveUsersByArray(User[] user); > > @DB(table = User.class) > @SAVE > DbResult saveFake(Fake fake); > > > @DB(table = User.class) > @SAVEORUPDATE > DbResult saveOrUpdateUser(User user); > > @DB(table = User.class) > @SAVEORUPDATE > DbResult saveOrUpdateUsersByList(List<User> user); > > @DB(table = User.class) > @SAVEORUPDATE > DbResult saveOrUpdateUsersByArray(User[] user); > > @DB(table = User.class) > @FIND > DbResult<User> findAll(); > > @DB(table = User.class) > @FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age desc") > DbResult<User> findUser(String name, int age, String sex); > > @DB(table = User.class) > @FIND(where = "name in ?",limit = 10) > DbResult<User> findUsers(List<String> users); > > @DB(table = User.class) > @DELETE(where = "name = ?") > DbResult deleteUsersByQuery(); > > @DB(table = User.class) > @FIND(where = "name in ?") > DbResult<User> findUserByIn(List<String> users); > > @DB(table = User.class) > @FIND(where = "name contains ?",distinct = "name") > DbResult<User> findUserByContains(String name); > > @DB(table = User.class) > @FIND(where = "name like ? and age > ?",distinct = "name") > DbResult<User> findUserByLike(String name, int age); > > @DB(table = User.class) > @FIND(where = "? notnull",limit = 2) > DbResult<User> findUserByNotNull(String name); > > @DB(table = User.class) > @UPDATE(where = "name = ?",set = "name = ? and age = ?") > DbResult updateUsersByQuery(String oldName,String newName,String age); > > > @JSON(key = "JSON_KEY",convert = User.class) > boolean saveJson(User user); > > @JSON(key = "JSON_ARRAY",convert = User.class) > boolean saveJsonArray(List<User> users); > > @JSON(key = "JSON_STR") > boolean saveStr(String str); > > @JSON(key = "JSON_INT") > boolean saveInt(int ints); > > @JSON(key = "JSON_BOOL") > boolean saveBool(boolean booleans); > > @GETJSON(key = "JSON_KEY",convert=User.class) > DbResult<User> getJson(); > > @GETJSON(key = "JSON_ARRAY",convert = User[].class) > DbResult<List<User>> getJsonArray(); > > @GETJSON(key = "JSON_STR") > DbResult<String> getStr(); > > @GETJSON(key="JSON_BOOL",convert = Boolean.class) > DbResult<Boolean> getBool(); > > @GETJSON(key = "JSON_INT",convert = Integer.class) > DbResult<Integer> getInt(); > > @GET("/getUser") > Call<User> getUser(); > > @GET("/getUserList") > Call<List<User>> getUserList(); > } #### 4)得到ApiDataBase接口实例 ``` public class ApiServiceModule { private volatile static ApiServiceModule mInstance; private ApiServiceModule(){ } public static ApiServiceModule getInstance(){ if (null == mInstance){ synchronized (ApiServiceModule.class){ if (null == mInstance){ mInstance = new ApiServiceModule(); } } } return mInstance; } private UStorage provideUStorage(){ return new UStorage.Builder() .setSchemaVersion(1) .build(); } <T> T provideApiService(Class<T> apiDataBase){ return provideUStorage().create(apiDataBase); } } ``` #### 5)程序调用 ``` mApiDataBase = ApiServiceModule.getInstance().provideApiService(ApiDataBase.class); mApiDataBase.findUser("sharkchao", 20, "男") .registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { UserData.setmResults(realmResults); Toast.makeText(MainActivity.this, "成功!"+ realmResults.size(), Toast.LENGTH_SHORT).show(); } @Override public void onChange(RealmResults<User> realmResults) { } }); ``` 到此数据库的查询操作已经完成。 ## 3.UnifyStorage注解 ### 3.1 指定数据库表`@DB` ``` eg: @DB(table = User.class) ``` | 注解代码 | 说明 | | --- | --- | | @DB | @DB(table = User.class),指定当前接口方法所要操作的数据库表,User.class需继承RealmObject | ### 3.2 存储数据`@SAVE` ``` eg: @SAVE(type = Constants.JSON_OBJECT) //默认为Constants.REALM_DATA ``` | type属性说明 | 详解| | --- | --- | | Constants.JSON_OBJECT|json字符串(单个对象)| | Constants.JSON_ARRAY | json字符串(多个对象) | | Constants.REALM_DATA | RealmObject对象,数组,集合| ### 3.3 存储或更新数据`@SAVEORUPDATE` ``` eg: @SAVEORUPDATE(type = Constants.JSON_OBJECT) //默认为Constants.REALM_DATA ``` | type属性说明 | 详解| | --- | --- | | Constants.JSON_OBJECT|json字符串(单个对象)| | Constants.JSON_ARRAY | json字符串(多个对象) | | Constants.REALM_DATA | RealmObject对象,数组,集合| ### 3.4 查询数据`@FIND` ``` eg: @FIND(where="name = ?",limit = 10,orderBy = "age",distinct = "name") ``` | 使用代码 | 详解| | --- | --- | | where="name = ? and age > ?" | where支持and,or 等组合条件查询 | | where="name in ?"|where支持查询条件是一个集合 | | where="name contains ?"|where支持部分字段满足条件查询 | | where="name like ?"|where支持模糊查询 | | where="? notnull"|where支持属性不为空查询 | | limit = 10|限定结果为10条 | | distinct = "name"|支持指定字段去重查询 | | orderBy = "age asc"|asc为升序,desc为降序,默认为升序 | ### 3.5 删除数据`@DELETE` ``` eg: @DELETE(where="name = ?",limit = 10,orderBy = "age",distinct = "name") ``` 删除数据注解同`@FIND`注解相同。 ### 3.6 更新数据`@UPDATE` @UPDATE只负责更新,不负责插入。如果想要更新数据库还没插入的数据,您注册的DbResult.DbResultCallback会回调onError。 ``` eg: @UPDATE(where="name = ?",limit = 10,orderBy = "age",distinct = "name",set="name = ?") ``` 更新数据注解除`@FIND`注解相同属性之外,还增加了`set="?"`,为要更新的值。 | set属性说明 | 详解| | --- | --- | | set = "name = "?" and age = ?"|可以把指定数据更新为set内的值| ### 3.7 key-value存储/获取 `@JSON @GETJSON` | set属性说明 | 详解| | --- | --- | | @JSON | 用来标识要存储数据到本地文件 | | @GETJSON | 用来标识要从本地文件中获取数据 | ## 4.存储 使用@SAVE注解,如果数据库中存在此条数据,则您注册的监听DbResult.DbResultCallback会回调onError()。如何注册监听下文会有详解。 UnifyStorage插入数据支持以下几种数据类型: > 1. 对象(需继承RealmObject) > 2. List<? extends RealmObject> > 3. RealmObject[] > 4. json字符串 ### 4.1存储对象 要存储的对象需要继承RealmObject ``` @DB(table = User.class) @SAVE DbResult saveUser(User user); ``` ### 4.2存储List 要存储的List为List<? extends RealmObject> ``` @DB(table = User.class) @SAVE DbResult saveUsersByList(List<User> user); ``` ### 4.3存储数组 要存储的数组为RealmObject[] ``` @DB(table = User.class) @SAVE DbResult saveUsersByArray(User[] user); ``` ### 4.4存储json对象字符串 要存储的数组为json对象字符串 ``` @DB(table = User.class) @SAVE(type = Constants.JSON_OBJECT) DbResult saveUsersByJsonObject(String jsonObject); ``` ### 4.5存储json数组字符串 要存储的数组为json数组字符串 ``` @DB(table = User.class) @SAVE(type = Constants.JSON_ARRAY) DbResult saveUsersByJsonArray(String jsonArray); ``` ## 5.存储或更新 使用@SAVEORUPDATE注解进行插入或更新时,如果数据库中存在此条数据,则会执行更新操作,否则执行插入操作。 具体使用同存储操作类似,详见上文。 ## 6.查询数据 查询的时候需要在接口方法上面添加@FIND注解,当然数据库相关的操作都需要@DB注解来注解。 首先我们看下@FIND注解的定义: ``` @Documented @Target(METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface FIND { String orderBy() default ""; String where() default ""; String distinct() default ""; int limit() default 0; boolean eager() default true; } ``` 其中where是标识查询条件的,orderBy是标识排序的,默认情况下是升序排序,distinct是标识去重操作的,limit是标识查询出来个数的,eager这个地方暂时未使用。 ### 6.1 条件查询 1.首先我们看下查询方法定义: ``` @DB(table = User.class) @FIND DbResult<User> findAll(); ``` 如果什么都不写,那么就是查询所有。如果添加上where条件,那么就是筛选: ``` @DB(table = User.class) @FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age desc") DbResult<User> findUser(String name, int age, String sex); ``` 先不用关注limit和orderby,这里主要是where,where里面的变量必须用`?`来标识,同时方法的参数必须跟`?`的个数一致。and和or用来条件连接,`()`用来表示优先级。 > 其中需要注意的是,=操作是支持String, Integer,Date;>,<,>=,<=操作符支持Integer和Date,in操作符支持List,contains操作符支持String,like操作符支持String,notnull和null支持String 例子如下: ``` @DB(table = User.class) @FIND(where = "name in ?",limit = 10) DbResult<User> findUsers(List<String> users); ``` ``` @DB(table = User.class) @FIND(where = "name in ?") DbResult<User> findUserByIn(List<String> users); ``` ``` @DB(table = User.class) @FIND(where = "name contains ?",distinct = "name") DbResult<User> findUserByContains(String name); ``` @DB(table = User.class) @FIND(where = "name like ? and age > ?",distinct = "name") DbResult<User> findUserByLike(String name, int age); ``` @DB(table = User.class) @FIND(where = "? notnull",limit = 2) DbResult<User> findUserByNotNull(String name); ``` 2.方法调用如下: ``` mApiDataBase.findUser("sharkchao", 20, "男") .registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { UserData.setmResults(realmResults); Toast.makeText(MainActivity.this, "成功!"+ realmResults.size(), Toast.LENGTH_SHORT).show(); } @Override public void onChange(RealmResults<User> realmResults) { } }); ``` 查询都是异步执行的,所以不用担心会阻塞主线程。其中onFirstFindResult()回调是第一次查询的回调。onChange()方法是查询到的集合发生变化时(比如被修改或者被添加)会调用。 ### 6.2 去重查询 查询方法定义如下: ``` @DB(table = User.class) @FIND(where = "name contains ?",distinct = "name") DbResult<User> findUserByContains(String name); ``` 其中distinct就是标识需要去重的字段。调用如6.1 ### 6.3 排序查询 查询方法定义如下: ``` @DB(table = User.class) @FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age desc") DbResult<User> findUser(String name, int age, String sex); ``` 其中orderBy就是标识需要按照什么字段排序,其中desc用来标识降序排序,asc用来标识升序排序。当然不写的话默认就是升序排序。调用如6.1 ### 6.4 limit限制条数 查询方法定义如下: ``` @DB(table = User.class) @FIND(where = "name in ?",limit = 10) DbResult<User> findUsers(List<String> users); ``` 其中limit为了标识需要限制结果返回多少条。调用如6.1 ### 6.5 其他如:sum,min,max,average 当查询结果返回为RealmResults<T>的时候,还可以调用如上几个方法对结果集的某个字段进行操作,如下所示: ``` RealmResults<User> results = realm.where(User.class).findAll(); long sum = results.sum("age").longValue(); long min = results.min("age").longValue(); long max = results.max("age").longValue(); double average = results.average("age"); long matches = results.size(); ``` ## 7.删除数据 删除数据目前两只方式: 1)通过@DELETE注解进行删除。 2)通过查询操作先查询出RealmResult,在RealmResult上进行删除操作。 ### 7.1通过@DELETE注解来删除 先定义接口方法: ``` @DB(table = User.class) @DELETE(where = "name = ?") DbResult deleteUsersByQuery(); ``` 再执行删除操作: ``` mApiDataBase.deleteUsersByQuery().registerCallback(new DbResult.DbResultCallback() { @Override public void onSuccess(int count) { Toast.makeText(MainActivity.this, "deleteUsersByQuery成功!"+count, Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable error) { Toast.makeText(MainActivity.this, "deleteUsersByQuery失败"+error.getMessage(), Toast.LENGTH_SHORT).show(); } }); ``` ### 7.2通过查询操作来删除 定义查询接口: ``` @DB(table = User.class) @FIND(where = "name in ?") DbResult<User> findUserByIn(List<String> users); ``` 在查询回调中执行删除操作 ``` List<String> users = new ArrayList<>(); users.add("yuzhijun"); users.add("sharkchao"); mApiDataBase.findUserByIn(users).registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { if (realmResults.size() > 0){ realmResults.getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realmResults.deleteAllFromRealm(); } }); }else { Toast.makeText(MainActivity.this, "realmResults为空", Toast.LENGTH_SHORT).show(); } } @Override public void onChange(RealmResults<User> realmResults) { } }); ``` ## 8.更新数据 更新数据时如果数据库中没有此条数据,则您注册的监听DbResult.DbResultCallback会回调onError()。如何注册监听下文会有详解。 更新数据目前两只方式: 1)通过@UPDATE注解进行更新。 2)通过查询操作先查询出RealmResult,在RealmResult上进行更新操作。 ### 8.1通过@UPDATE注解来更新 先在接口中定义方法: ``` @DB(table = User.class) @UPDATE(where = "name = ?",set = "name = ? and age = ?") DbResult updateUsersByQuery(String oldName,String newName,String age); ``` 然后执行更新操作: ``` mApiDataBase.updateUsersByQuery("sharkchao","小红","100").registerCallback(new DbResult.DbResultCallback() { @Override public void onSuccess(int count) { Toast.makeText(MainActivity.this, "updateUsersByQuery成功!"+count, Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable error) { Toast.makeText(MainActivity.this, "updateUsersByQuery失败"+error.getMessage(), Toast.LENGTH_SHORT).show(); } }); ``` ### 8.2通过查询操作来更新 先在接口中定义方法: ``` @DB(table = User.class) @FIND(where = "name in ?") DbResult<User> findUserByIn(List<String> users); ``` 然后在返回结果中执行更新操作,针对对象的set操作都会直接更新到数据库中 ``` List<String> users = new ArrayList<>(); users.add("yuzhijun"); users.add("sharkchao"); mApiDataBase.findUserByIn(users).registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { if (realmResults.size() > 0){ realmResults.getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realmResults.get(0).setName("刘超"); Toast.makeText(MainActivity.this, "更新成功!", Toast.LENGTH_SHORT).show(); } }); }else { Toast.makeText(MainActivity.this, "realmResults为空", Toast.LENGTH_SHORT).show(); } } @Override public void onChange(RealmResults<User> realmResults) { } }); ``` ## 9. 返回结果及监听 插入,更新,删除,查询都会返回DbResult, 其中插入,更新,删除需要注册DbResultCallback监听: 如果操作成功,会返回操作的总个数,如果失败会返回错误原因 代码如下: ``` mApiDataBase.saveOrUpdateUsersByArray(users).registerCallback(new DbResult.DbResultCallback() { @Override public void onSuccess(int count) { Toast.makeText(MainActivity.this, "成功!"+count, Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable error) { Toast.makeText(MainActivity.this, "失败"+error.getMessage(), Toast.LENGTH_SHORT).show(); } }); ``` 查询操作需要注册DbFindCallBack监听,第一次返回结果会回调onFirstFindResult(RealmResults<User> realmResults),之后对realmResults的操作都会执行到onChange(RealmResults<User> realmResults)。代码如下: ``` mApiDataBase.findUserByIn(users).registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { UserData.setmResults(realmResults); Toast.makeText(MainActivity.this, "成功!"+ realmResults.size(), Toast.LENGTH_SHORT).show(); } @Override public void onChange(RealmResults<User> realmResults) { Toast.makeText(MainActivity.this, "变化!"+ realmResults.size(), Toast.LENGTH_SHORT).show(); } }); ``` ## 10.数据库升级 1.数据库升级需要在ApiServiceModule中的provideUStorage方法中修改版本号,默认版本为0,如果不配置migration则数据库会先删除再重建,数据不会保留。 ``` private UStorage provideUStorage(){ return new UStorage.Builder() .setSchemaVersion(1) .build(); } ``` 2.如果想数据库升级同时保留表数据则需要继承BaseMigration,使用方法如下: ``` public class CustomMigration extends BaseMigration { @Override public void upgrade(DynamicRealm realm, long oldVersion, long newVersion) { RealmSchema schema = realm.getSchema(); if (oldVersion == 0 && newVersion == 1){ RealmObjectSchema user = schema.get("User"); user.addField("sex",String.class,FieldAttribute.REQUIRED) .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { obj.set("sex","男"); } }); oldVersion++; }else if (oldVersion == 1 && newVersion == 2){ RealmObjectSchema user = schema.get("User"); user.removeField("sex"); oldVersion++; } } } ``` 如果您想查看BaseMigration 的具体使用细节,请查看:[版本升级](https://www.jianshu.com/p/37af717761cc) (这篇文章会有比较详细的版本升级内容) ## 11.注意事项 通过查询之后的结果来执行更新或删除操作时,具体的操作应该放在事务之中,模板如下: ``` List<String> users = new ArrayList<>(); users.add("yuzhijun"); users.add("sharkchao"); mApiDataBase.findUserByIn(users).registerDbFindCallBack(new DbResult.DbFindCallBack<User>() { @Override public void onFirstFindResult(RealmResults<User> realmResults) { if (realmResults.size() > 0){ realmResults.getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realmResults.get(0).setName("刘超"); Toast.makeText(MainActivity.this, "更新成功!", Toast.LENGTH_SHORT).show(); } }); }else { Toast.makeText(MainActivity.this, "realmResults为空", Toast.LENGTH_SHORT).show(); } } @Override public void onChange(RealmResults<User> realmResults) { } }); ``` ## 12.key-value形式存储(类sharepreference) ### 12.1存储 存储数据到本地文件中,需要用到@JSON进行标识,具体支持的方式如下: #### 1.存储对象类型 ``` @JSON(key = "JSON_KEY",convert = User.class) boolean saveJson(User user); ``` 存储的key为自己定义的字符串,如果是自定义对象类型需要在convert中进行标识,这里就是User.class。 对结果进行操作实例如下: ``` DbResult<User> user = mApiDataBase.getJson(); if (null != user && null != user.getResult()){ System.out.println(user.getResult().getName()); } ``` #### 2.存储对象集合类型 ``` @JSON(key = "JSON_ARRAY",convert = User.class) boolean saveJsonArray(List<User> users); ``` 存储对象集合和存储对象的方式相同,只是参数传进去的是List<T>类型。 #### 3.存储基本类型 ``` @JSON(key = "JSON_STR") boolean saveStr(String str); @JSON(key = "JSON_INT") boolean saveInt(int ints); @JSON(key = "JSON_BOOL") boolean saveBool(boolean booleans); ``` 存储基本类型不需要指定convert,只要定义一个key,然后直接设置进相应的参数即可。 ### 12.2 获取 获取存储进本地的数据需要使用@GETJSON注解,具体如下: #### 1.获取对象类型的数据 ``` @GETJSON(key = "JSON_KEY",convert=User.class) DbResult<User> getJson(); ``` 其中key就是对应前面存储的key,对象类型的话需要指明convert为具体的对象类型,这里为User.class。 #### 2.获取对象集合类型数据 ``` @GETJSON(key = "JSON_ARRAY",convert = User[].class) DbResult<List<User>> getJsonArray(); ``` 唯一与对象类型不同的是convert这个地方为具体对象的数组类型。这里为User[].class #### 3.获取基本类型数据 ``` @GETJSON(key = "JSON_STR") DbResult<String> getStr(); @GETJSON(key="JSON_BOOL",convert = Boolean.class) DbResult<Boolean> getBool(); @GETJSON(key = "JSON_INT",convert = Integer.class) DbResult<Integer> getInt(); ``` 同样这里需要指定基本的类型,如果是String的话可以不写,因为默认就是String类型。 ## 13.mock网络返回数据 这里是兼容retrofit形式的网络访问,用户只需要在获取retrofit的里面添加库里面的callAdapter和convert即可,具体如下: ``` new Retrofit.Builder() .addCallAdapterFactory(MockCallAdapterFactory.create(context)) .addConverterFactory(MockConverterFactory.create()) .baseUrl("http://test.com") .build(); ``` 操作非常简单。同时要注意的是,在你准备本地json的时候,要注意本地json文件名称跟定义的接口相同: ``` @GET("/getUser") Call<User> getUser(); ``` 如果这个地方@GET注解里面的url为getUser(这里只会匹配最后一个/后面的名字),那么本地json名字也要取成getUser.json。然后就可以正常使用了