热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

多屏互动——H5中级进阶

前言随着智能硬件的普及,手机,平板,PC甚至路边的电子广告牌,现代浏览器已经无处不在。在浏览器里编织出我们自己的一片天地已经轻车熟路,但是这还不够,H5赋予了浏览器太多的新特性,等

前言

随着智能硬件的普及,手机,平板,PC甚至路边的电子广告牌,现代浏览器已经无处不在。在浏览器里编织出我们自己的一片天地已经轻车熟路,但是这还不够,H5赋予了浏览器太多的新特性,等待我们去使用。这篇文章介绍利用手机浏览器的罗盘API,在PC的浏览器实时地绘制一个3D盒模型。

这种炫酷的玩法叫做“多屏互动”,就像是把手机当做游戏手柄,PC显示器当做电视机,不过这些都是在浏览器里实现的。

先上效果图
《多屏互动——H5 中级进阶》

(测试机是刷了小米系统的裂了屏幕的HTC霹雳2+Chrome浏览器)

源码请戳这里:https://coding.net/u/OverTree…

本地测试过程:

  1. 在PC上,使用命令 node index.js,自动打开项目主页。(请关闭ADsafe,如有虚拟机,请停用虚拟网卡)

  2. 创建一个“房间”并自动进入“房间”。

  3. 用手机扫描“房间”内任意位置的二维码。

  4. 确保手机和PC可以相互PING通

ADsafe是个很好用的去广告软件,但是会阻止本机IP访问,可能造成项目首页打不开,所以请先暂时关闭
本程序会自动获取本机IP,如果有虚拟网卡,IP地址可能获取不正确

客户端(浏览器)

1. 手机浏览器端

一个物体在空间内的旋转体位,都可以用一个方向向量(x,y,z)和旋转角度(angle)来表示。也就是CSS3transformrotate3d(x,y,z,angle)这个函数的4个参数。

想要在浏览器里方便的绘制一个立体模型的的旋转,重点就是利用手机浏览器的H5新特性去获取手机旋转状态的数据,然后转化成这4个参数。

1.1 重力感应API

devicemotion 顾名思义设备运动
其实不仅仅有重力感应的数据,还有移动加速度,摆动角度。
不过这个接口倾向于运动时瞬间的数据展示,静止时,除了重力加速度,其他数据(移动加速度,摆动角度)基本为0。

window.addEventListener('devicemotion', deviceMotionHandler, true);
function deviceMotionHandler(evt){
if(evt.accelerationIncludingGravity){
document.body.innerHTML =
"x轴加速度: " + evt.accelerationIncludingGravity.x + "
"
+ "y轴加速度: " + evt.accelerationIncludingGravity.y + "
"
+ "z轴加速度: " + evt.accelerationIncludingGravity.z + "
"
}
if(evt.rotationRate ){
document.body.innerHTML +=
"x轴扭转: " + evt.rotationRate.beta + "
"
+ "y轴扭转: " + evt.rotationRate.gamma + "
"
+ "z轴扭转: " + evt.rotationRate.alpha + "
"
}
}

(魅族老机型,安卓4.4.4的自带浏览器对此API支持不完全,请另外安装QQ浏览器)
在手机浏览器里运行以上代码,并稍微晃动,会看到打印数据狂跳。
拿到了数据,接下来开始观察规律。
手机屏幕朝上,水平静止放置,Z轴重力加速度为9.8,Y,X为0。
手机屏幕朝下,水平静止放置,Z轴重力加速度为-9.8,Y,X为0。
手机话筒朝下,竖直静止放置,Y重力加速度为9.8, X,Z为0。
手机话筒朝上,竖直静止放置,Y重力加速度为-9.8, X,Z为0。
手机右侧朝上,竖直静止放置,X重力加速度为9.8, Y,Z为0。
手机左侧朝下,竖直静止放置,X重力加速度为-9.8, Y,Z为0。

