前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[数据绑定与响应式原理在Vue中的应用 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
HTML
...元素的基础知识及其应用后,我们发现网页设计与开发的世界远比这更为广阔和复杂。近期,随着Web组件化、响应式设计以及无障碍访问(accessibility)的日益普及,这两个基础元素的作用边界正发生着微妙变化。 例如,在Web组件化开发中, 常作为自定义组件的外壳,承载组件内部结构与样式,并通过Shadow DOM技术实现封装与复用。而则在ARIA角色标注中起到关键作用,帮助提升网站的无障碍性,为视觉障碍用户提供更精确的文本描述和导航提示。 同时,随着CSS Grid布局和Flexbox布局的广泛应用, 元素不再仅限于传统的块级容器功能,它能够构建出更加灵活和动态的网格布局。而CSS3新增的选择器和属性,让在实现精细文本样式控制的同时,也支持更多的交互状态样式设定。 进一步探究,现代前端框架如React、Vue等对DOM操作的抽象,使得开发者在使用 和时,更多地关注于数据驱动视图更新的理念,而非直接操作DOM本身。这些新的开发范式和技术趋势,无疑赋予了 和元素更深层次的含义与可能性,值得每一位前端开发者持续关注并深入学习实践。
2023-10-10 08:03:49
503
心灵驿站-t
转载文章
...idView控件作为数据展示和编辑的重要工具,其丰富的属性与功能为开发者提供了强大的灵活性。随着.NET框架的不断演进,特别是在.NET Core及.NET 5.0之后版本中,DataGridView的功能得到了进一步增强和完善。例如,对于大数据量处理场景,新增了虚拟模式以提升性能,允许仅加载当前视图中的行数据,有效降低了内存占用。 近期,微软在.NET社区发布了一系列关于DataGridView优化使用的最佳实践和技术指南,其中包括如何利用最新特性进行异步数据绑定、提升界面响应速度,以及如何结合其他现代UI组件(如Blazor)实现跨平台应用的数据表格交互设计。 另外,在实际项目开发中,为了满足多样化的用户需求,许多开发者开始探讨DataGridView与其他流行前端框架(如React或Angular)的集成方案,通过封装或自定义组件的方式实现在Web端也能享受到类似丰富功能的表格组件。 值得注意的是,随着无障碍技术的发展,针对DataGridView控件的可访问性改进也成为热点话题。遵循WCAG标准,开发者需要关注如何设置正确的行高、列宽、颜色对比度以及支持键盘导航等无障碍特性,确保所有用户都能高效便捷地使用DataGridView展现的数据信息。 总的来说,无论是在.NET原生环境下的深度挖掘,还是跨平台融合创新,亦或是紧跟前沿的无障碍设计,DataGridView控件都在持续进化,为开发者提供更多元、更高效的解决方案。而深入理解和掌握这些扩展特性和应用场景,将有助于我们构建出更具竞争力的应用程序。
2023-02-19 21:54:17
62
转载
Java
...现代软件开发中的实际应用与最新趋势。近年来,随着微服务架构和容器化技术的兴起,依赖注入(Dependency Injection, DI)作为一种解决依赖关系的有效手段,备受瞩目。通过Spring框架等工具,开发者能够更好地管理组件之间的依赖关系,降低耦合度,提升代码的可测试性和扩展性。 此外,关联关系在领域驱动设计(Domain-Driven Design, DDD)中也扮演着重要角色。DDD强调模型的核心地位,提倡将业务逻辑封装在具有关联关系的对象模型中。例如,在电商系统设计中,用户、订单和商品类之间形成的关联关系,能直观地反映并实现复杂的业务场景,确保系统的健壮性和一致性。 同时,关于数据流和对象交互的设计理念也在持续演进。响应式编程(Reactive Programming)利用流处理机制,使得对象间的数据流动更为动态和灵活,从而适应高并发、实时响应的应用需求。RxJava等Java库为开发者提供了在Java环境中实现响应式编程的强大支持,其背后的原理和实践便是对依赖和关联关系深刻理解和创新运用的体现。 总的来说,深入理解和掌握Java中对象的依赖关系和关联关系,并结合当前业界前沿的架构设计理念和技术趋势,对于构建高质量、高效率的软件系统至关重要。开发者应不断关注相关领域的最新研究进展和技术动态,以便于优化代码结构,提升系统性能和稳定性。
2023-05-30 09:47:08
319
电脑达人
AngularJS
...e维护,用于构建单页应用(SPA)。它采用MVC(模型-视图-控制器)架构模式,通过双向数据绑定、指令系统和依赖注入等功能,极大地简化了开发者构建动态网页和复杂Web应用程序的过程。 $http服务 , 在AngularJS框架中,$http服务是一个核心服务,主要用于处理应用程序与服务器之间的HTTP通信。它提供了一系列方法用于发送GET、POST等HTTP请求,并能自动处理响应的数据转换和错误处理,使得前端与后端API交互变得简单、直观且易于管理。 Promise , Promise是一种处理异步操作的编程模式,在JavaScript中被广泛使用,包括在AngularJS的$http服务中。它代表了一个可能尚未完成但最终会 resolved(解决)或 rejected(拒绝)的操作结果。通过.then、.catch等方法,开发者可以链式处理异步操作的各个阶段,以更优雅的方式组织异步代码逻辑,避免回调地狱问题。在文中提到的$http服务发送请求的例子中,.then函数就是用来处理Promise成功resolve后的回调逻辑。
2023-05-14 10:40:55
362
繁华落尽-t
HTML
...strap因其强大的响应式设计和丰富的组件库而广受欢迎。然而,在实际操作的时候,我们免不了会遇到一些小状况、小意外,就像是今天我们要一起解决的这个小烦恼:“Bootstrap滚动监听突然不给力了”。本文将通过一步步排查问题、理解原理,并结合实例代码来帮助大家解决这个问题。 2. 问题重现与初步分析 想象一下这样的场景:你正在使用Bootstrap的一个特性,即监听页面滚动事件以实现某个动态效果(如导航栏固定在顶部或底部)。你按照官方手册和其他教程,吭哧吭哧地捣鼓出那段JavaScript滚动监听代码,可结果呢,这功能就像个沉睡的湖面,无论你怎么上下滑动页面,愣是激不起半点儿波澜,真是让人捉急。 html 这个简单的示例中,我们试图在页面滚动超过100px时,为导航栏添加一个fixed-top类以使其固定在顶部。如果这段代码并未按预期工作,那可能是由多种原因导致的,例如jQuery库未正确引入、DOM元素加载完成前执行了滚动监听等。 3. 排查步骤与解决方案 (1) 确保jQuery已正确引入 Bootstrap的部分功能依赖于jQuery,因此首先需要确保jQuery库已经被成功引入到项目中。检查HTML头部是否包含如下引用: html (2) 使用DOMContentLoaded事件 确保在DOM完全加载完成后才执行滚动监听事件绑定,可以避免因元素未加载完毕而导致的监听失效问题: javascript document.addEventListener("DOMContentLoaded", function(event) { $(window).scroll(function() { // ... 后续滚动监听逻辑 }); }); (3) 检查CSS样式冲突 有时候滚动监听功能看似无效,实际上可能是CSS样式覆盖导致的视觉效果不符预期。对于上述例子中的.fixed-top,请确认Bootstrap CSS文件已被正确引入,并且没有其他CSS规则影响其行为。 4. 进一步讨论与思考 即使以上所有步骤都已正确执行,仍然可能因为某些特定环境或场景下出现滚动监听失效的情况。这就需要我们深入理解Bootstrap的工作原理,并结合具体的项目需求进行细致排查。 例如,如果你在一个复杂的单页面应用中使用Bootstrap,由于页面内容是异步加载的,那么可能需要在每次内容更新后重新绑定滚动事件。或者这样来说,假如你在捣鼓移动端开发,你得留心一个情况,那就是滚动容器可能不是我们通常认为的那个大环境window,而是某个具有“滚屏”特性的div小家伙。这时候,你就得找准目标,给这个div元素好好调教一番,让它成为你的监听对象啦。 5. 结语 面对Bootstrap滚动监听无效的问题,我们需要有耐心地逐层剥茧,从基础的库引用、DOM状态到更复杂的样式冲突和异步加载场景,逐一排查并尝试解决方案。在解决各种问题的实战过程中,我们不仅像健身一样锻炼了自身的技术肌肉,更是对Bootstrap这个工具有了接地气、透彻骨髓的理解和掌握,仿佛它已经成了我们手中的得力助手,随心所欲地运用自如。希望本文能为你带来启示,助你在前端开发的道路上越走越稳!
2023-01-14 23:09:39
594
清风徐来_
JQuery
...页 DOM 的交互和数据处理。有时候,特别是在页面内容采用异步加载或者咱们搞了个 AJAX 请求之后,我们得先拿到当前页面的 URL 地址,这样才能继续下一步操作,或者是传给服务器那边做进一步处理。好嘞,那么咱们就来聊聊一个实际问题:当你使用了 jQuery 中的那个 $.get 方法加载了一个页面后,怎么才能在这个新加载的页面里获取到当前的 URL 呢?接下来,咱俩就一起深入研究下这个问题,我还会给你分享几个超级实用的代码实例! 1. 获取当前完整 URL 使用浏览器内置对象 Location 首先,无论页面是否是通过 AJAX 加载的,JavaScript 都可以访问到浏览器提供的全局 window.location 对象,该对象包含了当前页面的 URL 信息: javascript // 不依赖 jQuery,直接使用原生 JavaScript 获取当前完整 URL var currentUrl = window.location.href; console.log("当前页面的完整 URL 是: ", currentUrl); 如果你确实需要在 jQuery 函数上下文中获取 URL,尽管这不是必须的,但完全可以这样做: javascript // 使用 jQuery 包装器获取当前完整 URL(实际上调用的是原生属性) $(function() { var currentUrlUsingJQuery = $(window).location.href; console.log("使用 jQuery 获取的当前 URL 是: ", currentUrlUsingJQuery); }); 2. 在 $.get 请求完成后获取 URL 当使用 jQuery 的 $.get 方法从服务器异步加载内容时,你可能想在请求完成并渲染新内容之后获取当前 URL。注意,这并不会改变原始页面的 URL,但在回调函数中获取 URL 的方法与上述相同: javascript // 示例:使用 jQuery $.get 方法加载数据,并在成功回调里获取当前 URL $.get('/some-url', function(responseData, textStatus, jqXHR) { // 页面内容更新后,仍可获取当前页面的 URL var urlAfterAjaxLoad = window.location.href; console.log('AJAX 加载后,当前页面的 URL 依然是: ', urlAfterAjaxLoad); // ... 其他针对响应数据的操作 ... }, 'json'); // 注意:$.get 方法默认采用异步方式加载数据 3. 获取 URL 参数及片段标识符(Hash) 在实际应用中,你可能不仅需要完整的 URL,还需要从中提取特定参数或哈希值(hash)。尽管这不是本问题的核心,但它与主题相关,所以这里也给出示例: javascript // 获取 URL 中的查询字符串参数(比如 topicId=361) function getParameterByName(name) { var urlParams = new URLSearchParams(window.location.search); return urlParams.get(name); } var topicId = getParameterByName('topicId'); console.log('当前 URL 中 topicId 参数的值为: ', topicId); // 获取 URL 中的哈希值(例如 section1) var hashValue = window.location.hash; console.log('当前 URL 中的哈希值为: ', hashValue); 综上所述,无论是同步还是异步场景下,通过 jQuery 或原生 JavaScript 获取当前页面 URL 都是一个相当直接的过程。虽然jQuery有一堆好用的方法,但说到获取URL这个简单任务,我们其实完全可以甩开膀子,直接借用浏览器自带的那个叫做window.location的小玩意儿,轻轻松松就搞定了。而且,对于那些更复杂的需求,比如解析URL里的小尾巴(参数)和哈希值这些难题,我们同样备有专门的工具和妙招来搞定它们。所以,在实际编程的过程中,摸透并熟练运用这些底层原理,就像掌握了一套独门秘籍,能让我们在应对各种实际需求时更加得心应手,游刃有余。
2023-02-17 17:07:14
56
红尘漫步_
Struts2
...式的Java web应用程序框架,由Apache软件基金会提供。它主要用于构建企业级Java Web应用,通过简化和标准化应用程序开发过程,提供了丰富的标签库、强大的表单处理功能以及灵活的插件扩展机制。在Struts2中,开发者可以使用配置文件struts.xml来定义Action、结果页面、全局常量等核心组件,实现请求与响应的映射、业务逻辑处理和页面跳转等功能。 DTD (Document Type Definition) , DTD是一种XML文档结构的标准定义方式,在本文中提到的“DOCTYPE Struts Configuration 2.3”即指Struts2.3版本的配置文件DTD定义。这个声明帮助XML解析器理解并验证struts.xml文件的语法和结构是否符合Struts2框架的要求,确保配置文件的有效性与合法性。 OGNL (Object-Graph Navigation Language) , OGNL是一种强大的表达式语言,被广泛应用于Struts2框架中进行数据绑定和访问对象属性。在Struts2中,OGNL允许开发者在Action、JSP页面和其他组件之间灵活地传递和操作数据,如从Action中提取属性值到JSP页面展现,或者动态地根据请求参数执行相应逻辑。在更高版本的Struts2中,支持了OGNL 3.0,增强了类型转换、表达式计算和安全性等方面的功能。 Convention over Configuration (约定优于配置) , 这是一种软件设计范式,强调通过遵循一定的命名约定和项目组织结构,减少开发人员编写大量配置的工作量。在Struts2框架中,通过引入注解等方式,使得一些常见的配置可以通过默认约定自动完成,从而提高开发效率和代码可读性。例如,当遵循特定的目录结构时,Struts2可以自动识别并映射Action类到相应的URL请求上,而无需手动在struts.xml中逐一配置。
2023-11-11 14:08:13
96
月影清风-t
Struts2
...术的发展,现代Web应用开发越来越倾向于采用更轻量级、模块化的解决方案,如Spring Boot和React/Vue等前端框架结合使用。这些新型技术栈通过清晰的路由管理和资源加载机制,有效地避免了传统MVC框架中可能遇到的资源定位难题。尽管如此,理解并掌握像Struts2这样的老牌框架在处理请求映射及资源访问时的工作原理,不仅有助于解决现有系统中的问题,也有助于开发者更好地理解和适应不断演进的Web开发趋势,提升自身技术栈的深度与广度。同时,无论技术如何变迁,代码编写时遵循规范、细致配置以及严谨调试的原则始终不变,这也是每一位开发者在面对各类技术挑战时应当秉持的基本素养。
2024-01-24 17:26:04
169
清风徐来
SpringBoot
...实际项目开发中的更多应用场景与最佳实践。近期,随着微服务架构的广泛应用,拦截器在API网关层的角色愈发重要。例如,Netflix Zuul和Spring Cloud Gateway等API网关框架也支持自定义拦截器机制,用于统一处理跨服务的安全认证、限流熔断、日志记录等功能。 此外,在Web安全领域,拦截器常被用来实现更精细的权限控制和会话管理策略。例如,通过集成OAuth2或JWT等身份验证机制,可以在拦截器中实现对请求令牌的有效性校验,从而确保资源服务器的安全访问。 对于性能优化层面,拦截器亦可发挥关键作用,比如进行SQL日志监控以分析数据库查询效率,或者整合AOP(面向切面编程)技术实现更为灵活的事务管理及缓存策略。 同时,结合Spring Boot 2.x的新特性,如反应式编程模型WebFlux,拦截器的设计与实现方式也将有所变化。在响应式场景下,开发者需要关注Reactive HandlerInterceptor接口,以便在异步非阻塞环境下高效地执行预处理和后处理逻辑。 综上所述,拦截器作为Spring生态乃至众多现代Java Web框架中的核心组件之一,其设计与应用值得广大开发者持续关注和深入研究。不断跟进最新的技术和实践案例,将有助于我们更好地运用拦截器解决实际业务问题,提升系统整体质量和稳定性。
2023-02-28 11:49:38
153
星河万里-t
AngularJS
...一点,就能把那些原始数据瞬间变魔法般地转化为我们所需要的格式,超级酷炫有木有!嘿,伙计们!在这篇指南里,我将手把手地带你们一步步搭建一个属于自己的AngularJS过滤器,让我们一起深入探索这背后的神秘世界,享受编程的乐趣,就像亲手揭开一个又一个的惊喜礼盒! 一、理解AngularJS过滤器(2) 首先,让我们一起理解一下AngularJS过滤器的本质。简单来说,过滤器就是一种用于处理数据展示的方式,它可以对绑定到视图上的数据进行格式化或筛选操作。想象一下,你可能会遇到这样一些情况:需要把日期字符串变个魔术,让它看起来更人性化易读;或者想把数字打扮得整整齐齐,来个四舍五入的处理;甚至有时候,你需要给一串数组排排队、分分类。这些日常的小需求,其实都可以通过自定义过滤器这个小帮手,轻轻松松、美美哒搞定! 二、创建你的第一个过滤器(3) 1. 创建过滤器函数 下面,我们将以一个简单的示例来演示如何创建一个过滤器。假设我们有一个用户列表,需要将用户的全名转化为仅显示姓氏的形式。首先,在AngularJS应用的模块中定义一个过滤器: javascript angular.module('myApp', []) .filter('lastName', function() { return function(input) { // 这里是我们的过滤逻辑 if (input && input.split) { var names = input.split(' '); return names[names.length - 1]; } else { return input; // 如果输入非字符串,则直接返回原值 } }; }); 上述代码中,我们定义了一个名为lastName的过滤器,它接受一个参数input(即用户全名),并返回该名字的最后一个单词作为姓氏。 2. 在视图中使用过滤器 接下来,我们在HTML模板中引用这个过滤器: html { { user.fullName | lastName } } 在这里,{ { user.fullName | lastName } }就是一个典型的过滤器使用方式,| lastName表示对user.fullName这个属性应用了我们刚刚创建的lastName过滤器。 三、进阶 添加更多功能和参数(4) 当然,AngularJS过滤器的功能远不止于此。我们可以让过滤器接收额外的参数,以便提供更多的定制能力。例如,如果我们想让用户可以选择是否显示中间名,可以这样修改过滤器: javascript angular.module('myApp') .filter('lastName', function() { return function(input, showMiddleName) { // 判断是否需要显示中间名 if (!showMiddleName) { // 仅显示姓氏 return (input || '').split(' ').pop(); } else { // 显示全名 return input; } }; }); 然后在视图中传递参数: html { { user.fullName | lastName:showMiddleName } } 以上,我们已经成功地从零开始创建了一个具备基础功能且支持参数化的AngularJS过滤器,并将其运用到了实际场景中。希望这次的探索旅程能帮助你更好地理解和掌握AngularJS过滤器的创建和使用方法。在未来面对更复杂的数据处理需求时,不妨尝试自定义过滤器,让你的应用更具灵活性和可维护性! 总结一下,无论是简化数据展示,还是丰富用户交互体验,AngularJS过滤器都扮演着至关重要的角色。只要我们善于利用并不断实践,就一定能解锁更多有趣且实用的玩法。所以,让我们保持好奇,持续探索,尽情享受编程的乐趣吧!
2024-03-09 11:18:03
476
柳暗花明又一村
Tomcat
...on是两个不可或缺的数据存储机制,它们在处理用户会话和数据持久化上发挥着关键作用。今天呢,咱们就来一起琢磨琢磨,看看这两个概念在Tomcat这个家伙里头是怎么相互扯上关系、纠缠不清的。 二、Cookie的基础知识 1.1 什么是Cookie? Cookie就像是浏览器和服务器之间的秘密信封,用来存储一些临时信息。当用户在浏览网页时,每当他们点开一个网站,服务器就像个小秘书一样,会悄悄地把一些信息(比如用户的专属ID)装进一个叫Cookie的小盒子里,再把这个小盒子递回给用户的浏览器保管。下次你再访问网站时,浏览器就像个小秘书,会贴心地把这些叫做Cookie的小东西一并带给服务器。这样一来,服务器就能轻松认出你,还能随时了解你的动态轨迹啦! java // 设置Cookie HttpServletResponse response = ...; Cookie cookie = new Cookie("userID", "123456"); cookie.setMaxAge(3600); // 有效期1小时 response.addCookie(cookie); 三、Session的出现 1.2 Session的登场 Session则是一个服务器端存储用户会话状态的数据结构,它在服务器端持久化,每次请求都会检查是否已经创建或者重新加载。相比Cookie,Session提供了更安全且容量更大的存储空间。 java // 创建Session HttpSession session = request.getSession(); session.setAttribute("username", "John Doe"); 四、Cookie与Session的关联 2.1 从Cookie到Session 当服务器接收到带有Cookie的请求时,可以通过Cookie中的信息找到对应的Session。如果Session不存在,Tomcat会自动创建一个新的Session。 java // 获取Session HttpSession session = request.getSession(true); // 如果不存在则创建 String userID = (String) session.getAttribute("userID"); 2.2 通过Session更新Cookie 为了保持客户端的登录状态,我们通常会在Session中存储用户信息,然后更新Cookie: java // 更新Cookie Cookie cookie = (Cookie) session.getAttribute("cookie"); cookie.setValue(userID); response.addCookie(cookie); 五、Cookie与Session的区别与选择 3.1 差异分析 Cookie数据存储在客户端,安全性较低,容易被窃取。而Session数据存储在服务器端,安全但需要更多网络开销。通常来说,那些重要的、涉及隐私的敏感信息啊,咱们最好把它们存放在Session里头,就像把贵重物品锁进保险箱一样。而那些不怎么敏感的信息呢,可以考虑用Cookie来存储,就相当于放在抽屉里,方便日常使用,但也不会影响到核心安全。 3.2 何时选择 如果你需要保持用户在长时间内的一致性(如购物车),Session是个好选择。而对于日常的简单对话标记,用Cookie就妥妥的了,因为它完全不需要咱去动用服务器端的资源。 六、总结 Cookie与Session是Web开发中的两个重要工具,理解它们的工作原理以及如何在Tomcat中使用,能帮助我们更好地构建高效、安全的Web应用。记住了啊,每一种技术都有它专属的“舞台”,就像选对了工具,才能让咱们编写的代码更酷炫、更流畅,让用户用起来爽歪歪,体验感直线飙升! 希望这篇文章能帮助你对Tomcat中的Cookie与Session有更深的理解,如果有任何疑问,欢迎随时探讨!
2024-03-05 10:54:01
189
醉卧沙场-t
JSON
...获取JSON对象中的数据后,我们进一步探讨这一主题的最新应用与挑战。随着Web服务和API接口的普及,JSON已成为现代开发中不可或缺的数据传输格式,尤其在微服务架构和实时数据流处理场景下更是如此。 近期,业界对于JSON数据安全性和隐私保护的关注度日益提高。例如,在GDPR等法规的要求下,开发者不仅需要确保能准确获取所需数据,还要关注如何在传输和处理过程中避免敏感信息泄露。为此,一些新的JSON标准或工具应运而生,如JSON Schema可以为JSON数据定义严格的结构和约束条件,有助于减少因数据格式错误引发的问题,并能在一定程度上起到数据过滤的作用。 另外,考虑到性能优化,JSON数据的高效解析与序列化也成为了研究热点。诸如simdjson、MessagePack等新型解决方案通过底层技术革新,极大地提升了JSON数据的处理速度,使得大规模数据交换更为流畅。 此外,对于复杂的嵌套式JSON数据结构,现代前端框架(React、Vue等)提供了便捷的数据绑定与状态管理方案,如Redux、Vuex等,它们能够简化对深层嵌套JSON数据的操作,有效防止因路径引用错误导致的数据获取失败问题。 总结来说,在实际项目开发中,理解和掌握JSON数据的处理技巧是基础,而持续关注JSON相关技术的发展与演进,则有助于我们应对更多复杂场景下的数据交互需求,实现更高效、安全的应用开发。
2023-04-06 16:05:55
719
烟雨江南
Tornado
...它碰到像React、Vue或者Angular这样的前端框架时,就会出现一些好玩儿的问题了。这些难题可能会让你在开发时头大如斗,别慌!咱们一起来搞定它们,找出解决的办法。 2. Tornado 基础知识 首先,让我们快速了解一下 Tornado 的基础知识。Tornado 可是一个很酷的Web服务器框架,它不堵车,能基于事件自动反应,超级适合处理异步操作!这就表示它能同时搞定很多任务,完全不会拖累主程序,让它干等着。这使得 Tornado 成为构建实时应用的理想选择。 2.1 Tornado 的核心概念 - Application:这是 Tornado 应用程序的入口点。你可以在这里定义路由、处理函数等。 - RequestHandler:这是处理 HTTP 请求的核心类。你需要继承这个类并重写 get、post 等方法来处理不同的请求类型。 - AsyncHTTPClient:这是一个异步的 HTTP 客户端,可以用来发送网络请求。 示例代码: python import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world!") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 这段代码创建了一个简单的 Tornado 应用,它监听 8888 端口,并在访问根路径时返回 "Hello, world!"。 3. 前端框架的集成 现在,我们来看看如何将 Tornado 与前端框架集成。这里,我们以 React 为例,但同样的原则也适用于 Vue 和 Angular。 3.1 静态文件服务 前端框架通常需要一个静态文件服务器来提供 HTML、CSS 和 JavaScript 文件。Tornado 可以很容易地实现这一点。 示例代码: python import tornado.ioloop import tornado.web class StaticFileHandler(tornado.web.StaticFileHandler): def set_extra_headers(self, path): 设置 Cache-Control 头,以便浏览器缓存静态文件 self.set_header('Cache-Control', 'max-age=3600') def make_app(): return tornado.web.Application([ (r"/static/(.)", StaticFileHandler, {"path": "./static"}), (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 在这个例子中,我们添加了一个静态文件处理器,它会从 ./static 目录中提供静态文件。这样一来,你的 React 应用就能通过 /static/ 这个路径找到需要的静态资源了。 3.2 实时数据传输 前端框架通常需要实时更新数据。Tornado 提供了 WebSocket 支持,可以轻松实现这一功能。 示例代码: python import tornado.ioloop import tornado.web import tornado.websocket class WebSocketHandler(tornado.websocket.WebSocketHandler): def open(self): print("WebSocket opened") def on_message(self, message): self.write_message(u"You said: " + message) def on_close(self): print("WebSocket closed") def make_app(): return tornado.web.Application([ (r"/ws", WebSocketHandler), (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 这段代码创建了一个 WebSocket 处理器,它可以接收来自客户端的消息并将其回传给客户端。你可以在 React 中使用 WebSocket API 来连接这个 WebSocket 服务器并实现双向通信。 4. 集成挑战与解决方案 在实际项目中,集成 Tornado 和前端框架可能会遇到一些挑战。比如,如何处理跨域请求、如何管理复杂的路由系统等。下面是一些常见的问题及解决方案。 4.1 跨域请求 如果你的前端应用和后端服务不在同一个域名下,你可能会遇到跨域请求的问题。Tornado 提供了一个简单的装饰器来解决这个问题。 示例代码: python from tornado import web class MainHandler(tornado.web.RequestHandler): @web.asynchronous @web.gen.coroutine def get(self): self.set_header("Access-Control-Allow-Origin", "") self.set_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.set_header("Access-Control-Allow-Headers", "Content-Type") self.write("Hello, world!") 在这个例子中,我们设置了允许所有来源的跨域请求,并允许 GET 和 POST 方法。 4.2 路由管理 前端框架通常有自己的路由系统。为了更好地管理路由,我们可以在Tornado里用URLSpec类来设置一些更复杂的规则,这样路由管理起来就轻松多了。 示例代码: python import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world!") class UserHandler(tornado.web.RequestHandler): def get(self, user_id): self.write(f"User ID: {user_id}") def make_app(): return tornado.web.Application([ (r"/", MainHandler), (r"/users/(\d+)", UserHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 在这个例子中,我们定义了两个路由:一个是根路径 /,另一个是 /users/。这样,我们就可以更灵活地管理 URL 路由了。 5. 结语 通过以上的讨论,我们可以看到,虽然 Tornado 和前端框架的集成有一些挑战,但通过一些技巧和最佳实践,我们可以轻松地解决这些问题。希望这篇文章能帮助你在开发过程中少走弯路,享受编程的乐趣! 最后,我想说,编程不仅仅是解决问题的过程,更是一种创造性的活动。每一次挑战都是一次成长的机会。希望你能在这个过程中找到乐趣,不断学习和进步!
2025-01-01 16:19:35
114
素颜如水
Netty
...近期,随着云计算、大数据和微服务等技术的快速发展,对高并发、低延迟网络通信的需求日益增强。例如,在云原生架构中,服务间的通信效率直接影响到整体系统的性能和稳定性,而Netty凭借其异步非阻塞I/O模型、高度优化的设计以及丰富生态,成为了众多分布式系统构建时首选的网络通信库。 此外,Netty 5.0版本的开发工作正在积极进行中,社区开发者们正致力于引入更多的新特性以适应现代网络编程挑战,如对HTTP/3协议的支持、更深度的性能优化以及更加友好的API设计,这些都使得Netty继续保持在网络编程领域的领先地位。 同时,对于希望深入了解Netty内部原理与最佳实践的开发者来说,可以阅读《Netty In Action》一书,书中详细剖析了Netty的工作机制,并提供了大量实战案例供读者参考。通过不断跟踪最新的技术动态,结合经典文献学习,开发者能够更好地运用Netty解决实际项目中的复杂网络问题,提升应用系统的整体效能。
2023-04-12 20:04:43
108
百转千回-t
Material UI
...的重中之重。针对大型应用或高流量网站,如何在不牺牲用户体验的前提下,提高页面加载速度和响应时间,成为亟待解决的问题。Material UI提供了多种优化选项,如懒加载、按需导入组件、减少HTTP请求等。此外,使用Web Performance API进行性能监控,分析瓶颈所在,采取相应措施,也是提升应用性能的有效手段。 再次,响应式设计和适配多设备需求是现代前端开发的重要考量。Material UI提供了丰富的响应式组件,支持自适应布局和动态样式调整。然而,面对复杂多变的屏幕尺寸和分辨率,如何在保持设计一致性的同时,确保每个用户都能获得最佳体验,是值得深入研究的课题。这涉及到对不同设备特性的深入理解,以及灵活运用CSS Flexbox、Grid等布局工具。 最后,安全性不容忽视。随着数据泄露事件频发,前端应用的安全防护变得尤为重要。Material UI虽然提供了安全的组件库,但开发者仍需了解跨站脚本攻击(XSS)、同源策略(CSP)等常见安全威胁,并采取相应措施。加强输入验证、合理使用CDN服务、定期更新依赖库版本,都是提高应用安全性的有效策略。 综上所述,随着技术的不断进步,Material UI的使用不再是简单的组件拼接,而是需要开发者具备更全面的知识和技能,包括组件化、性能优化、响应式设计以及安全防护等方面。通过不断学习和实践,开发者可以更好地应对挑战,构建出既美观又高效、安全的前端应用。
2024-09-28 15:51:28
101
岁月静好
转载文章
...在当前互联网环境下的应用与发展。近年来,随着用户隐私保护意识的增强和GDPR等法规的出台,浏览器对Cookie的限制日益严格,这对依赖Cookie记录用户行为的功能提出了新的挑战。 例如,2021年苹果公司在iOS 14.5系统中引入了ATT(App Tracking Transparency)框架,要求应用在跟踪用户数据前必须征得用户的明确同意,这一变化直接影响到网站和应用对用户浏览历史记录的收集方式。因此,开发者正在寻找替代方案,如使用IndexedDB进行本地存储或者采用Server-side session管理等技术手段。 此外,对于JavaScript追踪用户点击行为的方式也在不断优化。现代前端框架如React、Vue等提供了更强大的状态管理和事件处理机制,可以帮助开发者更高效地实现用户交互行为的记录与分析。同时,Google Analytics 4等先进的分析工具已经实现了无Cookie的用户行为追踪,并能够提供更为详尽且合规的用户行为洞察报告。 综上所述,在确保用户隐私的前提下,运用JavaScript实现在不同场景下的浏览历史记录是一项与时俱进的技术实践。开发者不仅需要关注最新的编程技术和规范,同时也需紧跟行业发展趋势及法律法规要求,以实现用户体验与数据安全之间的平衡。
2023-04-30 21:14:40
48
转载
转载文章
...设置为允许和特定地点绑定 3、Bind()//将套接字绑定特定地址端口 4、Listen()//打开监听端口属性 以下重复进行 5、Accept()//接收客户端的连接请求 6、Read()//从客户端读数据 7、Write()//将处理好的结果发送给客户端 二、HTTP传输协议 基于socket的TCP通信,按HTTP传输协议格式化传输内容。 示例: 1、客户端发送HTTP请求 GET/txt?hal=1000HTTP/1.1 Host:localhost:1024 User-Agent:Mozilla/5.0(X11;Linuxi686;rv:2.0)Gecko/20100101Firefox/4.0 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language:zh-cn,zh;q=0.5 Accept-Encoding:gzip,deflate Accept-Charset:GB2312,utf-8;q=0.7,;q=0.7 Keep-Alive:115 Connection:keep-alive GET:发送HTTP请求的方法,还可以是SET或者POST /txt?hal=1000是请求根目录下的txt文件内容并传入参数hal=1000 HTTP/1.1表示HTTP版本是1.1 2、服务端传回HTTP响应 HTTP/1.0200OK Server:ReageWebServer Content-Type:text/html <!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <!--Copyright(c)2000-2008QuadralayCorporation.Allrightsreserved.--> <head> <title>WebWorksHelp5.0</title> </head> <body>wuff</body> </html> 前面四行(包括空行)是消息体,后面是消息。一般要指明消息体的长度,方便客户端的接收处理。 三、示例程序 ====================================================================== / 主要实现功能,处理浏览器的get请求信息,发送网页文件。处理404、403等错误。 1.实现绑定本机机器的1024端口作为ReageWeb服务提供网页服务的端口。(避免与机器上装有web服务器产生端口冲突) 2.实现get获取网页方式。 3.实现index.html作为网站的首页面 作者:Reage blog:http://blog.csdn.net/rentiansheng / include<stdio.h> include<stdlib.h> include<string.h> include<sys/types.h> include<sys/socket.h> include<sys/un.h> include<netinet/in.h> include<arpa/inet.h> include<fcntl.h> include<string.h> include<sys/stat.h> include<signal.h> defineMAX1024 intres_socket; voidapp_exit(); / @description:开始服务端监听 @parameter ip:web服务器的地址 port:web服务器的端口 @result:成功返回创建socket套接字标识,错误返回-1 / intsocket_listen(charip,unsignedshortintport){ intres_socket;//返回值 intres,on; structsockaddr_inaddress; structin_addrin_ip; res=res_socket=socket(AF_INET,SOCK_STREAM,0); setsockopt(res_socket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); memset(&address,0,sizeof(address)); address.sin_family=AF_INET; address.sin_port=htons(port); address.sin_addr.s_addr=htonl(INADDR_ANY);//inet_addr("127.0.0.1"); res=bind(res_socket,(structsockaddr)&address,sizeof(address)); if(res){printf("portisused,nottorepeatbind\n");exit(101);}; res=listen(res_socket,5); if(res){printf("listenportiserror;\n");exit(102);}; returnres_socket; } / @description:向客户端发送网页头文件的信息 @parameter conn_socket:套接字描述符。 status:http协议的返回状态码。 @s_status:http协议的状态码的含义 @filetype:向客户端发送的文件类型 / voidsend_http_head(intconn_socket,intstatus,chars_status,charfiletype){ charbuf[MAX]; memset(buf,0,MAX); sprintf(buf,"HTTP/1.0%d%s\r\n",status,s_status); sprintf(buf,"%sServer:ReageWebServer\r\n",buf); sprintf(buf,"%sContent-Type:%s\r\n\r\n",buf,filetype); write(conn_socket,buf,strlen(buf)); } / @description:向客户端发送错误页面信息 @parameter conn_socket:套接字描述符。 status:http协议的返回状态码。 @s_status:http协议的状态码的含义 @filetype:向客户端发送的文件类型 @msg:错误页面信息内容 / voidsend_page_error(intconn_socket,intstatus,chars_status,charmsg){ charbuf[MAX]; sprintf(buf,"<html><head></head><body><h1>%s</h1><hr>ReageWebServer0.01</body></head>",msg); send_http_head(conn_socket,status,s_status,"text/html"); write(conn_socket,buf,strlen(buf)); } / @description:向客户端发送文件 @parameter conn_socket:套接字描述符。 @file:要发送文件路径 / intsend_html(intconn_socket,charfile){ intf; charbuf[MAX]; inttmp; structstatfile_s; //如果file为空,表示发送默认主页。主页暂时固定 if(0==strlen(file)){ strcpy(file,"index.html"); } //如果获取文件状态失败,表示文件不存的,发送404页面,暂时404页面内容固定。 if(stat(file,&file_s)){ send_page_error(conn_socket,404,"Notfound","Notfound<br/>Reagedoesnotimplementthismothod\n"); return0; } //如果不是文件或者无读权限,发送无法读取文件 if(!(S_ISREG(file_s.st_mode))||!(S_IRUSR&file_s.st_mode)){ send_page_error(conn_socket,403,"Forbidden","Forbidden<br/>Reagecouldn'treadthefile\n"); return0; } //发送头文件,现在只提供html页面 send_http_head(conn_socket,200,"OK","text/html"); f=open(file,O_RDONLY); if(0>f){ //打开文件失败,发送404页面,其实感觉发送5xx也可以的,服务器内部错误 send_page_error(conn_socket,404,"Notfound","Notfound<br/>Reagecouldn'treadthefile\n"); return0; } buf[MAX-1]=0;//将文件内容缓冲区最后的位设置位结束标志。 //发送文件的内容 while((tmp=read(f,buf,MAX-1))&&EOF!=tmp){ write(conn_socket,buf,strlen(buf)); } } / @description:提取url中可用的信息。访问的网页和数据访问方式 @parameter: conn_socket:与客户端链接的套接字 uri:要处理的url,注意不是浏览器中的url,而是浏览器发送的http请求 @resutl: / intdo_uri(intconn_socket,charuri){ charp; p=strchr(uri,'?'); if(p){p=0;p++;} send_html(conn_socket,uri); } voidulog(charmsg){} voidprint(charmsg){ ulog(msg); printf(msg); } intmain(intargc,charargv[]){ intconn_socket; inttmp; intline; structsockaddr_inclient_addr; charbuf[MAX]; intlen=sizeof(client_addr); charmethod[100],uri[MAX],version[100]; charpwd[1024]; res_socket=socket_listen("127.0.0.1",1024); //当按ctrl+c结束程序时调用,使用app_exit函数处理退出过程 signal(SIGINT,app_exit); while(1){ conn_socket=accept(res_socket,(structsockaddr)&client_addr,&len); printf("reage\n"); line=0; //从客户端获取请求信息 while(0==(tmp=read(conn_socket,buf,MAX-1))||tmp!=EOF){ buf[MAX-1]=0; break;//我只使用了第一行的请求信息,所以丢弃其他的信息 } //send_http_head(conn_socket,200,"text/html"); sscanf(buf,"%s%s%s",method,uri,version); //目前只处理get请求 if(!strcasecmp(method,"get")) //send_html(conn_socket,"h.html"); do_uri(conn_socket,uri+1); close(conn_socket); } } voidapp_exit(){ //回复ctrl+c组合键的默认行为 signal(SIGINT,SIG_DFL); //关闭服务端链接、释放服务端ip和端口 close(res_socket); printf("\n"); exit(0); } ====================================================================== 本篇文章为转载内容。原文链接:https://blog.csdn.net/iteye_9368/article/details/82520401。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-30 18:31:58
90
转载
转载文章
...以包含其他内容,例如数据表格、搜索表单或相关的logo图片。 我们可以使用该元素来写整个页面的标题部分: The most important heading on this page 同一个页面中,每一个内容区块都可以有自己的元素,例如: The most important heading on this page 在HTML5中,我们可以不使用div,而用更加语义化的footer来写: copyright sitemap contact to top 在同一个页面中可以使用多个元素,即可以用作页面整体的页脚,也可以作为一个内容区块的结尾,例如,我们可以将直接写在或是中: Section content appears here. Footer information for section. Article content appears here. Footer information for article. nav -- 作用 -- 导航栏 nav nav元素是一个可以用来作为页面导航的链接组;其中的导航元素链接到其他页面或当前页面的其他部分。并不是所有的链接组都要被放进元素;例如,在页脚中通常会有一组链接,包括服务条款、首页、版权声明等;这时使用元素是最恰当的,而不需要元素。 一直以来,我们都习惯用如下这种方式来定义导航条: Home About Blog 下面是W3C给出的一个代码示例: The Wiki Center Of Exampland Home Current Events ...more... Demos in Exampland Written by A. N. Other. Public demonstrations Demolitions ...more... Public demonstrations ...more... Demolitions ...more... ...more... Edit | Delete | Rename © copyright 1998 Exampland Emperor 关键自li,em,dl,ul,ol,footer,header,nav,aside,article section 版块 用于划分页面上的不同区域,或者划分文章里不同的节 header 页面头部或者版块(section)头部 footer 页面底部或者(section)底部 nav 导航 (包含链接 ... html5新特性-header,nav,footer,aside,article,section等各元素的详解 Html5新增了27个元素,废弃了16个元素,根据现有的标准规范,把HTML5的元素按优先级定义为结构性属性.级块性元素.行内语义性元素和交互性元素四大类. 下面是对各标签的详解,section.he ... h5中的结构元素header、nav、article、aside、section、footer介绍 结构元素不具有任何样式,只是使页面元素的的语义更加明确. header元素 header元素是一种具有引导和导航作用的的结构元素,该元素可以包含所有通常放在页面头部的内容.header元素通常用来放置 ... html5,html5教程 html5,html5教程 1.向后兼容 HTML5是这样被定义的:能向后兼容目前UA处理内容的方式.为了让语言更简单,一些老的元素和Attribute被舍弃.比如一些纯粹用于展现的元素(译注:即非语 ... 一步HTML5教程学会体系 HTML5是HTML最新的版本,万维网联盟. HTML5是下一代的HTML标准,HTML5是为了在移动设备上支持多媒体. 新特性: 绘画的canvas元素,用于媒介回放的video和audio元素,对 ... IT兄弟连 HTML5教程 了解HTML5的主流应用1 在很多人眼里,HTML5与互联网营销密切相关,但其实从开发者的角度而言,它是一种网页标准,定义了浏览器语言的编写规范.伴随HTML5标准尘埃落定,浏览器对HTML5特性的逐步支持,再加上国内对HTML ... 【转帖】39个让你受益的HTML5教程 39个让你受益的HTML5教程 闲话少说,本文作者为大家收集了网上学习HTML5的资源,期望它们可以帮助大家更好地学习HTML5. 好人啊! 不过,作者原来说的4 ... 【特别推荐】Web 开发人员必备的经典 HTML5 教程 对于我来说,Web 前端开发是最酷的职业之一,因为你可以用新的技术发挥,创造出一些惊人的东西.唯一的问题是,你需要跟上这个领域的发展脚步,因此,你必须不断的学习,不断的前进.本文将分享能够帮助您快速掌 ... HTML5教程之本地存储SessionStorage SessionStorage: 将数据保存在session对象中,所谓session是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间会话,也就是用户浏览这个网站所花费的时间就是sess ... 随机推荐 【转】MySQL索引背后的数据结构及算法原理 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ... IIS7 / IIS7.5 URL 重写 HTTP 重定向到 HTTPS(转) 转自: http://www.cnblogs.com/yipu/p/3880518.html 1.购买SSL证书,参考:http://www.cnblogs.com/yipu/p/3722135. ... OpenGL的glViewPort窗口设置函数实现分屏 之前实现过全景图片查看(OpenGL的几何变换3之内观察全景图),那么我们需要进行分屏该如何实现呢?如下图: 没错就是以前提过的glViewPort函数,废话不多说了,我直接上代码: //从这里开始进 ... hdu 4764 Stone (巴什博弈,披着狼皮的羊,小样,以为换了身皮就不认识啦) 今天(2013/9/28)长春站,最后一场网络赛! 3~5分钟后有队伍率先发现伪装了的签到题(博弈) 思路: 与取石头的巴什博弈对比 题目要求第一个人取数字在[1,k]间的某数x,后手取x加[1,k] ... android报表图形引擎(AChartEngine)demo解析与源码 AchartEngine支持多种图表样式,本文介绍两种:线状表和柱状表. AchartEngine有两种启动的方式:一种是通过ChartFactory.getView()方式来直接获取到view ... CSS长度单位及区别 em ex px pt in 1. css相对长度单位 Ø em 元素的字体高度 Ø ex 字体x的高度 Ø px ... es6的箭头函数 1.使用语法 : 参数 => 函数语句; 分为以下几种形式 : (1) ()=>语句 ( )=> statement 这是一种简写方法省略了花括号和return 相当于 ()=&g ... pdfplumber库解析pdf格式 参考地址:https://github.com/jsvine/pdfplumber 简单的pdf转换文本: import pdfplumber with pdfplumber.open(path) a ... KMP替代算法——字符串Hash 很久以前写的... 今天来谈谈一种用来替代KMP算法的奇葩算法--字符串Hash 例题:给你两个字符串p和s,求出p在s中出现的次数.(字符串长度小于等于1000000) 字符串的Hash 根据字面意 ... SSM_CRUD新手练习(5)测试mapper 上一篇我们使用逆向工程生成了所需要的bean.dao和对应的mapper.xml文件,并且修改好了我们需要的数据库查询方法. 现在我们来测试一下DAO层,在test包下新建一个MapperTest.j ... 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_35666639/article/details/118169985。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-16 11:42:34
252
转载
转载文章
...va 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。目前很多开源库都使用到了注解,最熟悉的ButtonKnife中的@ViewInject(R.id.x)就可以替代findViewId,不懂这一块技术的同学第一眼看上去肯定会一脸懵逼,下面会手把手带大家写出ButtonKnife的注解使用。使用注解可以简化代码,提高开发效率。本文简单介绍下注解的使用,并对几个 Android 开源库的注解使用原理进行简析。 1、作用 标记,用于告诉编译器一些信息 ; 编译时动态处理,如动态生成代码 ; 运行时动态处理,如得到注解信息。 2、分类 标准 Annotation, 包括 Override, Deprecated, SuppressWarnings。也都是Java自带的几个 Annotation,上面三个分别表示重写函数,不鼓励使用(有更好方式、使用有风险或已不在维护),忽略某项 Warning; 元 Annotation ,@Retention, @Target, @Inherited, @Documented。元 Annotation 是指用来定义 Annotation 的 Annotation,在后面 Annotation 自定义部分会详细介绍含义; 自定义 Annotation , 表示自己根据需要定义的 Annotation,定义时需要用到上面的元 Annotation 这里只是一种分类而已,也可以根据作用域分为源码时、编译时、运行时 Annotation。通过 @interface 定义,注解名即为自定义注解名。 一、自定义注解 例如,注解@MethodInfo: @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Inheritedpublic @interface MethodInfo {String author() default "annotation@gmail.com";String date();int version() default 1;} 使用到了元Annotation: @Documented 是否会保存到 Javadoc 文档中 ; @Retention 保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS,值为 SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings ; @Target 用来指定修饰的元素,如 CONSTRUCTOR:用于描述构造器、FIELD:用于描述域、LOCAL_VARIABLE:用于描述局部变量、METHOD:用于描述方法、PACKAGE:用于描述包、PARAMETER:用于描述参数、TYPE:用于描述类、接口(包括注解类型) 或enum声明。 @Inherited 是否可以被继承,默认为 false。 注解的参数名为注解类的方法名,且: 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常; 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组; 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation。 public class App {@MethodInfo(author = “annotation.cn+android@gmail.com”,date = "2011/01/11",version = 2)public String getAppName() {return "appname";} } 调用自定义MethodInfo 的示例,这里注解的作用实际是给方法添加相关信息: author、date、version 。 二、实战注解Butter Knife 首先,先定义一个ViewInject注解。 public @interface ViewInject { int value() default -1;} 紧接着,为刚自定义注解添加元注解。 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject {int value() default -1;} 再定义一个注解LayoutInject @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface LayoutInject {int value() default -1;} 定义一个基础的Activity。 package cn.wsy.myretrofit.annotation;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import java.lang.reflect.Field;public class InjectActivity extends AppCompatActivity {private int mLayoutId = -1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);displayInjectLayout();displayInjectView();}/ 解析注解view id/private void displayInjectView() {if (mLayoutId <=0){return ;}Class<?> clazz = this.getClass();Field[] fields = clazz.getDeclaredFields();//获得声明的成员变量for (Field field : fields) {//判断是否有注解try {if (field.getAnnotations() != null) {if (field.isAnnotationPresent(ViewInject.class)) {//如果属于这个注解//为这个控件设置属性field.setAccessible(true);//允许修改反射属性ViewInject inject = field.getAnnotation(ViewInject.class);field.set(this, this.findViewById(inject.value()));} }} catch (Exception e) {Log.e("wusy", "not found view id!");} }}/ 注解布局Layout id/private void displayInjectLayout() {Class<?> clazz = this.getClass();if (clazz.getAnnotations() != null){if (clazz.isAnnotationPresent(LayouyInject.class)){LayouyInject inject = clazz.getAnnotation(LayouyInject.class);mLayoutId = inject.value();setContentView(mLayoutId);} }} } 首先,这里是根据映射实现设置控件的注解,java中使用反射的机制效率性能并不高。这里只是举例子实现注解。ButterKnife官方申明不是通过反射机制,因此效率会高点。 package cn.wsy.myretrofit;import android.os.Bundle;import android.widget.TextView;import cn.wsy.myretrofit.annotation.InjectActivity;import cn.wsy.myretrofit.annotation.LayouyInject;import cn.wsy.myretrofit.annotation.ViewInject;@LayoutInject(R.layout.activity_main)public class MainActivity extends InjectActivity {@ViewInject(R.id.textview)private TextView textView;@ViewInject(R.id.textview1)private TextView textview1;@ViewInject(R.id.textview2)private TextView textview2;@ViewInject(R.id.textview3)private TextView textview3;@ViewInject(R.id.textview4)private TextView textview4;@ViewInject(R.id.textview5)private TextView textview5;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置属性textView.setText("OK");textview1.setText("OK1");textview2.setText("OK2");textview3.setText("OK3");textview4.setText("OK4");textview5.setText("OK5");} } 上面直接继承InjectActivity即可,文章上面也有说过:LayouyInject为什么作用域是TYPE,首先在加载view的时候,肯定是优先加载布局啊,ButterKnife也不例外。因此选择作用域在描述类,并且存在运行时。 二、解析Annotation原理 1、运行时 Annotation 解析 (1) 运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation,可手动调用下面常用 API 解析 method.getAnnotation(AnnotationName.class);method.getAnnotations();method.isAnnotationPresent(AnnotationName.class); 其他 @Target 如 Field,Class 方法类似 。 getAnnotation(AnnotationName.class) 表示得到该 Target 某个 Annotation 的信息,一个 Target 可以被多个 Annotation 修饰; getAnnotations() 则表示得到该 Target 所有 Annotation ; isAnnotationPresent(AnnotationName.class) 表示该 Target 是否被某个 Annotation 修饰; (2) 解析示例如下: public static void main(String[] args) {try {Class cls = Class.forName("cn.trinea.java.test.annotation.App");for (Method method : cls.getMethods()) {MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);if (methodInfo != null) {System.out.println("method name:" + method.getName());System.out.println("method author:" + methodInfo.author());System.out.println("method version:" + methodInfo.version());System.out.println("method date:" + methodInfo.date());} }} catch (ClassNotFoundException e) {e.printStackTrace();} } 以之前自定义的 MethodInfo 为例,利用 Target(这里是 Method)getAnnotation 函数得到 Annotation 信息,然后就可以调用 Annotation 的方法得到响应属性值 。 2、编译时 Annotation 解析 (1) 编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴 apt(Annotation Processing Tool) 解析自动解析。 使用方法: 自定义类集成自 AbstractProcessor; 重写其中的 process 函数 这块很多同学不理解,实际是 apt(Annotation Processing Tool) 在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理。 (2) 假设之前自定义的 MethodInfo 的 @Retention 为 CLASS,解析示例如下: @SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" })public class MethodInfoProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {HashMap<String, String> map = new HashMap<String, String>();for (TypeElement te : annotations) {for (Element element : env.getElementsAnnotatedWith(te)) {MethodInfo methodInfo = element.getAnnotation(MethodInfo.class);map.put(element.getEnclosingElement().toString(), methodInfo.author());} }return false;} } SupportedAnnotationTypes 表示这个 Processor 要处理的 Annotation 名字。 process 函数中参数 annotations 表示待处理的 Annotations,参数 env 表示当前或是之前的运行环境 process 函数返回值表示这组 annotations 是否被这个 Processor 接受,如果接受后续子的 rocessor 不会再对这个 Annotations 进行处理 三、几个 Android 开源库 Annotation 原理简析 1、Retrofit (1) 调用 @GET("/users/{username}")User getUser(@Path("username") String username); (2) 定义 @Documented@Target(METHOD)@Retention(RUNTIME)@RestMethod("GET")public @interface GET {String value();} 从定义可看出 Retrofit 的 Get Annotation 是运行时 Annotation,并且只能用于修饰 Method (3) 原理 private void parseMethodAnnotations() {for (Annotation methodAnnotation : method.getAnnotations()) {Class<? extends Annotation> annotationType = methodAnnotation.annotationType();RestMethod methodInfo = null;for (Annotation innerAnnotation : annotationType.getAnnotations()) {if (RestMethod.class == innerAnnotation.annotationType()) {methodInfo = (RestMethod) innerAnnotation;break;} }……} } RestMethodInfo.java 的 parseMethodAnnotations 方法如上,会检查每个方法的每个 Annotation, 看是否被 RestMethod 这个 Annotation 修饰的 Annotation 修饰,这个有点绕,就是是否被 GET、DELETE、POST、PUT、HEAD、PATCH 这些 Annotation 修饰,然后得到 Annotation 信息,在对接口进行动态代理时会掉用到这些 Annotation 信息从而完成调用。 因为 Retrofit 原理设计到动态代理,这里只介绍 Annotation。 2、Butter Knife (1) 调用 @InjectView(R.id.user) EditText username; (2) 定义 @Retention(CLASS) @Target(FIELD)public @interface InjectView {int value();} 可看出 Butter Knife 的 InjectView Annotation 是编译时 Annotation,并且只能用于修饰属性 (3) 原理 @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {Map<TypeElement, ViewInjector> targetClassMap = findAndParseTargets(env);for (Map.Entry<TypeElement, ViewInjector> entry : targetClassMap.entrySet()) {TypeElement typeElement = entry.getKey();ViewInjector viewInjector = entry.getValue();try {JavaFileObject jfo = filer.createSourceFile(viewInjector.getFqcn(), typeElement);Writer writer = jfo.openWriter();writer.write(viewInjector.brewJava());writer.flush();writer.close();} catch (IOException e) {error(typeElement, "Unable to write injector for type %s: %s", typeElement, e.getMessage());} }return true;} ButterKnifeProcessor.java 的 process 方法如上,编译时,在此方法中过滤 InjectView 这个 Annotation 到 targetClassMap 后,会根据 targetClassMap 中元素生成不同的 class 文件到最终的 APK 中,然后在运行时调用 ButterKnife.inject(x) 函数时会到之前编译时生成的类中去找。 3、ActiveAndroid (1) 调用 @Column(name = “Name") public String name; (2) 定义 @Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Column {……} 可看出 ActiveAndroid 的 Column Annotation 是运行时 Annotation,并且只能用于修饰属性 (3) 原理 Field idField = getIdField(type);mColumnNames.put(idField, mIdName);List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));Collections.reverse(fields);for (Field field : fields) {if (field.isAnnotationPresent(Column.class)) {final Column columnAnnotation = field.getAnnotation(Column.class);String columnName = columnAnnotation.name();if (TextUtils.isEmpty(columnName)) {columnName = field.getName();}mColumnNames.put(field, columnName);} } TableInfo.java 的构造函数如上,运行时,得到所有行信息并存储起来用来构件表信息。 ———————————————————————— 最后一个问题,看看这段代码最后运行结果: public class Person {private int id;private String name;public Person(int id, String name) {this.id = id;this.name = name;}public boolean equals(Person person) {return person.id == id;}public int hashCode() {return id;}public static void main(String[] args) {Set<Person> set = new HashSet<Person>();for (int i = 0; i < 10; i++) {set.add(new Person(i, "Jim"));}System.out.println(set.size());} } 答案:示例代码运行结果应该是 10 而不是 1,这个示例代码程序实际想说明的是标记型注解 Override 的作用,为 equals 方法加上 Override 注解就知道 equals 方法的重载是错误的,参数不对。 本篇文章为转载内容。原文链接:https://blog.csdn.net/csdn_aiyang/article/details/81564408。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-28 22:30:35
104
转载
转载文章
...也就是要在一个线程内响应多个网络 I/O。以前的同步阻塞方式下,一个线程只能处理一个请求,到这里不再适用,是不是可以用非阻塞 I/O 或者异步 I/O 来处理多个网络请求呢? 第二,怎么更节省资源地处理客户请求,也就是要用更少的线程来服务这些请求。是不是可以继续用原来的 100 个或者更少的线程,来服务现在的 10000 个请求呢? I/O 模型优化 异步、非阻塞 I/O 的解决思路是我们在网络编程中经常用到的 I/O 多路复用(I/O Multiplexing) 两种 I/O 事件通知的方式:水平触发和边缘触发,它们常用在套接字接口的文件描述符中。 水平触发:只要文件描述符可以非阻塞地执行 I/O ,就会触发通知。也就是说,应用程序可以随时检查文件描述符的状态,然后再根据状态,进行 I/O 操作。 边缘触发:只有在文件描述符的状态发生改变(也就是 I/O 请求达到)时,才发送一次通知。这时候,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止。如果 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了。 I/O 多路复用的方法有很多实现方法,我带你来逐个分析一下。 第一种,使用非阻塞 I/O 和水平触发通知,比如使用 select 或者 poll。 根据刚才水平触发的原理,select 和 poll 需要从文件描述符列表中,找出哪些可以执行 I/O ,然后进行真正的网络 I/O 读写。由于 I/O 是非阻塞的,一个线程中就可以同时监控一批套接字的文件描述符,这样就达到了单线程处理多请求的目的。所以,这种方式的最大优点,是对应用程序比较友好,它的 API 非常简单。 但是,应用软件使用 select 和 poll 时,需要对这些文件描述符列表进行轮询,这样,请求数多的时候就会比较耗时。并且,select 和 poll 还有一些其他的限制。 select 使用固定长度的位相量,表示文件描述符的集合,因此会有最大描述符数量的限制。比如,在 32 位系统中,默认限制是 1024。并且,在 select 内部,检查套接字状态是用轮询的方法,再加上应用软件使用时的轮询,就变成了一个 O(n^2) 的关系。 而 poll 改进了 select 的表示方法,换成了一个没有固定长度的数组,这样就没有了最大描述符数量的限制(当然还会受到系统文件描述符限制)。但应用程序在使用 poll 时,同样需要对文件描述符列表进行轮询,这样,处理耗时跟描述符数量就是 O(N) 的关系。 除此之外,应用程序每次调用 select 和 poll 时,还需要把文件描述符的集合,从用户空间传入内核空间,由内核修改后,再传出到用户空间中。这一来一回的内核空间与用户空间切换,也增加了处理成本。 有没有什么更好的方式来处理呢?答案自然是肯定的。 第二种,使用非阻塞 I/O 和边缘触发通知,比如 epoll。既然 select 和 poll 有那么多的问题,就需要继续对其进行优化,而 epoll 就很好地解决了这些问题。 epoll 使用红黑树,在内核中管理文件描述符的集合,这样,就不需要应用程序在每次操作时都传入、传出这个集合。 epoll 使用事件驱动的机制,只关注有 I/O 事件发生的文件描述符,不需要轮询扫描整个集合。 不过要注意,epoll 是在 Linux 2.6 中才新增的功能(2.4 虽然也有,但功能不完善)。由于边缘触发只在文件描述符可读或可写事件发生时才通知,那么应用程序就需要尽可能多地执行 I/O,并要处理更多的异常事件。 第三种,使用异步 I/O(Asynchronous I/O,简称为 AIO)。 在前面文件系统原理的内容中,我曾介绍过异步 I/O 与同步 I/O 的区别。异步 I/O 允许应用程序同时发起很多 I/O 操作,而不用等待这些操作完成。而在 I/O 完成后,系统会用事件通知(比如信号或者回调函数)的方式,告诉应用程序。这时,应用程序才会去查询 I/O 操作的结果。 异步 I/O 也是到了 Linux 2.6 才支持的功能,并且在很长时间里都处于不完善的状态,比如 glibc 提供的异步 I/O 库,就一直被社区诟病。同时,由于异步 I/O 跟我们的直观逻辑不太一样,想要使用的话,一定要小心设计,其使用难度比较高。 工作模型优化 了解了 I/O 模型后,请求处理的优化就比较直观了。 使用 I/O 多路复用后,就可以在一个进程或线程中处理多个请求,其中,又有下面两种不同的工作模型。 第一种,主进程 + 多个 worker 子进程,这也是最常用的一种模型。这种方法的一个通用工作模式就是:主进程执行 bind() + listen() 后,创建多个子进程;然后,在每个子进程中,都通过 accept() 或 epoll_wait() ,来处理相同的套接字。 比如,最常用的反向代理服务器 Nginx 就是这么工作的。它也是由主进程和多个 worker 进程组成。主进程主要用来初始化套接字,并管理子进程的生命周期;而 worker 进程,则负责实际的请求处理。我画了一张图来表示这个关系。 这里要注意,accept() 和 epoll_wait() 调用,还存在一个惊群的问题。换句话说,当网络 I/O 事件发生时,多个进程被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠。 其中,accept() 的惊群问题,已经在 Linux 2.6 中解决了; 而 epoll 的问题,到了 Linux 4.5 ,才通过 EPOLLEXCLUSIVE 解决。 为了避免惊群问题, Nginx 在每个 worker 进程中,都增加一个了全局锁(accept_mutex)。这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒。 不过,根据前面 CPU 模块的学习,你应该还记得,进程的管理、调度、上下文切换的成本非常高。那为什么使用多进程模式的 Nginx ,却具有非常好的性能呢? 这里最主要的一个原因就是,这些 worker 进程,实际上并不需要经常创建和销毁,而是在没任务时休眠,有任务时唤醒。只有在 worker 由于某些异常退出时,主进程才需要创建新的进程来代替它。 当然,你也可以用线程代替进程:主线程负责套接字初始化和子线程状态的管理,而子线程则负责实际的请求处理。由于线程的调度和切换成本比较低,实际上你可以进一步把 epoll_wait() 都放到主线程中,保证每次事件都只唤醒主线程,而子线程只需要负责后续的请求处理。 第二种,监听到相同端口的多进程模型。在这种方式下,所有的进程都监听相同的接口,并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中去。这一过程如下图所示。 由于内核确保了只有一个进程被唤醒,就不会出现惊群问题了。比如,Nginx 在 1.9.1 中就已经支持了这种模式。 不过要注意,想要使用 SO_REUSEPORT 选项,需要用 Linux 3.9 以上的版本才可以。 C1000K 基于 I/O 多路复用和请求处理的优化,C10K 问题很容易就可以解决。不过,随着摩尔定律带来的服务器性能提升,以及互联网的普及,你并不难想到,新兴服务会对性能提出更高的要求。 很快,原来的 C10K 已经不能满足需求,所以又有了 C100K 和 C1000K,也就是并发从原来的 1 万增加到 10 万、乃至 100 万。从 1 万到 10 万,其实还是基于 C10K 的这些理论,epoll 配合线程池,再加上 CPU、内存和网络接口的性能和容量提升。大部分情况下,C100K 很自然就可以达到。 那么,再进一步,C1000K 是不是也可以很容易就实现呢?这其实没有那么简单了。 首先从物理资源使用上来说,100 万个请求需要大量的系统资源。比如, 假设每个请求需要 16KB 内存的话,那么总共就需要大约 15 GB 内存。 而从带宽上来说,假设只有 20% 活跃连接,即使每个连接只需要 1KB/s 的吞吐量,总共也需要 1.6 Gb/s 的吞吐量。千兆网卡显然满足不了这么大的吞吐量,所以还需要配置万兆网卡,或者基于多网卡 Bonding 承载更大的吞吐量。 其次,从软件资源上来说,大量的连接也会占用大量的软件资源,比如文件描述符的数量、连接状态的跟踪(CONNTRACK)、网络协议栈的缓存大小(比如套接字读写缓存、TCP 读写缓存)等等。 最后,大量请求带来的中断处理,也会带来非常高的处理成本。这样,就需要多队列网卡、中断负载均衡、CPU 绑定、RPS/RFS(软中断负载均衡到多个 CPU 核上),以及将网络包的处理卸载(Offload)到网络设备(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各种硬件和软件的优化。 C1000K 的解决方法,本质上还是构建在 epoll 的非阻塞 I/O 模型上。只不过,除了 I/O 模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。 C10M 显然,人们对于性能的要求是无止境的。再进一步,有没有可能在单机中,同时处理 1000 万的请求呢?这也就是 C10M 问题。 实际上,在 C1000K 问题中,各种软件、硬件的优化很可能都已经做到头了。特别是当升级完硬件(比如足够多的内存、带宽足够大的网卡、更多的网络功能卸载等)后,你可能会发现,无论你怎么优化应用程序和内核中的各种网络参数,想实现 1000 万请求的并发,都是极其困难的。 究其根本,还是 Linux 内核协议栈做了太多太繁重的工作。从网卡中断带来的硬中断处理程序开始,到软中断中的各层网络协议处理,最后再到应用程序,这个路径实在是太长了,就会导致网络包的处理优化,到了一定程度后,就无法更进一步了。 要解决这个问题,最重要就是跳过内核协议栈的冗长路径,把网络包直接送到要处理的应用程序那里去。这里有两种常见的机制,DPDK 和 XDP。 第一种机制,DPDK,是用户态网络的标准。它跳过内核协议栈,直接由用户态进程通过轮询的方式,来处理网络接收。 说起轮询,你肯定会下意识认为它是低效的象征,但是进一步反问下自己,它的低效主要体现在哪里呢?是查询时间明显多于实际工作时间的情况下吧!那么,换个角度来想,如果每时每刻都有新的网络包需要处理,轮询的优势就很明显了。比如: 在 PPS 非常高的场景中,查询时间比实际工作时间少了很多,绝大部分时间都在处理网络包; 而跳过内核协议栈后,就省去了繁杂的硬中断、软中断再到 Linux 网络协议栈逐层处理的过程,应用程序可以针对应用的实际场景,有针对性地优化网络包的处理逻辑,而不需要关注所有的细节。 此外,DPDK 还通过大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率。 第二种机制,XDP(eXpress Data Path),则是 Linux 内核提供的一种高性能网络数据路径。它允许网络包,在进入内核协议栈之前,就进行处理,也可以带来更高的性能。XDP 底层跟我们之前用到的 bcc-tools 一样,都是基于 Linux 内核的 eBPF 机制实现的。 XDP 的原理如下图所示: 你可以看到,XDP 对内核的要求比较高,需要的是 Linux 4.8 以上版本,并且它也不提供缓存队列。基于 XDP 的应用程序通常是专用的网络应用,常见的有 IDS(入侵检测系统)、DDoS 防御、 cilium 容器网络插件等。 总结 C10K 问题的根源,一方面在于系统有限的资源;另一方面,也是更重要的因素,是同步阻塞的 I/O 模型以及轮询的套接字接口,限制了网络事件的处理效率。Linux 2.6 中引入的 epoll ,完美解决了 C10K 的问题,现在的高性能网络方案都基于 epoll。 从 C10K 到 C100K ,可能只需要增加系统的物理资源就可以满足;但从 C100K 到 C1000K ,就不仅仅是增加物理资源就能解决的问题了。这时,就需要多方面的优化工作了,从硬件的中断处理和网络功能卸载、到网络协议栈的文件描述符数量、连接状态跟踪、缓存队列等内核的优化,再到应用程序的工作模型优化,都是考虑的重点。 再进一步,要实现 C10M ,就不只是增加物理资源,或者优化内核和应用程序可以解决的问题了。这时候,就需要用 XDP 的方式,在内核协议栈之前处理网络包;或者用 DPDK 直接跳过网络协议栈,在用户空间通过轮询的方式直接处理网络包。 当然了,实际上,在大多数场景中,我们并不需要单机并发 1000 万的请求。通过调整系统架构,把这些请求分发到多台服务器中来处理,通常是更简单和更容易扩展的方案。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_23864697/article/details/114626793。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-11 18:25:52
260
转载
转载文章
...不可覆盖 扩展方法的原理 Kotlin 中类的扩展方法并不是在原类的内部进行拓展,通过反编译为Java代码,可以发现,其原理是使用装饰模式,对源类实例的操作和包装,其实际相当于我们在 Java中定义的工具类方法,并且该工具类方法是使用调用者为第一个参数的,然后在工具方法中操作该调用者 如: fun String?.toInt(): 反编译为对应的Java代码: public 扩展属性 类的扩展属性原理其实与扩展方法是一样的,只是定义的形式不同,扩展属性必须定义get和set方法 为MutableList扩展一个firstElement属性: var 反编译后的java代码如下: public static final Object getFirstElement(@NotNull List $this$firstElement) { 内部类 kotlin的内部类与java的内部类有点不同java的内部类可以直接访问外部类的成员,kotlin的内部类不能直接访问外部类的成员,必须用inner标记之后才能访问外部类的成员 没有使用inner标记的内部类 class A{ 反编译后的java代码 public 用inner标记的内部类 class A{ 反编译后的java代码 public 从上面可以看出,没有使用inner标记的内部类最后生成的是静态内部类,而使用inner标记的生成的是非静态内部类 匿名内部类 匿名内部类主要是针对那些获取抽象类或者接口对象而来的。最常见的匿名内部类View点击事件: //java,匿名内部类的写法 上面这个是java匿名内部类的写法,kotlin没有new关键字,那么kotlin的匿名内部类该怎么写呢? object : View.OnClickListener{ 方法的参数是一个匿名内部类,先写object:,然后写你的参数类型View.OnClickListener{} kotlin还有一个写法lambda 表达式,非常之方便: print( 数据类 在Java中没有专门的数据类,常常是通过JavaBean来作为数据类,但在Kotlin中提供了专门的数据类。 Java public 从上面的例子中可以看到,如果要使用数据类,需要手动写相应的setter/getter方法(尽管IDE也可以批量生成),但是从代码阅读的角度来说,在属性较多的情况下,诸多的seeter/getter方法还是不利于代码的阅读和维护。 Kotlin 在Kotlin中,可以通过关键字data来生成数据类: data 即在class关键字之前添加data关键字即可。编译器会根据主构造函数中的参数生成相应的数据类。自动生成setter/getter、toString、hashCode等方法 要声明一个数据类,需要满足: 主构造函数中至少有一个参数 主构造函数中所有参数需要标记为val或var 数据类不能是抽象、开发、密封和内部的 枚举类 枚举类是一种特殊的类,kotlin可以通过enum class关键字定义枚举类。 枚举类可以实现0~N个接口; 枚举类默认继承于kotlin.Enum类(其他类最终父类都是Any),因此kotlin枚举类不能继承类; 非抽象枚举类不能用open修饰符修饰,因此非抽象枚举类不能派生子类; 抽象枚举类不能使用abstract关键字修饰enum class,抽象方法和抽象属性需要使用; 枚举类构造器只能使用private修饰符修饰,若不指定,则默认为private; 枚举类所有实例在第一行显式列出,每个实例之间用逗号隔开,整个声明以分号结尾; 枚举类是特殊的类,也可以定义属性、方法、构造器; 枚举类应该设置成不可变类,即属性值不允许改变,这样更安全; 枚举属性设置成只读属性后,最好在构造器中为枚举类指定初始值,如果在声明时为枚举指定初始值,会导致所有枚举值(或者说枚举对象)的该属性都一样。 定义枚举类 / 定义一个枚举类 / 枚举类实现接口 枚举值分别实现接口的抽象成员 enum 枚举类统一实现接口的抽象成员 enum 分别实现抽象枚举类抽象成员 enum 委托 委托模式 是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承。 Java中委托: interface Printer { Kotlin: interface Printer { by表示 p 将会在 PrintImpl 中内部存储, 并且编译器将自动生成转发给 p 的所有 Printer 的方法。 委托属性 有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大家把他们只实现一次并放入一个库会更好。例如包括: 延迟属性(lazy properties): 其值只在首次访问时计算; 可观察属性(observable properties): 监听器会收到有关此属性变更的通知; 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。 为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性 。 委托属性的语法是: var : 在 by 后面的表达式是该 委托, 因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。 标准委托: Kotlin 标准库为几种有用的委托提供了工厂方法。 延迟属性 Lazy lazy() 接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托:第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。例如: val lazyValue: String 可观察属性 Observable Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值: class User { 如果想拦截赋的新值,并根据你是不是想要这个值来决定是否给属性赋新值,可以使用 vetoable() 取代 observable(),接收的参数和 observable 一样,不过处理程序 返回值是 Boolean 来决定是否采用新值,即在属性被赋新值生效之前 会调用传递给 vetoable 的处理程序。例如: class User { 把属性存在map 中 一个常见的用例是在一个映射(map)里存储属性的值。这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。 例如: class User(map: Map 在上例中,委托属性会从构造函数传入的map中取值(通过字符串键——属性的名称),如果遇到声明的属性名在map 中找不到对应的key 名,或者key 对应的value 值的类型与声明的属性的类型不一致,会抛出异常。 内联函数 当一个函数被声明为inline时,它的函数体是内联的,也就是说,函数体会被直接替换到函数被调用地方 inline函数(内联函数)从概念上讲是编译器使用函数实现的真实代码来替换每一次的函数调用,带来的最直接的好处就是节省了函数调用的开销,而缺点就是增加了所生成字节码的尺寸。基于此,在代码量不是很大的情况下,我们是否有必要将所有的函数定义为内联?让我们分两种情况进行说明: 将普通函数定义为内联:众所周知,JVM内部已经实现了内联优化,它会在任何可以通过内联来提升性能的地方将函数调用内联化,并且相对于手动将普通函数定义为内联,通过JVM内联优化所生成的字节码,每个函数的实现只会出现一次,这样在保证减少运行时开销的同时,也没有增加字节码的尺寸;所以我们可以得出结论,对于普通函数,我们没有必要将其声明为内联函数,而是交给JVM自行优化。 将带有lambda参数的函数定义为内联:是的,这种情况下确实可以提高性能;但在使用的过程中,我们会发现它是有诸多限制的,让我们从下面的例子开始展开说明: inline 假如我们这样调用doSomething: fun main(args: Array<String>) { 上面的调用会被编译成: fun main(args: Array<String>) { 从上面编译的结果可以看出,无论doSomething函数还是action参数都被内联了,很棒,那让我们换一种调用方式: fun main(args: Array<String>) { 上面的调用会被编译成: fun main(args: Array<String>) { doSomething函数被内联,而action参数没有被内联,这是因为以函数型变量的形式传递给doSomething的lambda在函数的调用点是不可用的,只有等到doSomething被内联后,该lambda才可以正常使用。 通过上面的例子,我们对lambda表达式何时被内联做一下简单的总结: 当lambda表达式以参数的形式直接传递给内联函数,那么lambda表达式的代码会被直接替换到最终生成的代码中。 当lambda表达式在某个地方被保存起来,然后以变量形式传递给内联函数,那么此时的lambda表达式的代码将不会被内联。 上面对lambda的内联时机进行了讨论,消化片刻后让我们再看最后一个例子: inline 上面的例子是否有问题?是的,编译器会抛出“Illegal usage of inline-parameter”的错误,这是因为Kotlin规定内联函数中的lambda参数只能被直接调用或者传递给另外一个内联函数,除此之外不能作为他用;那我们如果确实想要将某一个lambda传递给一个非内联函数怎么办?我们只需将上述代码这样改造即可: inline 很简单,在不需要内联的lambda参数前加上noinline修饰符就可以了。 以上便是我对内联函数的全部理解,通过掌握该特性的运行机制,相信大家可以做到在正确的时机使用该特性,而非滥用或因恐惧弃而不用。 Kotlin下单例模式 饿汉式实现 //Java实现 懒汉式 //Java实现 上述代码中,我们可以发现在Kotlin实现中,我们让其主构造函数私有化并自定义了其属性访问器,其余内容大同小异。 如果有小伙伴不清楚Kotlin构造函数的使用方式。请点击 - - - 构造函数 不清楚Kotlin的属性与访问器,请点击 - - -属性和字段 线程安全的懒汉式 //Java实现 大家都知道在使用懒汉式会出现线程安全的问题,需要使用使用同步锁,在Kotlin中,如果你需要将方法声明为同步,需要添加@Synchronized注解。 双重校验锁式 //Java实现 哇!小伙伴们惊喜不,感不感动啊。我们居然几行代码就实现了多行的Java代码。其中我们运用到了Kotlin的延迟属性 Lazy。 Lazy内部实现 public 观察上述代码,因为我们传入的mode = LazyThreadSafetyMode.SYNCHRONIZED, 那么会直接走 SynchronizedLazyImpl,我们继续观察SynchronizedLazyImpl。 Lazy接口 SynchronizedLazyImpl实现了Lazy接口,Lazy具体接口如下: public 继续查看SynchronizedLazyImpl,具体实现如下: SynchronizedLazyImpl内部实现 private 通过上述代码,我们发现 SynchronizedLazyImpl 覆盖了Lazy接口的value属性,并且重新了其属性访问器。其具体逻辑与Java的双重检验是类似的。 到里这里其实大家还是肯定有疑问,我这里只是实例化了SynchronizedLazyImpl对象,并没有进行值的获取,它是怎么拿到高阶函数的返回值呢?。这里又涉及到了委托属性。 委托属性语法是:val/var : by 。在 by 后面的表达式是该 委托, 因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()——对于 var 属性)。 而Lazy.kt文件中,声明了Lazy接口的getValue扩展函数。故在最终赋值的时候会调用该方法。 internal.InlineOnly 静态内部类式 //Java实现 静态内部类的实现方式,也没有什么好说的。Kotlin与Java实现基本雷同。 补充 在该篇文章结束后,有很多小伙伴咨询,如何在Kotlin版的Double Check,给单例添加一个属性,这里我给大家提供了一个实现的方式。(不好意思,最近才抽出时间来解决这个问题) class SingletonDemo private constructor( 其中关于?:操作符,如果 ?: 左侧表达式非空,就返回其左侧表达式,否则返回右侧表达式。请注意,当且仅当左侧为空时,才会对右侧表达式求值。 Kotlin 智能类型转换 对于子父类之间的类型转换 先看这样一段 Java 代码 public 尽管在 main 函数中,对 person 这个对象进行了类型判断,但是在使用的时候还是需要强制转换成 Student 类型,这样是不是很不智能? 同样的情况在 Kotlin 中就变得简单多了 fun main(args: Array<String>) { 在 Kotlin 中,只要对类型进行了判断,就可以直接通过父类的对象去调用子类的函数了 安全的类型转换 还是上面的那个例子,如果我们没有进行类型判断,并且直接进行强转,会怎么样呢? public static void main(String[] args) { 结果就只能是 Exception in thread "main" java.lang.ClassCastException 那么在 Kotlin 中是不是会有更好的解决方法呢? val person: Person = Person() 在转换操作符后面添加一个 ?,就不会把程序 crash 掉了,当转化失败的时候,就会返回一个 null 在空类型中的智能转换 需要提前了解 Kotlin 类型安全的相关知识(Kotlin 中的类型安全(对空指针的优化处理)) String? = aString 在定义的时候定义成了有可能为 null,按照之前的写法,我们需要这样写 String? = 但是已经进行了是否为 String 类型的判断,所以就一定 不是 空类型了,也就可以直接输出它的长度了 T.()->Unit 、 ()->Unit 在做kotlin开发中,经常看到一些系统函数里,用函数作为参数 public .()-Unit与()->Unit的区别是我们调用时,在代码块里面写this,的时候,两个this代表的含义不一样,T.()->Unit里的this代表的是自身实例,而()->Unit里,this代表的是外部类的实例。 推荐阅读 对 Kotlin 与 Java 编程语言的思考 使用 Kotlin 做开发一个月后的感想 扫一扫 关注我的公众号如果你想要跟大家分享你的文章,欢迎投稿~ 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_39611037/article/details/109984124。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-23 23:56:14
470
转载
转载文章
...后自己没事也做了两款应用,然后J哥 把应用展示给他看,他看了连连称赞不错啊。。。(lalala,其实都是J哥网上巴拉的项目啦。) (然后大体给他介绍了 项目基本框架,是 v4包里的 SlidingPaneLayout 嵌套了实现了轮询效果 自定义的viewpager 。然后 具体界面是用的瀑布流,项目的关键就是 对 图片的处理,因为有N张 图片,但是并没有卡顿,所以就说了 自己用 了开源的imagedownloader 和 volley 以及自己定义的 lrucache 缓存 bitmap 对象,这里大家一定要把图片的三级缓存 自己了解清楚,基本面试会问到。) 其实 当面试问你如何避免oom,内存泄露导致的原因,以及如何处理大图片等等,其实都是 如何优化内存。 可以按照我自己总结的回答,你可以说,这个问题 ,跟 oom以及 内存泄露,其实是一样的,关键 就是 如何 优化内存,避免不必要的 内存泄露, 而 内存泄露 的原因 ,我总结了 4点, 1. 匿名内部类,和非静态内部类, 举个栗子:我们用handler 进行线程间 假如 我们在activity中这样定义 handler : [java] view plain copy print ? Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { mImageView.setImageBitmap(mBitmap); } } 然后,我们用 右键 选中工程 运行 lint工具 , android tools---run lint ,就会提示我们这样一个warning: In Android, Handler classes should be static or leaks might occur.。 就是 ,推荐我们 把handler 定义成static,具体 看这里解释的很详细:http://www.linuxidc.com/Linux/2013-12/94065.htm 类似的还有 匿名子线程。 2.还是 拿网上的 栗子来说, [java] view plain copy print ? Vector v = new Vector( 10 ); for ( int i = 1 ;i < 100 ; i ++ ){ Object o = new Object(); v.add(o); o = null ; } 即便是 我们把 o 对象 置为 null,但是 vector 集合中还有有o的引用,所以 集合 没有被清空,这一部分内存 还是不能被释放,这就导致了内存泄露。 3, 当我们操作数据库的时候,我们在执行完 相应的crud 方法后,我们没有关闭 cursor .close()或者 db.close(),也同样会占用内存、因为只有关闭连接后,才会被GC 回收。 4.继续举个栗子 [java] view plain copy print ? Set<Person> set = new HashSet<Person>(); Person p1 = new Person("唐僧","pwd1",25); Person p2 = new Person("孙悟空","pwd2",26); Person p3 = new Person("猪八戒","pwd3",27); set.add(p1); set.add(p2); set.add(p3); System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素! p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变 set.remove(p3); //此时remove不掉,造成内存泄漏 set.add(p3); //重新添加,居然添加成功 System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素! J哥 亲自 实践了下,发现问题了,这个网上的栗子 是错的。实际上是可以remove掉得、真是个悲伤地故事。这个栗子是不正确的。。网上好有一片这样的文章,都是这个栗子。。 这里 看下其他网站上的总结吧 :强烈推荐http://developer.51cto.com/art/201111/302465.htm。很详细。 OK。还有最后一点,就是关于图片的,bitmap对象的及时释放,这里 就不细说了,等在图片三级缓存一起去总结。 此时 感觉 对面的android 小哥 已经被我吸引了。好像很认真的在听我讲课一样。 然后, 他问我问题。我大体总结了一下。 面试官01问:有没有自定义过view。 J哥回答:这个很常见,我自己定义过很多,比如 下拉刷新,上拉加载更多数据的listview,类似github 上面的pulltorefreshlistview。 还有图片轮询播放的viewpager,也是 继承viewpager,然后自己开启一个线程,去控制 切换的。还比如,跑马灯效果的textview ,scrollview与 listview 相互嵌套 导致 listview 高度计算不正确,我也是 自定义listview,复写了 onmeaure方法,然后解决冲突的。在比如 一些开源的 可以放大缩小的图片,我也是做过,主要是对onmeasure 方法,onlayout方法,ondraw 方法的复写。以及复写一下 view 自己的 touch事件等等,奥 对了,我们公司当时有需求 做一个 锁屏软件,侧滑解锁的,我也是自己定义的,然后展示给他看了一下,当时 那篇文章在这里。传送门http://blog.csdn.net/u011733020/article/details/41863861。 面试官01问:listview的优化、 J哥回答:(PS:这种问题,基本上 都快被问烂了,但是没办法 还是要回答。)listview作为最常见的 用来显示数据的view ,一般 从四个方面 去优化。 1 ,复用convertview, 不然假如有1000条数据,那么我们滑动,就会 产生1000个convertview ,这对内存是很大的浪费,所以 我们一定要复用。 2. 减少 findviewbyid 的次数, 因为 每次 去 执行 findviewbyid 也是要消耗资源的,我们要尽可能的减少,通常 我们定义一个viewholder,去管理 这些id ,然后通过tag 去直接拿到 id。 3, 分页加载,延迟加载 预加载。 这个在我们以前项目,有一个榜单,数据量很大,一次请求过来的数据量很大,这样有两个问题,一个是请求网络 时间可能会很长,另一个展示数据 上面 体验对不是很好,所以 我们做了 第一次加载 20条,然后每次请求 再去 加载10条新数据。 4.就是 对 listview 中一些 类似头像, 图片的 优化。这里 类似 三级缓存,推荐大家看一下 开源 的universal-image-loader 的源码。或者 这篇文章http://www.jb51.net/article/38162.htm,J哥有时间 专门写一篇过于 图片缓存的。 面试官01问: 看你简历上面 做过 社交,通信这块是怎么做的。 J哥回答:我看 咱们公司 也用到了 聊天,咱们公司是 自己做的 还是 用的第三方的类似 环信的。结果被J哥猜中,他说 是集成的环信(但是 有丢包现象,所以打算自己做通信)。 OK,J哥说 ,我们 项目中聊天 是基于xmpp协议的做的,在没有android以前 ,java有个开源的 smack ,android 上 现在有一个asmack ,其实 就是移植到android 中来了, 服务端是基于 openfire的 ,我们就是做的 openfire+asmack 的 聊天,这个原理主要 就是 绑定 ip 拿到 connection 然后 connect ,然后进行通信,我说,这个 跟http请求 其实原理上一样,都是 绑定ip,然后 设置一些property,然后通过类似流进行通信的, asmack,其实底层 就是xml通信的。 面试官01问: touch 事件的传递机制,还特意画了,一个 就是 button LinearLayout 嵌套 。 J哥回答:就是这个, 这也难不倒我。因为J哥觉得 这个问题肯定会问到 所以 早有准备,这里 我就大体说下结论,详细原理 给你传送门。 我回答,这个很简单,只要你继承一下 button 和 linearlayout 复写一下 三个方法 dispatchtouchEvent onInterceptTouchEvent 和onTouchEvent .就能很清楚的明白 传递的过程,我给你总的说下结论的,点击这个button,一般是 外面的父控件 先响应这个down 事件,然后 往子类里面传递,让子类 在往子类的下一级子类去传递,让最终的孩子去决定是不要要消费掉这个点击事件,如果消费掉,那么父类将不会响应,如果子类不消费,那么会退回到次级子类,然后看是否要消费,这样,一句话 就是父传子, 子决定要不要,不要 然后传回去。 这里有很详细 很详细的介绍, 包裹事件的分发。所以我就不罗嗦,http://blog.csdn.net/yanbober/article/details/45887547?ref=myread 面试官01问: 项目中图片的优化。 J哥回答:我给他展示的项目 其中有一款app 是有很多图片 ,但是 很流畅,也没有oom。关于图片 优化,一般我们采用三级缓存,1 。内存加载 2.本地加载 3 网络加载。 首先 我们看 内存中有没有,有直接拿来用,这里 我项目里是这样做的,我先获取一下 分配给我们应用的可用内存是多少,然后 拿1/4 或者 1/8做一个 lrucache. 把我们的bitmap对象添加进去。有些比较常用的图片,我会保存到本地,避免每次重复联网下载。结合 开源的 afinal universalimageloader 以及 13年谷歌官方推荐的volley(号称是 asynchttpclient 和universalimageloader)的结合、 所以 在我的项目中基本没有遇到过图片导致的oom 问题,对于单张的 大图片,我也会利用bitmapFactory,进行计算大小,然后 计算手机分辨率,进行定量的 压缩 处理。 面试官问: GC的回收 J哥回答:我说。GC 回收 应该不只是按照一种方式,应该有多种不同的算法,我看过谷歌 官网介绍的一点,有这样一块区域,他分为 latest(最近) middle(中等)permanent(永久的),这样三块子区域。里面分别存放,刚刚被创建的,以及 时间 靠后的,很久的,对象,不断地新对象 往latest里面添加,当达到相应对象区域的阀值的时候,就会触发GC,GC 进行回收的时候,对于latest 中回收的速度是最快的,而permanent 相对是最久的,而时间 也跟 每块区域中对象的个数有关系, 还有一种算法,是根据最近被引用的时间,或者 被引用的次数 去进行 GC的、、这里随便扯就是了。GC 回收并不是立即执行的。是不定时的。GC回收的时候 会阻塞线程,所以代码中要避免创建不必要的对象,例如for循环中 创建大量对象 就会容易引起GC。 当我们也可以主动 在方法中执行system.gc() 去手动释放一些资源。 面试官01问: 怎么避免 viewpager 预加载 fragment的、 J哥回答:这个问题 我也碰到过,我们都知道,viewpager 它本身会预加载 左右两个 和当前一个对象、而 我们viewpager setOffscreenPageLimit(0) 不生效因为看源码知道,这个方法默认最少也要加载一个。所以 这个fragment 还没有被当前页面显示出来,已经夹在好了,有可能数据不是最新的,我是在 setuservisibilityhint() 这个方法中跟参数 动态去判断 要不要刷新的。 问了一圈,这个哥们大概没什么问的了,然后 就让我等一下,说让他们技术总监过来 。 我就等。。。 然后等了几分钟,进来一小姑娘,坐下,看了我简历,我以为是人事,来跟我谈人生理想。结果,没说几句话,让我讲一下我的项目。我qu,惊呆我了。我问,你也是做android的,我去,是这样的、、把J哥吓到, 然后问了J哥几个问题。 Android 小姑娘问: 看你项目中的listview 中item类型 是统一的,而加入 item 差别挺大的 你怎么复用。 J哥回答:J哥装作很牛的样子说,我暂时想到两种方法,1.给这个对象 加一个type 然后 根据 type 去复用,或者 把这几种类型 一起加载,然后控制显示隐藏。然后 我反问小姑娘,假如 我这里 有一百条数据,这一百条是无序的,包含了 10种 item类型,你有没有什么好方法 去处理这个问题, 小姑娘说,你不是定义了类型吗,我们就是 通过type 去判断的。 Android 小姑娘问: onAttch onDetach还是onAttachedToWindow,onDetachedFromWindow J哥回答:其实 那个小姑娘忘记这两个方法了。我说什么方法,她说onAttachIntent() 和 onDetachIntent(). 反正 J哥是没听说过, 我只见过 onAttach ,但是 这个方法 我也没用过。我就问她,这两个方法是做什么的,小姑娘跟我说 是 把子view绑定到界面上的,那么的话 应该是onAttachedToWindow,onDetachedFromWindow方法了,小姑娘说: 在这个方法 可以计算子 view的高度宽度,在 oncreate 里面不能计算,其实虽然刚开始 在oncreate里面是不能计算,但是还是有方法计算的,(本人觉得面试 问你 API 是 最2的了,忍不住吐槽下,我遇到过,Camera 拍照,问我获取 一个图片,还是 视频的 方法,我去百度 一下,随便就知道,真是不懂 为什么会问方法。随便一个程序员 都会百度。。) 跟小姑娘聊得其他问题 不太记得了,感觉这个女程序员啊。。就问方法 给我的印象不太好,不管方法用没用到,我觉得面试 直接问你方法 好2 好2... 然后技术总监 有进来跟我聊了,后技术总监 有进来跟我聊了、技术总监 年龄30出头吧,到是没有问我什么技术问题, 总监: 问我 做没做过通信这块,能不能做这一块。 J哥回答:,我说做过,通信有几种协议的,我们用的 是xmpp协议的 ,服务器 是 基于apache的 openfire 搭建的,客户端 是用的asmack。还有一些 其他协议的 ,比如我知道有些项目中用的 soap协议的,还有ip 协议的。PS:反正就是扯 我说 通信 客户端这一块 我没问题,但是 服务端 我 从工作以来 一直偏向 android 移动端开发,后台这一块,如果数据量大了,还要考虑并发之类的,我是做不了,让我做个tomcat搭建的demo 我可能可以。 其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。 总监: 问我 什么时候能上班 J哥回答:我说 这个看公司需求啦。 其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。 这里 感觉应该没问题了。差不多能拿下了。 人事1:一进来,就问东问西。问加班看法啊,他们公司技术 一般都八九点走啊。说七点基本没有走的啊、、、 J哥回答:我说,一般遇到项目加功能 ,版本升级,等等 这些加班都没什么,只要不是一直在加班。。。。这里每个人自己看法就好了、、 反正人事 是一直跟我强调这个,她不停强调 我就暗暗下决心,薪资 我是不会要低了。 人事1:看你还年轻啊,还能拼一拼啊、、、、 J哥回答:我说现在 这几年对我人生规划也算比较重要的时期,也是过一年少一年了,其实她的意思 还是侧面强调加班。。。。日了UZI了。 中间一堆废话,然后我问了她 公司一般上下班时间啊。。之类的有没有技术交流啊,之类的。。。 最后到关键问题上啦,最关心的,薪资问题。 人事1:期望薪资 J哥回答:我说16K左右吧。她问 你以前公司多少 握手 15K。她说她们公司 是 14薪。反正 我还是说16K。她说 那好,你等下,然后就出去了。 不知道 跟什么人 讨论了许久,然后又来一个 可能是人事吧。又进来,问了一遍,也问了薪资。。哥还是说16K 。 。。估计是她们公司想要我,但是又觉得有点超出她们薪资期望吧,当场被没有给什么offer。然后就有点婉拒的说,两天给我答复,心里很气愤,饿着肚子 面试到三点,竟然婉拒、、、 反正我是很生气,我说,好,然后我就走。结果,没过一个小时,人事又打电话来,非要约我 见一下她们CEO。这是什么鬼,难道她们CEO要给我煲汤 了?我说可以,然后时间定在后天了,,反正心灵鸡汤对我是没用了、 OK ,这家面试 先写到这里,下面下午还有一家,等下在写。准备睡觉。今天面试回来,累的就睡着了,晚上十点多才醒过来,想了想还是 把今天面试的过程总结一下。 ------------------------------待续------------------------- 第二弹http://blog.csdn.net/u011733020/article/details/46058273 本篇文章为转载内容。原文链接:https://blog.csdn.net/haluoluo211/article/details/51010955。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-19 17:42:52
336
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
ln -s /path/original_file /path/symlink
- 创建指向原始文件的符号链接。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"