Android基于HTTP协议的多线程断点下载器的实现(二)

本文介绍了一个基于观察者模式的游戏下载器的设计与实现。该下载器能够实现多页面UI同步更新,确保流畅不混乱的用户体验。文章详细展示了下载器的代码结构,包括被观察者、下载管理和观察者布局等关键部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


自己写的下载

主要用到观察者模式,多个页面同步ui进度,页面流畅不错乱



下面是被观察者


package com.anjoyo.gamecenter5.loader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.Observable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.content.Intent;
import android.os.Environment;
import android.util.Log;

import com.anjoyo.gamecenter5.GameCenter5Application;
import com.anjoyo.gamecenter5.apktools.Cons;
import com.anjoyo.gamecenter5.apktools.DownloadFile;
import com.anjoyo.gamecenter5.apktools.MyDb;
import com.anjoyo.gamecenter5.constant.WifiDialog;
import com.anjoyo.gamecenter5.fragment.TabDFm_Management;
import com.anjoyo.gamecenter5.interf.DownloadListener;
import com.anjoyo.gamecenter5.tools.ConnectionUtil;
import com.anjoyo.gamecenter5.tools.SharedpreferensUitls;
import com.anjoyo.gamecenter5.tools.ToastUtil;
//继承Observealbe成为给观察者
public class Observed extends Observable implements DownloadListener {
	private Downloader downloader;
	// ===========================
	private static final int BUFFER_SIZE = 1024;
	private URL url;
	private File file;
	private int startPostion;
	private int endPostion;
	private int curPosition;
	private boolean finished = false;
	private int downloadSize = 0;
	private MyDb myDb;
	private DownloadFile downloadFile;
	private GameCenter5Application myApp;
	private int opention;
	private Context context;
	private int fileSize;
	private static ExecutorService es = Executors.newFixedThreadPool(3);
//构造方法只在DownloadManager里使用使同一个下载公用一个被观察者
	public Observed(DownloadListener downloadListener,
			final DownloadFile downloadFile1, Context context) {
		this.context = context;
		myApp = ((GameCenter5Application) context.getApplicationContext());
		myDb = MyDb.getInstance(context);
		Map<String, DownloadFile> hashMap = myApp.getFileMap();
		if (hashMap.containsKey(downloadFile1.appPackageName)) {
			this.downloadFile = myApp.getFileMap().get(
					downloadFile1.appPackageName);
		} else {
			this.downloadFile = downloadFile1;
			final String addr = downloadFile.downLoadAddress;
			new Thread() {
				@Override
				public void run() {

					try {
						URL url = new URL(addr);

						downloadFile.fileSize = url.openConnection()
								.getContentLength();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					super.run();
				}
			}.start();
		}
		if (TabDFm_Management.appIsStalled(downloadFile.appPackageName,
				downloadFile.version))
			downloadFile.downloadState = Cons.INSTALLED;
	}

	public Downloader getDownloader() {
		return downloader;
	}
//在更新ui布局是调用,得到下载的当前状态
	public DownloadFile replaceBean() {
		return downloadFile;
	}

	public class Downloader implements Runnable {

		private DownloadListener downloadListener;
		private DownloadFile downloadFile;

		public Downloader(DownloadListener downloadListener,
				DownloadFile downloadFile) {
			this.downloadListener = downloadListener;
			this.downloadFile = downloadFile;
			try {

				url = new URL(downloadFile.downLoadAddress);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	

		@Override
		public void run() {

			BufferedInputStream bis = null;
			RandomAccessFile fos = null;
			byte[] buf = new byte[BUFFER_SIZE];
			URLConnection con = null;
			curPosition = startPostion = downloadFile.downloadSize;
			endPostion = downloadFile.fileSize;
			String downloadDir = Environment.getExternalStorageDirectory()
					+ "/anjoyodownload/";
			File file = new File(downloadDir);
			if (!file.exists()) {
				file.mkdirs();
			}
			myDb.updateGameDownload(downloadFile);
			myApp.getApplicationContext()
					.sendBroadcast(new Intent("ddownload"));
			int fileNameStart = downloadFile.downLoadAddress.lastIndexOf("/");
			String fileName = downloadFile.downLoadAddress
					.substring(fileNameStart);
			file = new File(file, fileName);
			try {
				Log.d("FileDownloadThread", "========" + url);
				con = url.openConnection();
				con.setAllowUserInteraction(true);

				con.setRequestProperty("Range", "bytes=" + startPostion + "-"
						+ endPostion);
				fos = new RandomAccessFile(file, "rw");
				fos.seek(startPostion);
				bis = new BufferedInputStream(con.getInputStream());
				int updateSize = 0;
				if (SharedpreferensUitls.getTagWifi(context) == 1
						&& ConnectionUtil.getConnectedType(context) != 1) {
					// 非Wifi提示对话框
					Log.d("vivi",
							"wifi状态====="
									+ SharedpreferensUitls.getTagWifi(context));
					Log.d("vivi",
							"网络状态====="
									+ ConnectionUtil.getConnectedType(context));
					WifiDialog.showWifiDialog(context);
				} else {
					myApp.addThread();
					downloadFile.downloadState = Cons.LOADING;
					while (curPosition < endPostion
							& Cons.LOADING == downloadFile.downloadState) {
						if (!ConnectionUtil.isConn(myApp))
							downloadFile.downloadState = Cons.FAIL;

						int len = bis.read(buf, 0, BUFFER_SIZE);
						if (len == -1) {
							break;
						}
						fos.write(buf, 0, len);
						curPosition = curPosition + len;
						updateSize += len;
						if (curPosition > endPostion) {
							downloadFile.downloadSize += len
									- (curPosition - endPostion) + 1;
						} else {
							downloadFile.downloadSize += len;
						}
						if (updateSize >= downloadFile.fileSize / 100) {
//达到一定的状态,通知观察者更新
							Observed.this.setChanged();
							Observed.this.notifyObservers();
							updateSize = 0;
						}
						// downloadFile.downloadSize += downloadSize;
						myDb.updateGameDownload(downloadFile);
						// this.finished = true;
					}
				}
				if (curPosition >= endPostion)
					downloadFile.downloadState = Cons.COMPLETE;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				downloadFile.downloadState = Cons.FAIL;
				e.printStackTrace();
			} finally {
//完成,通知观察者更新
				Observed.this.setChanged();
				Observed.this.notifyObservers();
				myApp.cutThread();
				if (downloadFile != null)
					myDb.updateGameDownload(downloadFile);
				myApp.getApplicationContext().sendBroadcast(
						new Intent("ddownload"));
				try {
					if (bis != null)
						bis.close();
					if (fos != null)
						fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}
	}

	@Override
	public void start() {
		// TODO Auto-generated method stub
		if (myApp.threadNum < 3) {
			this.downloadFile.downloadState = Cons.LOADING;
			opention = Cons.LOADING;
		} else {
			this.downloadFile.downloadState = Cons.WAITING;
			opention = Cons.WAITING;

		}
		downloader = new Downloader(null, downloadFile);
		// downloader.setName(downloadFile.appPackageName);
		downloadFile.threadName = downloadFile.appPackageName;
//开始一个下载,通知所有观察者更新
		Observed.this.setChanged();
		Observed.this.notifyObservers();
		if (downloadFile != null)
			myDb.updateGameDownload(downloadFile);
		myApp.getApplicationContext().sendBroadcast(new Intent("ddownload"));
		es.execute(downloader);
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub
		this.opention = Cons.SUSPEND;
		downloadFile.downloadState = Cons.SUSPEND;
		Observed.this.setChanged();
		Observed.this.notifyObservers();
	}

	@Override
	public void cancle() {
		// TODO Auto-generated method stub
		this.opention = Cons.CANCLE;
		MyDb.getInstance(context).deleteGameDownload(downloadFile.downLoadAddress);
		
		Observed.this.setChanged();
		Observed.this.notifyObservers();
	}

	@Override
	public void waiting() {

	}
}
下边是下载管理

package com.anjoyo.gamecenter5.loader;

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.util.Log;

import com.anjoyo.gamecenter5.apktools.DownloadFile;
import com.anjoyo.gamecenter5.interf.DownloadListener;

/**
 * 单例DownloadManager
 * 
 * @author czt
 * 
 */
public class DownloadManager {
	private static DownloadManager loadManager;

	private Map<String, Observed> loadMap;

	private DownloadManager() {
		this.loadMap = new HashMap<String, Observed>();
	}

	public void putObserved(String key, Observed observed) {
		this.loadMap.put(key, observed);
	}

	public Observed getObserved(DownloadListener downloadListener,
			DownloadFile downloadFile, Context context) {
		Observed observed = null;
		if (this.loadMap.containsKey(downloadFile.appPackageName)) {
			observed = this.loadMap.get(downloadFile.appPackageName);
			downloadFile = observed.replaceBean();
		} else {
			observed = new Observed(downloadListener, downloadFile, context);
			putObserved(downloadFile.appPackageName, observed);
		}
		Log.d("downloadmaneger", "downloadMap==--->>>>>>>>" + this.loadMap);
		return observed;
	}

	/**
	 * 得到Manager的实例
	 * 
	 * @return
	 */
	public static DownloadManager getManager() {
		if (loadManager == null) {
			loadManager = new DownloadManager();
		}
		return loadManager;
	}
}

下边是观察者布局里的要更新的view封装在一个继承此观察者的类里

package com.anjoyo.gamecenter5.loader;

import java.util.Observer;

import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * adapter里填充的view工具
 * 
 * @author czt
 * 
 */
public class ViewHolder implements Observer {

	private UpdateListener listener;

	public ViewHolder() {
		
	}

	public void setUpdateListener(UpdateListener listener) {
		this.listener = listener;
	}

	

	@Override
	public void update(java.util.Observable observable, Object data) {
		if (this.listener != null) {
			listener.update();
		}
	}



	public interface UpdateListener {
		void update();

	}
}

Adapter里的要填充的布局(所有Adapter)


@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		final ViewHolder viewHolder;
		if (convertView == null) {
			viewHolder = new ViewHolder();
			convertView = mInflater
					.inflate(R.layout.rankinglistview_item, null);
			viewHolder.txtView_Title = (TextView) convertView
					.findViewById(R.id.ranking_item_title);
			viewHolder.txtView_FileSize = (TextView) convertView
					.findViewById(R.id.ranking_item_filesize);
			viewHolder.tv_rankingitemDownload = (TextView) convertView
					.findViewById(R.id.ranking_item_download);
			viewHolder.imgView_Icon = (ImageView) convertView
					.findViewById(R.id.ranking_item_icon);
			viewHolder.ranking_item_pb = (ProgressBar) convertView
					.findViewById(R.id.ranking_item_pb);
			progressbarMax = viewHolder.ranking_item_pb.getMax();
			int[] star_id = { R.id.star1, R.id.star2, R.id.star3, R.id.star4,
					R.id.star5 };
			for (int i = 0; i < viewHolder.imgView_Star.length; i++) {
				viewHolder.imgView_Star[i] = (ImageView) convertView
						.findViewById(star_id[i]);
			}
			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}

		final ViewHolder vh1 = viewHolder;
		// 得到当前下载的被观察者(对应的下载类)
		final Observed observed = DownloadManager.getManager().getObserved(
				null, new DownloadFile(mRecommendBeans.get(position)), context);
		DownloadFile downloadFile = observed.replaceBean();

		viewHolder
				.setUpdateListener(new com.anjoyo.gamecenter5.loader.ViewHolder.UpdateListener() {

					@Override
					public void update() {//获得最新状态
						DownloadFile downloadFile = observed.replaceBean();
						Log.d("update",
								"===========.RecommendedAdapter>>>>>>>>>>>>>"
										+ downloadFile.appPackageName);
//把最新状态通过handler传递个主线程更新ui
						Message msg = ha.obtainMessage();
						msg.obj = viewHolder;
						msg.arg1 = downloadFile.downloadState;
						msg.arg2 = (int) (downloadFile.downloadSize * 1000.0 / downloadFile.fileSize);
						ha.sendMessage(msg);
					}
				});
//把当前观察者viewholder对象注册给被观察者
		observed.addObserver(viewHolder);
		Log.d("recommend",
				"observed" + observed + "===mRecommendBeans.get(position)==="
						+ mRecommendBeans.get(position).pachagename);
		viewHolder.tv_rankingitemDownload.setTag(position);
		final RecommendBean bean = mRecommendBeans.get(position);
		for (int i = 0; i < viewHolder.imgView_Star.length; i++) {
			viewHolder.imgView_Star[i].setImageResource(R.drawable.star_gray);
		}
		for (int i = 0, starNum = bean.star; i < starNum; i++) {
			viewHolder.imgView_Star[i].setImageResource(R.drawable.star_yellow);
		}

		viewHolder.tv_rankingitemDownload
				.setOnClickListener(new OnClickListener() {

					@Override
					public void onClick(View v) {
						TextView tv = (TextView) v;
						int index = (Integer) v.getTag();
						Log.d("ccc", "TAG = " + index);
						Log.d("ccc", "po:" + index);
						if (tv.getText().equals("下载")) {
							observed.start();
							tv.setBackgroundColor(Color
									.argb(250, 255, 255, 255));
							tv.setTextColor(Color.BLACK);
							tv.setText("0.0%");
						} else if (((String) tv.getText()).indexOf("%") != -1) {// 存在%时为正在下载
							if (observed != null)
								observed.pause();
							tv.setBackgroundColor(Color
									.argb(250, 255, 255, 255));
							tv.setTextColor(Color.BLACK);
							tv.setText("继续");
						} else if (tv.getText().equals("继续")) {
							tv.setText("准备中");
							observed.start();

						} else if (tv.getText().equals("安装")) {
							TabDFm_Management.installApk(context,
									mRecommendBeans.get(position).flashurl);
						} else if (tv.getText().equals("打开"))
							TabDFm_Management.startApplitation(context,
									mRecommendBeans.get(position).pachagename);
					};
				});

		if (SharedpreferensUitls.getTagGPRS(context) == 1
				&& ConnectionUtil.getConnectedType(context) == 0) {
			viewHolder.imgView_Icon.setImageResource(R.drawable.imagedefault);
		} else {
			utils.display(viewHolder.imgView_Icon, bean.icon);
		}
		changeState(
				viewHolder,
				downloadFile.downloadState,
				(int) (downloadFile.downloadSize * 1000.0 / downloadFile.fileSize));
		viewHolder.txtView_Title.setText(Html.fromHtml(bean.title));
		viewHolder.txtView_FileSize.setText(bean.filesize);
		return convertView;
	}

	Handler ha = new Handler(Looper.getMainLooper()) {
		public void handleMessage(android.os.Message msg) {
			ViewHolder viewHolder = (ViewHolder) msg.obj;
			int status = msg.arg1;
			int progress = msg.arg2;
			changeState(viewHolder, status, progress);
		};
	};

	private void changeState(final ViewHolder viewHolder, int status,
			int progress) {
		// Integer status = downloadFile.downloadState;

		if (Cons.LOADING == status) {
			viewHolder.tv_rankingitemDownload.setBackgroundColor(Color.argb(
					250, 255, 255, 255));
			viewHolder.tv_rankingitemDownload.setTextColor(Color.BLACK);
			String prg = progress / 10.0 + "";
			if (prg.contains("."))
				prg = prg.substring(0, prg.indexOf(".") + 2) + "%";
			viewHolder.tv_rankingitemDownload.setText(prg);
		} else if (Cons.SUSPEND == status) {
			viewHolder.tv_rankingitemDownload.setBackgroundColor(Color.argb(
					250, 255, 255, 255));
			viewHolder.tv_rankingitemDownload.setTextColor(Color.BLACK);
			viewHolder.tv_rankingitemDownload.setText("继续");
		} else if (Cons.COMPLETE == status) {
			viewHolder.tv_rankingitemDownload.setBackgroundColor(Color.argb(
					250, 255, 255, 255));
			viewHolder.tv_rankingitemDownload.setTextColor(Color.BLACK);
			viewHolder.tv_rankingitemDownload.setText("安装");
		} else if (Cons.WAITING == status) {
			viewHolder.tv_rankingitemDownload.setBackgroundColor(Color.argb(
					250, 255, 255, 255));
			viewHolder.tv_rankingitemDownload.setTextColor(Color.BLACK);
			viewHolder.tv_rankingitemDownload.setText("等待");
		} else if (Cons.INSTALLED == status) {
			viewHolder.tv_rankingitemDownload.setText("打开");

		} else {
			viewHolder.tv_rankingitemDownload
					.setBackgroundResource(R.drawable.download_normal);
			viewHolder.tv_rankingitemDownload.setText("下载");
			viewHolder.tv_rankingitemDownload.setTextColor(Color.WHITE);
		}
	}
//继承观察者Viewholder
	class ViewHolder extends com.anjoyo.gamecenter5.loader.ViewHolder {
		TextView txtView_Title;
		TextView txtView_FileSize;
		TextView tv_rankingitemDownload;
		ImageView imgView_Icon;
		ImageView[] imgView_Star = new ImageView[5];
		ProgressBar ranking_item_pb;

	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值