「安卓开发」Android防暴力点击事件处理解决方法及其存在的问题

  Android中防爆力点击很多App中都会用到,但是我看过几个项目中的源码,大部分都是使用的System.currentTimeMillis()来获取当前时间与上一次点击时间间隔来做防暴力点击处理。
  但是问题来了,由于System.currentTimeMillis()获取的是系统时间,这个时间会根据手机设置的时间而变化,那么就会导致一种情况,即当用户点击了一个View后记录了上一次的点击时间戳,这时将时间往前调之后,下一次再点击时System.currentTimeMillis()获取到的时间戳是比上一次点击时间戳要小的,因此就会导致点击无响应的情况发生。虽然说,这种情况几乎不会发生,或者发生了影响也不会太大,但是既然是一个问题,并且可以找到其他解决的方法,那么为什么不使用更优的方式来计算处理点击事件的时间间隔呢。
  如下的代码中使用到了几种计算点击事件时间间隔的方式:

package org.devlang.demo.click;

import android.os.SystemClock;
import android.util.Log;
import android.view.View;

import java.util.Calendar;

public abstract class OnThrottleClickListener implements View.OnClickListener {
    private static final int THROTTLE_CLICK_GAP_TIME = 300;
    private long preClickTime;

    @Override
    public void onClick(View view) {
//        long currentTimeMillis = System.currentTimeMillis();
//        long currentTimeMillis = Calendar.getInstance().getTimeInMillis();
        long currentTimeMillis = SystemClock.uptimeMillis();
//        long currentTimeMillis = SystemClock.elapsedRealtime();
        if (currentTimeMillis - preClickTime > THROTTLE_CLICK_GAP_TIME) {
            onThrottleClick(view);
            preClickTime = currentTimeMillis;
            Log.d("cmf", "perform");
        } else {
            Log.d("cmf", "break");
        }
    }

    public abstract void onThrottleClick(View view);
}

  • System.currentTimeMillis() 系统时间,会根据手机系统中设置的时间改变;
  • Calendar.getInstance().getTimeInMillis() 这种方式是最low的。因为每次点击都会创建一个Calendar实例,在后端开发中,都是拒绝使用这个类的,因为效率会比较低。
  • SystemClock.uptimeMillis() 自开机后,经过的时间,不包括深度睡眠的时间。Looper中所使用的也是这个方法,对时间间隔的计算比较有保证。
  • SystemClock.elapsedRealtime() 自开机后,经过的时间,包括深度睡眠的时间。

  这也是之前开发中遇到的问题,虽然影响不大,但也可以注意一下这个问题。
  如果你的项目使用的是jdk8以上的版本,则可以使用以下形式显得更为美观。

public interface OnThrottleClickListener extends View.OnClickListener {
    SparseArray<Long> map = new SparseArray<>();
    int THROTTLE_CLICK_GAP_TIME = 300;

    default void onClick(View v) {
        int id = v.getId();
        Long preClickTime = map.get(id, 0L);
        long nowClickTime = SystemClock.uptimeMillis();
        if (nowClickTime - preClickTime > THROTTLE_CLICK_GAP_TIME) {
            onThrottleClick(v);
            map.put(id, nowClickTime);
        }
    }

    void onThrottleClick(View view);
}

  如果项目还没有设置成使用jdk8以上版本,可以在build.gradle中配置版本。如下:

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

  相比之前抽象类的写法,这种接口式的写法能够更加美观且实用。但这种写法需要注意一个问题,就是map至始至终都是同一个对象,因此如果两个view的id是相同的,在设置的GAP_TIME时间间隔内点击会出现失效的情况,不过这种情况在list的时候可能会出现,其他情况出现的可能性倒是不高。

发表评论

电子邮件地址不会被公开。 必填项已用*标注