那么手机的空间坐标如下图:
《多屏互动——H5 中级进阶》
箭头指向都是坐标正方向。

当手机开始倾斜,X,Y,Z轴的加速度分量都有值,且绝对值都小于9.8。根据分量的数值,是可以算出手机在三维空间的倾斜状态,只不过这个计算过程复杂,而且在手机运动时,重力加速度的值并不准确表达当前倾斜。一般不用这个数据去计算手机在三维空间的倾斜。

当手机水平放置,拨动手机,使其慢慢旋转,重力加速度的数据并没有变化。
所以,重力感应的这个API,只能获取设备当前的倾斜状态,而无法获取设备的旋转方向。而一些简单的功能,比如摇一摇,晃一晃,就可以用这个接口去实现。

利用重力感应的API,可以轻松利用高中数学的反三角函数,实现XY二维平面的旋转,效果如下:

《多屏互动——H5 中级进阶》

代码如下:

function deviceMotionHandler(evt){
var angle =
Math.atan2(
0 - evt.accelerationIncludingGravity.x ,    
evt.accelerationIncludingGravity.y
).toFixed(2) / Math.PI * 180 ;
}

这个 angle 就可以直接应用在DOM的CSS属性transform:rotate(angle deg)上。

1.2 罗盘API

window.addEventListener('deviceorientation', deviceOrientationHandler, true);
function deviceMotionHandler(evt){
document.body.innerHTML =
"z轴旋转(罗盘方向) alpha:  " + event.alpha + "
"
+ "y轴旋转 gamma:  " + event.gamma + "
"
+ "x轴旋转 beta:  " + event.beta
}

重点来了,deviceorientation能够很好的表现物体在空间中的状态,旋转方向,倾斜角度,无论是静止还是运动或者加速运动。

这里要和devicemotionevt.rotationRate区分一下,虽然都有alpha,gamma,beta 但是 devicemotion 描述的是旋转变化了的角度值,物体角度变化才会有数据,静止了之后就变为0,而 deviceorientation 的是描述是静止时的角度值。

这三个数值的单位都是deg,如何转化为CSS3 transform:rotate3d(x,y,z,angle) 的4个参数,对于没有任何3D知识的前端狗来说是个挺麻烦的问题。

现在要引入一个概念:四元数

四元数是个高阶复数 q = [w,x,y,z]。
四元数的基本数学方程为 :
q = cos (a/2) + i(x sin(a/2)) + j(y sin(a/2)) + k(z * sin(a/2)) 其中a表示旋转角度,(x,y,z)表示旋转轴。
四元数表示一个完整的旋转。
四元数可以由各轴旋转角(alpha,beta,gamma)求得。
四元数可以转换旋转轴(x,y,z)和旋转角度(angle)。

作为初试,本篇并不深入讨论四元数的具体定义,难点是获取四元数[w,x,y,z]。
好在官方提供了旋转角(alpha,beta,gamma)转换成四元数的方法
https://w3c.github.io/deviceo…
在这个页面内搜索 getQuaternion

另外我根据数学公式反求,写了一个四元数转(x,y,z,angle) 的函数 getAcQuaternion
代码如下:

