java handle类类ontimeout什么时候调用

javascript(6)
Ajax和XMLHttpRequest
我们通常将Ajax等同于XMLHttpRequest,但细究起来它们两个是属于不同维度的2个概念。
以下是我认为对Ajax较为准确的解释:(摘自what is Ajax)
AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique for creating better, faster, and more interactive web applications with the help of XML, HTML, CSS, and Java Script.
AJAX is based on the following open standards:
Browser-based presentation using HTML and Cascading Style Sheets (CSS).
Data is stored in XML format and fetched from the server.
Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.
JavaScript to make everything happen.
从上面的解释中可以知道:ajax是一种技术方案,但并不是一种新技术。它依赖的是现有的CSS/HTML/Javascript,而其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。
所以我用一句话来总结两者的关系:我们使用XMLHttpRequest对象来发送一个Ajax请求。
XMLHttpRequest的发展历程
XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和Level 2。
XMLHttpRequest Level 1主要存在以下缺点:
受同源策略的限制,不能发送跨域请求;
不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;
在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;
那么Level 2对Level 1 进行了改进,XMLHttpRequest Level 2中新增了以下功能:
可以发送跨域请求,在服务端允许的情况下;
支持发送和接收二进制数据;
新增formData对象,支持发送表单数据;
发送和获取数据时,可以获取进度信息;
可以设置请求的超时时间;
当然更详细的对比介绍,可以参考阮老师的这篇文章,文章中对新增的功能都有具体代码示例。
XMLHttpRequest兼容性
关于xhr的浏览器兼容性,大家可以直接查看“Can I use”这个网站提供的结果XMLHttpRequest兼容性,下面提供一个截图。
从图中可以看到:
IE8/IE9、Opera Mini 完全不支持xhr对象
IE10/IE11部分支持,不支持 xhr.responseType为json
部分浏览器不支持设置请求超时,即无法使用xhr.timeout
部分浏览器不支持xhr.responseType为blob
细说XMLHttpRequest如何使用
先来看一段使用XMLHttpRequest发送Ajax请求的简单示例代码。
function sendAjax() {
//构造表单数据
var formData = new FormData();
formData.append(‘username’, ‘johndoe’);
formData.append(‘id’, 123456);
//创建xhr对象
var xhr = new XMLHttpRequest();
//设置xhr请求的超时时间
xhr.timeout = 3000;
//设置响应返回的数据格式
xhr.responseType = “text”;
//创建一个 post 请求,采用异步
xhr.open(‘POST’, ‘/server’, true);
//注册相关事件回调处理函数
xhr.onload = function(e) {
if(this.status == 200||this.status == 304){
alert(this.responseText);
xhr.ontimeout = function(e) { … };
xhr.onerror = function(e) { … };
xhr.upload.onprogress = function(e) { … };
//发送数据
xhr.send(formData);
上面是一个使用xhr发送表单数据的示例,整个流程可以参考注释。
接下来我将站在使用者的角度,以问题的形式介绍xhr的基本使用。
我对每一个问题涉及到的知识点都会进行比较细致地介绍,有些知识点可能是你平时忽略关注的。
如何设置request header
在发送Ajax请求(实质是一个HTTP请求)时,我们可能需要设置一些请求头部信息,比如content-type、connection、cookie、accept-xxx等。xhr提供了setRequestHeader来允许我们修改请求 header。
void setRequestHeader(DOMString header, DOMString value);
方法的第一个参数 header 大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-T
Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;
setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;
setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。下面是一个示例代码:
var client = new XMLHttpRequest();
client.open(‘GET’, ‘demo.cgi’);
client.setRequestHeader(‘X-Test’, ‘one’);
client.setRequestHeader(‘X-Test’, ‘two’);
// 最终request header中”X-Test”为: one, two
client.send();
如何获取response header
xhr提供了2个用来获取响应头部的方法:getAllResponseHeaders和getResponseHeader。前者是获取 response 中的所有header 字段,后者只是获取某个指定 header 字段的值。另外,getResponseHeader(header)的header参数不区分大小写。
DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);
这2个方法看起来简单,但却处处是坑儿。
你是否遇到过下面的坑儿?——反正我是遇到了。。。
使用getAllResponseHeaders()看到的所有response header与实际在控制台 Network 中看到的 response header 不一样
使用getResponseHeader()获取某个 header 的值时,浏览器抛错Refused to get unsafe header “XXX”
经过一番寻找最终在 Stack Overflow找到了答案。
原因1:W3C的 xhr 标准中做了限制,规定客户端无法获取 response 中的 Set-Cookie、Set-Cookie2这2个字段,无论是同域还是跨域请求;
原因2:W3C 的 cors 标准对于跨域请求也做了限制,规定对于跨域请求,客户端允许获取的response header字段只限于“simple response header”和“Access-Control-Expose-Headers” (两个名词的解释见下方)。
“simple response header”包括的 header 字段有:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,P
“Access-Control-Expose-Headers”:首先得注意是”Access-Control-Expose-Headers”进行跨域请求时响应头部中的一个字段,对于同域请求,响应头部是没有这个字段的。这个字段中列举的 header 字段就是服务器允许暴露给客户端访问的字段。
所以getAllResponseHeaders()只能拿到限制以外(即被视为safe)的header字段,而不是全部字段;而调用getResponseHeader(header)方法时,header参数必须是限制以外的header字段,否则调用就会报Refused to get unsafe header的错误。
如何指定xhr.response的数据类型
有些时候我们希望xhr.response返回的就是我们想要的数据类型。比如:响应返回的数据是纯JSON字符串,但我们期望最终通过xhr.response拿到的直接就是一个 js 对象,我们该怎么实现呢?
有2种方法可以实现,一个是level 1就提供的overrideMimeType()方法,另一个是level 2才提供的xhr.responseType属性。
xhr.overrideMimeType()
overrideMimeType是xhr level 1就有的方法,所以浏览器兼容性良好。这个方法的作用就是用来重写response的content-type,这样做有什么意义呢?比如:server 端给客户端返回了一份document或者是 xml文档,我们希望最终通过xhr.response拿到的就是一个DOM对象,那么就可以用xhr.overrideMimeType(‘text/ charset = utf-8’)来实现。
再举一个使用场景,我们都知道xhr level 1不支持直接传输blob二进制数据,那如果真要传输 blob 该怎么办呢?当时就是利用overrideMimeType方法来解决这个问题的。
下面是一个获取图片文件的代码示例:
var xhr = new XMLHttpRequest();
//向 server 端获取一张图片
xhr.open(‘GET’, ‘/path/to/image.png’, true);
// 这行是关键!
//将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集
xhr.overrideMimeType(‘text/ charset=x-user-defined’);
xhr.onreadystatechange = function(e) {
if (this.readyState == 4 && this.status == 200) {
//通过 responseText 来获取图片文件对应的二进制字符串
var binStr = this.responseT
//然后自己再想方法将逐个字节还原为二进制数据
for (var i = 0, len = binStr. i & ++i) {
var c = binStr.charCodeAt(i);
//String.fromCharCode(c & 0xff);
var byte = c & 0
xhr.send();
代码示例中xhr请求的是一张图片,通过将 response 的 content-type 改为’text/ charset=x-user-defined’,使得 xhr 以纯文本格式来解析接收到的blob 数据,最终用户通过this.responseText拿到的就是图片文件对应的二进制字符串,最后再将其转换为 blob 数据。
xhr.responseType
responseType是xhr level 2新增的属性,用来指定xhr.response的数据类型,目前还存在些兼容性问题,可以参考本文的【XMLHttpRequest的兼容性】这一小节。那么responseType可以设置为哪些格式呢,我简单做了一个表,如下:
xhr.response 数据类型
String字符串
默认值(在不设置responseType时)
String字符串
“document”
Document对象
希望返回 XML 格式数据时使用
javascript 对象
存在兼容性问题,IE10/IE11不支持
“arrayBuffer”
ArrayBuffer对象
下面是同样是获取一张图片的代码示例,相比xhr.overrideMimeType,用xhr.response来实现简单得多。
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, ‘/path/to/image.png’, true);
//可以将xhr.responseType设置为"blob"也可以设置为" arrayBuffer"
//xhr.responseType = ‘arrayBuffer’;
xhr.responseType = ‘blob’;
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.
xhr.send();
虽然在xhr level 2中,2者是共同存在的。但其实不难发现,xhr.responseType就是用来取代xhr.overrideMimeType()的,xhr.responseType功能强大的多,xhr.overrideMimeType()能做到的xhr.responseType都能做到。所以我们现在完全可以摒弃使用xhr.overrideMimeType()了。
如何获取response数据
xhr提供了3个属性来获取请求返回的数据,分别是:xhr.response、xhr.responseText、xhr.responseXML
xhr.response
默认值:空字符串””
当请求完成时,此属性才有正确的值
请求未完成时,此属性的值可能是”“或者 null,具体与 xhr.responseType有关:当responseType为”“或”text”时,值为”“;responseType为其他值时,值为 null
xhr.responseText
默认值为空字符串””
只有当 responseType 为”text”、”“时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错
只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串”“:请求未完成、请求失败
xhr.responseXML
默认值为 null
只有当 responseType 为”text”、”“、”document”时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错
只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时
如何追踪ajax请求的当前状态
在发一个ajax请求后,如果想追踪请求当前处于哪种状态,该怎么做呢?
用xhr.readyState这个属性即可追踪到。这个属性是只读属性,总共有5种可能值,分别对应xhr不同的不同阶段。每次xhr.readyState的值发生变化时,都会触发xhr.onreadystatechange事件,我们可以在这个事件中进行相关状态判断。
xhr.onreadystatechange = function () {
switch(xhr.readyState){
case 1://OPENED
//do something
case 2://HEADERS_RECEIVED
//do something
case 3://LOADING
//do something
case 4://DONE
//do something
UNSENT (初始状态,未打开)
此时xhr对象被成功构造,open()方法还未被调用
OPENED (已打开,未发送)
open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错
HEADERS_RECEIVED (已获取响应头)
send()方法已经被调用, 响应头和响应状态已经返回
LOADING (正在下载响应体)
响应体(response entity body)正在下载中,此状态下通过xhr.response可能已经有了响应数据
DONE (整个数据传输过程结束)
整个数据传输过程结束,不管本次请求是成功还是失败
如何设置请求的超时时间
如果请求过了很久还没有成功,为了不会白白占用的网络资源,我们一般会主动终止请求。XMLHttpRequest提供了timeout属性来允许设置请求的超时时间。
xhr.timeout
单位:milliseconds 毫秒
默认值:0,即不设置超时
很多同学都知道:从请求开始 算起,若超过 timeout 时间请求还没有结束(包括成功/失败),则会触发ontimeout事件,主动结束该请求。
【那么到底什么时候才算是请求开始 ?】
——xhr.onloadstart事件触发的时候,也就是你调用xhr.send()方法的时候。
因为xhr.open()只是创建了一个连接,但并没有真正开始数据的传输,而xhr.send()才是真正开始了数据的传输过程。只有调用了xhr.send(),才会触发xhr.onloadstart 。
【那么什么时候才算是请求结束 ?】
—— xhr.loadend事件触发的时候。
另外,还有2个需要注意的坑儿:
可以在 send()之后再设置此xhr.timeout,但计时起始点仍为调用xhr.send()方法的时刻。
当xhr为一个sync同步请求时,xhr.timeout必须置为0,否则会抛错。原因可以参考本文的【如何发一个同步请求】一节。
如何发一个同步请求
xhr默认发的是异步请求,但也支持发同步请求(当然实际开发中应该尽量避免使用)。到底是异步还是同步请求,由xhr.open()传入的async参数决定。
open(method, url [, async = true [, username = null [, password = null]]])
method: 请求的方式,如GET/POST/HEADER等,这个参数不区分大小写
url: 请求的地址,可以是相对地址如example.php,这个相对是相对于当前网页的url路径;也可以是绝对地址如
async: 默认值为true,即为异步请求,若async=false,则为同步请求
在我认真研读W3C 的 xhr 标准前,我总以为同步请求和异步请求只是阻塞和非阻塞的区别,其他什么事件触发、参数设置应该是一样的,事实证明我错了。
W3C 的 xhr标准中关于open()方法有这样一段说明:
Throws an “InvalidAccessError” exception if async is false, the JavaScript global environment is a document environment, and either the timeout attribute is not zero, the withCredentials attribute is true, or the responseType attribute is not the empty string.
从上面一段说明可以知道,当xhr为同步请求时,有如下限制:
xhr.timeout必须为0
xhr.withCredentials必须为 false
xhr.responseType必须为”“(注意置为”text”也不允许)
若上面任何一个限制不满足,都会抛错,而对于异步请求,则没有这些参数设置上的限制。
之前说过页面中应该尽量避免使用sync同步请求,为什么呢?
因为我们无法设置请求超时时间(xhr.timeout为0,即不限时)。在不限制超时的情况下,有可能同步请求一直处于pending状态,服务端迟迟不返回响应,这样整个页面就会一直阻塞,无法响应用户的其他交互。
另外,标准中并没有提及同步请求时事件触发的限制,但实际开发中我确实遇到过部分应该触发的事件并没有触发的现象。如在 chrome中,当xhr为同步请求时,在xhr.readyState由2变成3时,并不会触发 onreadystatechange事件,xhr.upload.onprogress和 xhr.onprogress事件也不会触发。
如何获取上传、下载的进度
在上传或者下载比较大的文件时,实时显示当前的上传、下载进度是很普遍的产品需求。
我们可以通过onprogress事件来实时显示进度,默认情况下这个事件每50ms触发一次。需要注意的是,上传过程和下载过程触发的是不同对象的onprogress事件:
上传触发的是xhr.upload对象的 onprogress事件
下载触发的是xhr对象的onprogress事件
xhr.onprogress = updateP
xhr.upload.onprogress = updateP
function updateProgress(event) {
if (event.lengthComputable) {
var completedPercent = event.loaded / event.
可以发送什么类型的数据
void send(data);
xhr.send(data)的参数data可以是以下几种类型:
ArrayBuffer
如果是 GET/HEAD请求,send()方法一般不传参或传 null。不过即使你真传入了参数,参数也最终被忽略,xhr.send(data)中的data会被置为 null.
xhr.send(data)中data参数的数据类型会影响请求头部content-type的默认值:
如果data是 Document 类型,同时也是HTML Document类型,则content-type默认值为text/charset=UTF-8;否则为application/charset=UTF-8;
如果data是 DOMString 类型,content-type默认值为text/charset=UTF-8;
如果data是 FormData 类型,content-type默认值为multipart/form- boundary=[xxx]
如果data是其他类型,则不会设置content-type的默认值
当然这些只是content-type的默认值,但如果用xhr.setRequestHeader()手动设置了中content-type的值,以上默认值就会被覆盖。
另外需要注意的是,若在断网状态下调用xhr.send(data)方法,则会抛错:Uncaught NetworkError: Failed to execute ‘send’ on ‘XMLHttpRequest’。一旦程序抛出错误,如果不 catch 就无法继续执行后面的代码,所以调用 xhr.send(data)方法时,应该用 try-catch捕捉错误。
xhr.send(data)
}catch(e) {
//doSomething…
xhr.withCredentials与 CORS 什么关系
我们都知道,在发同域请求时,浏览器会将cookie自动加在request header中。但大家是否遇到过这样的场景:在发送跨域请求时,cookie并没有自动加在request header中。
造成这个问题的原因是:在CORS标准中做了规定,默认情况下,浏览器在发送跨域请求时,不能发送任何认证信息(credentials)如”cookies”和”HTTP authentication schemes”。除非xhr.withCredentials为true(xhr对象有一个属性叫withCredentials,默认值为false)。
所以根本原因是cookies也是一种认证信息,在跨域请求中,client端必须手动设置xhr.withCredentials=true,且server端也必须允许request能携带认证信息(即response header中包含Access-Control-Allow-Credentials:true),这样浏览器才会自动将cookie加在request header中。
另外,要特别注意一点,一旦跨域request能够携带认证信息,server端一定不能将Access-Control-Allow-Origin设置为*,否则就会面临攻击危险。
xhr相关事件
xhr相关事件有很多,有时记起来还挺容易混乱。但当我了解了具体代码实现后,就容易理清楚了。下面是XMLHttpRequest的部分实现代码:
interface XMLHttpRequestEventTarget : EventTarget {
// event handlers
attribute EventH
attribute EventH
attribute EventH
attribute EventH
attribute EventH
attribute EventH
attribute EventH
interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
interface XMLHttpRequest : XMLHttpRequestEventTarget {
// event handler
attribute EventHandle
readonly attribute XMLHttpRequestU
从代码中我们可以看出:
XMLHttpRequestEventTarget接口定义了7个事件:
onloadstart
onprogress
每一个XMLHttpRequest里面都有一个upload属性,而upload是一个XMLHttpRequestUpload对象
XMLHttpRequest和XMLHttpRequestUpload都继承了同一个XMLHttpRequestEventTarget接口,所以xhr和xhr.upload都有第一条列举的7个事件
onreadystatechange是XMLHttpRequest独有的事件
所以这么一看就很清晰了:
xhr一共有8个相关事件:7个XMLHttpRequestEventTarget事件+1个独有的onreadystatechange事件;而xhr.upload只有7个XMLHttpRequestEventTarget事件。
事件触发条件
下面是我自己整理的一张xhr相关事件触发条件表,其中最需要注意的是 onerror 事件的触发条件。
onreadystatechange
每当xhr.readyState改变时触发;但xhr.readyState由非0值变为0时不触发。
onloadstart 调用xhr.send()方法后立即触发,若xhr.send()未被调用则不会触发此事件。
onprogress
xhr.upload.onprogress在上传阶段(即xhr.send()之后,xhr.readystate=2之前)触发,每50ms触发一次;xhr.onprogress在下载阶段(即xhr.readystate=3时)触发,每50ms触发一次。
当请求成功完成时触发,此时xhr.readystate=4
当请求结束(包括请求成功和请求失败)时触发
onabort 当调用xhr.abort()后触发
xhr.timeout不等于0,由请求开始即onloadstart开始算起,当到达xhr.timeout所设置时间请求还未结束即onloadend,则触发此事件。
onerror 在请求过程中,若发生Network error则会触发此事件(若发生Network error时,上传还没有结束,则会先触发xhr.upload.onerror,再触发xhr.onerror;若发生Network error时,上传已经结束,则只会触发xhr.onerror)。注意,只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,如响应返回的xhr.statusCode是4xx时,并不属于Network error,所以不会触发onerror事件,而是会触发onload事件。
事件触发顺序
当请求一切正常时,相关的事件触发顺序如下:
触发xhr.onreadystatechange(之后每次readyState变化时,都会触发一次)
触发xhr.onloadstart
//上传阶段开始:
触发xhr.upload.onloadstart
触发xhr.upload.onprogress
触发xhr.upload.onload
触发xhr.upload.onloadend
//上传结束,下载阶段开始:
触发xhr.onprogress
触发xhr.onload
触发xhr.onloadend
发生abort/timeout/error异常的处理
在请求的过程中,有可能发生 abort/timeout/error这3种异常。那么一旦发生这些异常,xhr后续会进行哪些处理呢?后续处理如下:
一旦发生abort或timeout或error异常,先立即中止当前请求
将 readystate 置为4,并触发 xhr.onreadystatechange事件
如果上传阶段还没有结束,则依次触发以下事件:
xhr.upload.onprogress
xhr.upload.[onabort或ontimeout或onerror]
xhr.upload.onloadend
触发 xhr.onprogress事件
触发 xhr.[onabort或ontimeout或onerror]事件
触发xhr.onloadend 事件
在哪个xhr事件中注册成功回调?
从上面介绍的事件中,可以知道若xhr请求成功,就会触发xhr.onreadystatechange和xhr.onload两个事件。 那么我们到底要将成功回调注册在哪个事件中呢?我倾向于 xhr.onload事件,因为xhr.onreadystatechange是每次xhr.readyState变化时都会触发,而不是xhr.readyState=4时才触发。
xhr.onload = function () {
//如果请求成功
if(xhr.status == 200){
//do successCallback
上面的示例代码是很常见的写法:先判断http状态码是否是200,如果是,则认为请求是成功的,接着执行成功回调。这样的判断是有坑儿的,比如当返回的http状态码不是200,而是201时,请求虽然也是成功的,但并没有执行成功回调逻辑。所以更靠谱的判断方法应该是:当http状态码为2xx或304时才认为成功。
xhr.onload = function () {
//如果请求成功
if((xhr.status &= 200 && xhr.status & 300) || xhr.status == 304){
//do successCallback
终于写完了……
看完那一篇长长的W3C的xhr 标准,我眼睛都花了……
希望这篇总结能帮助刚开始接触XMLHttpRequest的你。
最后给点扩展学习资料,如果你:
想真正搞懂XMLHttpRequest,最靠谱的方法还是看 W3C的xhr 标准;
想结合代码学习如何用XMLHttpRequest发各种类型的数据,可以参考html5rocks上的这篇文章
想粗略的了解XMLHttpRequest的基本使用,可以参考MDN的XMLHttpRequest介绍;
想了解XMLHttpRequest 的发展历程,可以参考阮老师的文章;
想了解Ajax的基本介绍,可以参考AJAX T
想了解跨域请求,则可以参考W3C的 cors 标准;
想了解http协议,则可以参考HTTP T
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1958次
排名:千里之外
原创:11篇
(1)(4)(8)(5) 上传我的文档
 下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
正在努力加载中...
即时聊天myicq代码分析
下载积分:900
内容提示:即时聊天myicq代码分析
文档格式:TXT|
浏览次数:9|
上传日期: 09:52:46|
文档星级:
该用户还上传了这些文档
即时聊天myicq代码分析
官方公共微信Android进阶(24)
Android开发中经常会用到周期性执行一个动作的需求,大的场景有推送,统计,即时通讯,小的场景有客户端进行一些小范围的计时器,列入有以下场景。
统计:客户端不断轮询去请求服务器某个接口,上报数据等
1. 统计方案见
2. 日志抓取见:
推送:客户端定时去检测服务器有无新的消息,也有采用socket进行长连接主动推,那么这一类我们可以归类到即时通信中
聊天: 客户端和服务端双向采用轮询机制,业内不叫轮询,称之为心跳机制。客户端定时的连接服务器,服务器轮询去检测客户端是否在线,这叫保证了客户端断线时能及时连接到服务器,服务器也能及时在和客户端掉线时更新状态,
不死进程:话说不死进程我们可以用轮询监测某个服务是否存活,但是一般实现不死进程时候不建议采取轮询机制,一般采用三方互相守护来实现。
常有客户端轮询方案有如下:
一 采用Thread+Service方式
此方式在客户单开启时成功开启一个后台服务,并在服务里启动一个线程,让线程定时去执行应任务,
public class PollService extends Service {
private Boolean isStart =
public IBinder onBind(Intent intent) {
new MyThread().start();
public void onCreate() {
System.out.println("oncreate()");
MyThread thread = new MyThread();
thread.start();
super.onCreate();
private class MyThread extends Thread {
public void run() {
while (isStart) {
// 每个5秒向服务器发送一次请求
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
// do something
public void onDestroy() {
super.onDestroy();
二 采用Handler 进行定时轮询
此方式只采用handler加入到 ThreadLocal中,定时sendMessge和handleMessage,来完成我们的定时功能。
public class StatiPollMgr {
/** 超期消息 */
private static final int MSG_TIMEOUT = 1;
private PaStaticsManagerImpl staticsManagerI
/** 心跳周期 */
private long mCardiacC
/** 默认心跳周期,即初始化而来的周期 */
private long mDefaultC
public StatiPollMgr(){
* 开启心跳
* @param aCardiacCycle
public void start(long aCardiacCycle) {
mDefaultCycle = aCardiacC
mCardiacCycle = aCardiacC
checkDateChanging();
* 停止心跳
public void stop() {
sPrivateHandler.get().removeMessages(MSG_TIMEOUT);
private void loop() {
Message msg = sPrivateHandler.get().obtainMessage(MSG_TIMEOUT, this);
sPrivateHandler.get().sendMessageDelayed(msg, mCardiacCycle);
* 超期通知
public void onTimeOut() {
// do something
* 检测将要跨天时,调整心跳周期;跨天之后,调回默认值
private void checkDateChanging() {
Time time = new Time();
time.setToNow();
int hour = time. //24小时制
int minute = time.
if (hour == 23) { //SUPPRESS CHECKSTYLE
int cycle = 61 - // SUPPRESS CHECKSTYLE 12:01访问
long timeSchedule = cycle * DateUtils.MINUTE_IN_MILLIS;
if (timeSchedule & mCardiacCycle) {
mCardiacCycle = timeS
if (mCardiacCycle != mDefaultCycle) {
mCardiacCycle = mDefaultC
private static final ThreadLocal&Handler& sPrivateHandler = new ThreadLocal&Handler&() {
protected Handler initialValue() {
return new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TIMEOUT:
StatiPollMgr schedule = (StatiPollMgr) msg.
if (schedule != null) {
schedule.onTimeOut();
schedule.checkDateChanging();
schedule.loop();
三 采用AlarmManager和Broadcast
采用Anrdoid自带的Alarm机制来做定时操作,本API本来用作系统的铃声操作,我们可以借助他来完成定时操作,一般我们在Alarm来启动一个Receiver,在Receiver中去执行我们的需求代码就可以了
Receiver 用来接收消息
public class CoreReceiver extends BroadcastReceiver {
public static final String REPORT_ACTION = "action.base.send_report";
public void onReceive(Context context, Intent intent) {
if (context == null || intent ==null ) {
if (TextUtils.equals(intent.getAction(), REPORT_ACTION)) {
Toast.makeText(context, "send statData", Toast.LENGTH_LONG).show();
//do some 列入去请求网络,或其它更新UI操作等
AlarmManager
用来设定时间和触发广播
public class PollUtil {
static CoreR
/**开启轮询服务
* @param context
* @param seconds
* @param cls
* @param action
public static void startPollingService(Context context, int seconds, Class&?& cls, String action) {
//获取AlarmManager系统服务
AlarmManager manager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
//包装需要执行Service的Intent
Intent intent = new Intent(context, cls);
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//触发服务的起始时间
long triggerAtTime = SystemClock.elapsedRealtime();
//使用AlarmManger的setRepeating方法设置定期执行的时间间隔(seconds秒)和需要执行的Service
manager.setRepeating(AlarmManager.ELAPSED_REALTIME, triggerAtTime,
seconds * 1000, pendingIntent);
* 停止轮询服务
* @param context
* @param cls
* @param action
public static void stopPollingService(Context context, Class&?& cls, String action) {
AlarmManager manager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, cls);
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//取消正在执行的服务
manager.cancel(pendingIntent);
接下来我们的Activity中直接可以调用轮询工具类的start方法,
PollUtil.stopPollingService(mContext, CoreReceiver.class,
CoreReceiver.REPORT_ACTION , 3);
上面三种方法都可以实现本地轮询器,那么哪种方式比较靠谱呢,
显然第一种是开启一个Service,这样消耗显而易见,在app内存持续暴涨情况下这个服务又被kill的可能,我们可以把Service声明成进程,以及提高到前台,也可以采用广播来启动,但逃不过第三方安全软件kill的可能,在保证功能情况下,这种方式也不可取的。我们APP退出时更保证不正常的轮询
第二种采用handler, 性能开销相对很低,我们也可以在app启动时做一些轮询操作,但是当我们的App退出时就无法进行轮询操作的,这种实现方式,我们可以app启动的相对需求中采用,比如去计时去采集app的行为日志,性能开销等,
第三种,借助系统的Api,那么本身的系统就进行闹铃操作,也可以在app退出是进行定时,比如推送功能我们可以借助这种方案,但是会存在手机兼容问题,比如国产的手机已经对我们的Alarm加入权限,第三方应用无法对此操作。
不管那种方式,我们也可以采取多方式配合,比如我们广播启动Service,Service在被杀的时候发送来触发广播,广播竟可能的监听系统多种广播来启动我们的Service, 也可以将Alarm加入进来,来守护某个Service
当然性能方面第二种是最优的,功能全面来说第三种最优
自定义统计SDK:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:95629次
积分:1782
积分:1782
排名:第16559名
原创:67篇
评论:178条
Tamic:曾参与百度浏览器开发,目前一致研究移动开发技术!
Android TV交流请加QQ群:
微信公众号:Tamic开发社区
谢谢您一直的支持
阅读:12490
文章:13篇
阅读:31777

我要回帖

更多关于 java handle类 的文章

 

随机推荐