这半个月一直在做一件事,升级sdk的修改、打包、集成,移交给我的sdk源码是个半成品,效率低,逻辑混乱,好多代码是直接复制粘贴,连空格都没对齐。强迫症犯了,拿过来直接重新改造。
首先是一个显示新版本信息的DialogFragment,点击升级会启动一个service,service会启动一个downloadtask类,里面有一个成员变量List《DownloadThread》(英文尖括号里内容无法显示),DownloadThread继承了Thread,根据传过来的下载文件信息、线程数量来创建每个下载线程的信息,如第一个线程从0字节到总大小/线程数量,并且要把每个线程信息保存到数据库;初始化完成后,downloadtask开始下载,将保存的线程依次取出并运行,每下载一定长度,downloadtask有个回调把下载的状态和文件大小传回给service,然后service再返回给DialogFragment,DialogFragment更新下载进度条。
类图不完整,只包含了主要的函数和成员。下面是这三个类的关系图:
DialogFragment和service通过messager传递消息,点击下载后,dialogfragment调用startDownLoadByBindService(),以bindservice方式启动downloadservice,返回IBindder对象用于构造mSender:
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected...");
........
mSender = new Messenger(service);
Message message = Message.obtain();
message.what = Constant.DOWNLOAD_INIT;
message.replyTo = mReceiver;
long fileSizelong = Long.parseLong(fileSize);
fileInfo = new FileInfo(0, apkDownloadURL,
apkName, fileSizelong, 0);
Bundle bundle = new Bundle();
bundle.putParcelable(Constant.FILE_INFO, fileInfo);
message.setData(bundle);
try {
mSender.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mReceiver是DialogFragment成员变量,接收来自service的消息:
private Messenger mReceiver = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
switch (msg.what){
case Constant.UPDATE_PROGRESSBAR:
......
break;
case Constant.DOWNLOAD_FINISHED:
......
break;
case Constant.DOWNLOAD_EXCEPTION:
......
break;
}
super.handleMessage(msg);
}
});
mSender把要下载的文件信息传给service(FileInfo不是一般数据类型,用messager传递需要实现Parcelable接口,就是要实现writeToParcel()、describeContents()并包含一个Parcelable.Creator的方法)。
再看看DownloadService中对消息的处理:
private Messenger mMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "get message,msg is " + msg.what);
mSender = msg.replyTo;
Bundle bundle = msg.getData();
mFileInfo = bundle.getParcelable(Constant.FILE_INFO);
switch (msg.what){
case Constant.DOWNLOAD_INIT:
startDownLoad();
break;
case Constant.DOWNLOAD_STOP:
//按下暂停
if (mFileInfo != null){
DownLoadTask task = hashMap.get(mFileInfo.getFileUrl());
task.isPause = true;
}
break;
case Constant.DOWNLOAD_START:
startDownLoad();
break;
case Constant.NETWORK_EXCEPTION:
if (mFileInfo != null){
DownLoadTask task = hashMap.get(mFileInfo.getFileUrl());
task.isPause = true;
}
break;
}
}
});
Constant.DOWNLOAD_INIT表示刚开始下载,Constant.DOWNLOAD_START表示下载暂停后又点击了继续,这两种情况都是调用了startDownLoad();其余两种情况是用户点击暂停下载或网络出现异常都是将暂停的标志位置为true,标志位控制了一个while循环,当它变成true会退出线程。下面看看startDownLoad():
private void startDownLoad(){
Log.i(TAG, "startDownLoad...");
DownLoadTask task = new DownLoadTask(DownLoadService.this,mFileInfo,Constant.DOWNLOAD_THREAD_COUNT);
hashMap.put(mFileInfo.getFileUrl(),task);
task.downLoad(new IDownloadCB() {
@Override
public void downloadStatus(int event, Object object) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
switch (event){
case Constant.UPDATE_PROGRESSBAR:
message.what = Constant.UPDATE_PROGRESSBAR;
bundle.putInt("progress", Integer.parseInt(object.toString()));
message.setData(bundle);
break;
case Constant.DOWNLOAD_FINISHED:
Log.i(TAG, "downloadStatus DOWNLOAD_FINISHED");
message.what = Constant.DOWNLOAD_FINISHED;
bundle.putParcelable(Constant.FILE_INFO, mFileInfo);
message.setData(bundle);
break;
case Constant.DOWNLOAD_EXCEPTION:
Log.i(TAG, "downLoad callback:Constant.DOWNLOAD_EXCEPTION");
message.what = Constant.DOWNLOAD_EXCEPTION;
bundle.putString("exception", object.toString());
message.setData(bundle);
break;
}
if (mSender != null){
try {
mSender.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
task.isPause = false;
}
传入文件信息、线程数量创建一个DownLoadTask然后调用task.downLoad并将标志位isPause置为false,开启下载线程,回调从downloadtask传回来状态,downloadservice做一个封装通过mSender传回给DialogFragment更新UI显示。
最后来看看每个线程在下载时的回调:
if (connection.getResponseCode() == 206)
{
// 读取数据
inputStream = connection.getInputStream();
byte buf[] = new byte[1024];
int len = -1;
long time = System.currentTimeMillis();
while ((len = inputStream.read(buf)) != -1)
{
//Log.i(TAG, "threadId = " + mThreadInfo.getThreadId() +";len="+len);
// 写入文件
raf.write(buf, 0, len);
// 累加整个文件完成进度
add(len);
// 累加每个线程完成的进度
Log.d(TAG, "len = " + len +";mFinised="+mFinised );
mThreadInfo.setThreadHasFinished(mThreadInfo.getThreadHasFinished() + len);
if (System.currentTimeMillis() - time > 300)
{
time = System.currentTimeMillis();
long f = (1L*mFinised * 100) /(1L* mFileInfo.getFileLength());
// Log.i(TAG, "threadId = " + mThreadInfo.getThreadId() +";mFinised="+mFinised +"文件下载进度="+f+"%");
iDownloadCB.downloadStatus(Constant.UPDATE_PROGRESSBAR, f);
}
// 在下载暂停时,保存下载进度
if (isPause)
{
Log.i(TAG, "run: ispasuse true " );
mDao.updateThread(mThreadInfo.getThreadUrl(),
mThreadInfo.getThreadId(),
mThreadInfo.getThreadHasFinished());
// iDownloadCB.downloadStatus(Constant.DOWNLOAD_STOP, "下载终止");
return;
}
}
Log.i(TAG, "threadId = "+mThreadInfo.getThreadId()+" isFinished");
// 标识线程执行完毕
isFinished = true;
checkAllThreadFinished();
}
if(System.currentTimeMillis() - time > 300)即每隔300毫秒把下载大小传给service,service再传给DialogFragment实现了进度条的更新。
刚接手时,downloadtask把下载进度直接用广播发出来,DialogFragment接收广播更新UI,记得网上有人说过,广播发送消息效率很低,而且这么频繁的发送下载进度广播感觉很奇怪,加上最近研究了下进程间通信,除了aidl,还可以用messager,不如实践下,最后做出了上面的实现方式。
修改好,下面就是打包和集成sdk了,在下一篇文章里说。