var degtorad = Math.PI / 180;
function getQuaternion( alpha, beta, gamma ) { //官方求四元数方法
var _x = beta ? beta * degtorad : 0; // beta value
var _y = gamma ? gamma * degtorad : 0; // gamma value
var _z = alpha ? alpha * degtorad : 0; // alpha value
var cX = Math.cos( _x/2 );
var cY = Math.cos( _y/2 );
var cZ = Math.cos( _z/2 );
var sX = Math.sin( _x/2 );
var sY = Math.sin( _y/2 );
var sZ = Math.sin( _z/2 );
var w = cX * cY * cZ - sX * sY * sZ;
var x = sX * cY * cZ - cX * sY * sZ;
var y = cX * sY * cZ + sX * cY * sZ;
var z = cX * cY * sZ + sX * sY * cZ;
return [ w, x, y, z ];
}
function getAcQuaternion( _w, _x, _y, _z ) { //我的四元数转旋转轴和旋转角度方法
var rotate = 2 * Math.acos(_w)/degtorad ;
var x = _x / Math.sin(degtorad * rotate/2) || 0;
var y = _y / Math.sin(degtorad * rotate/2) || 0;
var z = _z / Math.sin(degtorad * rotate/2) || 0;
return {x:x,y:y,z:z,rotate:rotate};
}
function deviceMotionHandler(evt){ // deviceorientation 事件处理函数
var qu = getQuaternion(evt.alpha,evt.beta,evt.gamma);
var rotate3d = getAcQuaternion(qu[0],qu[1],qu[2],qu[3]);
// rotate3d的参数已经有了,随你处理咯。我是把他送给服务器,交给PC,在PC上显示旋转
}

1.3 校准

这里有个3D里的概念,摄像机位置。我们的PC显示器就是一个摄像机。只能被动的从某一个角度展示拍摄的景象。正常情况下,手机所在平面应该和显示器所在平面平行,且垂直于地平面的角度。就好比是,摄像机正对着手机正面拍摄。
如果校准的时候手机并没有垂直于地平面,摄像机的位置就不一定是正前方了。这时候展示的画面并不是水平同步的了。
如下图所示,校准时,手机屏幕朝上。这时候摄像机位置就在天花板上了,你看到的成像就是俯视图。

《多屏互动——H5 中级进阶》

同理,校准时,手机屏幕朝下,这时候摄像机的位置就是在地上,往上拍摄,你看到的成像就是仰视图。

总结起来就是:校准时,手机屏幕朝着哪里,摄像机就在那里拍摄着屏幕,一动不动。

1.4 兼容性

demo的兼容性测试并不理想
在iOS平台上测试良好,且流畅。

在安卓平台上,除了chrome浏览器之外的浏览器,会出现各种问题,主要表现在罗盘数据不准确。
而chrome浏览器并没有扫一扫功能,因为在国外并不流行这个玩意。所以在安卓平台上就很蛋疼,还要多装一个我查查,才能完整体验。
(如果出现旋转不准确的问题,可以尝试校准罗盘,大概就是拿着手机画8。百度一下方法有很多)

代码如果有兼容写法,或者有其他兼容问题请赐教,可以在coding上私信我(OverTree ),不胜感激。

2. PC浏览器端

PC浏览器的作用就是能够显示房间信息,创建房间。

显示房间,创建时间,参与人数,点击进入。
创建一个房间,成功后自动进入房间。

在房间内,接受服务器转发的手机端的消息,并作出相应动作,包括上线,校准,旋转,下线。

上线时,安排就坐(隐藏二维码,显示模型)
校准时,重新设置模型的显示角度。
旋转时,就旋转咯。
下线时,重新显示二维码(显示二维码,隐藏模型)

2.1初始化, 建立ws连接

重点是房间里的事情。所以这里就只介绍进入房间发生的事吧。
首先房间参数要正确,至少有房间编号。

房间路由:
/room/[roomNumber]
roomNumber是一串16位随机字符串。
座位路由:
/room/[roomNumber]/[seatNumber]

