Android下载apk和自动安装,断点下载

昨天练习了一个从网络上下载一个apk,然后自动安装的demo,具体使用了服务、广播、数据库、文件,由于demo支持断点下载,后面有源码

附上截图


一、下面介绍下demo的创建过程

1、总体思路

通过网络请求首先获取 到资源的大小,然后根据资源的大小创建一个与之相对应的文件准备,然后通过线程下载资源,并将下载资源的线程的信息存储在数据库中,当点击暂停按钮之后, 更新数据库中下载线程的信息,将已经下载的资源位置作标记,继续下载的时候将会从,暂停的地方开始下载,这样就实现的了资源的断点下载,当文件下载完毕之后,通过文件的路径, 打开apk跳到安装界面,点击可以安装

2、首先要完成资源的下载,要个资源设计一个实体类,里面存储的都是文件的信息

3、既然要用线程进行下载,并实现断点下载功能,那就需要将下载的线程也存储起来,线程也需要一个实体类,存储下载文件的起始位置,终止位置,完成情况

4、线程信息需要存储在数据库中,所以我们使用SQLite来存储线程的信息

5、下载的进度的更新,我们使用的是广播,每0.5s发送一条更新进度条的广播,实现进度条的实时更新

以上几个是比较重要的地方和大致思路

二、下面介绍一下几个必要重要的类

1、SQLite数据库方面就不再介绍,上面有一篇博文有SQlite数据库的详细介绍,下面附上几个主要的接口和实现方法

public interface ThreadDao {

	public void  insertThread(ThreadIfo threadIfo);
	public void  deleteThread(String url,int thread_id );
	public void  updateThread(String url,int thread_id,int finished );
	public List<ThreadIfo>  getThread(String url);
	public boolean isexists(String url,int thread_id);
}

下面是接口的实现

public class ThreadDaoImpl implements ThreadDao {

	private DBhelper dBhelper;

	public ThreadDaoImpl(Context context) {
		super();
		dBhelper = new DBhelper(context);
	}
//插入线程
	@Override
	public void insertThread(ThreadIfo threadIfo) {
		// TODO Auto-generated method stub
		SQLiteDatabase db=dBhelper.getWritableDatabase();
		db.execSQL("insert into thread_info(thread_id,url,start,end,finished) values(?,?,?,?,?)",
				new Object[]{threadIfo.getId(),threadIfo.getUrl(),threadIfo.getStart(),threadIfo.getStop(),threadIfo.getFinished()});
		db.close();
	}
//删除线程
	@Override
	public void deleteThread(String url, int thread_id) {
		// TODO Auto-generated method stub
		SQLiteDatabase db=dBhelper.getWritableDatabase();
		db.execSQL("delete from thread_info where url=? and thread_id=?",new Object[]{url,thread_id});
		db.close();
	}

	@Override
	public void updateThread(String url, int thread_id, int finished) {
		// TODO Auto-generated method stub
		SQLiteDatabase db=dBhelper.getWritableDatabase();
		db.execSQL("update thread_info set finished=? where url=? and thread_id=?",new Object[]{finished,url,thread_id});
		db.close();
	}
//查找线程
	@Override
	public List<ThreadIfo> getThread(String url) {
		// TODO Auto-generated method stub
		SQLiteDatabase db=dBhelper.getWritableDatabase();
		Cursor cursor=db.rawQuery("select * from thread_info where url=? ",new String[]{url});
		List<ThreadIfo> list=new ArrayList<ThreadIfo>();
		while (cursor.moveToNext()) {
			ThreadIfo threadIfo=new ThreadIfo(cursor.getInt(cursor.getColumnIndex("thread_id")), 
					cursor.getString(cursor.getColumnIndex("url")), 
					cursor.getInt(cursor.getColumnIndex("start")), 
					cursor.getInt(cursor.getColumnIndex("end")), 
					cursor.getInt(cursor.getColumnIndex("finished")));
			list.add(threadIfo);
		}
		cursor.close();
		db.close();
		return list;
	}
//判断线程是否存在
	@Override
	public boolean isexists(String url, int thread_id) {
		SQLiteDatabase db=dBhelper.getWritableDatabase();
		Cursor cursor=db.rawQuery("select * from thread_info where url=? and thread_id=?",new String[]{url,thread_id+""});
		if(cursor.moveToNext()) {
			return true;
		}
		cursor.close();
		db.close();
		return false;
	}

}

2、下面写一下最主要的地方也就是文件的下载

首先通过url获取到下载资源,得到资源的大小,根据资源的大小,在本地创建一个相对应的文件

public class DownloadService extends Service {

	
	private DownloadTask task=null;
	public static final int initmsg = 0;
	public static final String ACTION_START = "ACTION_START";
	public static final String ACTION_STOP = "ACTION_STOP";
	public static final String ACTION_UPDATE = "ACTION_UPDATE";
	public static final String DOWNLOAD_PATH = Environment
			.getExternalStorageDirectory().getAbsolutePath() + "/downloads/";

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		if (ACTION_START.equals(intent.getAction())) {
			FileIfo fileIfo = (FileIfo) intent.getSerializableExtra("fileifo");
			System.out.println("文件信息已经开始" + fileIfo.toString());
			
			//启动初始化线程
			new InitThread(fileIfo).start();
		} else if (ACTION_STOP.equals(intent.getAction())) {
			FileIfo fileIfo = (FileIfo) intent.getSerializableExtra("fileifo");
			System.out.println("文件信息已经停止" + fileIfo.toString());
			if (task!=null) {
				task.ispause=true;
			}
		}

		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
//在<span style="font-family: Arial, Helvetica, sans-serif;">Handler中启动下载的任务</span><span style="font-family: Arial, Helvetica, sans-serif;">DownloadTask</span><span style="font-family: Arial, Helvetica, sans-serif;">
</span>

	Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case initmsg:
				FileIfo fileIfo=(FileIfo) msg.obj;
				
				System.out.println("handler中的fileIfo"+fileIfo.toString());
				task=new DownloadTask(getApplicationContext(), fileIfo);
				task.download();
				break;

