Android 推送和统计最优轮循(心跳策略)探究实践
http://blog.csdn.net/sk719887916/article/details/51398416 skay亲笔
Android开发中经常会用到周期性执行一个动作的需求,大的场景有推送,统计,即时通讯,小的场景有客户端进行一些小范围的计时器,列入有以下场景。
统计:客户端不断轮询去请求服务器某个接口,上报数据等
1. 统计方案见《 Android 优质精准的用户行为和日志打捞方案》
2. 日志抓取见:《Android全局异常处理(可以做强制退出和carsh日志抓取)》
推送:客户端定时去检测服务器有无新的消息,也有采用socket进行长连接主动推,那么这一类我们可以归类到即时通信中
聊天: 客户端和服务端双向采用轮询机制,业内不叫轮询,称之为心跳机制。客户端定时的连接服务器,服务器轮询去检测客户端是否在线,这叫保证了客户端断线时能及时连接到服务器,服务器也能及时在和客户端掉线时更新状态,
不死进程:话说不死进程我们可以用轮询监测某个服务是否存活,但是一般实现不死进程时候不建议采取轮询机制,一般采用三方互相守护来实现。
常有客户端轮询方案有如下:
一 采用Thread+Service方式
此方式在客户单开启时成功开启一个后台服务,并在服务里启动一个线程,让线程定时去执行应任务,
public class PollService extends Service {
private Boolean isStart = true;
@Override
public IBinder onBind(Intent intent) {
new MyThread().start();
return null;
}
@Override
public void onCreate() {
System.out.println("oncreate()");
MyThread thread = new MyThread();
thread.start();
super.onCreate();
}
private class MyThread extends Thread {
@Override
public void run() {
while (isStart) {
try {
// 每个5秒向服务器发送一次请求
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// do something
}
}
}
@Override
public void onDestroy() {
isStart = false;
super.onDestroy();
}
}
二 采用Handler 进行定时轮询
此方式只采用handler加入到 ThreadLocal中,定时sendMessge和handleMessage,来完成我们的定时功能。
public class StatiPollMgr {
/** 超期消息 */
private static final int MSG_TIMEOUT = 1;
private PaStaticsManagerImpl staticsManagerImpl;
/** 心跳周期 */
private long mCardiacCycle;
/** 默认心跳周期,即初始化而来的周期 */
private long mDefaultCycle;
public StatiPollMgr(){
}
/**
* 开启心跳
*
* @param aCardiacCycle
* 心跳周期
*/
public void start(long aCardiacCycle) {
mDefaultCycle = aCardiacCycle;
mCardiacCycle = aCardiacCycle;
checkDateChanging();
stop();
loop();
}
/**
* 停止心跳
*/
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.hour; //24小时制
int minute = time.minute;
if (hour == 23) { //SUPPRESS CHECKSTYLE
int cycle = 61 - minute; // SUPPRESS CHECKSTYLE 12:01访问
long timeSchedule = cycle * DateUtils.MINUTE_IN_MILLIS;
if (timeSchedule < mCardiacCycle) {
mCardiacCycle = timeSchedule;
}
} else {
if (mCardiacCycle != mDefaultCycle) {
mCardiacCycle = mDefaultCycle;
}
}
}
/**
* Handler
*/
private static final ThreadLocal<Handler> sPrivateHandler = new ThreadLocal<Handler>() {
@Override
protected Handler initialValue() {
return new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TIMEOUT:
StatiPollMgr schedule = (StatiPollMgr) msg.obj;
if (schedule != null) {
schedule.onTimeOut();
schedule.checkDateChanging();
schedule.loop();
}
break;
default:
break;
}
}
};
}
};
}
三 采用AlarmManager和Broadcast
采用Anrdoid自带的Alarm机制来做定时操作,本API本来用作系统的铃声操作,我们可以借助他来完成定时操作,一般我们在Alarm来启动一个Receiver,在Receiver中去执行我们的需求代码就可以了
Receiver 用来接收消息
public class CoreReceiver extends BroadcastReceiver {
public static final String REPORT_ACTION = "action.base.send_report";
@Override
public void onReceive(Context context, Intent intent) {
if (context == null || intent ==null ) {
return;
}
if (TextUtils.equals(intent.getAction(), REPORT_ACTION)) {
Toast.makeText(context, "send statData", Toast.LENGTH_LONG).show();
//do some 列入去请求网络,或其它更新UI操作等
}
AlarmManager
用来设定时间和触发广播
public class PollUtil {
static CoreReceiver receiver;
/**开启轮询服务
* @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:
Android 推送和统计最优轮循(心跳策略)探究实践的更多相关文章
- Android推送服务(2)微信智能心跳方案
http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207243549&idx=1&sn=4ebe4beb8123f1b5 ...
- Android推送服务——百度云推送
一.推送服务简介 消息推送,顾名思义,是由一方主动发起,而另一方与发起方以某一种方式建立连接并接收消息.在Android开发中,这里的发起方我们把它叫做推送服务器(Push Server),接收方叫做 ...
- Mosquitto搭建Android推送服务(一)MQTT简介
总体概要: MQTT系列文章分为4部分 1.MQTT简介 2.mosquitto服务器搭建 3.编写Mosquitto的可视化工具 4.使用Mosquitto完成Android推送服务 文章钢要: 对 ...
- Android 推送实现
解决数据同步的问题:常用的方法有2种. (1) 定时去服务器上查询数据,也叫Polling. (2) 手机跟服务器之间维护一个 TCP 长连接,或者使用SMS,当服务器有数据时,实时推送到客户端,也就 ...
- Android推送通知指南
Android推送通知指南 在开发Android和iPhone应用程序时,我们往往需要从服务器不定的向手机客户端即时推送各种通知消息,iPhone上已经有了比较简单的和完美的推送通知解决方案,可是 ...
- Android推送技术研究
前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些东西偏底层硬件和通信行业, 我对这些一窍不通, 只能说说自己的理解. 为什 ...
- Android推送分析
cpu多核利用能够实现Android推送的吞吐量. 讲明白这点,我们需要了解Android推送的基本原理了.如果实现C(客户端)与server(客户端)实时通讯了.这里有两种思路了: 1.一种是定时 ...
- Android推送方案
一. 常见的推送原理: 1)轮询(Pull)方式:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等.而且你还要考虑轮询的频率,如果太慢可能 ...
- 转:Android推送技术研究
Android推送技术研究 字数5208 阅读4026 评论5 喜欢35 前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些 ...
随机推荐
- bzoj 4010: [HNOI2015]菜肴制作
Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...
- Python入门之装饰器九步学习入门
第一步:最简单的函数,准备附加额外功能 '''示例1: 最简单的函数,表示调用了两次''' def myfunc(): print("myfunc() called.") myfu ...
- 利用 socket 发送 get/post 请求
思路:利用 fsockopen 函数与要请求的主机建立一个通信通道,再将请求行.头信息.主体信息通过这个通道传输给主机实现请求的发送.利用这种方式发送 get 请求就是常说的小偷程序,发送 post ...
- Spring使用@Scheduled定时调度
一.spring配置文件中增加对这个注解的支持: 配置文件如下: <?xml version="1.0" encoding="UTF-8"?> &l ...
- Spring系列之装配Bean
Spring 的三种装配Bean的方式 组件扫描+自动装配(隐式) 通过Java config装配bean(显示) 通过XML装配bean(显示) 一.组件扫描+自动装配(隐式配置) 组件扫描: Sp ...
- Ubuntu一些常用的软件安装及配置
软件 安装 Vim echo "y" | sudo apt-get install vim 安装搜狗输入法 这个我在虚拟机里面尝试了好多遍,不断恢复备份然后重试.终于有了这个纯靠命 ...
- js 在iframe子页面获取父页面元素,或在父页面 获取iframe子页面的元素的几种方式
用JS或jquery访问页面内的iframe,兼容IE/FF 注意:框架内的页面是不能跨域的! 假设有两个页面,在相同域下. index.html 文件内含有一个iframe: XML/HTML代码 ...
- python中input()和raw_input()的区别
两者均是python的内置函数,通过读取控制台的输入与用户实现交互.raw_input:将所有输入作为字符串看待,不管用户输入什么类型的都会转变成字符串. raw的 ...
- 论文笔记--PCN:Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks
关键词:rotation-invariant face detection, rotation-in-plane, coarse-to-fine 核心概括:该篇文章为中科院计算所智能信息处理重点实验室 ...
- C++雾中风景8:Lambda表达式
上一篇C++的博客是Long Long ago了,前文讲到在看Lambda表达式的内容.笔者首次接触Lambda表达式应该是学习Python语言的时候,当时也不太明白这种表达方式的精髓,后续接触了Sc ...