壁纸风波
许多有内涵的朋友都喜欢将自己的电脑桌面壁纸设置为美女图片,并且隔一段时间,自动更换,看起来十分炫酷。的确,用过windows的朋友们都知道,windows的个性化设置里面,可以将壁纸设置幻灯片放映,这就解决了壁纸常年不变的尴尬局面,原因在于很多懒猪们拿到自己的电脑以后,往往自己在网上找一张自己觉得好看的壁纸,设置为桌面壁纸以后,就懒得再去管它,这一用就是几个月,乃至上年。
很多稍微有点内涵的朋友们,喜欢经常换换壁纸,换换心情,他们自己去网上下载一些风景画,或是美女图片,然后自己建一个文件夹专门将其存放,于是电脑壁纸也经常自动换过来,换过去,看起来十分高大上。
但是往往上面的这些操作都十分的麻烦,那么有没有一种很便捷的方式,让壁纸自动下载与更换呢?
有的,下载一个壁纸软件就搞定了,但是又有许多朋友,不想让自己电脑安装太多的软件,本来安几个IDE,安个LOL,安个绝地求生等,还有很多开机启动的软件,这就已经很卡了,笔者16G运存,感觉都不舒服!
既然这样,那干脆自己写一个程序算了,占用内存小,没有垃圾文件,何乐而不为?
java实现壁纸自动下载并且自动切换
原理说明:
请求图片接口,下载图片并保存至一个电脑上的一个文件夹,这里我保存的是D盘下面的wallpapers文件夹,另外使用程序控制定时任务,每天早上9:00准时下载图片,且如果9:00电脑处于关机状态,当天开启电脑后,程序自动为我们补上,由于必应的图片接口是每天更换一张的,故我们每天都换下载到不同的图片,然后将其设置为windows幻灯片放映的文件夹,这样每天都会下载一张新的图片,并且由windows自动为我们切换了。最后,我们将程序打包为jar文件,并且设置为开机启动,这样我们就再也不用去管它了,就可以享受每天自动下载的必应图片并交由windows自动作为壁纸切换了(说实话,必应的图片都还是比较经典的,他们往往都出自非一般摄影师之手,经典不在于它们的景深、虚化之类的,在于它们所描绘的往往是常人很难捕捉到的自然奇观,或人性温暖的瞬间,天时地利人和才能拍摄出的照片,会让我们感受到未曾感知过的世界)
首先要下载壁纸,就必须得有提供壁纸的接口,API网站上面有许多图片的接口,这里笔者为了做测试,采用了郭霖先生提供的:必应每日一图(谨慎使用,感谢郭神)
图片链接接口:http://guolin.tech/api/bing_pic,这里接会返回必应图片的真实地址。
下面开始利用这个接口进行操作:
一、图片下载
使用HttpURLConnection进行网络请求,首先请求http://guolin.tech/api/bing_pic获得图片的真实地址,再请求真实地址下载图片,由于请求网络是耗时操作,我们将其在子线程中进行,使用接口回调监听下载是否完成。
监听请求完成的接口
public interface RequestFinishListener {
void finish(String response);
void error(Exception ex);
}
监听下载完成的接口
public interface DownloadFinishListener {
void finish();
void error(Exception ex);
}
网络请求工具
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtil {
/**
* 获取图片地址
*
* @param address
* @param listener
*/
public static void doGet(String address, RequestFinishListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection conn = null;
InputStream in = null;
BufferedReader reader = null;
StringBuffer result = new StringBuffer();
try {
URL url = new URL(address);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(3000);
conn.connect();
in = conn.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
result.append(line);
}
if (listener != null) {
listener.finish(result.toString());
}
reader.close();
in.close();
} catch (Exception e) {
listener.error(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}).start();
}
/**
* 下载图片
*
* @param address图片网址
* @param path保存路径
* @param name文件名
* @throws IOException
*/
public static void download(String address, String dir, String name, DownloadFinishListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection conn = null;
FileOutputStream out = null;
InputStream in = null;
File saveFile = null;
try {
saveFile = new File(dir + "\\" + name);
if (saveFile.exists()) {
saveFile.delete();
}
out = new FileOutputStream(saveFile);
URL url = new URL(address);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(3000);
conn.connect();
in = conn.getInputStream();
int len = -1;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
listener.finish();
out.close();
in.close();
} catch (Exception ex) {
// 删除未正确下载的图片
if (saveFile != null) {
saveFile.delete();
}
listener.error(ex);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}).start();
}
}
二、文件保存的说明
将其保存在D盘下的wallpapers文件下,命名从为数字,一次递增,1.JPG、2.jpg、3.jpg …
下载工具
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class DownUtil {
private final static int FAILED = 0;
private final static int SUCCEED = 1;
/**
* 检查当天的下载是否已经执行,防止重复下载
*
* @return
*/
private static boolean check() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String name = sdf.format(date);
File file1 = new File("D:\\wallpapers\\logs\\" + name + ".txt");
File file2 = new File("D:\\wallpapers\\errors\\" + name + ".txt");
if (file1.exists() || file2.exists()) {
return true;
}
return false;
}
public static void downWallPaper() {
if (!check()) {
String requestAddress = "http://guolin.tech/api/bing_pic";
HttpUtil.doGet(requestAddress, new RequestFinishListener() {
@Override
public void finish(String response) {
downPhoto(response);
}
@Override
public void error(Exception ex) {
CommUtil.saveLog(FAILED, "请求图片网址时异常: " + ex.toString());
}
});
}
}
// 下载图片
public static void downPhoto(String picAddress) {
String dir = "D:\\wallpapers";
File saveDir = new File(dir);
if (!saveDir.exists()) {
saveDir.mkdir();
}
File[] allFiles = saveDir.listFiles();
List<File> files = new ArrayList<File>();
for (File file : allFiles) {
if (file.isFile()) {
files.add(file);
}
}
if (files.size() == 0) {
// 文件夹不存在图片,从1开始命名
String name = "1.jpg";
HttpUtil.download(picAddress, dir, name, new DownloadFinishListener() {
@Override
public void finish() {
CommUtil.saveLog(SUCCEED, "下载成功");
}
@Override
public void error(Exception ex) {
CommUtil.saveLog(FAILED, "下载图片时异常: " + ex.toString());
}
});
} else {
// 文件夹存在图片,获取最大名称,将其加1作为新图片的名称
int names[] = new int[files.size()];
for (int i = 0; i < files.size(); i++) {
String fileName = files.get(i).getName();
names[i] = Integer.parseInt(fileName.substring(0, fileName.lastIndexOf(".")));
}
String name = CommUtil.getMax(names) + 1 + ".jpg";
HttpUtil.download(picAddress, dir, name, new DownloadFinishListener() {
@Override
public void finish() {
CommUtil.saveLog(SUCCEED, "下载成功");
}
@Override
public void error(Exception ex) {
CommUtil.saveLog(FAILED, "下载图片时异常: " + ex.toString());
}
});
}
}
}
三、日志记录
不论下载成功或失败,我们都将其使用时间作为命名记录下日志,保存在D盘下,用 txt 文件记录下载成功的日志保存到wallpapers文件夹下的子文件logs中,下载时发生的异常的日志保存到wallpapers文件夹下的子文件errors中
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class CommUtil {
public static int getMax(int[] array) {
Arrays.sort(array);
return array[array.length - 1];
}
/**
* 日志记录
*
* @param state日志状态分类
* @param message日志详情
*/
public static void saveLog(int state, String message) {
FileOutputStream out = null;
try {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String name = sdf.format(date);
File saveDir = null;
if (state == 1) {
saveDir = new File("D:\\wallpapers\\logs");
} else if (state == 0) {
saveDir = new File("D:\\wallpapers\\errors");
}
if (!saveDir.exists()) {
saveDir.mkdir();
}
File file = new File(saveDir + "\\" + name + ".txt");
out = new FileOutputStream(file);
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(date);
String log = "时间:" + time + ", " + message;
out.write(log.getBytes());
} catch (Exception e) {
}
}
}
四、第一次测试
到这里,我们先测试一下程序,待成功测试后,又设置定时任务,这里不建议大家使用Junit等测试工具,而是直接写Main函数进行测试。
public class Main {
public static void main(String[] args) {
//下载图片保存到D:\wallpapaers,生成日志保存到D:\wallpapers\logs(errors)
DownUtil.downWallPaper();
}
执行结果:
可以看到,程序自动为我们创建了wallpapers文件夹
打开该文件夹,可以看到程序为我们下载了一张图片,并产生了下载成功的日志
此时我们使用windows的个性化—壁纸—幻灯片放映—设置文件夹,选择wallpapers这个文件就可以了,过一会儿,会看到windows自动将我们壁纸切换了。
五、定时任务
由于必应每天更换一张图片,我们将其设置为每天早上9:00时执行该程序,借助TimerTask与线程计时Timer轻松实现,每天自动下载图片了。修改Main函数。
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Main {
public static void main(String[] args) {
downloadTimer();
}
public static void downloadTimer() {
TimerTask task = new TimerTask() {
@Override
public void run() {
DownUtil.downWallPaper();
}
};
// 设置执行时间
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);// 每天
calendar.set(year, month, day, 9, 00, 00);
// 定制每天的09:10:00执行,不重复执行
Date date = calendar.getTime();
Timer timer = new Timer();
timer.schedule(task, date);
}
}
注意:上面程序实现定时执行,我们使用Timer每天9:10执行TimerTask中的任务,如果现在时间是8:10,则会等待一个小时,等到9:00执行。但是如果已经超过了9:00,假如现在是10:00,我们才执行这个程序,并不会因为超过了9:00就不执行,同样会执行该任务,所以这样可以保证每天9:10如果没有开机,10:00才开机,同样会执行。这顺便也解决了超市补上下载的问题。
六、打包成Jar—脱离eclipse运行
到这里我们如果运行程序,只要不关闭eclipse(我是用eclipse写的),便可以每天09:00自动下载一张图片了,但是仔细想想,这是不可以能的,因为我们不可能让eclipse一直开着,所以我们将程序打包为一个jar包。
包名右键—Export—Jar file—设置Main函数—Finish
这里一定不能忘记设置Main函数,不然我打包的jar文件是不能运行的哦!
我是将其打包后,放在D盘的 jars 文件夹里面
此时我们双击该jar文件夹编可以运行了,每天9:00为我们下载一张图片到D:\wallpapers文件里下面,但是问题又来了,我们电脑总得关机吧,关机了程序便停止运行了,这下怎么办呢?
七、开机自启动Jar文件
我们只需编写一个windows的bat脚本就可以实现开机自动运行了
①编写javaProgramsStart.bat,其内容为:start java -jar wallpapers.jar
②创建该bat的快捷方式
③将该快捷方式放在windows的开机自启动文件夹内
其位置为:C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
很多朋友在C盘中可能找不到这个文件夹,可以直接搜索就可以了
到此便可以开机自启动了,每天9:00准时为我们下载一张必应的图片,如果那时没有网,便会记录错误的日志,如果网络正常,便会记录下载成功的日志。
八、解决最后一个问题—启动黑框
当我们重启电脑时,会发现我们实际上还存在一个很严重的问题,这会很严重的影响我们的体验,那就是我么在启动电脑后,会发现jar的自动程序,由于涉及到定时任务,使用了TimerTask与Timer,那么程序会一直运行,自启动后,会出现java程序运行时的黑框。
这会严重的影响用户体验,每次开机一个黑框什么鬼,就算用户点击了最小化,底部的状态栏也会有一杯咖啡的图标,这样会让用户觉得不舒服。
一种解决办法是使用Jframe图形界面,将宽度和高度设置为接近0 ,这样产生了图形界面之后,黑框便会消失,使用GUI线程来执行任务,就解决了黑框问题。但是这种解决方式不太优雅。
优雅且简单的解决办法:
修改javaProgramsStart.bat,将 java -jar 改成 javaw -jar 就可以了,此时黑框问题也得到了解决。
更改前:
更改后:
至此开机不会再有黑框的困扰了。
到此,我们便可以实现自动下载壁纸,并且下载有日志进行记录,另外也会开机自启动,我们部署好了以后,就不用再去管它,而每天9:00会自动下载必应的精美图片,如果9:00电脑处于关机状态,那么当天我们开机后,系统会自动为我们补上下载,并且不会重复下载,保证每天一张,这真是十分美妙的一件事情。我们做的只需要将windows的个性化中的幻灯片放映设置文件夹为我们程序生产的wallpapers就ok了!
笔者等到了第二天,发现桌面壁纸有的新了,再次查看:
可见第二天又成功的下载了一张。
九、还能继续完善
到这里了,我们就已经可以直接使用这个程序了。但是运气不好,问题又来了。我们每次开机就会自动运行程序,当时间走到了程序该执行下载的时候,从程序的设定来看,如果我们那时电脑没网的话,便会产生一个error日志记录下来,里面则是装的错误日志。是的,仅仅就只有一个错误日志,里面记录的是一个找不到网络的异常。很遗憾,我们由于没网,就一个异常就算了,可是我们还是想要得到图片啊,那我们就继续完善。
解决办法:新增多个时刻,9:00、9:30、10:00…都来监听是否下载。我们每隔半小时,重复执行一次,直到产生下载成功的日志。这样来尽量保证我们每天都下载成功,而不会因为某一段时间刚好没网而错过了。这样只有检测到到下载成功的日志才会进行停止下载,而且由于通过命名检测的原因,error中的日志也会被覆盖,下载失败的日志始终每天最多只会有一条存在。下面给出修改方法:
在DownUtil中,我们上面通过检测error日志与成功日志来判断是否下载:
/**
* 检查当天的下载是否已经执行,防止重复下载
*
* @return
*/
private static boolean check() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String name = sdf.format(date);
File file1 = new File("D:\\wallpapers\\logs\\" + name + ".txt");
File file2 = new File("D:\\wallpapers\\errors\\" + name + ".txt");
if (file1.exists() || file2.exists()) {
return true;
}
return false;
}
那么这里我们就需要改一下,只检测下载成功的日志来判断:
/**
* 检查当天的下载是否已经执行,防止重复下载
*
* @return
*/
private static boolean check() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String name = sdf.format(date);
File file1 = new File("D:\\wallpapers\\logs\\" + name + ".txt");
if (file1.exists()) {
return true;
}
return false;
}
再次修改Main方法:
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Main {
public static void main(String[] args) {
downloadTimer();
}
public static void downloadTimer() {
// 设置执行时间
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);// 每天
Date date = calendar.getTime();
calendar.set(year, month, day, 9, 00, 00);
Timer timer = new Timer();
// 第一次执行
TimerTask task = new TimerTask() {
@Override
public void run() {
DownUtil.downWallPaper();
}
};
// 每天9:00时刻执行,每隔30分钟重复执行一次
int peroid = 30 * 60 * 1000;
timer.schedule(task, date, peroid);
}
}
这样每隔半小时,检测一次,尽量保证每天都能够下载成功。
GitHub 地址:https://github.com/zhoupengwa/WallPapers
感谢大家的观看!