完成Iconfont、应用页、网络缓存以及代理
This commit is contained in:
parent
756108fb8d
commit
c47b5207de
|
@ -0,0 +1,9 @@
|
||||||
|
// 是否启用代理
|
||||||
|
const ProxyEnable = false;
|
||||||
|
|
||||||
|
/// 代理服务IP
|
||||||
|
// const PROXY_IP = '192.168.1.105';
|
||||||
|
const ProxyIP = '172.16.43.74';
|
||||||
|
|
||||||
|
/// 代理服务端口
|
||||||
|
const ProxyPort = 8866;
|
|
@ -13,7 +13,10 @@ class Global {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
|
||||||
|
// 调整系统UI
|
||||||
setSystemUi();
|
setSystemUi();
|
||||||
|
|
||||||
|
// 初始换easyLoading
|
||||||
Loading();
|
Loading();
|
||||||
|
|
||||||
await Get.putAsync<StorageService>(() => StorageService().init());
|
await Get.putAsync<StorageService>(() => StorageService().init());
|
||||||
|
@ -24,6 +27,7 @@ class Global {
|
||||||
|
|
||||||
static void setSystemUi() {
|
static void setSystemUi() {
|
||||||
if (GetPlatform.isAndroid) {
|
if (GetPlatform.isAndroid) {
|
||||||
|
// android 状态栏为透明的沉浸
|
||||||
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(
|
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
statusBarBrightness: Brightness.light,
|
statusBarBrightness: Brightness.light,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'application_controller.dart';
|
||||||
|
|
||||||
|
class ApplicationBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => ApplicationController());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:news_getx/theme/app_colors.dart';
|
||||||
|
import 'package:news_getx/utils/iconfont.dart';
|
||||||
|
|
||||||
|
class ApplicationController extends GetxController {
|
||||||
|
/// 响应式成员变量
|
||||||
|
// 当前 tab 页码
|
||||||
|
final _page = 0.obs;
|
||||||
|
|
||||||
|
set page(value) => _page.value = value;
|
||||||
|
|
||||||
|
get page => _page.value;
|
||||||
|
|
||||||
|
/// 成员变量
|
||||||
|
///
|
||||||
|
// tab 页标题
|
||||||
|
late final List<String> tabTitles;
|
||||||
|
|
||||||
|
// 页控制器
|
||||||
|
late final PageController pageController;
|
||||||
|
|
||||||
|
// 底部导航项目
|
||||||
|
late final List<BottomNavigationBarItem> bottomTabs;
|
||||||
|
|
||||||
|
/// 事件
|
||||||
|
// tab栏动画
|
||||||
|
void handleNavBarTap(int page) {
|
||||||
|
pageController.animateToPage(
|
||||||
|
page,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tab栏页码切换
|
||||||
|
void handlePageChange(int newPage) {
|
||||||
|
page = newPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// scheme 内部打开 TODO uriLink 通过地址打开程序指定页面
|
||||||
|
bool isInitialUriIsHandled = false;
|
||||||
|
StreamSubscription? uriSub;
|
||||||
|
|
||||||
|
/// 生命周期
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
|
||||||
|
// 准备静态数据 作为tab
|
||||||
|
tabTitles = ['Welcome', 'Cagegory', 'Bookmarks', 'Account'];
|
||||||
|
bottomTabs = <BottomNavigationBarItem>[
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(
|
||||||
|
Iconfont.home,
|
||||||
|
color: AppColors.tabBarElement,
|
||||||
|
),
|
||||||
|
activeIcon: Icon(
|
||||||
|
Iconfont.home,
|
||||||
|
color: AppColors.secondaryElementText,
|
||||||
|
),
|
||||||
|
label: "main",
|
||||||
|
backgroundColor: AppColors.primaryBackground,
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(
|
||||||
|
Iconfont.grid,
|
||||||
|
color: AppColors.tabBarElement,
|
||||||
|
),
|
||||||
|
activeIcon: Icon(
|
||||||
|
Iconfont.grid,
|
||||||
|
color: AppColors.secondaryElementText,
|
||||||
|
),
|
||||||
|
label: 'category',
|
||||||
|
backgroundColor: AppColors.primaryBackground,
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(
|
||||||
|
Iconfont.tag,
|
||||||
|
color: AppColors.tabBarElement,
|
||||||
|
),
|
||||||
|
activeIcon: Icon(
|
||||||
|
Iconfont.tag,
|
||||||
|
color: AppColors.secondaryElementText,
|
||||||
|
),
|
||||||
|
label: 'tag',
|
||||||
|
backgroundColor: AppColors.primaryBackground,
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(
|
||||||
|
Iconfont.me,
|
||||||
|
color: AppColors.tabBarElement,
|
||||||
|
),
|
||||||
|
activeIcon: Icon(
|
||||||
|
Iconfont.me,
|
||||||
|
color: AppColors.secondaryElementText,
|
||||||
|
),
|
||||||
|
label: 'my',
|
||||||
|
backgroundColor: AppColors.primaryBackground,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
pageController = PageController(initialPage: page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
uriSub?.cancel();
|
||||||
|
pageController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:news_getx/modules/widgets/app_bar.dart';
|
||||||
|
import 'package:news_getx/theme/app_colors.dart';
|
||||||
|
|
||||||
|
import 'application_controller.dart';
|
||||||
|
|
||||||
|
class ApplicationPage extends GetView<ApplicationController> {
|
||||||
|
const ApplicationPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
AppBar _buildAppBar() {
|
||||||
|
return transparentAppBar(
|
||||||
|
title: Obx(
|
||||||
|
() => Text(
|
||||||
|
controller.tabTitles[controller.page],
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.primaryText,
|
||||||
|
fontFamily: "Montserrat",
|
||||||
|
fontSize: 18.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: AppColors.primaryText,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPageView() {
|
||||||
|
return PageView(
|
||||||
|
// 禁止滚动
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
controller: controller.pageController,
|
||||||
|
onPageChanged: controller.handlePageChange,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Main'),
|
||||||
|
Text('Category'),
|
||||||
|
Text('BookmarksPage'),
|
||||||
|
Text('AccountPage'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBottomNavigationBar() {
|
||||||
|
return Obx(
|
||||||
|
() => BottomNavigationBar(
|
||||||
|
items: controller.bottomTabs,
|
||||||
|
currentIndex: controller.page,
|
||||||
|
// 固定类型 导航栏中的所有项都会显示在屏幕上,无论有多少项,它们的宽度都会平均分配
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
onTap: controller.handleNavBarTap,
|
||||||
|
showSelectedLabels: false,
|
||||||
|
showUnselectedLabels: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: _buildAppBar(),
|
||||||
|
body: _buildPageView(),
|
||||||
|
bottomNavigationBar: _buildBottomNavigationBar(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'main_controller.dart';
|
||||||
|
|
||||||
|
class MainBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => MainController());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class MainController extends GetxController {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'main_controller.dart';
|
||||||
|
|
||||||
|
class MainPage extends StatelessWidget {
|
||||||
|
const MainPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final controller = Get.find<MainController>();
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,11 +10,8 @@ AppBar transparentAppBar({
|
||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
title: title != null
|
centerTitle: true,
|
||||||
? Center(
|
title: title,
|
||||||
child: title,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
leading: leading,
|
leading: leading,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:news_getx/modules/application/application_binding.dart';
|
||||||
|
import 'package:news_getx/modules/application/application_page.dart';
|
||||||
import 'package:news_getx/modules/not_found/not_found_binding.dart';
|
import 'package:news_getx/modules/not_found/not_found_binding.dart';
|
||||||
import 'package:news_getx/modules/not_found/not_found_page.dart';
|
import 'package:news_getx/modules/not_found/not_found_page.dart';
|
||||||
import 'package:news_getx/modules/sign_in/sign_in_binding.dart';
|
import 'package:news_getx/modules/sign_in/sign_in_binding.dart';
|
||||||
|
@ -33,5 +35,11 @@ abstract class AppPages {
|
||||||
page: () => NotFoundPage(),
|
page: () => NotFoundPage(),
|
||||||
binding: NotFoundBinding(),
|
binding: NotFoundBinding(),
|
||||||
),
|
),
|
||||||
|
// 应用页
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.Application,
|
||||||
|
page: () => ApplicationPage(),
|
||||||
|
binding: ApplicationBinding(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:dio/io.dart';
|
||||||
import 'package:get/get.dart' hide FormData;
|
import 'package:get/get.dart' hide FormData;
|
||||||
import 'package:news_getx/config/cache.dart';
|
import 'package:news_getx/config/cache.dart';
|
||||||
|
import 'package:news_getx/config/proxy.dart';
|
||||||
import 'package:news_getx/config/server.dart';
|
import 'package:news_getx/config/server.dart';
|
||||||
import 'package:cookie_jar/cookie_jar.dart';
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
|
import 'package:news_getx/data/services/config.dart';
|
||||||
import 'package:news_getx/data/services/user.dart';
|
import 'package:news_getx/data/services/user.dart';
|
||||||
import 'package:news_getx/utils/loading.dart';
|
import 'package:news_getx/utils/loading.dart';
|
||||||
|
import 'package:news_getx/utils/net_cache.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* http 操作类
|
* http 操作类
|
||||||
|
@ -56,10 +62,29 @@ class HttpUtil {
|
||||||
|
|
||||||
dio = Dio(options);
|
dio = Dio(options);
|
||||||
|
|
||||||
|
// 代理设置
|
||||||
|
if (!ConfigService.to.isRelease && ProxyEnable) {
|
||||||
|
dio.httpClientAdapter = IOHttpClientAdapter(
|
||||||
|
createHttpClient: () {
|
||||||
|
final client = HttpClient();
|
||||||
|
client.findProxy = (uri) {
|
||||||
|
return "PROXY $ProxyIP:$ProxyPort";
|
||||||
|
};
|
||||||
|
// 代理工具会提供一个抓包的自签名证书,会通不过证书校验,所以我们禁用证书校验
|
||||||
|
client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Cookie管理
|
// Cookie管理
|
||||||
CookieJar cookieJar = CookieJar();
|
CookieJar cookieJar = CookieJar();
|
||||||
// 增加拦截器CookieManager
|
// 增加拦截器CookieManager
|
||||||
dio.interceptors.add(CookieManager(cookieJar));
|
dio.interceptors.add(CookieManager(cookieJar));
|
||||||
|
// 添加内存缓存
|
||||||
|
if (CacheEnable) {
|
||||||
|
dio.interceptors.add(NetCache());
|
||||||
|
}
|
||||||
|
|
||||||
// 添加拦截器
|
// 添加拦截器
|
||||||
dio.interceptors.add(InterceptorsWrapper(
|
dio.interceptors.add(InterceptorsWrapper(
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Iconfont {
|
||||||
|
// iconName: share
|
||||||
|
static const share = IconData(
|
||||||
|
0xe60d,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: fav
|
||||||
|
static const fav = IconData(
|
||||||
|
0xe60c,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-linkedin
|
||||||
|
static const sociallinkedin = IconData(
|
||||||
|
0xe605,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-apple
|
||||||
|
static const socialapple = IconData(
|
||||||
|
0xe606,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-octocat
|
||||||
|
static const socialoctocat = IconData(
|
||||||
|
0xe607,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-reddit
|
||||||
|
static const socialreddit = IconData(
|
||||||
|
0xe608,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-snapchat
|
||||||
|
static const socialsnapchat = IconData(
|
||||||
|
0xe609,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-skype
|
||||||
|
static const socialskype = IconData(
|
||||||
|
0xe60a,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: social-twitter
|
||||||
|
static const socialtwitter = IconData(
|
||||||
|
0xe60b,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: me
|
||||||
|
static const me = IconData(
|
||||||
|
0xe604,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: tag
|
||||||
|
static const tag = IconData(
|
||||||
|
0xe603,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: grid
|
||||||
|
static const grid = IconData(
|
||||||
|
0xe602,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// iconName: home
|
||||||
|
static const home = IconData(
|
||||||
|
0xe601,
|
||||||
|
fontFamily: 'Iconfont',
|
||||||
|
matchTextDirection: true,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:news_getx/config/cache.dart';
|
||||||
|
|
||||||
|
class CacheObject {
|
||||||
|
Response response;
|
||||||
|
int timestamp;
|
||||||
|
|
||||||
|
CacheObject(this.response)
|
||||||
|
: timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return response.hashCode == other.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => response.realUri.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetCache extends Interceptor {
|
||||||
|
// 为确保迭代器顺序和对象插入时间一致顺序一致,我们使用LinkedHashMap, 默认的字面量{}就是有序的
|
||||||
|
var cache = <String, CacheObject>{};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||||
|
// refresh标记是否是"下拉刷新"
|
||||||
|
bool refresh = options.extra['refresh'] == true;
|
||||||
|
if (refresh) {
|
||||||
|
// 如果是下拉刷新,先删除相关缓存
|
||||||
|
if (options.extra['list']) {
|
||||||
|
//若是列表,则只要url中包含当前path的缓存全部删除(简单实现,并不精准)
|
||||||
|
cache.removeWhere((key, value) => key.contains(options.path));
|
||||||
|
} else {
|
||||||
|
cacheDelete(options.uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get 请求,开启缓存
|
||||||
|
if (options.extra['noCache'] != true &&
|
||||||
|
options.method.toLowerCase() == "get") {
|
||||||
|
String key = options.extra['cacheKey'] ?? options.uri.toString();
|
||||||
|
var ob = cache[key];
|
||||||
|
|
||||||
|
if (ob != null) {
|
||||||
|
//若缓存未过期,则返回缓存内容
|
||||||
|
if ((DateTime.now().millisecondsSinceEpoch - ob.timestamp) / 1000 <
|
||||||
|
CacheMaxAge) {
|
||||||
|
CacheObject? cacheRes = cache[key];
|
||||||
|
if (cacheRes != null) {
|
||||||
|
handler.resolve(cacheRes.response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//若已过期则删除缓存,继续向服务器请求
|
||||||
|
cacheDelete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onRequest(options, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||||
|
// 将返回结果保存到缓存
|
||||||
|
_setCache(response);
|
||||||
|
super.onResponse(response, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setCache(Response response) {
|
||||||
|
RequestOptions options = response.requestOptions;
|
||||||
|
// 只缓存 get 的请求
|
||||||
|
if (options.extra["noCache"] != true &&
|
||||||
|
options.method.toLowerCase() == "get") {
|
||||||
|
// 如果缓存数量超过最大数量限制,则先移除最早的一条记录
|
||||||
|
if (cache.length >= CacheMaxCount) {
|
||||||
|
cacheDelete(cache.keys.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = options.extra['cacheKey'] ?? options.uri.toString();
|
||||||
|
cache[key] = CacheObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cacheDelete(String key) {
|
||||||
|
cache.remove(key);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue