Android 项目必备(十一)--> 轮询操作

在这里插入图片描述

文章目录

前言

  • 什么叫轮询请求?
    简单理解就是 App 端每隔一定的时间重复请求的操作就叫做轮询请求,比如:App 端每隔一段时间上报一次定位信息,App 端每隔一段时间拉去一次用户状态等,这些应该都是轮询请求。

  • 为何不用长连接代替轮询操作?
    长连接并不是稳定的可靠的,而执行轮询操作的时候一般都是要稳定的网络请求,而且轮询操作一般都是有生命周期的,即在一定的生命周期内执行轮询操作,而长连接一般都是整个进程生命周期的,所以从这方面讲也不太适合。

实战

1. 与长连接相关的轮训请求

public class LoopRequestService extends Service {
    /**
     * 是否已经绑定了推送设备id
     */
    private volatile isBindDevIdEnum isBindDevId = isBindDevIdEnum.NONE;
    public enum isBindDevIdEnum {
        NONE, RUN, SUCCESS
    }

    public static final String ACTION = "LoopService";
    /**
     * 客户端执行轮询的时间间隔,该值由StartQueryInterface接口返回,默认设置为30s
     */
    public static int LOOP_INTERVAL_SECS = 5;
    /**
     * 当前服务是否正在执行
     */
    public static boolean isServiceRunning = false;
    /**
     * 定时任务工具类
     */
    public static Timer timer = new Timer();

    private Context context;

    public LoopRequestService() {
        isServiceRunning = false;
    }

    //-------------------------------使用闹钟执行轮询服务------------------------------------

    /**
     * 启动轮询服务
     */
    public static void startLoopService(Context context) {
        if (context == null){
            return;
        }
        quitLoopService(context);
        LogUtils.i("LoopService开启轮询服务,轮询间隔:" + LOOP_INTERVAL_SECS + "s");
        AlarmManager manager = (AlarmManager) context.getApplicationContext()
                .getSystemService(Context.ALARM_SERVICE);
        //开启服务service
        Intent intent = new Intent(context.getApplicationContext(), LoopRequestService.class);
        intent.setAction(LoopRequestService.ACTION);
        PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(),
                1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        /*
         * 闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间。需要注意的是,本属性与第一个属性(type)密切相关,
         * 如果第一个参数对应的闹钟使用的是相对时间(ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于系统启动时间来说),
         *      比如当前时间就表示为:SystemClock.elapsedRealtime();
         * 如果第一个参数对应的闹钟使用的是绝对时间(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,
         *      比如当前时间就表示为:System.currentTimeMillis()。
         */
        if (manager != null) {
            manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
                    LOOP_INTERVAL_SECS * 1000, pendingIntent);
        }
    }

    /**
     * 停止轮询服务
     */
    public static void quitLoopService(Context context) {
        if (context == null){
            return;
        }
        LogUtils.i("LoopService关闭轮询闹钟服务...");
        AlarmManager manager = (AlarmManager) context.getApplicationContext()
                .getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context.getApplicationContext(), LoopRequestService.class);
        intent.setAction(LoopRequestService.ACTION);
        PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(),
                1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        if (manager != null) {
            manager.cancel(pendingIntent);
        }
        // 关闭轮询服务
        LogUtils.i("LoopService关闭轮询服务...");
        context.stopService(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtils.i("LoopService开始执行轮询服务... \n 判断当前用户是否已登录...");
        // 若当前网络不正常或者是用户未登陆,则不再跳转
        if (NetworkUtils.isConnected()) {
            // 判断当前长连接状态,若长连接正常,则关闭轮询服务
            LogUtils.i("LoopService当前用户已登录... \n 判断长连接是否已经连接...");
            if (isBindDevId != null && isBindDevId == isBindDevIdEnum.SUCCESS) {
                LogUtils.i("LoopService已经绑定id成功,退出轮询服务...");
                quitLoopService(context);
            } else {
                if (isServiceRunning) {
                    return START_STICKY;
                }
                // 启动轮询拉取消息
                startLoop();
            }
        } else {
            LogUtils.i("LoopService没有网络时,关闭轮询服务...");
            quitLoopService(context);
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtils.i("LoopService轮询服务退出,执行onDestroy()方法,inServiceRunning赋值false");
        isServiceRunning = false;
        timer.cancel();
        timer = new Timer();
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /**
     * 启动轮询拉去消息
     */
    private int count = 0;
    private void startLoop() {
        if (timer == null) {
            timer = new Timer();
        }
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                isServiceRunning = true;
                LogUtils.i("LoopService执行轮询操作...轮询服务中请求getInstance接口...");
                //开始执行轮询的网络请求操作sendLoopRequest();
                //这里只是假设数据操作
                if (count==5){
                    isBindDevId = isBindDevIdEnum.SUCCESS;
                }
                count++;
            }
        }, 0, LOOP_INTERVAL_SECS * 1000);
    }
}