var uri = win.location.pathname.split('/'),roomNumber;
function initUrlData(){
if(uri.length>=3 && uri[1] == "room"){
roomNumber = uri[2];
document.title = "虚拟房间 "+ roomNumber + "号"
return 1;
}else{
window.location.href = "/index";
return 0;
}
}
function initWebSocket(){
var wsUri = "ws://"+ window.location.hostname +":<%= config.wsport %>"+"/ws/room"; //这里用了一个ejs的占位符,已便在服务器更改websocket端口时可以及时使用正确端口。
var websocket = new WebSocket(wsUri);
websocket.Onopen= function(evt) {
websocket.send(JSON.stringify({room:roomNumber}));
}; //链接建立后,发送一个消息,表明在哪个房间
websocket.Onclose= function(evt) {
};
websocket.Onmessage= function(evt) {
parseMessage(evt.data) //解析数据
};
websocket.Onerror= function(evt) {
};
//绑定了这些处理函数之后,websocket开始建立链接,而不是 New 的时候开始建立
}
$(".room-place .qrcode").each(function(index,item){
$(item).qrcode({
"size": 200,
"color": "#3a3",
"text": window.location.origin + "/room/" + roomNumber + "/" + (index+1)
});
//这里用jQuery的插件,jquery-qrcode 按照座位路由初始化二维码
})

2.2 纯CSS3立体模型

做为一名普通的前端人员,想要画一个3D的模型,按照最熟悉的方法就是用CSS3了。
(如果是用Three.js的大神请跳过本节)
不过要很快画出一个六面体出来,还是需要想一想的,毕竟这个技能很少用。

画一个长方体












对这样的css有什么要吐槽的么?

这样的stylesheet简直是刀耕火种时期的

如果用sass写法,那么只需要写一次#box和多层嵌套就可以了。

效果如下:
《多屏互动——H5 中级进阶》

