随想录:开发一流Android SDK

2023-11-04

http://blog.csdn.net/dd864140130/article/details/53558011

自从前段时间离职后,因为个人的事情一直没有选择再工作,也导致原有的文章并没有按时产出.最近个人的事情整理的也差不多了,恰好有不少朋友来问有关SDK开发方面的事情,在此就做个简单的梳理,希望能帮助各位.

目前更多开发者热衷于应用开发,极少数的开发者才有机会从事SDK开发工作,而市面上关于SDK开发介绍的文章少之又少,以至于让大家觉得SDK开发是相对比较难而且非常无聊的工作,今天我们就来简单的聊聊SDK开发的哪点事.


关于SDK的解释

什么是SDK

在开始正文之前,首先来聊聊SDK是个啥玩意.

SDK是Software Development Kit的缩写,译为”软件开发工具包”,通常是为辅助开发某类软件而编写的特定软件包,框架集合等,SDK一般包含相关文档,范例和工具.

SDK可以分为系统SDK和应用SDK.所谓的系统SDK是为特定的软件包,软件框架,硬件平台,操作系统等简历应用时所使用的开发工具集合.而应用SDK则是基于系统SDK开发的独立于具体业务而具有特定功能的集合.

比如在进行Android 应用开发时,我们使用Google提供的系统SDK(Android SDK),而我们经常使用的友盟SDK,极光SDK则是基于系统SDK开发的.

明确SDK的概念之后,再来聊一聊这三个概念:Library,API,Framework

什么是Library

Library即我们所说的库,通常是一组或者几组类的集合,通常是应用中某些功能的具体实现或者对系统已有功能的增强或补充.对Android开发者而言,最常见的莫过于是Support Library,另外就是我们经常使用各种网络请求库(OkHttp,Volley),数据库操作,图片加载库(Glide,ImageLoader)等.

什么是Framework

Framework即我们所说的框架,通常是系统或者应用的骨架,很多时候,它表现为一组抽象的构建及构件实例间交互的方法.因此,可以认为,Framework规定了应用的体系结构,阐明了整体设计,写作构件之间的依赖关系以及控制流程.注意自处的Framework并不完全等同于你所熟知的Android Framework框架,可以认为Android Framework中体现了Framework的思想,并进行了实现.

什么是API

API是Application Programming Interface,又称为应用编程接口,是软件系统不同组成部分衔接的约定。更加通俗的说就API就是我们常见和编写的方法或函数.

小结

明确了上面提到的概念之后,现在就可以来描述这四者之间的关联: 
SDK主要包含Framework,API及Library的三部分.Framework定义了SDK整体的可重用设计,规定了SDK各功能模块的职责以及依赖关系.其中个功能模块体现为Library.模块之间的内部通信及SDK外部通信(SDK对外提供服务的接口)则通过API进行.

另外完整的SDK还应该包含大量的示例和其他工具.比如在Android SDK的tools目录下提供了大量的辅助开发工具.

对我们而言,大部分情况下是为某种具体的业务需求开发对应的SDK,以便作为第三正提供给其他需求方使用.比如百度推送的SDK主要实现消息推送功能,需求方只需要集成百度推送的SDK便可以使自己应用具备推送功能.

到现在已经介绍了SDK的主要构成,接下来我们重点来介绍SDK的实现目标以及在SDK架构中的一些核心点.


浅谈SDK实现目标

上面介绍了开发中常见的概念,现在来谈谈SDK的实现目标.任何应用都应具备:简洁易用,稳定,高效,轻量,SDK作为一种特定应用当然也不例外.

简洁易用

按照”奥卡姆剃须刀”理论,一个好的产品对第三方使用者使用而言应该是简洁易用,不用改让使用者花费太长时间学习的.这对SDK同样适用—SDK不应该对宿主应用有过多的代码侵入,也不应该有复杂频繁的接入工作.比如当开发者需要使用SDK的服务时,只需要在缘由的代码中新增一行即可.常见的SDK初始化如下:

public class Ad{

