Android实现FTP客户端服务端多图下载【两部android手机之间传输】

版权声明:本文为天涯原创文章,未经天涯允许不得转载。 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);
	}
	
}

Activty类:
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程序。

四、DEMO下载


点此下载

猜你喜欢

转载自blog.csdn.net/tyyj90/article/details/48418543