如果我们使用webGL去绘制的话,导入一些现成的3D模型,无论物体还是人物,都可以360度无死角的玩弄于手掌了。
(如果有苍老师的模型,想想还有点小激动呢,VR的感觉说来就来啊 &#8211; -)

接下来就是等待来自手机端的旋转信息,x,y,z,angle,使#box进行transform旋转就是了。

$seat.find("#box").
css("transform","rotate3d("
+ (-parseFloat(content.x))+"," //取反
+ (+parseFloat(content.y))+","
+ (-parseFloat(content.z))+"," //取反
+ content.rotate +"deg)");

不取反的话,旋转是错误的。我曾多次尝试给不同的坐标取反,最终得出这个取反方法,是唯一显示正常的组合。

无法理解这两个取反,猜测是因为css的x,y,z的坐标和物理设备x,y,z的坐标方向有差异吧。毕竟显示器是平面的,他的x,y,z的定义不能和手机传感器一致。

2.3 校准

PC端的校准就简单多了,在#box外套一层div.adjust。
当接受来自手机端的校准信息 x,y,z,angle,设置外套的 div.adjust 的旋转为 x,y,z,-angle 就好了。

$seat.find(".adjust").
css("transform","rotate3d("
+ (-parseFloat(content.x))+","
+ (+parseFloat(content.y))+","
+ (-parseFloat(content.z))+","
+ (-parseFloat(content.rotate)) +"deg)"); //取反

当然,这个adjust的样式至少包含以下样式

.adjust{
position: absolute;
transform-style:preserve-3d;
}

2.4 兼容性

PC端的兼容性就好多了,只要是现代H5浏览器基本上没有兼容性问题。

服务端

1.数据结构

这个服务只做临时数据的保存和消息转发。
临时数据:比如,各端的webSocket连接句柄,房间信息等,我把它们放在global全局对象下,就好比是共享内存,访问方便,速度快。

global.ShareMem = {
rooms:{
"12345678":{ //房间号做为key,方便查找
player:[{socket:connection,place:place}], //手机端数组:连接句柄,座位号
projector:[], //PC端数组
id:"12345678",
startTime:Date.now(),
maxplayer:2, //最多座位数
type:"ddd" //房间类型
}
}
};

2.webServer

如果您是nodejs的大神,或者在用koajs、express等nodejs框架,请跳过本大节。因为我用原生的nodejs写了一遍webServer,虽然重复造轮子不好,但是复习复习webServer的基本知识,还是不错的,本节适合新手入门。
包含知识点:header解析,静态文件查找,gzip,文件hash计算,状态码。

2.1 目录结构

/API
/funMap.js /*http功能函数集合*/
/xxx.js
/socketAPI
/funMap.js /*webSocket功能函数集合*/
/xxx.js
/Util /*工具目录,获取本地IP,打开默认浏览器*/
/webRoot
/common /*公共资源目录*/
/js
/lib
/css
/m /*移动端html,js,css等*/
/p /*PC端html,js,css等*/
/index.js /*入口文件*/
/config.js /*配置文件,端口号,ws最大数据包大小等*/
/socketServer.js /*webSocket处理函数*/
/webServer.js

2.2 webServer

基本规则是这样的,搭建静态服务器,静态资源正常读取返回,html文件用ejs渲染后返回。

由于ejs的原因,html文件并没有被修改,但是渲染后的内容被修改,比如,更改了ws的端口,但是html文件没有修改。所以不能使用Last-Modified来判断是文件是否最新,而是要根据返回内容有没有被改变来判断,所以要用Etag

Etag需要根据内容算出hash值,一般用md5计算。

返回内容之前,需要进行gzip压缩,用来节省带宽。90KB的jquery.min.js可以被gzip到30KB,压缩才是王道。

因为手机端和PC端执行的是完全不同的代码,所以要判断从客户端传过来的user-agent是否包含Mobile字符串,以来区分客户端是PC还是手机,以便返回正确的资源。

通过简单的约定,来区分静态文件和REST请求

if (libPath.extname(pathName) == "") {
//如果路径没有扩展名
if(params.length<=2){
pathName += "/"; //访问根目录
}else if(params[1]=="api"){ //访问以api开头
parseAPI(params,req,res); //功能函数
return ;
}else{
pathName = params[1]+".html";
}
}

我在这里做了一个简单的框架,在API目录或者socketAPI目录下新增js文件,一个js文件对应一个处理函数,然后在funMap.js中聚合为一个Map,方便查找函数,也容易隔离和修改函数名。

var funMap = {
"room":require("./room"),
"changeName":require("./xxx"),
"changeName2":require("./xxxyyy")
};
module.exports = funMap;

客户端访问时就可以通过 /api/[functionName] 来访问想要的服务了。

3 webSocketServer

nodejs本身并没有提供webSockerServer的模块,所以需要另外安装一个。

在npm install的时候会安装一个ws模块,require(&#8220;ws&#8221;) 就可以用了。用法与http模块相似,都用 createServer({options},MainHandlerFunction) 创建服务,只是ws多了几个参数。

主要是port,注意不要和webserver端口重复。
还有一个 maxPayload 就是单个ws数据包最大大小,单位是bytes,自己估计项目传输数据时候数据包大小。默认值是65535 即 64KB。一般webSocket用于小包传输,不用太大,我设置了1024 , 1KB。

主处理函数MainHandlerFunction,在有客户端连接进来时会传入一个参数connection,这个对象内容非常丰富,不看手册,可以打印出来也慢慢研究。
成功建立连接的方法就是要connection绑定message方法。

由于wsSocket访问是可以带着url的,所以我们可以用url隔离不同的功能函数,而不是去解析message主体。

var cOnnectHandler= function(connection){
// :4002/api/Function1
var URIarray = connection.upgradeReq.url.split("/")
if(funMap[URIarray[2]]){
funMap[URIarray[2]](connection);
}else{
connection.send("{err:Function Not Found!!}");
}
}

3.1 消息,广播,保活

每当有ws连接进来,都有类似文件描述符的id来区分每个不同的连接。
connection._ultron.id 用它可以区分自己与别人的连接,很有用。

//消息格式
function msgPack(){
return JSON.stringify({
"who":arguments[0], // Mobile , PC
"place":arguments[1], // 座位
"dowhat":arguments[2], // "connect","ready","message","lost"
"content":arguments[3]||"" // 内容
})
}
//以room为单位广播,广播房间内所有角色
function boradCast(room,msg,ignore){
room.projector.forEach(function(item,index){
if(ignore&&ignore._ultron.id===item.socket._ultron.id){
// console.log("ignore!!!")
// 忽略自己不发送给自己
}
else{
try{
item.socket.send(msg);
}catch(e){
console.log(e);
}
}
});
room.player.forEach(function(item,index){
if(ignore&&ignore._ultron.id===item.socket._ultron.id){
// console.log("ignore!!!")
// 忽略自己不发送给自己
}
else{
try{
item.socket.send(msg);
}catch(e){
console.log(e);
}
}
});
}

为了检查客户端是否掉线,在建立连接时手动加入保活机制,方法很简单:
给客户端发送空消息时lastkeeplife为1,只要客户端返回任意消息,那么更新lastkeeplife为0,如果5秒之内,没有任何回复判定为掉线。
如果客户端掉线,那么关闭连接,从连接池中移除。并广播掉线消息给房间内其他角色。

var keeplifeHandler = setInterval(function(){
if(lastkeeplife == 0){
connection.close();
connection.emit("close");
clearInterval(keeplifeHandler);
}
try{
lastkeeplife = 0;
connection.send("{}");
}catch(e){
console.log("keep live error! "+ e +"\n\n");
connection.close();
connection.emit("close");
clearInterval(keeplifeHandler);
}
},5000)
connection.on('close',function(msg){
if(keeplifeHandler){ //关闭保活循环
clearInterval(keeplifeHandler);
}
console.log("close!",roomid,place);
var room = global.ShareMem.rooms[roomid];
if(!room)
return;
//从连接池移除连接句柄
if(platform === PC){
room.projector.forEach(function(item,index){
if(item.socket === connection){
room.projector.splice(index,1);
return false;
}
})
}else{
room.player.forEach(function(item,index){
if(item.socket === connection){
room.player.splice(index,1);
return false;
}
})
}
//发送掉线消息
boradCast( room, msgPack(platform,place,"lost") , connection );
});

iOS设备如果锁屏,会发送断开信息给服务器,而安卓不会。想要断开链接,必须等到默认120秒超时后关闭。
ws初始化时并没有提供初始化timeout的配置。通过修改
ws._server.timeout = 1000;//1秒超时
并不会生效。问题来了,怎么修改才能设置超时时间呢?

目前只能用上述比较捉急的方法来及时断开掉线设备。

最后

多屏互动已经不是新鲜的东西了,我做这个Demo还是受chrome实验室一个叫做【光剑出鞘】的项目的启发。因为体验时需要手机端和PC同时翻-墙,导致体验差,然后自己才想做一个。做出来的时候感觉好酷炫,好神奇,好兴奋。
后续还是有很多可以拓展和改进的,希望最终可以变为一个成熟的产品,而不是仅仅止步于Demo。

相关阅读

  • 无需Flash实现图片裁剪——HTML5中级进阶

  • 5个提高Node.js应用性能的技巧

  • 浏览器存储及使用

作者信息
作者来自力谱宿云 LeapCloud 团队_UX成员:王诗诗 【原创】
首发地址:https://blog.maxleap.cn/archi&#8230;

王诗诗,前端新人,专职前端工作两年。曾供职于AMI做底层软件开发。喜欢分析H5代码,追崇用简单的CSS,构建精美动效,做前端之前,这些是业余爱好。现任职于MaxLeap UX 组,负责MaxWon 的开发和维护。现热衷于Real-time WebApp。

欢迎关注微信订阅号:MaxLeap_yidongyanfa


推荐阅读
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 在Kubernetes上部署JupyterHub的步骤和实验依赖
    本文介绍了在Kubernetes上部署JupyterHub的步骤和实验所需的依赖,包括安装Docker和K8s,使用kubeadm进行安装,以及更新下载的镜像等。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
author-avatar
努力的蚂蚁
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有