最近在研究ftp的多任务、多线程、后台下载的相关功能实现,由于每个任务都是点击下载按钮后开启独立的线程进行下载,同时还需要处理各种暂停、完成、继续下载等。adapter通常都会对holder进行复用,所以当列表滑动的时候,如果上面有一个暂停或者已经完成的任务,那么下面的列表就会复用到上面的holder,从而会导致相关功能和列表显示错乱的问题,如果要解决这个问题那么必须要处理几个问题:
- 列表复用的时候必须要将前面任务(主要是正在下载和已经完成从)的状态清除掉(比如停止下载中任务的进度更新)
- 列表复用结束后必须马上恢复到原来的状态
遇到的问题主要就是以下几个:
- 如果前面有一个任务正在下载,那么列表向下滑动的时候没有开始的下载任务状态却变成了正在下载那个任务的状态;
- 如果前面有一个任务已经下载完成,那么列表向下滑动的时候没用开始的下载任务状态却变成了已完成。
- 下载按钮功能错乱的问题
解决的方法其实也很简单,不过要是没想到的话也够折腾的,就是在holder复用的时候对正在下载的任务停止进度刷新,也就是移除hanlder,同时重新设置下载按钮的监听和初始化进度和文字描述。这里每个下载任务都用一个hash键值对保存了起来,状态等信息都保存在一个文件信息类中。所以所有的文件状态都是全局保存的。
下面是adpter里面的处理
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.ftp.FTPFile;
import android.R.bool;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.android.newnotebook.Date;
import com.example.listenclass.R;
import com.listenclass.common.Constant;
import com.listenclass.common.FileInfo;
import com.listenclass.common.IRetrieveListener;
import com.listenclass.db.DownloadDBManager;
import com.listenclass.ftpdown.FTPCfg;
import com.listenclass.ftpdown.FTPManager;
import com.listenclass.util.Util;
import com.llistenclass.service.DownloadService;
/**
* FTP列表适配器.
*
*
*/
public class RemoteAdapter extends BaseAdapter {
/**
* FTP文件列表.
*/
private List<FTPFile> list = new ArrayList<FTPFile>();
private FTPCfg cfg;
private static final String LOCAL_PATH = Environment.getExternalStorageDirectory()+"/ftpdownload/";
/**
* 布局.
*/
private LayoutInflater inflater;
private Context mContext;
private ListView mListView;
/**
* 文件夹显示图片.
*/
private Bitmap icon1;
/**
* 文件显示图片.
*/
private Bitmap icon2;
// private boolean mispause = false;
/**
* 构造函数.
* @param context 当前环境
* @param li FTP文件列表
*/
public RemoteAdapter(Context context, List<FTPFile> li,FTPCfg fp,ListView listView) {
mContext = context;
cfg = fp;
this.list = li;
this.inflater = LayoutInflater.from(context);
mListView = listView;
// 文件夹显示图片
// icon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.folder);
// // 文件显示图片
// icon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.doc);
}
@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 view, ViewGroup parent) {
ViewHolder holder= null;
if (view == null)
{
// 设置视图
view = this.inflater.inflate(R.layout.item_file_download, null);
// 获取控件实例
holder = new ViewHolder();
// holder.icon = (ImageView) view.findViewById(R.id.image_icon);
holder.tvdownloadstate = (TextView) view.findViewById(R.id.txt_download_state);
holder.fileName = (TextView) view.findViewById(R.id.txt_download_filename);
holder.fileSize = (TextView) view.findViewById(R.id.txt_download_filesize);
holder.beginpause = (ImageButton) view.findViewById(R.id.imgbtn_filedownload_opreation);
holder.mProgressBar = (ProgressBar) view.findViewById(R.id.proBar_download_peogress);
// 设置标签
view.setTag(holder);
}
else
{
// 获取标签
holder = (ViewHolder) view.getTag();
}
// 获取文件名
holder.fileName.setText(list.get(position).getName());
holder.tvdownloadstate.setText("");
//文件分为四种状态,在列表显示数据时分别判断再处理
if (Constant.filestates.get(list.get(position).getName()).getState() == Constant.FILE_DOWNPAUSE)
{
long completesize = Constant.filestates.get(list.get(position).getName()).getCompleteSize();
long totalsize = Constant.filestates.get(list.get(position).getName()).getFileSize();
String downloadpro = Util.getFormatSize(completesize) + "/"+Util.getFormatSize(totalsize);
holder.setPosition(position);
holder.beginpause.setBackgroundResource(R.drawable.file_download_start);
holder.fileSize.setText(downloadpro);
holder.mProgressBar.setProgress(Constant.filestates.get(list.get(position).getName()).getProgress());
}
else if(Constant.filestates.get(list.get(position).getName()).getState() == Constant.FILE_DOWNLOADING)
{
holder.beginpause.setBackgroundResource(R.drawable.file_download_pause);
holder.downloadingInit(position);
}
else if (Constant.filestates.get(list.get(position).getName()).getState() == Constant.FILE_DOWNOVER)
{
holder.reInit(position,false);
long totalsize = Constant.filestates.get(list.get(position).getName()).getFileSize();
holder.fileSize.setText("");
holder.tvdownloadstate.setText("已完成"+Util.getFormatSize(totalsize));
holder.beginpause.setBackgroundResource(R.drawable.file_download_complete);
holder.mProgressBar.setProgress(Constant.filestates.get(list.get(position).getName()).getProgress());
}
else if (Constant.filestates.get(list.get(position).getName()).getState() == Constant.FILE_NOT_DOWNLOAD)
{
System.out.println("---position " +position +"--------name--------"+list.get(position).getName());
System.out.println("---------state-------"+Constant.filestates.get(list.get(position).getName()).getState());
holder.fileSize.setText(Util.getFormatSize(list.get(position).getSize()));
//holder.beginpause.setOnClickListener();
holder.reInit(position,false);
holder.beginpause.setBackgroundResource(R.drawable.file_download_start);
holder.mProgressBar.setProgress(0);
}
System.out.println("------------------>getview position "+position+
" ------>firstvisibleposition "+mListView.getFirstVisiblePosition() + "---> filename "
+list.get(mListView.getFirstVisiblePosition()).getName());
return view;
}
@SuppressLint("NewApi")
private void showToolsMenu(PopupWindow mPopupWindow,View locateview) {
View view = LayoutInflater.from(mContext).inflate(R.layout.popup_menu_selector, null);
float x = locateview.getX();
float y = locateview.getY();
System.out.println(" x :"+x +" y: "+y);
// if (mPopupWindow == null) {
//
// }
mPopupWindow = new PopupWindow(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
//view.findViewById(R.id.tools_select).setOnClickListener(this);
// view.findViewById(R.id.tools_notebook).setOnClickListener(this);
// view.findViewById(R.id.tools_download).setOnClickListener(this);
// view.findViewById(R.id.tools_record).setOnClickListener(this);
mPopupWindow.setFocusable(true);
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
// mPopupWindow.setAnimationStyle(R.style.PopupAnimation);
view.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
//mPopupWindow.showAtLocation(locateview, Gravity.LEFT, 0, 0);
mPopupWindow.showAsDropDown(locateview);
}
/**
* 获取控件.
*/
private class ViewHolder {
//private int mpositon;
private ImageView icon;
private TextView fileName;
private TextView fileSize;
private TextView tvdownloadstate;
private ImageButton beginpause;
private ProgressBar mProgressBar;
private int position;
private long totalsize = 0;
private long mnowOffset = -1;
private String strfilesize ;
private PopupWindow mPopupWindow;
private boolean isstophander = false;
//暂停状态的文件调用
public void setPosition(int position)
{
this.position = position;
totalsize = list.get(position).getSize();
strfilesize = Util.getFormatSize(totalsize);
isstophander = false;
beginpause.setOnClickListener(new btnClickListener());
}
//下载中的列表item解除复用时调用
public void downloadingInit(int position)
{
this.position = position;
isstophander = false;
totalsize = list.get(position).getSize();
strfilesize = Util.getFormatSize(totalsize);
mHandler.sendEmptyMessage(2);
}
//未下载和下载完成的列表复用其他holder时调用
public void reInit(int position,boolean isstophander)
{
this.position = position;
this.isstophander = isstophander;
totalsize = list.get(position).getSize();
strfilesize = Util.getFormatSize(totalsize);
mHandler.removeMessages(2);
beginpause.setOnClickListener(new btnClickListener());
}
public class btnClickListener implements OnClickListener
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (Constant.filestates.get(list.get(position).getName()).getState() != Constant.FILE_DOWNOVER)
{
download();
}
else
{
showToolsMenu(mPopupWindow, v);
}
}
}
private void download()
{
//如果当前文件状态不是正在下载,则开始下载
if (Constant.filestates.get(list.get(position).getName()).getState() != Constant.FILE_DOWNLOADING)
{
Log.i("FILE_DOWNLOADING", "-------startdownload-------> FILE_DOWNLOADING filename "+list.get(position).getName());
beginpause.setBackgroundResource(R.drawable.file_download_pause);
Constant.filestates.get(list.get(position).getName()).setState(Constant.FILE_DOWNLOADING);
String remotefilename = Util.convertString(list.get(position).getName(), "GBK");
Intent intent = new Intent();
intent.setClass(mContext, DownloadService.class);
intent.putExtra("remotefilename", remotefilename);
intent.putExtra("localfilename", list.get(position).getName());
intent.putExtra("flag", "startDownload");
mContext.startService(intent);
mHandler.sendEmptyMessageDelayed(2, 200);
}
else
{
Log.i("FILE_DOWNPAUSE", "-------pausedownload-------> FILE_DOWNPAUSE filename "+list.get(position).getName());
mHandler.removeMessages(2);
Constant.filestates.get(list.get(position).getName()).setState(Constant.FILE_DOWNPAUSE);
String remotefilename = Util.convertString(list.get(position).getName(), "GBK");//发送到服务器的文件名需要转码,否则中文文件无法下载
Intent intent = new Intent();
intent.setClass(mContext, DownloadService.class);
intent.putExtra("remotefilename", remotefilename);
intent.putExtra("localfilename", list.get(position).getName());
intent.putExtra("flag", "changeState");
mContext.startService(intent);
beginpause.setBackgroundResource(R.drawable.file_download_start);
}
}
private Handler mHandler = new Handler()
{
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 2:
mnowOffset = Constant.filestates.get(list.get(position).getName()).getCompleteSize();
if ((mnowOffset != -1) && mnowOffset != totalsize)
{
// Log.i("----", "-------position "+position +" isstophander "+isstophander);
if (!isstophander)
{
int progress = (int)((mnowOffset /(double) totalsize)*100);
// Log.i("progress", "progress :" +progress +" mnowOffset: "+mnowOffset +"totalsize " +totalsize);
mProgressBar.setProgress(progress);
String downloadpro = Util.getFormatSize(mnowOffset) + "/"+strfilesize;
fileSize.setText(downloadpro);
sendEmptyMessageDelayed(2, 200);
}
}
else
{
//如果复用粘合在一起的时候可能会有问题
DownloadDBManager dbManager = new DownloadDBManager(mContext);
FileInfo fileInfo = new FileInfo();
fileInfo.setCoursename("英语");
fileInfo.setFilelength(totalsize);
fileInfo.setFilename(list.get(position).getName());
fileInfo.setDate(new Date().getDate());
dbManager.add(fileInfo);
mProgressBar.setProgress(100);
beginpause.setBackgroundResource(R.drawable.file_download_complete);
tvdownloadstate.setText(strfilesize + " 已完成");
fileSize.setText("");
Toast.makeText(mContext, "下载成功,文件保存路径: " +LOCAL_PATH+list.get(position).getName() , Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
};
};
}
}