			default:
				break;
			}
		};
	};

	/*
	 * 初始化子线程
	 */
	class InitThread extends Thread {

		private FileIfo fileIfo = null;

		public InitThread(FileIfo fileIfo) {
			super();
			this.fileIfo = fileIfo;
		}

		@Override
		public void run() {
			HttpURLConnection conn = null;
			RandomAccessFile raf= null;
			try {

				// 连接网络文件
				URL url = new URL(fileIfo.getFileurl());
				conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(3000);
				conn.setRequestMethod("GET");
				int length = -1;
				if (conn.getResponseCode() == 200) {
					// 获得文件长度
					length = conn.getContentLength();
					System.out.println("这是文件的长度"+length);
				}
				if (length <= 0) {
					return;
				}
				File dir = new File(DOWNLOAD_PATH);
//如果路径不存在,就进行重新创建
				if (!dir.exists()) {
					dir.mkdir();
				}
				// 在本地创建文件
				File file = new File(dir, fileIfo.getFilename());
				raf = new RandomAccessFile(file, "rwd");
				
				// 设置文件长度
				raf.setLength(length);
				fileIfo.setFilelength(length);
				handler.obtainMessage(initmsg,fileIfo).sendToTarget();
				
				raf.close();
				conn.disconnect();
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

3、下载任务放在DownloadTask中,使用url获取到网络资源,然后通过文件的写入将文件下载下来

public class DownloadTask {

	private Context mContext = null;
	private FileIfo fileIfo = null;
	private ThreadDao threadDao = null;
	private int mfinished = 0;
	public boolean ispause = false;

	public DownloadTask(Context mContext, FileIfo fileIfo) {
		super();
		this.mContext = mContext;
		this.fileIfo = fileIfo;
		threadDao = new ThreadDaoImpl(mContext);
	}

	public void download() {
		// 读取数据库的线程信息
		List<ThreadIfo> mlist = threadDao.getThread(fileIfo.getFileurl());
		ThreadIfo threadIfo = null;
		if (mlist.size() == 0) {
			threadIfo = new ThreadIfo(0, fileIfo.getFileurl(), 0,
					fileIfo.getFilelength(), 0);
		} else {
			threadIfo = mlist.get(0);
		}
		// 创建子线程进行下载
		new Downloadthread(threadIfo).start();
	}

	class Downloadthread extends Thread {
		private ThreadIfo threadIfo = null;

		public Downloadthread(ThreadIfo threadIfo) {
			super();
			this.threadIfo = threadIfo;
		}

		@Override
		public void run() {
			// 向数据库插入线程信息
			if (!threadDao.isexists(threadIfo.getUrl(), threadIfo.getId())) {
				threadDao.insertThread(threadIfo);
			}
			// 设置下载位置
			URL url = null;
			HttpURLConnection conn = null;
			RandomAccessFile raf = null;
			InputStream is = null;
			try {
				url = new URL(threadIfo.getUrl());
				conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(3000);
				conn.setRequestMethod("GET");

				int start = threadIfo.getStart() + threadIfo.getFinished();
				conn.setRequestProperty("Range", "bytes=" + start + "-"
						+ threadIfo.getStop());

				// 设置文件的写入位置
				File file = new File(DownloadService.DOWNLOAD_PATH,
						fileIfo.getFilename());
				raf = new RandomAccessFile(file, "rwd");
				raf.seek(start);
				Intent intent = new Intent(DownloadService.ACTION_UPDATE);
				mfinished += threadIfo.getFinished();
				// 开始下载
				if (conn.getResponseCode() == 206) {
					// 读取数据
					is = conn.getInputStream();
					byte[] buffer = new byte[1024 * 4];
					int len = -1;
					long time = System.currentTimeMillis();
					while ((len = is.read(buffer)) != -1) {
						// 写入文件
						raf.write(buffer, 0, len);
						mfinished += len;
						// 把下载进度发送广播给activity
						if (System.currentTimeMillis() - time > 500) {
							intent.putExtra("finished", mfinished * 100
									/ fileIfo.getFilelength());
							mContext.sendBroadcast(intent);
							time = System.currentTimeMillis();
						}

						// 在下载暂停时保存下载进度
						if (ispause) {
							threadDao.updateThread(threadIfo.getUrl(),
									threadIfo.getId(), mfinished);
							return;
						}
					}
//下载完成后,实现文件的自动安装
					Intent intent2 = new Intent();
					intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
					intent2.setAction(android.content.Intent.ACTION_VIEW);
					Uri uri = Uri.fromFile(new File(Environment
							.getExternalStorageDirectory().getAbsolutePath()
							+ "/downloads/imooc.apk")); // 这里是APK路径
					intent2.setDataAndType(uri,
							"application/vnd.android.package-archive");
					mContext.startActivity(intent2);
//下载完成后删除数据库中下载线程的相关信息
					threadDao.deleteThread(threadIfo.getUrl(),
							threadIfo.getId());
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				conn.disconnect();
				try {
					raf.close();
					is.close();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}

		}
	}
}

4、MainActivity中的主要作用是:创建文件对象、启动服务,接收广播更新进度、广播的注销

public class MainActivity extends ActionBarActivity {

	private Button start;
	private Button stop;
	private TextView name;
	private ProgressBar pBar;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();

		IntentFilter intentFilter=new IntentFilter();
		intentFilter.addAction(DownloadService.ACTION_UPDATE);
		registerReceiver(broadcastReceiver, intentFilter);
		// 创建文件信息对象

		final FileIfo fileIfo = new FileIfo(
				0,
				"imooc.apk",
				"http://www.imooc.com/mobile/imooc.apk",
				0, 0);
		name.setText(fileIfo.getFilename());
		start.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				
				Intent intent=new Intent(MainActivity.this,DownloadService.class);
				intent.setAction("ACTION_START");
				intent.putExtra("fileifo", fileIfo);
				startService(intent);
			}
		});
		stop.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				
				Intent intent=new Intent(MainActivity.this,DownloadService.class);
				intent.setAction("ACTION_STOP");
				intent.putExtra("fileifo", fileIfo);
				startService(intent);
			}
		});
	}

	private void initView() {
		// TODO Auto-generated method stub
		start = (Button) findViewById(R.id.download_bt);
		stop = (Button) findViewById(R.id.stop_bt);
		name = (TextView) findViewById(R.id.name_tv);
		pBar = (ProgressBar) findViewById(R.id.myprogressBar);
		
		pBar.setMax(100);
	}
	
	BroadcastReceiver broadcastReceiver=new BroadcastReceiver() {
		
		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
				pBar.setProgress(intent.getIntExtra("finished", 0));
			}
		}
	};
	
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(broadcastReceiver);
	};
}

下面将会附上demo下载地址: http://download.csdn.net/download/jl_stone/9182601


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值