项目实战
定时鼠标自动单击桌面
- Java中提供了很多用于自动化测试包,可以用来模拟用户进行如移动鼠标、单击鼠标、滚动鼠标滚轮、右击、按下一个或多个键盘按键等
- 下面的代码就是模拟用户自动单击桌面指定区域,每间隔1分钟,鼠标自动点击一次,测试一个晚上,时间误差完全可以忽略,非常稳定
代码实现
package test;
import java.awt.*;
import java.awt.event.InputEvent;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Administrator on 2018/6/23 0023.
* 创建模拟用户单击的定时器任务
* 可以创建一次执行或重复执行的任务
* TimeTask类似Runnable接口
*/
public class ClickTask extends TimerTask {
/**
* 用户单击计数
*/
private AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 同样实现run方法
*/
@Override
public void run() {
/**
* 单数次与偶数次分别单击桌面两个不同的位置
*/
if (atomicInteger.addAndGet(1) % 2 == 0) {
clickScreenByXY(100, 300);
} else {
clickScreenByXY(1200, 300);
}
System.out.println(new Date() + " click " + atomicInteger.get() + " time ,Thread name is :" + Thread.currentThread().getName());
}
/**
* 模拟用户单击屏幕指定区域,默认单击屏幕最中央
*
* @param x:x坐标
* @param y:y坐标
*/
public static final void clickScreenByXY(Integer x, Integer y) {
try {
/**创建工具包对象*/
Toolkit toolkit = Toolkit.getDefaultToolkit();
/**创建自动化对象*/
Robot robot = new Robot();
/**利用工具包对象获取屏幕分辨率*/
if (x == null) {
x = toolkit.getScreenSize().width / 2;
}
if (y == null) {
y = toolkit.getScreenSize().height / 2;
}
/**
* 移动鼠标到指定位置
* 然后按下鼠标左键,再松开,模拟单击操作
*/
robot.mouseMove(x, y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(100);
} catch (AWTException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
/**
* 创建任务对象
*/
TimerTask timerTask = new ClickTask();
/**
* 创建定时器对象,相当于新开一个线程去执行定时器任务
*/
Timer timer = new Timer();
/**
* 定时器执行定时器任务,10秒后开始执行任务
* 任务执行完成后,间隔1分钟再次执行,循环往复
*/
timer.schedule(timerTask, 10000, 60000);
}
}
运行结果
定时往服务端发送心跳
- 做服务端客户端通信项目时,因为采用UDP通信,所以让客户端间隔1分钟就往服务端做http的get请求,表示通信正常,客户端在线
代码实现
import com.lct.thread.HeartbeatTimerTask;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.Timer;
import java.util.TimerTask;
/**
* 应用程序入口
* 1、继承Application
*/
public class MainApp extends Application {
/**
* 实现start方法
*/
@Override
public void start(Stage primaryStage) throws Exception {
/**创建一个堆栈面板,面板用于包含控件*/
StackPane root = new StackPane();
/**设置窗口(舞台)标题并为舞台添加场景*/
primaryStage.setTitle("gxg_client");
primaryStage.setScene(new Scene(root, 500, 200));
/**设置窗口无法缩放大小,默认为true,用户可以调节窗口大小*/
primaryStage.setResizable(false);
primaryStage.setIconified(true);
/**
* 使用定时器执行心跳任务--即定时往服务端发送消息
* 指定定时器线程名称,以及指定为守护线程,这样主线程关闭后,定时器线程就会结束,应用会退出|
* 指定此定时器任务立即执行,且任务执行完之后,延迟1分钟再次执行,依次循环往复
*/
TimerTask timerTask = new HeartbeatTimerTask();
Timer timer = new Timer("heartbeat thread", true);
timer.schedule(timerTask, 0, 1 * 5 * 1000);
/**显示窗口*/
primaryStage.show();
}
public static void main(String[] args) {
/**
* 调用Application的launch方法启动应用
*/
launch(args);
}
}
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ResourceBundle;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import com.lct.utils.SystemUtils;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.WinHttpClients;
import org.apache.http.util.EntityUtils;
/**
* Created by Administrator on 2018/6/26 0026.
* http://192.168.1.20:8080/gxg_server/clientController/heartbeat.action?clientIp=xx&clientMac=yyy
*/
public class HeartbeatTimerTask extends TimerTask {
/**
* serverHeartbeatRequestPath:服务器心跳请求路径
* getCountAtomicInteger:记录客户端请求的次数
*/
private static final StringBuilder serverHeartbeatRequestPath = new StringBuilder();
private static final AtomicInteger getCountAtomicInteger = new AtomicInteger(0);
static {
try {
ResourceBundle resourceBundle = ResourceBundle.getBundle("systemConfig");
serverHeartbeatRequestPath.append(resourceBundle.getString("serverHeartbeatRequestPath"));
InetAddress inetAddress = InetAddress.getLocalHost();
String clientIp = inetAddress.getHostAddress();
String clientMac = SystemUtils.getLocalMac();
serverHeartbeatRequestPath.append("?clientIp=" + clientIp);
serverHeartbeatRequestPath.append("&clientMac=" + clientMac);
serverHeartbeatRequestPath.append("&clientPort=" + resourceBundle.getString("localUdpListenerPort"));
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
@Override
public void run() {
if (!serverHeartbeatRequestPath.toString().startsWith("http")) {
System.out.println("资源文件配置的服务器请求地址错误,放弃请求...");
return;
}
if (getCountAtomicInteger.addAndGet(1) != 1) {
int getCountIndex = serverHeartbeatRequestPath.indexOf("getCount=");
serverHeartbeatRequestPath.replace(getCountIndex + 9, serverHeartbeatRequestPath.length(), getCountAtomicInteger.get() + "");
} else {
serverHeartbeatRequestPath.append("&getCount=" + getCountAtomicInteger.get());
}
CloseableHttpClient httpclient = WinHttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.setSocketTimeout(5000)
.setRedirectsEnabled(true)
.build();
HttpGet httpget = null;
CloseableHttpResponse response = null;
try {
httpget = new HttpGet(serverHeartbeatRequestPath.toString());
httpget.setConfig(requestConfig);
response = httpclient.execute(httpget);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
System.out.println("statusCode:" + statusCode);
if (statusCode == 200) {
HttpEntity httpEntity = response.getEntity();
String feedback = EntityUtils.toString(httpEntity, "UTF-8");
System.out.println("feedback:" + feedback);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
e.printStackTrace();
}
if (httpget != null && !httpget.isAborted()) {
httpget.releaseConnection();
httpget.abort();
}
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (httpclient != null) {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
错误结果分析
- TimerTask实现的也是Runnable接口
- Runnable 的run() 或者 Callable的call() 方法执行完的时候线程就会自动结束
- 注意如果在run方法中发生了异常而没有被捕获处理时,那么此线程就会自动结束
- 所以上面的代码是有问题的,只要当TimerTask中run方法在get连接服务器时发生错误,则会抛出异常,从而直接导致Timer定时器线程结束,也就意味着定时器无法再运行
- 这种情况执行建议使用ScheduledExecutorService,可以参考《ScheduledExecutorService 详解1》
Timer 常用API
- public class Timer extends Object:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
- public Timer() :创建一个新计时器,相关的线程不作为守护程序运行,即主线程死掉之后,此线程还是继续执行,应用不会关闭。
- public Timer(boolean isDaemon):创建一个新计时器,可以指定其相关的线程作为守护程序运行。isDaemon - 如果应该将相关的线程作为守护程序运行,则为 true。
- public Timer(String name):创建一个新计时器,其相关的线程具有指定的名称。相关的线程不作为守护程序运行。 name - 相关线程的名称。
- public Timer(String name,boolean isDaemon):创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行。
- public void cancel():终止此计时器,丢弃所有当前已安排的任务。这不会干扰当前正在执行的任务(如果存在)。一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。 注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。可以重复调用此方法;但是第二次和后续调用无效。
- public void schedule(TimerTask task,Date time):安排在指定的时间去执行指定的任务。如果此时间已过去,则安排立即执行该任务。 task - 所要安排的任务;time - 执行任务的时间。
- public void schedule(TimerTask task, Date firstTime,long period):安排指定的任务从指定的时间开始进行重复的延迟执行。task - 所要安排的任务;firstTime - 首次执行任务的时间;period - 执行各后续任务之间的时间间隔,单位是毫秒。
- public void schedule(TimerTask task, long delay):安排在指定延迟后执行指定的任务。
- public void schedule(TimerTask task, long delay, long period):安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行。task - 所要安排的任务;delay - 执行任务前的延迟时间,单位是毫秒;period - 执行各后续任务之间的时间间隔,单位是毫秒。
TimerTask 常用API
- public abstract class TimerTaskextends Object implements Runnable:由 Timer 安排为一次执行或重复执行的任务。
- public boolean cancel():取消此计时器任务。如果任务安排为一次执行且还未运行,或者尚未安排,则永远不会运行。如果任务安排为重复执行,则永远不会再运行。(如果发生此调用时任务正在运行,则任务将运行完,但永远不会再运行。)
注意,从重复的计时器任务的 run 方法中调用此方法绝对保证计时器任务不会再运行。 此方法可以反复调用;第二次和以后的调用无效。
- public abstract void run():此计时器任务要执行的操作,继承TimerTask之后必须实现的方法。