2. 与页面相关的轮询请求
2.1 使用 TimerTask 实现轮询
在当前 Activity 页面有一个定时的拉去订单信息的轮询请求

/**
 * TimerTask对象,主要用于定时拉去服务器信息
 */
public class Task extends TimerTask {
    @Override
    public void run() {
        L.i("开始执行执行timer定时任务...");
        handler.post(new Runnable() {
            @Override
            public void run() {
                isFirstGetData = false;
                getData(true);
            }
        });
    }
}

当用户退出这个页面的时候需要清除这里的轮询操作

@Override
public void onDestroy() {
    super.onDestroy();
    if (timer != null) {
        timer.cancel();
        timer = null;
    }
    if (timerTask != null) {
        timerTask.cancel();
        timerTask = null;
    }
}

2.2 通过 Handler 对象实现轮训请求

/**
 * 默认的时间间隔:1分钟
 */
private static int DEFAULT_INTERVAL = 60 * 1000;
/**
 * 异常情况下的轮询时间间隔:5秒
 */
private static int ERROR_INTERVAL = 5 * 1000;
/**
 * 当前轮询执行的时间间隔
 */
private static int interval = DEFAULT_INTERVAL;
/**
 * 轮询Handler的消息类型
 */
private static int LOOP_WHAT = 10;
/**
 * 是否是第一次拉取数据
 */
private boolean isFirstRequest = false;
/**
 * 第一次请求数据是否成功
 */
private boolean isFirstRequestSuccess = false;

/**
 * 开始执行轮询,正常情况下,每隔1分钟轮询拉取一次最新数据
 * 在onStart时开启轮询
 */
private void startLoop() {
    L.i("页面onStart,需要开启轮询");
    loopRequestHandler.sendEmptyMessageDelayed(LOOP_WHAT, interval);
}

/**
 * 关闭轮询,在界面onStop时,停止轮询操作
 */
private void stopLoop() {
    L.i("页面已onStop,需要停止轮询");
    loopRequestHandler.removeMessages(LOOP_WHAT);
}

/**
 * 处理轮询的Handler
 */
private Handler loopRequestHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 如果首次请求失败,
        if (!isFirstRequestSuccess) {
            L.i("首次请求失败,需要将轮询时间设置为:" + ERROR_INTERVAL);
            interval = ERROR_INTERVAL;
        } else {
            interval = DEFAULT_INTERVAL;
        }

        L.i("轮询中-----当前轮询间隔:" + interval);

        loopRequestHandler.removeMessages(LOOP_WHAT);

        // 首次请求为成功、或者定位未成功时执行重定位,并加载网点数据
        if (!isFirstRequestSuccess || !Config.locationIsSuccess()) {
            isClickLocationButton = false;
            doLocationOption();
        } else {
            loadData();
        }
        System.gc();
        loopRequestHandler.sendEmptyMessageDelayed(LOOP_WHAT, interval);
    }
};

2.3 通过 RxJava 实现轮询操作

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin-Dev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值