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:

http://www.jianshu.com/p/cd83e81b78aa

Android 推送和统计最优轮循(心跳策略)探究实践的更多相关文章

  1. Android推送服务(2)微信智能心跳方案

    http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207243549&idx=1&sn=4ebe4beb8123f1b5 ...

  2. Android推送服务——百度云推送

    一.推送服务简介 消息推送,顾名思义,是由一方主动发起,而另一方与发起方以某一种方式建立连接并接收消息.在Android开发中,这里的发起方我们把它叫做推送服务器(Push Server),接收方叫做 ...

  3. Mosquitto搭建Android推送服务(一)MQTT简介

    总体概要: MQTT系列文章分为4部分 1.MQTT简介 2.mosquitto服务器搭建 3.编写Mosquitto的可视化工具 4.使用Mosquitto完成Android推送服务 文章钢要: 对 ...

  4. Android 推送实现

    解决数据同步的问题:常用的方法有2种. (1) 定时去服务器上查询数据,也叫Polling. (2) 手机跟服务器之间维护一个 TCP 长连接,或者使用SMS,当服务器有数据时,实时推送到客户端,也就 ...

  5. Android推送通知指南

      Android推送通知指南 在开发Android和iPhone应用程序时,我们往往需要从服务器不定的向手机客户端即时推送各种通知消息,iPhone上已经有了比较简单的和完美的推送通知解决方案,可是 ...

  6. Android推送技术研究

    前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些东西偏底层硬件和通信行业, 我对这些一窍不通, 只能说说自己的理解. 为什 ...

  7. Android推送分析

     cpu多核利用能够实现Android推送的吞吐量. 讲明白这点,我们需要了解Android推送的基本原理了.如果实现C(客户端)与server(客户端)实时通讯了.这里有两种思路了: 1.一种是定时 ...

  8. Android推送方案

    一. 常见的推送原理: 1)轮询(Pull)方式:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等.而且你还要考虑轮询的频率,如果太慢可能 ...

  9. 转:Android推送技术研究

    Android推送技术研究 字数5208 阅读4026 评论5 喜欢35 前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些 ...

随机推荐

  1. APIO 2016

    我好菜啊都不会 T1.boats 题目大意:给你N段区间,按顺序决定每段区间可以选一个数或不选,若选则选的这个数必须大于所有在这之前选的数,求有多少种方案.(N<=500,区间在[1,1e9]范 ...

  2. hdu5569 BestCoder Round #63 (div.2)

    题意: 给你一个矩阵,要求从左上角走到右下角,走个的费用:a[1]*a[2] + a[3]*a[4] + ......+ a[2n-1]*a[2n] 思路: 果然不机智,自己把自己套路了 对于每个奇数 ...

  3. ubuntu16.04安装eclipse后启动栏图标为问号

    ubuntu创建eclipse快捷方式图标. cd /usr/share/applications sudo touch eclipse.desktop sudo gedit eclipse.desk ...

  4. SparkSQL——用之惜之

    SparkSql作为Spark的结构化数据处理模块,提供了非常强大的API,让分析人员用一次,就会为之倾倒,为之着迷,为之至死不渝.在内部,SparkSQL使用额外结构信息来执行额外的优化.在外部,可 ...

  5. I/O控制的主要功能

    主要功能: 1.  解释用户的I/O系统调用.将用户I/O系统调用转换为I/O控制模块认识的命令模式. 2.  设备驱动.根据得到的I/O命令,启动物理设备完成指定的I/O操作. 3.  中断处理.对 ...

  6. 春招实习面经分享(已拿到腾讯春招Offer)

    十分幸运地收到了腾讯的Offer..回来报答一下各位牛客网的战友们,说起来也是有点运气成分,最后通过腾讯的内推收到了offer!之前也屡遭重创,阿里的内推一面挂了(寒假过早地投简历,电话面试应对地不太 ...

  7. 锐捷Linux版的下载和使用(福大客户端)

    下载锐捷程序包 点此下载 没有连接到锐捷里就进不了这个安装包的官方下载界面(好矛盾啊这个),所以我把它上传到博客园了. 解压文件 schaepher:~$ cd Downloads/ schaephe ...

  8. jquery选择器选取class

  9. 去除html标记和替换script标记

    1: /// <summary> 2: /// 去除HTML标记 3: /// </summary> 4: /// <param name="NoHTML&qu ...

  10. OpenCV设置摄像头分辨率及全屏显示

    OpenCV3.0下 设置摄像头分辨率为1920*1440,并全屏显示图像窗口. int _tmain(int argc, _TCHAR* argv[]) { Mat frame; VideoCapt ...