    @TargetApi(9)
    public synchronized static void init(Context context, SdkParams params) {
        //省略多行代码
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当我们需要使用该SDK的服务时,通过一行代码便可启用Ad.init(this,params)

要保证较少的代码侵入主要在对外提供服务时充分考虑到使用者的使用场景来设计出优良的API.一个优良的API在定义的时候应该满足绝大数开发者所预期的方式—语义上要求通俗易懂,使用上要求简单可靠.

一个优良的API首先是简单可靠的.在正常使用的情况下体现为稳定可靠的执行,在异常情况下体现为及时的告知使用者使用错误.初次之外,遵循一致的明明规则,并是所有的API呈现出一致的风格对开发而言无疑是个好消息.

稳定

站在SDK使用者角度来看,我们期望第三方的SDK服务应该是稳定高效的,体现在提供稳定可靠的服务,在不影响宿主稳定性的前提下足够的高效,这就要求我们SDK设计者在设计并实现SDK时要尽可能的做到以下几点:

  • 对外提供稳定的API.SDK的API一旦确定,如无非常严重情况不可更改.作为提供服务方,发生API变更所带来的变更成本非常大.
  • 对外提供稳定的业务.在稳定的API后,必须要有稳定的业务来支撑.
  • SDK运行时的稳定,作为服务提供方,我们必须确保SDK自身运行的稳定,并且保证接入方不会因为我们的SDK产生不稳定的情况.
  • 版本稳定更新.和面向普通用户的应用相比,SDK版本的迭代是非常缓慢的.并且需要尽可能的对开发者屏蔽迭代过程,以免给开发者带来不必要的适配开销.

高效

无论是普通的应用开发还是SDK开发,都应该考虑到性能问题,SDK设计者应该着重考虑以下问题:

  • 更少的内存占用.在不使用多进程的情况下,SDK服务和宿主程序运行在同一进程中,这种情况下必须要求限制SDK内存的占用,不能因为说因为我们SDK占用太多的内存资源,导致应用的存活时间变短.
  • 更少的内存抖动.在占用更少内存的前提下,SDK设计者必须刻意的减少反复GC造成的内存抖动问题.
  • 更少的电量消耗.尽管很多时候无法对电量消耗做一个很好的权衡,但是仍然有一些可以参考的做法,比如减少使用耗电模块的时间.比如在使用定位服务时,不要求非常高的精度下优先使用网络定位而不是GPS定位.
  • 更少的流量消耗.

SDK整体架构设计

SDK的架构实现决定了SDK后续的维护难度,因此有必要在此对SDK整体架构中的一些点做些简单的说明.

模块化开发

根据单一职责将系统拆分为不同的小模块,每个模块保持相对独立。

模块之间通过协议或接口通信,以减少相互之间的依赖耦合.模块内部按照设计的几大原则进行实现,以保证模块本身可以灵活实现

对于现代开发而言,模块化是常用的手段,从宏观角度来看,模块是系统最小的组成单元.

组件化开发

组件开发同样是个老生常提的概念,但从我个人的感受来说,组件是对逻辑的封装,并具备单个可移植性.比如可以把日志记录做成一个组件,之后它可以被轻松在应用在不同的项目中.对于android 开发者而言,Android 提供的每个UI 控件同样也是组件,比如Button,TextView等.

在明确了组件这一概念之后,组件化开发也就不难理解:所谓的组件化就是将整个项目划分成多个模块,几个模块或者单个模块作为一个组件,开发过程中我们可以对每个组件进行并行开发,最后发布时通过依赖将组件合并成完整的应用.

那为什么要使用组件化呢? 
随着android的逐渐成熟,现在的app业务越来越复杂,与此同时,android工程也变得日益庞大,代码行数十几万已经是常态,此时有几个问题便会凸显出来:

  1. 工程任何一点改动都会造成整个工程的重新编译.记忆最深的就是早期在没有进行组件化的时候,庞大的工程动辄需要十几分钟的编译时间,一杯茶的时间就出来了,很多时候,不得不眼巴巴的等着,尽管现在可以使用facebook出品的buck以及来自阿里的feeline来加速编译过程,单仍然不够.
  2. 整个工程中充斥的大量重复或者冗余的子模块,业务耦合度非常高,牵一发而动全身.这就造成了”老人不敢改,新人无法改”,因为谁也不能预知在做修改之后,会产生什么影响.
  3. 协作开发基本上是不可能的,天知道彼此在做什么.代码合并的的时候更是令人痛苦.
  4. 不方便测试.高度耦合的业务和模块导致无法下手进行测试,只能草草了事.

通过引入组件化,上面遇到的问题便可迎刃而解.在SDK当中,根据实际情况对其进行组件化,比如我们将分享功能组件化,可以轻松的支持多种渠道的分享,在需要更新分享功能时,可以对其进行单独的编译和测试.

通过组件化,我们也可以轻松的实现SDK的定制功能,通过编写编译脚本,我们可以决定哪些组件被依赖,最终合并到完整的应用当中.比如友盟中的提供的可定制分享组件(如下图)的原理就是如此.

这里写图片描述

插件化开发

什么是插件化开发这里就不做介绍了,一方面插件化并不是个新概念,另外就是插件化到目前为止理论层次上已经非常成熟,不想15念开始研究的时候资料相对较少.

在SDK中为什么使用插件化呢?SDK不同于普通应用,不能频繁的进行更新,以免让开发者觉得SDK不稳定或者让开发者频繁的集成.SDK看起来变化较慢,实则变化频繁.就以以前做的广告SDK而言,有时候经常需要对某类机型进行数据采集或者及时更新反作弊模块,在没有使用插件化之前,解决该问题是非常麻烦的.但是在我们利用插件化之后,解决该问题就变得非常容易:我们将SDK整体划分为两部分:宿主和插件.宿主只向开发者提供必要的服务接口,并提供了自定义插件加载器.而核心的逻辑则是存在于插件中.当需要采集数据的时候,只需要由开发人员开发好数据采集插件并下发到指定设备即可;当需要修复SDK缺陷时,同样也只需要下发新的插件包即可.

通过在SDK使用插件化方案,可以有效的对开发者屏蔽手动更新的过程.宿主相对稳定,一旦确定,一般不会变动,而后续的业务变化则只需要通过更新插件来支撑.

除了上面谈到的利用插件化解决动态更新之外,通过将整个工程分为宿主和插件可以实现宿主的并行开发和分开编译,并且能有效的解决方法数65535的限制.在没有使用插件化之前,我们整个项目是由很多组件通过依赖形成的庞大工程,不得不通过


SDK初始化

和应用开发不同,很多情况下SDK没有自身的上下文Context,而必须要借助应用提供.SDK初始化的常见做法:Ad.init(Context context,AdParams params),我们往往推荐开发者在应用Application组件中的onCreate()中去掉用该方法,这就意味着该初始化过程是同步的,假如SDK本身初始化时间较长,就会影响应用的启动速度.

在这种情况下,作为SDK的设计者必须着手解决该问题.通常将SDK服务进一步划分成核心服务和辅助服务,之后通过并行初始化和延迟初始化的手段来减少SDK初始化耗时.曾经在我所负责的广告SDK中,有开发者反馈我们的SDK启动较慢,通过对整个SDK启动流程进行分析后,我们将插件加载服务和云控服务并行初始化,而对于像日志服务则采用颜值初始化,通过该手段有效的减少了初始化耗时

云更新控制

云控服务作为一种服务端控制客户端的手段在SDK中开发中非常重要,现在的SDK开发可以不支持插件化,但是必须要提供云控服务,以便让服务端能控制SDK,比如在不需要进行数据采集的时候,可以通过云控服务关闭SDK采集功能,在需要的时候在将其打开.

对本身是基于插件化开发的SDK而言,云控服务更是不可或缺.

从实现的角度而言,云控服务分为服务端主动和客户端主动.服务端主动是指服务端会将最新的云控开关的信息推送到SDK,而客户端主动则是SDK在进行操作之前会首先请求云控信息.对有推送开发经营的同学而言,这非常容易理解,就是像是为了实现消息推送功能,我们可以通过客户端轮训也可以通过服务端保持长连接进行消息推送一样.

安全

SDK自身安全

为了区分接入者并挑高SDK自身安全性,我们通常会为开发者分配api key和api secret,SDK会读取开发者配置的api key和api secret,并用于随后的网络通信中.这是非常常见的做法,比如当你集成极光推送SDK的时候,它也许需要你提供api key和api secret,如果没有则需要到官网进行申请.

核心逻辑采用C/C++

为了安全起见,数据加密类,模块算法类都都应该采用NDK开发,将其封装在so文件当中.有很多开发者不明白为什么这样会增强安全性.这里我们简单的做个说明.由于.so文件是通过c/c++编译出的文件,相对于Java的反编译文件来说,可读性更差,另外大部分的Android开发者并不具备较深的C/C++能力,因此一定程度上增加了被破解的能力.

通讯加密

针对实际情况对通讯协议进行加密,具体是采用对称加密还是非对称加密,则需要根据实际情况做选择.另外,请尽可能使用https来代替http.

设备安全

在很多情况下,比如广告SDK中,有一些开发者会通过虚拟机来刷广告,因此有必要针对此情况做判断.一旦SDK检测出非法请求后可以采取两种方案,一种是SDK拒绝服务,另外一种则是正常服务,SDK会将作弊信息上传至服务器,以便后端服务定向排除数据.

减少传输数据大小

在设计SDK和服务端通讯之间的数据协议时,需要根据实际情况考虑,但有以下几条建议值得我们接受:

  • 如果对传输的数据大小有要求,建议对数据进行压缩.
  • 可以采用json/xml/Protobuf等协议,如果它们仍然不能满足则可以考虑自定义二进制协议.

选择支持最低系统版本

作为SDK的设计者,面临一个很大的问题是我们不得不考虑开发者应用所支持的系统最小版本,但是在SDK发布之前,我们并不知道会什么样的开发者使用我们提供的服务,因此为了让SDK支持更广泛的设备,我们需要降低最低支持的系统版本.比如现在失眠上主流的系统版本是Android 5.0,那么对SDK而言,起码要支持到Android 4.0,甚至是Android 2.3.

降低最低支持版本看起来很容易,但是我们不得不做更多的工作来确保SDK能表现出一致的工作行为(通常,我们在SDK内部检测当前系统版本来确定哪些方法可以被调用).更残酷的真相是我们花费了很大的精力去支持2.3,但来自2.3系统版本的请求量却连1%都不到.

权限管理

Android中任何开发都避不开权限申请.作为SDK的设计者,对于权限遵循”如无必要,无需增加”,换句话说就是用不到的权限,就不要加上去,这也是我们所谓的最小权限原则,该原则同样适用于普通应用开发.

在刚接触SDK开发时,某些早期功能需要某些权限,但是后期该功能被砍掉了,但是权限却忘记去掉,这就导致不必要权限仍然存在的情况.

另外过多的权限申请,会让开发者怀疑你的目的.比如一个广告SDK的你申请照相机权限是想干嘛?恩,我怀疑你在偷拍我….好吧,这里我只是开个玩笑.

另外,从android 6.0以上,google改变了权限申请的策略,因此需要单独对此做适配.

日志服务

无论系统大小,日志服务是基本的服务.一个良好的日志服务能够帮助我们快速的发现问题,定位缺陷,从而获得问题的解决方案.

SDK的日志服务和其他常见的日志服务并无太大的不同,但是要保证以下几点:

  1. 日志服务能够记录有效的信息,在SDK要关键位置进行打点.
  2. 日志服务上传日志信息到服务器时,要保证最大的可靠性,不能发生上传失败后抛弃日志的情况.
  3. 日志服务不能影响对正常的操作流程有过多的性能影响.SDK产生的日志信息往往是非常多的,因此必须考虑日志IO操作所带来的开销.

深究API设计

API的设计在任何开发中都是非常重要的,很多时候软件的质量好不好在API的设计可以得到体现.在普通的应用开发中,API只会在应用开发人员间流通而不会暴露给非本应用开发的其他人员,但是SDK作为一种服务,需要向开发者暴露一部分API.通常我们将内部流通的API称之为内部API,而开放给开发者的称之为SDK API.

两者使用场景虽然不同,但是都遵循着一些通用的设计规则,这里无法细说,只列出我认为需要重点关注的十一条原则:

方法名能够表明其用途

方法名是理解方法含义的第一渠道.一个好的方法名首先是能够向他人展示自身功能,这样做的好处就是能够减少不必要的沟通成本,对于开发者而言,还有什么比直接读代码更直观呢.

参数的合法性检验

对参数进行合法性检验是非常重要的,请不要想当然的认为可以用运行时异常来代替.当合法性校验不通过时,针对方法权限不同分别对应不同不同的处理策略:

  • 对于公开方法通过显示检查抛出异常的方式,并且使用javadoc的@throw来说明抛出异常的原因
  • 对于私有方法通过断言的方式来检查参数的合法
  • 检查构造方法的参数的合法性,以使对象处在统一状态 
    需要注意,如果检查的代价太大,需要综合考量,比如如果接受的是一个很大的List,此时检查的代价可能很大

方法要明确其单一的功能

一个方法应该具有单一的功能,尽可能做更少,但是更专的事情.这也是我们常说的单一职责原则.另外一定要记住宁可提供小而美的方法也不要提供大而全的方法,经验正面大而全的方法往往发生变动,产生风险的可能性更高,因此不如提供更小的方法以便组合使用

方法异常问题

对于需要暴露给开发者的方法要及时的抛出可查异常来帮助开发者在编译阶段发现问题,另外,对于运行时异常,SDK设计者必须保证该类异常不会导致宿主程序出问题并且需要告知开发者.

方法权限控制

方法的权限也是需要着重考虑的,SDK设计者必须同时从安全和业务的角度考虑哪些方法是可公开的,哪些是不可公开以及哪些是静态的.

避免过长参数

过长的参数会造成记忆上困难,需要慎重对待.在无法避免过长参数的情况下,需要考虑其他的方法进行解决:

a. 通过使用Builder模式来实现 
b. 通过使用辅助类,通常采用静态内部类的方式,具体见静态内部类的使用 
c. 通过将多个参数封装成类对象 
d. 通过将参数拆解成多个方法的参数

谨慎使用方法重载

重载不应该让使用者感到疑惑,即不应该出现这种情况:同样的参数,但是开发者不能明确哪个方法会被执行.换言之就是不要产生歧义性.

另外需要注意,不要存在参数类型经过自动转换就可以运行在另外一个方法的情况,我曾经在code review中看到这样的代码:list中的remove(Object)remove(int),请务必保证自己不会犯类似的错误.尽管在java当中能够使用重载,但是我不建议使用,尤其是不要重载变长参数,在需要重载的时候宁可使用不同方法名来代替也要好的多.关于这点java中提供的ObjectOutputStream类给我们做了很好的示范:它的write对于每个基本类型都有一个变形,比如写出字符,写出boolean等操作,我们发现设计者,并没有使用重载将其设计成write(Long l),write(Boolean b),而是将其设计为writeLong(l),writeBoolean().

对于构造函数,则可以通过是用静态工厂的方式来代替重载.

谨慎使用变长参数

多数情况下不需要使用变长参数,一般方法的参数在5个以上的时候,才 建议使用变长参数.在还有其他非变长参数的情况下,我觉得变长参数放在形参列表的最后.

避免方法直接返回NUll

对于需要返回数组或这集合的方法,不要返回null.比如我们去买糕点店买面包,面包没了是一种正常状态,就不应该返回null,而是返回长度为0的数组或集合.

必要时进行保护性拷贝

当类接受来自客户端的对象或者需要向客户端返回对象,如果该类不能容忍进来的对象再发生变化,那么有必要对对象进行保护性拷贝.另外要注意参数的合法性检验发生在保护性拷贝之后. 
需要注意的是如果需要进行保护性拷贝的对象非常大,比如list集合中存在十多万个对象,需要权衡处理.

这十一条原则是我在团队中推广并要求严格遵守的,下面,将对这十条原则分别进行说明.


SDK开发流程

关于SDK开发流程,我会从以下三个方面写:一时团队中如何协同开发,二是SDK的持续集成,三是SDK多仓库拆分和管理.

这三方面会再另外的篇章中展现(具体什么时候写完目前还未确定)


SDK版本管理策略

SDK 版本号命名及修改原则

SDK版本号命名和我们以往的命名规则并无太大不同,通由4部分组成,格式为: 
V主版本号子版本号阶段版本号_日期版本号加希腊字母版本号.比如V1_1_2_161209_beta.

希腊字母版本号说明

  • Alpha版:内部测试版,此版本表示该软件在该阶段主要是以实现功能为主,Bug相对较多,需要继续修改,通常只在内部流通流通而不对外开放.
  • Beta版:外部测试版,该版本相对Alpha已经有了很大的改进,不存在严重的Bug,但还是存在一些缺陷,需要进一步的测试以检查和消除Bug.
  • RC版:该版本已经相当成熟,不存在导致错误的Bug.与正式版相差无几.
  • Release版:该版本意味着”最终版本”,是最终交付用户或者公开发布的版本,也称为标准版.需要注意的是,该版本在发布的时候回以符合R来代替Release单词.

版本号修改规则

  1. 主版本号变化:当功能模块有较大的变化或者整体架构发生变化
  2. 子版本号变化:当功能有一定变化
  3. 阶段版本号变化:一般是Bug修复或者较小的变动,根据反馈,需要经常发布修订版本.
  4. 日期版本号(161209):用于记录修改项目的当前日期,每天对项目的修改都要更改日期版本号.
  5. 希腊字母版本号:此版本�号用于标注当前软件处于那个开发阶段,当软件进入到另一个阶段是需要修改.

API版本管理

和普通应用API版本管理不同,SDK设计者需要着重关注SDK API的管理.原则上SDK API一旦公开发布后其状态(签名和具体实现)应为不可变.

对于特殊情况下API的变更,需要遵守”开闭原则”,即一个类,模块,方法应该对扩展开发,对修改关闭.这就要求我们做到以下几点:

  1. 在需要调整SDK API时,优先选择添加新方法,而不是在原方法上修改.对于实现相同功能的新方法,尽可能的要兼容原始方法.
  2. 在需要废除某些方法时,需要在正式版发版前使用@deprecated标识,并给出替代方案和废弃的时间(通常是SDK版本号)

接入文档和API文档版本管理

接入文档是用来告诉SDK使用者,如何使用SDK,使用的详细步骤和可能发生的问题,每个公司会有自己的一套规则,这个不需要做太多的解释.

另外,接入文档通常分为两份:内部版和公开版.内部版通常用于内部开发人员和测试人员,信息较为详细,而公开版则是面向开发者,相比内部版会省略的一些信息.

API文档其实就是对SDK API的更详细说明,类似java中的api doc,可以借助jdk的自带javadoc直接生成,当然在android studio也提供了便捷的生成方式.

无论是接入文档还是api说明文档,其变更一般发生在SDK版本发生变化时.当SDK发生变更时,文档必须随之更新,不能出现SDK更新后说明文档不与之匹配的情况.

集成Demo版本管理

集成Demo通常是一个简单的app,用来展示如何快速的接入SDK.其版本变更策略和SDK版本的变化保持一致.


总结

SDK开发中需要关注的点非常多,每个点都不能用三言两语完成的,后面会在此基础上慢慢的补充.


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

随想录:开发一流Android SDK 的相关文章

  • MFC窗口销毁过程

    MFC窗口销毁过程 考虑单窗口情况 假设自己通过new创建了一个窗口对象pWnd 然后pWnd gt Create 则销毁窗口的调用次序 1 手工调用pWnd gt DestroyWindow 2 DestroyWin
  • 学会Mybatis框架:一篇文章带你掌握双剑合璧的技术【四.MyBatis与Spring集成】

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于Mybatis的相关操作吧 导读 我们都知道 MyBatis是一个优秀的持久层框架 它支持定制化SQL 存储过程以及高级映射 Spring是一个全方位的Jav
  • 什么是node,node怎么用?

    Node js 是一个基于Chrome V8 引擎的JavaScript运行环境 Node js使用了一个事件驱动 非阻塞式I O的模型 使其轻量又高效 事件驱动 任务执行 发布者 订阅者 事件驱动 on emit 非阻塞 执行某一个任务的
  • 内存泄露的检测方法

    本文来自http blog csdn net lijun84 引用必须注明出处 在谈及内存泄漏时 对于没有太多经验的新人来说总是很头疼的一件事 因为如果项目早期没有将其纳入代码框架 后期部署上线之后 仅从进程 crash 的 dump 很难
  • SSH框架搭建的全过程(eclipse)

    SSH框架是最常用的框架之一 在搭建SSH框架的时候总有人遇到这样 那样的问题 下面我介绍一下SSH框架搭建的全过程 第一步 准备工作 下载好eclipse Struts2 Spring Hibernate 1 eclipse eclips
  • POI框架导出EXCEL的简单列子(跨行跨列)合并单元格

    public static void main String args throws IOException try HSSFWorkbook wb new HSSFWorkbook HSSFSheet sheet wb createShe
  • RxDownload-基于RxJava打造的下载工具, 支持多线程和断点续传

    http www jcodecraeer com a anzhuokaifa androidkaifa 2016 1104 6743 html 大文件下载测试中 内存占用一直趋于平稳 主要功能 使用Retrofit OKHTTP来进行网络请
  • 在Maven中前端构建实践

    NodeJS为前端技术的发展带来了一次革新 层出不穷的前端库 框架以及打包工具让大家应接不暇 然而这使得前端技术越来越依赖于NodeJS 基于NodeJS编写的前后台项目可以使用同一编译或者打包工具进行管理从而做到无缝的前后端版本控制以及联
  • SpringMVC:从入门到精通,7篇系列篇带你全面掌握--七.自定义注解

    目录 Welcome Huihui s Code World 一 Java注解简介 1 原生注解的分类 1 JDK基本注解 Override SuppressWarnings value unchecked 2 JDK元注解 Retenti
  • vue中引用cdn中的js文件或者json的用法

    1 现在有一个js文件要放在cdn上 这个js文件的内容如下 var testArr a 1 2 我要在vue项目中使用这个变量 因为这个变量可能是经常变化的 但是不能变化一次就打包一次 所以将他放在cdn上 有使用的话直接改变cdn上的j
  • 使用mybatis+spring整合,完成DAO及Service的整合,并完成对图书表的怎删改查操作

    SQL语句如下 CREATE TABLE tb book bookNo int NOT NULL AUTO INCREMENT name varchar 20 CHARACTER SET utf8 COLLATE utf8 bin NOT
  • AVFoundation 框架小结

    AVFoundation 小结 概述 AVFoundation 是 Objective C 中创建及编辑视听媒体文件的几个框架之一 其提供了检查 创建 编辑或重新编码媒体文件的接口 也使得从设备获取的视频实时数据可操纵 但是 通常情况 简单
  • 如何设计一个麻雀般的微型分布式架构?

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由mariolu 发表于云 社区专栏 序言 初衷 设计该系统初衷是基于描绘业务 或机器集群 存储模型 分析代理缓存服务器磁盘存储与回源率的关系 系统意义是在腾讯云成本优化过程中
  • 服务器架构的演进

    服务器端的架构随着公司以及业务的发展 它不断演进 其演进的过程如下 https www cnblogs com joelan0927 p 10425530 html https blog csdn net daogla article de
  • Weex 介绍

    文章目录 一丶Weex的介绍 二丶前置知识 三丶适用人群 四丶Weex的优势 五丶难点 六丶Weex ReactNative Flutter的区别 七丶设计理念 八丶基本原理 九丶有谁在用 十丶Weex调试工具 十一丶构建一个最简单的应用
  • Spring面向切面编程-AOP

    前言 在软件开发中 面向切面编程 Aspect Oriented Programming AOP 是一个非常重要的编程范式 Spring AOP是Spring框架提供的AOP实现 在Spring中使用AOP实现企业应用开发已经非常普遍 本文
  • SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询

    摘要 本文我们将继续分享介绍Spring Boot在整合Mybatis开发企业级应用时其他典型的业务场景 即Mybatis是如何实现多表关联查询时将查询结果集与对象进行映射的 主要的内容包含 一对一的表关联 和 一对多 多对多的表关联 查询
  • 封装的几种形式

    你说的时cpu的封装吗 供你参考 集成电路芯片的封装形式 自从美国Intel公司1971年设计制造出4位微处a理器芯片以来 在20多年时间内 CPU从Intel4004 80286 80386 80486发展到Pentium和Pentium
  • 10分钟带你了解轻量级插件框架x3py

    写在前面 由于本人目前主要从事的是Windows客户端开发方面的工作 所以本文介绍x3py的侧重点也是从客户端程序开发者方面叙述的 本文主要参考整理自x3py的官方Wiki 修正了一些官方示例中的错误 有兴趣的同学可以直接阅读原文 设计目的
  • 值得学习与推荐的c/c++框架和函数库

    这几天不上班 翻翻Evernote中记录的一些笔记 刚好有时间把记录的一些好玩链接转载一下 这篇文章里提到的很多库都用过 尤其是图像处理相关库 尤其是opencv及cximage 当时做图像算法时 很多算法就是从上面找来 然后自己修改的 比

随机推荐

  • div向右偏移设置 css让div靠右移一定距离

    转自 https www thinkcss com shili 1372 shtml div对象盒子向右偏移设置 使用css让div靠右一定距离 div向右移教程实例篇 div向右偏移一定距离 可采用margin外边距实现 也可以使用pad
  • shell脚本模块化

    shell脚本模块化 模块化的优点 功能清晰 易于维护 便于阅读 代码复用 源代码 只有单一的一个run sh文件 bin bash 功能 更新小程序并重新启动 设置程序出错时不再继续执行 set e 查找app的进程号并杀死该进程 ech
  • 网络端口号和协议号(大全)

    网络端口号 作用 端口号的主要作用是表示一台计算机中的特定进程所提供的服务 网络中的计算机是通过IP地址来代表其身份的 它只能表示某台特定的计算机 但是一台计算机上可以同时提供很多个服务 如数据库服务 FTP服务 web服务等 我们就通过端
  • python中哈希表和set的使用

    哈希表不能将可变对象作为key值 即引用类型的内容不能是可变的 这样不安全 因为hashcode函数是根据对象的内容计算出key和value的位置 如果引用的内容可变 那么每次查找的位置结果都不一样 之前存储的键值对就会找不到 不符合has
  • 区块链技术之分布式存储

    随着互联网技术应用技术的普遍使用 所有行业的数据量指数级增长 数据存储技术都需要更新 分布式存储是一种数据存储技术 它可以跨多个物理服务器传播文件 块存储或者对象存储 以实现高可用性 数据备份和灾难恢复目的 可扩展的存储服务以及数据中心的巨
  • K8S的金丝雀发布(Canary Release)

    金丝雀发布 Canary Release 1 概念 2 相关架构理念 3 金丝雀发布部署操作 4 访问测试 5 金丝雀隔离新的pod 6 重建 7 获取当前集群中所有的终结点 8 登录旧的pod中测试 9 查看更新状态信息 总结 1 概念
  • 树链剖分

    树链剖分 两个核心思想 将一棵树转化成一个序列 树中路径转化成 log n 段连续区间 相关概念 重儿子 某个节点的子节点所构成的子树中 子树节点数量最多对应的子节点为重儿子 如果有多个相同的最大数量 则任选一个为重儿子 也就是说 每个节点
  • Node.js 创建一个简单的web服务器

    Node可以写 web服务器 命令行工具 网络爬虫 桌面应用程序开发等 今天 我们利用node写一个简单的web服务器 一 引入主模块 let http require http 二 创建一个服务器 createServer可以看到源码注入
  • 微信小程序子页面自定义tabbar组件

    一 先言 有时候微信小程序会遇到代码合并 就比如把B小程序代码迁移到A小程序 要使得B作为A小程序的一个子页面子功能 因为本身小程序都有tabbar 原来B也有 这时候就要给B子功能自定义一个tabbar底部导航栏 注意 这个不是微信小程序
  • <转>Java集合框架之小结

    转载自 http jiangzhengjun iteye com blog 553191 1 Java容器类库的简化图 下面是集合类库更加完备的图 包括抽象类和遗留构件 不包括Queue的实现 2 ArrayList初始化时不可指定容量 如
  • python爬虫04 - xpath和lxml模块

    可以说 xpath中 x就是不确定 而path就是路径 指向 1 xpath介绍 1 1 基本概念 XPath XML Path Language 是一种XML的查询语言 他能在XML树状结构中寻找节点 XPath 用于在 XML 文档中通
  • FreeRTOS系列第6篇---FreeRTOS内核配置说明

    FreeRTOS内核是高度可定制的 使用配置文件FreeRTOSConfig h进行定制 每个FreeRTOS应用都必须包含这个头文件 用户根据实际应用来裁剪定制FreeRTOS内核 这个配置文件是针对用户程序的 而非内核 因此配置文件一般
  • PCL common模块应用实例

    目录 一 common模块中的头文件 二 基本函数 1 angles h 2 centriod h 3 common h 4 distance h 5 copy point h 6 geometry h 参考链接 本文由CSDN点云侠原创
  • 2022最新苹果开发者账号注册、付款流程图解【图文并茂】

    更新日期 2022 07 22 每年续费就有一个坑 文章末尾已注明 1 申请一个邮箱 用于注册苹果开发者账号 我的邮箱 密码 2 注册开发者账号 1 注册官网 官网地址 Apple Developer Program Apple Devel
  • 国内android第三方rom,安卓手机第三方rom大盘点,曾经火到不行,如今几乎全军覆没...

    自从谷歌接手安卓以后 从2005年到现在 经过了几十年的发展 安卓已经发布了数十个版本 成为目前唯一能和苹果匹敌的手机操作系统 虽然华为有了鸿蒙 但目前至少还没有完全用在手机上 打开APP 查看更多精彩图片 安卓由于其开放性而受到许多用户的
  • copilot command line

    copilot 支持command line了 linux macos都可以安装 npm i githubnext github copilot cli 安装之后 如果要能够正常使用的话 得先进行authorization github账户
  • app怎么修改服务器IP地址,怎么修改手机服务器ip地址

    怎么修改手机服务器ip地址 内容精选 换一换 如果私钥文件丢失了 可以为服务器替换新的密钥对 并使用新的私钥文件连接云手机 以下为替换服务器密钥对的操作指导 请提前在云服务器控制台创建密钥对 并将密钥对对应的私钥文件下载至本地 登录管理控制
  • nslookup命令详解:域名解析=>得到IP地址

    1 nslookup作用 nslookup用于查询DNS的记录 查询域名解析是否正常 在网络故障时用来诊断网络问题 2 查询 a 直接查询 nslookup domain dns server 如果没有指定dns服务器 就采用系统默认的dn
  • chrome 下载东西 失败禁止_如何修复最常见的Google Chrome下载错误

    尽管事实上大多数情况下Google Chrome浏览器都是很漂亮的浏览器 但有时候有些事情并不能完全按预期运行 其中有些涉及文件下载 正如许多Google Chrome用户在某个时候发现的那样 文件下载有时会失败 并不一定是直截了当的错误
  • 随想录:开发一流Android SDK

    http blog csdn net dd864140130 article details 53558011 自从前段时间离职后 因为个人的事情一直没有选择再工作 也导致原有的文章并没有按时产出 最近个人的事情整理的也差不多了 恰好有不少