版权声明:本文为天涯原创文章,未经天涯允许不得转载。 https://blog.csdn.net/tyyj90/article/details/48418543
一、背景介绍
关于FTP下载,FTP是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。"下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。
二、FTP配置
两部手机如何实现一部手机作为服务器,另一部手机作为客户端下载呢,服务端我们可以采用开源项目
http://ppareit.github.io/swiftp/,将这个源码运行在我们的手机上(称之为服务端),设置用户名、密码和作为FTP服务器的根目录(作为客户端下载的根路径)。这里需要注意一下我们可以用服务端这部手机建立一个热点,客户端通过 WiFi 的方式连接这个热点,此时可以看到swiftp上显示的主机地址和端口号,接下来需要在客户端的程序Config类里配置主机地址、端口号、用户名、密码和请求目录。当然也可以同时将客户机和服务机连入一个局域网实现FTP功能。我下面的测试方式就是采用将客户机和服务机同时连入一个局域网(连到同一个热点上)实现的,将nexus 5作为服务器运行swiftp,defy作为客户机运行DEMO程序进行图片下载。
下面是服务机运行截图,可以看到主机地址、端口号、配置用户名和密码、以及根路径。
以下为客户机运行截图:
此为下载到ftp文件夹下的图片文件,可以正常下载。
三、FTP实现
Android下实现FTP客户端下载,我使用了一个第三方封装的包commons-net-3.3.jar,Apache 上可以下载到commons-net这个jar ,里面封装了很多协议,有兴趣的可以去官网查看,下载对应的API文档研究。这里我只实现了图片的下载,并可以异步加载到listview,同时实现了滚动停止加载,停止滚动只加载当前屏条目,多图也不会卡顿崩溃。
commons-net-3.3.jar包含的协议:
- FTP/FTPS
- FTP over HTTP (experimental)
- NNTP
- SMTP(S)
- POP3(S)
- IMAP(S)
- Telnet
- TFTP
- Finger
- Whois
- rexec/rcmd/rlogin
- Time (rdate) and Daytime
- Echo
- Discard
- NTP/SNTP
将FTP相关代码封装成一个类,利用网上一个朋友写的代码,我扩充了一下,加入listfile函数;异步加载代码独立出来写成抽象类,方便其他代码以后复用,实现了本地加载图片类;实现滚动停止加载,停止滚动只加载当前屏条目,多图滑动不卡顿在Adapter 中实现,并非使用线程锁方式;同时引入线程池进行线程管理,防止太多线程造成资源极大浪费和程序崩溃。下面贴出核心代码。
配置类:
public class Config {
// 查看服务器软件后进行配置主机地址、端口号、用户名、密码
//public static final String hostName = "192.168.43.1";
public static final String hostName = "192.168.1.120";
public static final int serverPort = 2121;
public static final String userName = "ftp";
public static final String password = "ftp";
// 对应FTP服务器上的目录
public static final String FTP_SERVER_PATH = File.separator + "Pictures" + File.separator;
// 对应FTP服务器上的目录aa
public static final String FTP_PATH_AA = FTP_SERVER_PATH + File.separator + "Screenshots" + File.separator;
/**
* 软件使用路径配置
*/
public static final String APP_ROOT_PATH_RELATIVE = "com.example.ftpdemo";// 软件相对根路径
public static final String APP_ROOT_SD_PATH_ABSOLUTE = SDCardUtil.getSDCardPath() + APP_ROOT_PATH_RELATIVE + File.separator;// 软件绝对SD根路径
public static final String FTP_PATH_RELATIVE = "ftp"; // ftp下载相对路径
}
/**
* @Description TODO 异步图片加载,抽象父类,需子类实现他的方法loadImage(final String url)
*/
public abstract class AsyncImageLoader {
private HashMap<String, SoftReference<Drawable>> imageCache;
// 创建线程池对内存进行优化处理
private ExecutorService executorService;
public AsyncImageLoader() {
imageCache = new HashMap<String, SoftReference<Drawable>>();
// 最大5条线程同时执行
executorService = Executors.newFixedThreadPool(5);
}
@SuppressLint("HandlerLeak")
public Drawable loadDrawable(final int id, final String imagePath, final ImageCallback imageCallback) {
if (imageCache.containsKey(imagePath)) {
SoftReference<Drawable> softReference = imageCache.get(imagePath);
if (softReference != null) {
Drawable drawable = softReference.get();
if (drawable != null) {
imageCallback.imageLoaded(drawable, id);
return drawable;
}
}
}
final Handler handler = new Handler() {
public void handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, id);
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
Drawable drawable = loadImage(imagePath);
imageCache.put(imagePath, new SoftReference<Drawable>(drawable));
Message message = handler.obtainMessage(Constant.IMAGE_LOAD_SUCCESS, drawable);
handler.sendMessage(message);
}
};
executorService.execute(runnable);
return null;
}
protected abstract Drawable loadImage(final String url);
public interface ImageCallback {
public void imageLoaded(Drawable imageDrawable, int id);
}
}
/**
* @Description TODO 继承抽象父类,实现获取图片方法,从文件路径中加载图片
*/
public class PathAsyncImageLoader extends AsyncImageLoader {
private Context context;
private int imgWidth, imgHeight;
/**
* @param context 上下文
* @param imgWidth 图片宽度
* @param imgHeight 图片高度
*/
public PathAsyncImageLoader(Context context, int imgWidth, int imgHeight) {
this.context = context;
this.imgWidth = imgWidth;
this.imgHeight = imgHeight;
}
@Override
protected Drawable loadImage(String url) {
if (imgWidth <= 0) {
imgWidth = context.getResources().getDimensionPixelSize(R.dimen.ftp_img_width);
}
if (imgHeight <= 0) {
imgHeight = context.getResources().getDimensionPixelSize(R.dimen.ftp_img_height);
}
return CommonUtil.bitmap2Drawable(CommonUtil.getBitmapFromPath(url, imgWidth, imgHeight), context);
}
}
FTP工具类:
package com.example.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;
import org.apache.commons.net.ftp.FTPReply;
import android.util.Log;
import com.example.constant.Config;
import com.example.constant.Constant;
import com.example.other.ProgressInputStream;
public class FtpUtil {
private final static String TAG = "FtpUtil";
private final static boolean DEBUG = true;
/**
* 服务器名.
*/
private String hostName;
/**
* 端口号
*/
private int serverPort;
/**
* 用户名.
*/
private String userName;
/**
* 密码.
*/
private String password;
/**
* FTP连接.
*/
private FTPClient ftpClient;
public FtpUtil() {
hostName = Config.hostName;
serverPort = Config.serverPort;
userName = Config.userName;
password = Config.password;
ftpClient = new FTPClient();
}
// -------------------------------------------------------文件上传方法------------------------------------------------
/**
* 上传单个文件.
*
* @param singleFile
* 本地文件
* @param remotePath
* FTP目录
* @param listener
* 监听器
* @throws IOException
*/
public void uploadSingleFile(File singleFile, String remotePath, FtpProgressListener listener) throws IOException {
// 上传之前初始化
this.uploadBeforeOperate(remotePath, listener);
boolean flag;
flag = uploadingSingle(singleFile, listener);
if (flag) {
listener.onFtpProgress(Constant.FTP_UPLOAD_SUCCESS, 0, singleFile);
} else {
listener.onFtpProgress(Constant.FTP_UPLOAD_FAIL, 0, singleFile);
}
// 上传完成之后关闭连接
this.uploadAfterOperate(listener);
}
/**
* 上传多个文件.
*
* @param fileList
* 本地文件List<File>
* @param remotePath
* FTP目录
* @param listener
* 监听器
* @throws IOException
*/
public void uploadMultiFile(LinkedList<File> fileList, String remotePath, FtpProgressListener listener) throws IOException {
// 上传之前初始化
this.uploadBeforeOperate(remotePath, listener);
boolean flag;
for (File singleFile : fileList) {
flag = uploadingSingle(singleFile, listener);
if (flag) {
listener.onFtpProgress(Constant.FTP_UPLOAD_SUCCESS, 0, singleFile);
} else {
listener.onFtpProgress(Constant.FTP_UPLOAD_FAIL, 0, singleFile);
}
}
// 上传完成之后关闭连接
this.uploadAfterOperate(listener);
}
/**
* 上传单个文件.
*
* @param localFile
* 本地文件
* @return true上传成功, false上传失败
* @throws IOException
*/
private boolean uploadingSingle(File localFile, FtpProgressListener listener) throws IOException {
boolean flag = true;
/** 不带进度的方式 */
// // 创建输入流
// InputStream inputStream = new FileInputStream(localFile);
// // 上传单个文件
// flag = ftpClient.storeFile(localFile.getName(), inputStream);
// // 关闭文件流
// inputStream.close();
// 带有进度的方式
BufferedInputStream buffIn = new BufferedInputStream(new FileInputStream(localFile));
ProgressInputStream progressInput = new ProgressInputStream(buffIn, listener, localFile);
flag = ftpClient.storeFile(localFile.getName(), progressInput);
buffIn.close();
return flag;
}
/**
* 上传文件之前初始化相关参数
*
* @param remotePath
* FTP目录
* @param listener
* 监听器
* @throws IOException
*/
private void uploadBeforeOperate(String remotePath, FtpProgressListener listener) throws IOException {
// 打开FTP服务
try {
this.openConnect();
listener.onFtpProgress(Constant.FTP_CONNECT_SUCCESS, 0, null);
} catch (IOException e1) {
e1.printStackTrace();
listener.onFtpProgress(Constant.FTP_CONNECT_FAIL, 0, null);
return;
}
// 设置模式
ftpClient.setFileTransferMode(org.apache.commons.net.ftp.FTP.STREAM_TRANSFER_MODE);
// FTP下创建文件夹
ftpClient.makeDirectory(remotePath);
// 改变FTP目录
ftpClient.changeWorkingDirectory(remotePath);
// 上传单个文件
}
/**
* 上传完成之后关闭连接
*
* @param listener
* @throws IOException
*/
private void uploadAfterOperate(FtpProgressListener listener) throws IOException {
this.closeConnect();
listener.onFtpProgress(Constant.FTP_DISCONNECT_SUCCESS, 0, null);
}
// -------------------------------------------------------文件下载方法------------------------------------------------
/**
* 下载单个文件,可实现断点下载.
*
* @param serverPath
* Ftp目录及文件路径
* @param localPath
* 本地目录
* @param fileName
* 下载之后的文件名称
* @param listener
* 监听器
* @throws IOException
*/
public void downloadSingleFile(String serverPath, String localPath, String fileName, FtpProgressListener listener) throws Exception {
// 打开FTP服务
try {
this.openConnect();
listener.onFtpProgress(Constant.FTP_CONNECT_SUCCESS, 0, null);
} catch (IOException e1) {
e1.printStackTrace();
listener.onFtpProgress(Constant.FTP_CONNECT_FAIL, 0, null);
return;
}
// 先判断服务器文件是否存在
FTPFile[] files = ftpClient.listFiles(serverPath);
if (files.length == 0) {
listener.onFtpProgress(Constant.FTP_FILE_NOTEXISTS, 0, null);
return;
}
// 创建本地文件夹
File mkFile = new File(localPath);
if (!mkFile.exists()) {
mkFile.mkdirs();
}
localPath = localPath + fileName;
// 接着判断下载的文件是否能断点下载
long serverSize = files[0].getSize(); // 获取远程文件的长度
File localFile = new File(localPath);
long localSize = 0;
if (localFile.exists()) {
localSize = localFile.length(); // 如果本地文件存在,获取本地文件的长度
if (localSize >= serverSize) {
File file = new File(localPath);
file.delete();
}
}
// 进度
long step = serverSize / 100;
long process = 0;
long currentSize = 0;
// 开始准备下载文件
OutputStream out = new FileOutputStream(localFile, true);
ftpClient.setRestartOffset(localSize);
InputStream input = ftpClient.retrieveFileStream(serverPath);
byte[] b = new byte[1024];
int length = 0;
while ((length = input.read(b)) != -1) {
out.write(b, 0, length);
currentSize = currentSize + length;
if (currentSize / step != process) {
process = currentSize / step;
if (process % 5 == 0) { // 每隔%5的进度返回一次
listener.onFtpProgress(Constant.FTP_DOWN_LOADING, process, null);
}
}
}
out.flush();
out.close();
input.close();
// 此方法是来确保流处理完毕,如果没有此方法,可能会造成现程序死掉
if (ftpClient.completePendingCommand()) {
listener.onFtpProgress(Constant.FTP_DOWN_SUCCESS, 0, new File(localPath));
} else {
listener.onFtpProgress(Constant.FTP_DOWN_FAIL, 0, null);
}
// 下载完成之后关闭连接
this.closeConnect();
listener.onFtpProgress(Constant.FTP_DISCONNECT_SUCCESS, 0, null);
return;
}
// -------------------------------------------------------文件删除方法------------------------------------------------
/**
* 删除Ftp下的文件.
*
* @param serverPath
* Ftp目录及文件路径
* @param listener
* 监听器
* @throws IOException
*/
public void deleteSingleFile(String serverPath, FtpDeleteFileListener listener) throws Exception {
// 打开FTP服务
try {
this.openConnect();
listener.onFtpDelete(Constant.FTP_CONNECT_SUCCESS);
} catch (IOException e1) {
e1.printStackTrace();
listener.onFtpDelete(Constant.FTP_CONNECT_FAIL);
return;
}
// 先判断服务器文件是否存在
FTPFile[] files = ftpClient.listFiles(serverPath);
if (files.length == 0) {
listener.onFtpDelete(Constant.FTP_FILE_NOTEXISTS);
return;
}
// 进行删除操作
boolean flag = true;
flag = ftpClient.deleteFile(serverPath);
if (flag) {
listener.onFtpDelete(Constant.FTP_DELETEFILE_SUCCESS);
} else {
listener.onFtpDelete(Constant.FTP_DELETEFILE_FAIL);
}
// 删除完成之后关闭连接
this.closeConnect();
listener.onFtpDelete(Constant.FTP_DISCONNECT_SUCCESS);
return;
}
// -------------------------------------------------------获取FTP服务器文件-----------------------------------------
/**
* @Description TODO 获取指定服务器指定目录下的文件
* @param serverPath
* 服务器路径
* @param filter
* 过滤器
* @param listener
* 监听器
* @return 返回FTPFile
* @throws Exception
*/
public List<FTPFile> listsFiles(String serverPath, FTPFileFilter filter, FtpListFileListener listener) throws Exception {
List<FTPFile> ftpFileList = new ArrayList<FTPFile>();
// 打开FTP服务
try {
this.openConnect();
listener.onFtpListFile(Constant.FTP_CONNECT_SUCCESS, ftpFileList);
if (DEBUG) {
Log.d(TAG, "--FTP_CONNECT_SUCCESS--");
}
} catch (IOException e1) {
e1.printStackTrace();
listener.onFtpListFile(Constant.FTP_CONNECT_FAIL, ftpFileList);
return null;
}
FTPFile[] ftpFiles = ftpClient.listFiles(serverPath, filter);
if (0 == ftpFiles.length) {
listener.onFtpListFile(Constant.FTP_LISTFILE_FAIL, ftpFileList);
} else {
if (DEBUG) {
Log.d(TAG, "--FTP_LISTFILE_SUCCESS--");
for (FTPFile ftpfile : ftpFiles) {
Log.d(TAG, "ftpfile = " + ftpfile.getName());
}
}
for (FTPFile ftpfile : ftpFiles) {
ftpFileList.add(ftpfile);
}
listener.onFtpListFile(Constant.FTP_LISTFILE_SUCCESS, ftpFileList);
}
// 关闭连接
this.closeConnect();
listener.onFtpListFile(Constant.FTP_DISCONNECT_SUCCESS, ftpFileList);
return ftpFileList;
}
// -------------------------------------------------------打开关闭连接------------------------------------------------
/**
* 打开FTP服务.
*
* @throws IOException
*/
public void openConnect() throws IOException {
// 中文转码
ftpClient.setControlEncoding("UTF-8");
int reply; // 服务器响应值
// 连接至服务器
ftpClient.connect(hostName, serverPort);
// 获取响应值
reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
// 断开连接
ftpClient.disconnect();
throw new IOException("connect fail: " + reply);
}
// 登录到服务器
ftpClient.login(userName, password);
// 获取响应值
reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
// 断开连接
ftpClient.disconnect();
throw new IOException("connect fail: " + reply);
} else {
// 获取登录信息
FTPClientConfig config = new FTPClientConfig(ftpClient.getSystemType().split(" ")[0]);
config.setServerLanguageCode("zh");
ftpClient.configure(config);
// 使用被动模式设为默认
ftpClient.enterLocalPassiveMode();
// 二进制文件支持
ftpClient.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE);
}
}
/**
* 关闭FTP服务.
*
* @throws IOException
*/
public void closeConnect() throws IOException {
if (ftpClient != null) {
// 退出FTP
ftpClient.logout();
// 断开连接
ftpClient.disconnect();
}
}
// ---------------------------------------------------上传下载进度、删除、获取文件监听---------------------------------------------
/*
* 进度监听器
*/
public interface FtpProgressListener {
/**
*
* @Description TODO FTP 文件长传下载进度触发
* @param currentStatus
* 当前FTP状态
* @param process
* 当前进度
* @param targetFile
* 目标文件
*/
public void onFtpProgress(int currentStatus, long process, File targetFile);
}
/*
* 文件删除监听
*/
public interface FtpDeleteFileListener {
/**
*
* @Description TODO 删除FTP文件
* @param currentStatus
* 当前FTP状态
*/
public void onFtpDelete(int currentStatus);
}
/*
* 获取文件监听
*/
public interface FtpListFileListener {
/**
*
* @Description TODO 列出FTP文件
* @param currentStatus
* 当前FTP状态
* @param ftpFileList
* 获取的List<FTPFile>
*/
public void onFtpListFile(int currentStatus, List<FTPFile> ftpFileList);
}
}
package com.example.ftpdemo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilters;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import com.example.adapter.FtpAdapter;
import com.example.base.BaseActivity;
import com.example.constant.Config;
import com.example.constant.Constant;
import com.example.constant.DownLoadStatus;
import com.example.entity.ListItemFtpObj;
import com.example.other.PathAsyncImageLoader;
import com.example.utils.CommonUtil;
import com.example.utils.FileUtil;
import com.example.utils.FtpUtil;
import com.example.utils.FtpUtil.FtpListFileListener;
import com.example.utils.FtpUtil.FtpProgressListener;
public class MainActivity extends BaseActivity {
private final static String TAG = "MainActivity";
private final static boolean DEBUG = true;
private Button rightBtn;
private ListView ftpListView;
private FtpAdapter ftpAdapter;
private List<ListItemFtpObj> itemFtpObjList;
private PathAsyncImageLoader asyncImageLoader;
private Context context = MainActivity.this;
private List<FTPFile> ftpFiles = new ArrayList<FTPFile>();
// 当前正在下载图片个数
private int downloadCounter = 0;
// 当前是否处于下载模式
private boolean isDownloadMode = false;
// 创建线程池对内存进行优化处理
private ExecutorService executorService;
private ProgressDialog pd;
private static Boolean isExitSystemFlag = false; // 退出应用标志
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.FTP_LISTFILE_SUCCESS + Constant.MESSAGE_OFFSET:
if (DEBUG) {
Log.d(TAG, "--receive FTP_LISTFILE_SUCCESS + MESSAGE_OFFSET--");
}
convertFtpFile2ListItemFtpObj();
// 设置adapter
ftpListView.setAdapter(ftpAdapter);
if (pd.isShowing()) {
pd.dismiss();
}
break;
case Constant.FTP_LISTFILE_FAIL + Constant.MESSAGE_OFFSET:
showShortTimeToast(context.getString(R.string.download_tip_get_file_fail));
break;
case Constant.FTP_DOWN_SUCCESS + Constant.MESSAGE_OFFSET:
int temp = 0;
for (ListItemFtpObj listItemFtpObj : itemFtpObjList) {
if (listItemFtpObj.getDownLoadStatus() == DownLoadStatus.FINISH) {
temp++;
}
}
if (downloadCounter == temp) {
downloadCounter = 0;
temp = 0;
isDownloadMode = false;
showShortTimeToast(context.getString(R.string.download_tip_file_download_finish));
}
ftpAdapter.notifyDataSetChanged();
break;
case Constant.FTP_DOWN_FAIL + Constant.MESSAGE_OFFSET:
showShortTimeToast(context.getString(R.string.download_tip_file_download_error));
break;
case Constant.FTP_DOWN_LOADING + Constant.MESSAGE_OFFSET:
ftpAdapter.notifyDataSetChanged();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListener();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (pd.isShowing()) {
pd.dismiss();
}
}
private void initView() {
rightBtn = (Button) findViewById(R.id.btn_right_topbar);
ftpListView = (ListView) findViewById(R.id.lv_activity_ftp);
rightBtn.setVisibility(View.GONE);
rightBtn.setText(context.getString(R.string.download));
pd = new ProgressDialog(context);
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setMessage(context.getString(R.string.please_wait));
pd.setCanceledOnTouchOutside(false);
pd.show();
// 最大五条线程同时执行
executorService = Executors.newFixedThreadPool(5);
getData2FitListView();
asyncImageLoader = new PathAsyncImageLoader(context, context.getResources().getDimensionPixelSize(R.dimen.ftp_img_width), context
.getResources().getDimensionPixelSize(R.dimen.ftp_img_height));
ftpAdapter = new FtpAdapter(itemFtpObjList, asyncImageLoader, ftpListView, context);
}
/**
* @Description TODO 从ftp服务器获取file List
*/
private void getData2FitListView() {
itemFtpObjList = new ArrayList<ListItemFtpObj>();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
new FtpUtil().listsFiles(Config.FTP_PATH_AA, FTPFileFilters.NON_NULL, new FtpListFileListener() {
@Override
public void onFtpListFile(int currentStatus, List<FTPFile> ftpFileList) {
switch (currentStatus) {
case Constant.FTP_LISTFILE_SUCCESS:
ftpFiles.addAll(ftpFileList);
handler.sendEmptyMessage(Constant.FTP_LISTFILE_SUCCESS + Constant.MESSAGE_OFFSET);
if (DEBUG) {
Log.d(TAG, "--sendEmptyMessage FTP_LISTFILE_SUCCESS + MESSAGE_OFFSET--");
}
break;
case Constant.FTP_LISTFILE_FAIL:
handler.sendEmptyMessage(Constant.FTP_LISTFILE_FAIL + Constant.MESSAGE_OFFSET);
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
}
/**
* @Description TODO
* @param serverPath
* 服务器路径(不带文件名)
* @param listItemFtpObj
* ListItemFtpObj对象
* @param indexInList
* List 中的索引
*/
private void downloadFileFromFtpServer(String serverPath, final ListItemFtpObj listItemFtpObj, final int indexInList) {
final String serverPathWithFileName = serverPath + listItemFtpObj.getFtpFile().getName();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
// 单文件下载
new FtpUtil().downloadSingleFile(serverPathWithFileName, CommonUtil.getAbsoluteEnvironmentRootPath(context)
+ Config.FTP_PATH_RELATIVE + File.separator, listItemFtpObj.getFtpFile().getName(), new FtpProgressListener() {
@Override
public void onFtpProgress(int currentStatus, long process, File targetFile) {
switch (currentStatus) {
case Constant.FTP_DOWN_SUCCESS:
if (CommonUtil.verifyFile(CommonUtil.getAbsoluteEnvironmentRootPath(context) + Config.FTP_PATH_RELATIVE
+ File.separator + itemFtpObjList.get(indexInList).getFtpFile().getName(), itemFtpObjList
.get(indexInList).getFtpFile().getSize())) {
itemFtpObjList.get(indexInList).setDownLoadStatus(DownLoadStatus.FINISH);
itemFtpObjList.get(indexInList).setProgress(100);
handler.sendEmptyMessage(Constant.FTP_DOWN_SUCCESS + Constant.MESSAGE_OFFSET);
} else {
FileUtil.deleteFile(CommonUtil.getAbsoluteEnvironmentRootPath(context) + Config.FTP_PATH_RELATIVE
+ File.separator + itemFtpObjList.get(indexInList).getFtpFile().getName());
// 重新下载
downloadFileFromFtpServer(Config.FTP_PATH_AA, itemFtpObjList.get(indexInList), indexInList);
}
break;
case Constant.FTP_DOWN_FAIL:
handler.sendEmptyMessage(Constant.FTP_DOWN_FAIL + Constant.MESSAGE_OFFSET);
break;
case Constant.FTP_DOWN_LOADING:
itemFtpObjList.get(indexInList).setDownLoadStatus(DownLoadStatus.DOWNLOADING);
itemFtpObjList.get(indexInList).setProgress((int) process);
handler.sendEmptyMessage(Constant.FTP_DOWN_LOADING + Constant.MESSAGE_OFFSET);
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
}
/**
* @Description TODO 将ftp获取的FTPFile 转化为 ListItemFtpObj
*/
private void convertFtpFile2ListItemFtpObj() {
for (int i = 0; i < ftpFiles.size(); i++) {
// 判断是否为文件
if (FTPFile.FILE_TYPE == ftpFiles.get(i).getType()) {
ListItemFtpObj listItemFtpObj = new ListItemFtpObj();
listItemFtpObj.setFtpFile(ftpFiles.get(i));
listItemFtpObj.setChecked(false); // 默认未选择
if (CommonUtil.verifyFile(CommonUtil.getAbsoluteEnvironmentRootPath(context) + Config.FTP_PATH_RELATIVE + File.separator
+ ftpFiles.get(i).getName(), ftpFiles.get(i).getSize())) {
listItemFtpObj.setDownLoadStatus(DownLoadStatus.EXIST);
listItemFtpObj.setProgress(100);
} else {
FileUtil.deleteFile(CommonUtil.getAbsoluteEnvironmentRootPath(context) + Config.FTP_PATH_RELATIVE + File.separator
+ ftpFiles.get(i).getName());
listItemFtpObj.setDownLoadStatus(DownLoadStatus.NODOWNLOAD);
listItemFtpObj.setProgress(0);
}
itemFtpObjList.add(listItemFtpObj);
}
}
if (DEBUG) {
for (ListItemFtpObj listItemFtpObj : itemFtpObjList) {
Log.d(TAG, "listItemFtpObj =" + listItemFtpObj.toString());
}
}
}
private void setListener() {
rightBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
rightBtn.setVisibility(View.GONE);
isDownloadMode = true;
// 开始下载任务,恢复为未选中状态并显示进度条
for (int i = 0; i < itemFtpObjList.size(); i++) {
if (true == itemFtpObjList.get(i).isChecked()) {
downloadFileFromFtpServer(Config.FTP_PATH_AA, itemFtpObjList.get(i), i);
itemFtpObjList.get(i).setDownLoadStatus(DownLoadStatus.WAITTIINGDOWNLOAD);
itemFtpObjList.get(i).setChecked(false);
}
}
ftpAdapter.notifyDataSetChanged();
}
});
ftpListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (!isDownloadMode) {
if (itemFtpObjList.get(position).isChecked()) {
itemFtpObjList.get(position).setChecked(false);
} else {
itemFtpObjList.get(position).setChecked(true);
}
ftpAdapter.notifyDataSetChanged();
// 检索需要下载文件的个数
downloadCounter = 0;
for (ListItemFtpObj listItemFtpObj : itemFtpObjList) {
if (listItemFtpObj.isChecked() && DownLoadStatus.NODOWNLOAD == listItemFtpObj.getDownLoadStatus()) {
downloadCounter++;
}
}
if (downloadCounter > 0) {
rightBtn.setVisibility(View.VISIBLE);
} else {
rightBtn.setVisibility(View.GONE);
}
if (DEBUG) {
Log.d(TAG, "onItemClick downloadCounter=" + downloadCounter);
}
}
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
exitAppByDoubleClick();
}
return false;
}
/**
* @Description TODO 双击退出函数
*/
private void exitAppByDoubleClick() {
Timer timer = null;
if (isExitSystemFlag == false) {
isExitSystemFlag = true; // 准备退出
showShortTimeToast(getResources().getString(R.string.double_click_to_exit_app));
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
isExitSystemFlag = false; // 取消退出
}
}, 2000); // 如果2秒钟内没有按下返回键,则启动定时器取消掉刚才执行的任务
} else {
Intent intent = new Intent();
intent.setAction(Constant.SYSTEM_EXIT);
sendBroadcast(intent);
}
}
}
适配器类:
package com.example.adapter;
import java.io.File;
import java.util.List;
import com.example.constant.Config;
import com.example.constant.DownLoadStatus;
import com.example.entity.ListItemFtpObj;
import com.example.ftpdemo.R;
import com.example.other.AsyncImageLoader;
import com.example.other.AsyncImageLoader.ImageCallback;
import com.example.utils.CommonUtil;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class FtpAdapter extends BaseAdapter {
public final static String TAG = "FtpAdapter";
public final static boolean DEBUG = true;
private List<ListItemFtpObj> list;
private Context context;
private AsyncImageLoader asyncImageLoader;
private ListView listView;
private boolean first = true; // 是否首屏,首次进入
public FtpAdapter(List<ListItemFtpObj> list, AsyncImageLoader asyncImageLoader, ListView listView, Context context) {
this.list = list;
this.asyncImageLoader = asyncImageLoader;
this.listView = listView;
this.context = context;
listView.setOnScrollListener(onScrollListener);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (null == convertView) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
convertView = layoutInflater.inflate(R.layout.listview_ftp_item, null);
viewHolder = new ViewHolder();
viewHolder.imageViewImg = (ImageView) convertView.findViewById(R.id.iv_ftp_item);
viewHolder.imageViewSelect = (ImageView) convertView.findViewById(R.id.iv_duigou_ftp_item);
viewHolder.textViewName = (TextView) convertView.findViewById(R.id.tv_name_ftp_item);
viewHolder.textViewDownloadStatus = (TextView) convertView.findViewById(R.id.tv_down_status_ftp_item);
viewHolder.progressBarDownload = (ProgressBar) convertView.findViewById(R.id.pb_ftp_item);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (DEBUG) {
Log.d(TAG, "FTPFile name = " + list.get(position).getFtpFile().getName());
}
viewHolder.imageViewImg.setTag(position);
// 其他控件显示逻辑
viewHolder.textViewName.setTextColor(Color.BLACK);
viewHolder.textViewName.setText(list.get(position).getFtpFile().getName());
viewHolder.imageViewSelect.setVisibility(View.GONE);
viewHolder.progressBarDownload.setVisibility(View.GONE);
if (DownLoadStatus.NODOWNLOAD == list.get(position).getDownLoadStatus()) {
viewHolder.textViewDownloadStatus.setText(context.getString(R.string.download_status_no_download));
if (list.get(position).isChecked()) {
viewHolder.imageViewSelect.setVisibility(View.VISIBLE);
}
viewHolder.imageViewImg.setImageResource(R.drawable.ic_launcher);
} else {
viewHolder.progressBarDownload.setVisibility(View.VISIBLE);
viewHolder.progressBarDownload.setMax(100);// 最大值以100计
viewHolder.progressBarDownload.setProgress(list.get(position).getProgress());
if (DownLoadStatus.WAITTIINGDOWNLOAD == list.get(position).getDownLoadStatus()) {
viewHolder.textViewDownloadStatus.setText(context.getString(R.string.download_status_waiting));
} else if (DownLoadStatus.DOWNLOADING == list.get(position).getDownLoadStatus()) {
viewHolder.textViewDownloadStatus.setText(context.getString(R.string.download_status_downloading));
} else if (DownLoadStatus.FINISH == list.get(position).getDownLoadStatus()) {
viewHolder.textViewDownloadStatus.setText(context.getString(R.string.download_status_finish));
// 下载完成置灰
viewHolder.textViewName.setTextColor(Color.GRAY);
// 下载完成隐藏进度条
viewHolder.progressBarDownload.setVisibility(View.GONE);
// 下载完成开启一条线程重新加载图片
loadImg2Item(position);
} else if (DownLoadStatus.EXIST == list.get(position).getDownLoadStatus()) {
viewHolder.textViewDownloadStatus.setText(context.getString(R.string.download_status_finish));
viewHolder.textViewName.setTextColor(Color.GRAY);
viewHolder.progressBarDownload.setVisibility(View.GONE);
}
}
return convertView;
}
/**
* 屏幕停止滚动监听器
*/
AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
pageImgLoad();
}
if (DEBUG) {
Log.d(TAG, "onScroll scrollState=" + scrollState);
}
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (DEBUG) {
Log.d(TAG, "onScroll firstVisibleItem=" + firstVisibleItem + ",visibleItemCount=" + visibleItemCount + ",totalItemCount="
+ totalItemCount);
}
// 保证首屏始正常显示
if (0 == firstVisibleItem && 0 != visibleItemCount && 0 != totalItemCount && totalItemCount == getCount()) {
if (first) {
for (; firstVisibleItem <= visibleItemCount; firstVisibleItem++) {
loadImg2Item(firstVisibleItem);
}
first = false;
}
}
}
};
/**
* 加载当前屏的图片,首屏不使用
*/
private void pageImgLoad() {
int start_index = listView.getFirstVisiblePosition();
int end_index = listView.getLastVisiblePosition();
if (end_index >= getCount()) {
end_index = getCount() - 1;
}
if (DEBUG) {
Log.d(TAG, "getCount()=" + getCount() + ",start_index=" + start_index + ",end_index=" + end_index);
}
for (; start_index <= end_index; start_index++) {
loadImg2Item(start_index);
}
}
/**
* @Description TODO 异步将图片加载到ITME显示
* @param position
*/
private void loadImg2Item(int position) {
String path = CommonUtil.getAbsoluteEnvironmentRootPath(context) + Config.FTP_PATH_RELATIVE + File.separator
+ list.get(position).getFtpFile().getName();
// 异步从Local Path 中加载图片时,校验数据是否完整
if (DEBUG) {
Log.d(TAG, "verifyfile = " + CommonUtil.verifyFile(path, list.get(position).getFtpFile().getSize()));
}
if (CommonUtil.verifyFile(path, list.get(position).getFtpFile().getSize())) {
asyncImageLoader.loadDrawable(position, path, imageCallback);
}
}
/** 回调函数 */
AsyncImageLoader.ImageCallback imageCallback = new ImageCallback() {
public void imageLoaded(Drawable image, int position) {
if (image != null) {
// 获取刚才标识的组件,并更新
ImageView imageView = (ImageView) listView.findViewWithTag(position);
if (imageView != null) {
imageView.setImageDrawable(image);
}
}
}
};
class ViewHolder {
public ImageView imageViewImg;
public TextView textViewName;
public ImageView imageViewSelect;
public TextView textViewDownloadStatus;
public ProgressBar progressBarDownload;
}
}
其他代码见DEMO程序。