范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

关于网络框架设计封装的扯淡

  关于网络框架设计封装的扯淡
  本blog的代码库:
  HttpUtil21. 前后端交互协议设计
  常规是data-code-msg三字段设计
  也有data-code-msg-isSuccess. 其中isSuccess和code其实互为冗余.
  但看了Facebook,google等大公司的接口交互协议,发现其实最全的是:
  data-code-msg-errorData.
  请求正确时:{   "data": {     "uid": "898997899788997"   },   "code": "0",   "msg": "success!",   "success": true,   "errorData": null }
  请求错误时
  错误原因千奇百怪,应使用map来解析errorData,避免解析异常.或直接使用optJSONObject("errorData"){   "data": null,   "code": "user.login.401",   "msg": "unlogin",   "success": false,   "errorData": {     "reason":"kickout",     "time":1689799989   } }
  为了debug方便,在开发/测试环境,后台500时,应将异常栈信息直接塞在msg里返回给前端.2. 应该包含哪些功能
  底层
  从urlconnection到httpclient到okhttp
  封装层
  从volley/asyncHttpClient到retrofit
  如今基本上是okhttp一统底层,上层retrofit+rxjava.
  即使用retrofit,仍然有很多重复代码要写,需要更进一层的封装,方便日常crtl+c ,ctrl+v.
  即使是crtl+c,也希望代码能少一点是一点.
  那么一个封装完善的网络框架,还需要哪些功能?
  先看看几个star比较多的封装库:
  OkGo:
  大强子的NET
  rxHttp
  结合日常开发经验,总结一下,其实有如下可塞入框架中:
  其实,再想想,一个完善的客户端网络库,应该像postman一样基于配置,傻瓜易用.
  封装网络框架,无非是吧这些个gui变成api而已.3. 几个设计上的思想开箱即用
  跟spring boot一样,约定大于配置. 里面的配置项大多都有默认值.
  初始化即使只是调用最简单的init方法,也能使用所有功能.全量信息可访问
  回调里要能拿到本次请求和响应的全量信息.
  比如okhttp在他的callback里就能拿到整个call对象,以及整个response信息.
  很多框架callback里只有解析后的data. 需要用到其他信息时就懵逼了.全方位适应页面生命周期
  管你传view,fragment,activity,lifecycleowner,viewmodel,通通自动处理.
  你说view怎么拿到页面生命周期? context里层层剥开,总能拿到activity.
  生命周期结束后自动取消请求.
  取消请求有两种做法:
  (在等待队列里没有区别,都是移出队列-->只是... okhttp-rxjava的线程模型下,基本都是立刻发出,没有等待)直接socket.close()关掉连接不干预底层,只是在回调里通过boolean值切断回调
  retrofit和rxjava的takeutil,都是用的第一种.简单粗暴易实现,只是后端接口监控里多了一些0或者499的错误.不用kotlin协程
  kotlin协程很牛逼?抱歉,只是假协程,底层还是线程池切换.只是用同步方式写异步代码而已(跟js的async,await差不多).
  当然这并非kotlin不行,而是jvm本身并未支持协程.
  要真能实现像go一样的真协程,或者跳出jvm,自己调用epoll实现多路复用,那就牛逼了,我肯定抢着用kotlin来改写这个框架.
  下面开始讲讲每个关键点的实现和使用4. api使用:
  直接看readmeHttpUtil.requestAsJsonArray("article/getArticleCommentList/v1.json",PostStandardJsonArray.class)                         .addParam("pageSize","30")                         .addParam("articleId","1738")                         .addParam("pageIndex","1")                         .post()                         .setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)                        // .setCacheMode(CacheStrategy.REQUEST_FAILED_READ_CACHE)                         .callback(new MyNetCallback>>(true,null) {                             @Override                             public void onSuccess(ResponseBean> response) {                                 MyLog.json(MyJson.toJsonStr(response.data));                             }                              @Override                             public void onError(String msgCanShow) {                                 MyLog.e(msgCanShow);                              }                         }); String url2 = "https://kiwivm.64clouds.com/dist/openvpn-install-2.4.5-I601.exe";                         HttpUtil.download(url2)                                 .setFileDownlodConfig(                                         FileDownlodConfig.newBuilder()                                         .verifyBySha1("76DAB206AE43FB81A15E9E54CAC87EA94BB5B384")                                         .isOpenAfterSuccess(true)                                         .build())                                 .callback(new MyNetCallback>() {                                     @Override                                     public void onSuccess(ResponseBean response) {                                         MyLog.i("path:"+response.data.filePath);                                     }                                       @Override                                     public void onError(String msgCanShow) {                                         MyLog.e(msgCanShow);                                     }                                 }); 5.关键点5.1 同步异步的支持
  其实okhttp本身就有同步和异步的写法.
  同步直接return,用try-catch包裹.
  异步就使用callback.
  但我们这里内部使用retrofit,基于rxjava.全部变成了回调的形式.
  那么,就不追求同步的写法,直接以异步的形式写同步执行.
  rxjava怎么同步执行?
  不进行线程切换,就同步执行了. so easyHttpUtil.requestString("article/getArticleCommentList/v1.json")         .post()         .setSync(true)//同步执行         .addParam("pageSize","30")         .addParam("articleId","1738")         .addParam("pageIndex","1")         .callback(new MyNetCallback>(true,null) {           @Override           public void onSuccess(ResponseBean response) {             MyLog.i(response.data);           }            @Override           public void onError(String msgCanShow) {             MyLog.e(msgCanShow);           }         }); 5.2 自动处理生命周期原始时代:
  本库使用的方式.
  用静态map存储activity/fragment对象和请求, activity/fragment destory时,从map中取出请求,判断状态,进行cancel./**      * 取消请求,常在activity ondestory处调用.直接传入activity即可,不会保存引用,直接识别其名字作为tag      *      * @param obj      */     public static void cancelByTag(Object obj) {         if (obj == null) {             return;         }         List calls = callMap.remove(obj);//从gc root引用中删除         if (calls != null && calls.size() > 0) {             for (retrofit2.Call call : calls) {                 try {                     if (call.isCanceled()) {                         return;                     }                     call.cancel();                 } catch (Exception e) {                     ExceptionReporterHelper.reportException(e);                 }              }         }      } RxLifecycle + rxjava
  onDestory时构建transformer,传给rxjava的takeUtil操作符.
  本库未实现livedata
  observable转livedata,直接跟lifecyclerOwner挂钩.
  本库已实现.
  5.3 通用UI支持loadingDialog
  内置,默认不显示.可配置开关,UI样式
  错误msg的toast
  比较方便的做法是在onError里统一处理,默认关闭,可以通过链式api开启.
  测试环境应toast: code+" "+msg. 且测试环境的msg应尽量带栈信息.错误码转文案
  一般,应在框架内统一处理.
  分三个类型:
  底层框架抛出的exception,应转为友好文案
  http请求本身的错误码,比如400,500之类的,应提供统一文案
  业务data-code-msg内,如果msg部分后台不做国际化,那么客户端应配置对应的翻译文案.
  框架应自动处理前两个,并提供第三种业务错误文案的配置接口:
  ExceptionFriendlyMsg.init(context, new IFriendlyMsg() {             Map errorMsgs = new HashMap<>();             {                 errorMsgs.put("user.login.89899",R.string.httputl_unlogin_error);             }             @Override             public String toMsg(String code) {                 Integer res = errorMsgs.get(code);                 if(res != null && res != 0){                     return context.getResources().getString(res);                 }                 return "";             }         });
  内部已配置文案:(中文+英文)
  5.4 响应体格式校验
  bean validator这件事情在服务端接收客户端/浏览器请求时比较常用.已经发展成为了一项java规范.
  其实这个需求在客户端并不强烈.服务端的返回大多数情况还是比较稳定的,出现丢字段,字段错误等情况比较少.
  不过,为了小装一个X,我还是把这个功能实现了-->
  其实也不是实现,只是把服务端常用的功能迁移到移动端,并进行了适配. 做了一点微小的工作.
  请看:
  AndroidBeanValidator
  要移植到Android,需要考虑java8兼容性问题,性能(方法耗时),以及对apk大小的影响,默认使用的是Apache BVal 1.1.2.String errorMsg = BeanValidator.validate(bean); //返回的errorMsg为空就说明校验通过 if(!TextUtils.isEmpty(errorMsg)){     //Toast.makeText(this,errorMsg,Toast.LENGTH_LONG).show();   Observable.error(xxx)//把errorMsg和指定errorCode往外抛 }else {     //拿到合格的bean }
  这个操作,放到bean刚被解析出来的时候做就行.5.5 缓存控制:丰富的缓存模式
  超越http协议本身的缓存控制模式
  http协议本身缓存控制有哪些局限:只能缓存get请求老复杂的请求头
  自己写的客户端,能受这点气?必须得改,大改!要能缓存任何请求要能一键支持常用业务模式.setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)  //缓存策略,分类参考:https://github.com/jeasonlzy/okhttp-OkGo //不使用缓存,该模式下,cacheKey,cacheMaxAge 参数均无效     public static final int NO_CACHE = 1; //完全按照HTTP协议的默认缓存规则,例如有304响应头时缓存。     public static final int DEFAULT = 2; //先请求网络,如果请求网络失败,则读取缓存,如果读取缓存失败,本次请求失败。成功或失败的回调只有一次     public static final int REQUEST_FAILED_READ_CACHE = 3; //优先使用缓存,如果缓存不存在才请求网络,成功或失败的回调只有一次     public static final int IF_NONE_CACHE_REQUEST = 4; //先使用缓存,不管是否存在,仍然请求网络,可能导致两次成功的回调或一次失败的回调. //成功回调里,有标识识别本次是缓存还是网络返回.     public static final int FIRST_CACHE_THEN_REQUEST = 5; //只读取缓存,不请求网络     public static final int ONLY_CACHE = 6; 5.6 cookie
  okhttp底层默认没有存cookie,但提供了接口,我们基于他的接口cookiejar实现.
  一般有:不存储cookie只在内存存储cookiecookie序列化到shareprefences/文件:
  第三种跟浏览器行为比较像了.只不过没有浏览器恶心的各种跨域,安全限制,随便玩.
  你说httpOnly?sameSite?不存在的,在我这就是几个key-value,想怎么搞就怎么搞.
  不过作为一个框架,还是遵循一下最基本的,响应一下host和path还是要的.其他的,提供接口给别人自定义吧.松或者严都随意.public static final int COOKIE_NONE = 1;     public static final int COOKIE_MEMORY = 2;     public static final int COOKIE_DISK = 3;     private int cookieMode = COOKIE_DISK;//默认是做持久化操作      /**      * 设置cookie管理策略      */     public GlobalConfig setCookieMode(int cookieMode) {         this.cookieMode = cookieMode;         return this;     } 5.7 公共请求头,请求参数/请求体参数可初始化时用map存储,每次请求时加入:
  如果值初始化后就不变,那推荐使用这种方式.
  如果会变化,就不能用这种.或者变化后更新缓存的map.也可以利用okhttp的拦截器,在拦截器里加入.
  对于请求头,get请求,很简单就实现了
  但对于post json或者multiPart,就需要将json再变成map,然后加入,将multiPart还原,再加入.
  可参考:
  AddCommonHeaderAndParamInterceptor
  如果涉及到请求体签名,那么务必将此拦截器加到签名拦截器之前.5.8 请求超时
  okhttp不是有超时设置么?
  之前只有connecTimeout,read,write三个超时时间,现在看,已新增callTimeout,涵盖了okhttp层面的整个请求过程.
  对于当初没有calltimeout的时代,单纯设置下面三个是不够的,因为dns解析过程并不能被这三者覆盖.
  可以使用rxjava的timeout来控制整个流程的耗时.
  如今依然优先使用rxjava来控制.因为okhttp的calltimeout无法覆盖自定义缓存读写的超时.
  这种一般提供全局配置和单个请求配置5.9 请求重试
  okhttp本身有个重试api:builder.retryOnConnectionFailure(boolean)
  但只是tcp连接失败的重试.且只能重试一次
  要不论什么错误都重试,且可指定重试次数,还是得靠rxjava的api. 这就不说了,直接用就行.5.10 异常捕获和上报
  别管okhttp/retrofit崩不崩,你作为一个封装框架,肯定不能崩.
  任何情况都不能崩,得做到100% crash free.
  有几个关键的地方:拦截器内
  作为应用拦截器第一个,对chain.proceed(request)加上try-catch,降级为ioException,可以被okhttp的error回调处理.@Override     public Response intercept(Chain chain) throws IOException {         try {             Response response = chain.proceed(chain.request());         } catch (Throwable e) {             if (e instanceof IOException) {                 throw e;             } else {                //降级,让okhttp框架能处理错误,而不是crash                 throw new IOException(e);             }         }     } rxjava全局异常捕获:
  这个一般在主工程做.框架内不参与.   RxJavaPlugins.setErrorHandler(new Consumer() {             @Override             public void accept(Throwable e) throws Exception {               report(e);             }    }); 自己框架层的回调里
  回调的onSuccess和onError是使用者实现的,如果也出现了崩溃怎么办?也给你兜住!
  onSuccess抛异常,降级给onError
  onError还抛异常,模仿rxjava,降级给全局错误处理if(bean.success){     try {         onSuccess(callback,t);     }catch (Throwable throwable){         onError(callback,throwable);     } }else {     onError(callback,bean.errorInfo); }      private static  void onError(MyNetCallback callback, Throwable e) {         try {             Tool.logd("-->http is onError: "+callback.getUrl() );             Tool.dismissLoadingDialog(callback.dialogConfig, callback.tagForCancel);             ErrorCallbackDispatcher.dispatchException(callback, e);         }catch (Throwable e2){             if(GlobalConfig.get().getErrorHandler() != null){                 try {                     GlobalConfig.get().getErrorHandler().accept(e2);                 } catch (Exception exception) {                     exception.printStackTrace();                 }                              }else {                 if(!GlobalConfig.get().isDebug()){                     e2.printStackTrace();                 }             }             //测试环境,都崩溃,提醒一下             if(GlobalConfig.get().isDebug()){                 throw e2;             }         }     } 5.11 debug功能
  网络嘛,debug主要形式还是抓包
  提供丰富多彩的看包的形式:logcat 改造okhttpLoggingInterceptor,请求体响应体直接一行打印,方便拷贝. 大于4000个字符切割分行.手机内抓包 改造的chuck,基于okhttp拦截器,通知栏显示抓包内容.提供过滤过于频繁的刷屏请求,比如各种行为日志上报之类的.pc代理抓包 通常用fiddler或者chales. 需要配置: 7.0以上debugable环境忽略证书              或者直接网络框架在debug环境忽略证书stetho-> flipper 基于okhttp拦截器,抓包内容发送到pc上的客户端显示. 显示界面更高端大气上档次. 改写flipper内置的拦截器,有额外加密的,解密后发明文过去显示. 一行脚本集成: flipperUtil5.12 线上监测
  上报不麻烦,关键是统计分析怎么搞?有哪些现成的,自己搭又要怎么搭.
  在上面的拦截器里添加上报即可. 关键是上报到哪里
  构建exception,上报到sentry.
  或者自己搭一条flume+elk的分析系统.
  或者猥琐一点,构建event上报到事件统计平台,蹭他们的流量.哪些参数错误信息: 在上面拦截器/统一的错误回调里拿到并上报即可. 一般上报到统计平台看错误趋势,根据趋势看某时段前后台服务是否有异常. 这通常只是后台本身请求监控的补充. 前几年利用谷歌分析的事件实时分析功能,将错误信息变成event上报,能实时看1min内,30min内的网络错误趋势,自带排序,爽得一逼,可惜后面谷歌分析移动端下线了,firebase上这个功能被运营占用了.请求分时信息: 比如dns耗时,tcp耗时,tls,http请求响应,这些都可以通过okhttp的eventListener接口来获取.5.13 安全
  手段基本是:https上玩的一些操作自定义加密请求头,请求体签名-防篡改https
  基本上就是这几个问题
  什么是中间人攻击
  如何防范中间人攻击
  什么是单向证书校验,框架层如何实现
  什么是双向证书校验,框架层如何实现
  如何对抗证书校验? root手机+frida+okhttplogging的dex 参考: frida使用自定义加密
  拦截器里拿到请求体字节数组,加密,再构建新的requestBody,继续走即可. final Buffer buffer = new Buffer(); requestBody.writeTo(buffer);  final long size = buffer.size();  final byte[] bytes = new byte[(int) size]; buffer.readFully(bytes);  final byte[] bytesEncrypted = encrypt(bytes); //加密成功/失败,最好在请求头加一个标识  return new RequestBody() {             @Override             public MediaType contentType() {                 return MediaType.parse(type);             }              @Override             public long contentLength() {                 return bytesEncrypted.length;             }              @Override             public void writeTo(BufferedSink sink) throws IOException {                 sink.write(bytesEncrypted);             }         }; 请求头请求体签名
  无非是加盐来生成sha1,sha256什么的,没什么好讲的.5.14 gzip
  okhttp已内置对响应体的gzip处理,这个不用再说.
  如果请求体是比较大的字符串,那么用gzip压缩,流量收益方面还是可以的.
  需要前后端支持.
  我们在拦截器里进行gzip压缩.
  gzip前无法指定gzip后的大小,可以再包裹一层,以设定请求体的contentLengthprivate RequestBody gzip(final RequestBody body, String type) {         return new RequestBody() {             @Override             public MediaType contentType() {                 return body.contentType();             }              @Override             public long contentLength() {                 return -1; // We don"t know the compressed length in advance!             }              @Override             public void writeTo(BufferedSink sink) throws IOException {                 BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));                 body.writeTo(gzipSink);                 gzipSink.close();             }         };     }
  后端nginx上用lua脚本进行解压缩后再转发即可.5.15 断点上传/下载
  利用的是http头的range和content-range, 加上java 的randomAccessFile api.
  主要还是工程问题比较难处理.写得好的框架不多.我这个没有做这个断点续传功能.
  5.16 下载后处理
  抄了些迅雷等下载软件的功能,用api的形式提供出来
  比如:下载后校验md5/sha1下载后自动打开: 需要处理Android7的File uri permission下载后通知mediastore扫描是否隐藏文件: 下载一些隐私文件时用,你懂的.利用.nomedia空文件隐藏,防君子不防小人.通知栏显示下载进度/对话框显示下载进度
  5.17 回调形式callbacklivedata返回observable5.18 接口聚合
  场景1 多图异步上传public static io.reactivex.Observable> uploadImgs(String type, final List filePaths){         final List infos = new ArrayList<>();         io.reactivex.Observable> observable =                 HttpUtil.requestAsJsonArray(getUploadTokenPath,S3Info.class)                 .get()                 .addParam("type", type)                 .addParam("contentType", IMAGE_JPEG)                 .addParam("cnt",filePaths.size())                 .asObservable()                 .flatMap(new Function>, ObservableSource>>() {                     @Override                     public ObservableSource> apply(ResponseBean> bean) throws Exception {                          infos.addAll(bean.bean);                         List>> observables = new ArrayList<>();                         for(int i = 0; i< bean.bean.size(); i++){                             S3Info info = bean.bean.get(i);                             String filePath = filePaths.get(i);                             io.reactivex.Observable> observable =                                     HttpUtil.request(info.getUrl(),S3Info.class)                                             .uploadBinary(filePath)                                             .put()                                             .setExtraFromOut(info)                                             .responseAsString()                                             .treatEmptyDataAsSuccess()                                             .asObservable();                             observables.add(observable);                         }                         return io.reactivex.Observable.merge(observables);                     }                 });          return observable;     } 场景2:多接口异步请求,统一回调一次后台微服务拆得太细,又不愿做聚合,只能客户端自己做.
  在客户端,基于Rxjava实现通用的聚合接口请求.
  每个接口可配置能否接受失败代码
  https://github.com/hss01248/HttpUtil2
  https://github.com/hss01248/flipperUtil
  https://github.com/hss01248/AndroidBeanValidator
  https://github.com/hss01248/okhttpInterceptors
  frida使用: https://github.com/skyNet2017/r0capture/blob/main/frida%E4%BD%BF%E7%94%A8.md

650元INTEL1440I710750处理器火了,阴谋?虽然魔改君一再看不上INTEL的笔记本魔改处理器,但客观事实上,确实它有那么点火了。也许,只是因为大家都缺少便宜的玩具,但确实它也足够性能强大。今天就跟大家说一些鲜为人知的秘密,从10月16日电脑硬件行情150元金邦8G内存3600C16之前在内存条的领域里,一直都知道金邦这个品牌的售后口碑特别特别的好!但并没有真实的接触过。最近内存涨价,阿斯加特已经失去了性价比,于是魔改君又找来一大堆内存条测试体质。接触到金邦的INTELI310100F处理器价格暴降至519元?厚道其实这个消息估计太多小伙伴都已经知道了,就是519元的英特尔最新发布的酷睿I310100F处理器。价格是真心便宜!性价比是真心的高!也算INTEL良心?但是这种上车方式貌似有点小恶499元映泰B550M主板!支援AMD锐龙15代处理器,送99元4热管散热之前在粉丝团曾经发过消息,魔改君终于跟台湾二线老牌取得了联系,一方面各种映泰的魔改层出不穷,魔改粉丝和魔改君早已对映泰暗生情愫,另外一方面,官方那边也终于看到了这一系列骚操作,迈出疯狂涨价后的AMD锐龙APU处理器还有性价比?电脑DIY市场复苏无望刚来到沈阳的时候,曾经给粉丝开过一波车,就是AMD4350G4650GMSIB450的套装,反复没过几天,1030元的4350G就涨价到了1260元,当然,4650G4750G估计不用卖掉损失不大!英特尔10代主板更新BIOS强上11代CPU稳定流畅今天收到了粉丝发过来的两块Z490主板,型号分别是ASUSZ490MPLUS大师MSIZ490MEDGEWIFI版之前技嘉的Z490GAMINGX主板已经通过测试,问题不大。今天就我用国产我傲娇!100多元长鑫颗粒D48G3000频率内存条11代绝配最近的内存市场出现了这样几种情况1,价格大涨!2,英特尔11代酷睿处理器上市,平民推荐频率为3600,而不是之前的40003,非国产极品颗粒的内存条货源稀缺!(CJRDJRBDIE700元华擎B560ITX妖板配1200元11代I9处理器,台湾二线何时崛起?首先,时下的电脑硬件DIY领域基本已经成为冰冻状态,除了魔改领域还能提供不错的性价比产品之外,基本上人人憨憨欲睡,毫无欲望。以前觉得矿难不矿难地跟个人没什么关系,现在看看,持续下去为什么你配的电脑价格贵速度慢?3999元AMD新版锐龙处理器实战为什么花了同样的钱,你配出的电脑跑分没人家的高?甚至价格更贵,速度却还是比别人的慢?同样的处理器,为什么人家的功耗更低,温度也更低?电竞大神为什么一场吃鸡可以杀10个人?而你,却总降价至1900元!FPS上1000!AMD游戏之王锐龙5600处理器不再缺货虽然STEAM上的3A大作画质出色,但实际上游戏市场占有率中最高的反而不是这些。腾讯全家桶,LOLDNFCF逆战等等就占据了半壁江山。CSGO等FPS射击对战类游戏也是长盛不衰。如英特尔10代I9皇帝处理器2999元比11代低一半!电脑配置彻底混乱之前有人预测,比I511400F处理器便宜了小300元的10400F处理器可以超频内存的B560主板,将会成为将来INTEL平台装机界的主流选择。没错!现在的INTEL酷睿11代处
远程办公神器向日葵控控A2,有了它可以在家轻松摸鱼前言从北京撤离之后,我就成了彻底在家办公的闲散人员,公司有事或者客户单位有事情要处理,我都是远程过去直接一顿操作,也算是游刃有余。不过依然会存在一些客户的机房是内网机器,布署的系统必买很实用的推荐这几款神器游玩必备心里总想买点啥?看看必买,全网最有料的场景种草指南。眼下正值元旦假期,相信各位小伙伴早早已经安排了出门的行程。今天给大家盘点几款出门游玩必备的神器,是一份非常实用的推荐。移动充电宝L3L4自动驾驶何时到来?绝对定位是瓶颈作者周彦武除了地图标准问题外,定位也是面临难题。地图和定位是一体的,没有高精度定位,高精度地图毫无意义。有关无人车的定位有两种,一种称之为绝对定位,不依赖任何参照物和任何先验信息,node后端服务保活引言目前的项目中使用了node,作为一个简单的后端服务,随着承担着越来越多的线上业务的服务,就要求了服务端的稳定性,而其中最重要的一点就是服务保活。有进程终止后自动重启的能力。foJZ和为S的两个数字和为S的两个数字题目描述输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。返回值描述对应每个测试案例,太强了,终于有一篇文章能够将IP地址和子网掩码讲的明明白白TCPIP(Internet协议)是一套通信协议,用于互连Internet上的网络设备(路由器交换机等)。IP地址子网掩码和默认网关是TCPIP配置中的必需品,TCPIP网络的寻址废柴Npm即将被抛弃?Node。js新宠Corepack又是什么?废柴NPM即将被Node。js官方抛弃?在Node。js16。9。0的官方文档里多了一个实验工具Corepack,以后也会作为内置CLI管理包管理器(yarnnpmpnpm)。详细云南省级财政科技计划项目下达5526项昆明信息港讯(昆明日报记者张怡)日前,省科技厅下达2021年省级财政科技计划项目(明细),共5526项,财政经费22。84亿元。此次下达的计划分为基础研究计划重大专项(重点研发)计BlackBerryOS设备将终止服务支持,手里的黑莓没用了从只能接听电话的功能机,到如今智能手机的频繁迭代,也不过短短二十多年。这些年里,多少手机品牌在这滚滚向前的浪潮中沉浮,有的乘浪前行,而有的曾站在巅峰,如今就像冲向沙滩的浪花,逐渐消一年罚了200亿,市值跌了十万亿,2021中国互联网从喧嚣到静寂文江小桥对于中国互联网来说,过去的这一年可能是况味最为复杂的一年。在开年的一场春节红包大战中,支付宝抖音快手百度淘宝拼多多QQ等12个互联网平台发出了超过132亿元的红包。在这场用三星新机曝光,120Hz5000mAh187g,值得期待三星手机虽然没有华为,苹果这些手机品牌知名度高,但是它在国外市场还是比较受欢迎的,而且很多其他的手机品牌大部分都会使用它的三星AMOLED屏幕,而且三星在质量把控方面确实做的不错,