本文續(xù)接我上一篇文章《Android實戰(zhàn):簡易斷點續(xù)傳下載器實現(xiàn)》
鏈接地址:http://www.reibang.com/p/5b2e22c42467
本項目Github地址:
https://github.com/liaozhoubei/MultiDownload
說到多線程下載据忘,也許大家會覺得很迷惑穆刻,但多線程的原理實際上與單線程下載的原理并無區(qū)別耳标。
多線程下載只需要確定好下載一個文件需要多少個線程逢勾,一般來說最好為3條線程,因為線程過多會占用系統(tǒng)資源粉铐,而且線程間的相互競爭也會導致下載變慢疼约。
其次下載的時候?qū)⑽募指顬槿荩僭O(shè)用3條線程下載)下載,在java中就要用到上次提到的RandomAccessFile這個API蝙泼,它的開始結(jié)束為止用以下代碼確定:
conn.setRequestProperty("Range", "bytes=" + start + "-" + end)
最后就是斷點續(xù)傳了忆谓,只需要才程序停止下載的時候記錄下最后的下載位置就好了,當下次下載的時候從當前停止的位置開始下載踱承。
OK,那么現(xiàn)在就開始我們的多線程下載+通知欄控制的實戰(zhàn)之旅吧!
多線程斷點續(xù)傳下載
我們這次要做的并非簡單的多線程下載哨免,而是要做到多文件多線程的同時下載
重寫布局
這次下載需要展示多個下載的文件茎活,所以使用ListView控件,界面效果如下
每個ListView的item都很簡單琢唾,基本上只需要將上次寫的下載界面搬過來就好了载荔。
新建一個Layout,命名為item采桃,將中的界面布局剪切過來懒熙,然后在中設(shè)置一個ListView空間。
activity_main.xml代碼如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
至于Item的布局普办,為了省功夫工扎,就不寫了,大家可以去我的Github下載名為MultiDownload的項目來參考衔蹲。
建立FileAdapter類
布局寫好了肢娘,但是ListView總是要有個Adapter類來綁定視圖,填充布局的不是么舆驶,所以接下來就開始寫FileAdapter了橱健。
話說回來,ListView真的是個很重要的空間沙廉,不熟悉的小伙伴抓緊多看看怎么做吧拘荡。
創(chuàng)建一個繼承自BaseAdapter類的FileAdapter,里面擁有以下三個成員變量:
private Context mContext = null;
private List<FileInfo> mFilelist = null;
private LayoutInflater layoutInflater;
然后重寫構(gòu)造函數(shù):
public FileAdapter(Context mContext, List<FileInfo> mFilelist) {
this.mContext = mContext;
this.mFilelist = mFilelist;
layoutInflater = LayoutInflater.from(mContext);
}
再將繼承的getCount/getItem/getItemId三個方法的返回值寫好撬陵,用于ListView找到各自的Item珊皿。
接下來就是重頭戲网缝,重寫getView方法了!
我們先定義一個靜態(tài)的ViewHolder內(nèi)部類亮隙,這樣在ListView屬性的時候才不會重復創(chuàng)建對象途凫,減輕內(nèi)存壓力,這個谷歌官方推薦的哦溢吻!
static class ViewHolder {
TextView textview;
Button startButton;
Button stopButton;
ProgressBar progressBar;
}
然后在getView中綁定布局item中的各個控件维费,并且設(shè)置按鈕的點擊事件,getView代碼如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
final FileInfo mFileInfo = mFilelist.get(position);
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.item, null);
viewHolder = new ViewHolder();
viewHolder.textview = (TextView) convertView.findViewById(R.id.file_textview);
viewHolder.startButton = (Button) convertView.findViewById(R.id.start_button);
viewHolder.stopButton = (Button) convertView.findViewById(R.id.stop_button);
viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressBar2);
viewHolder.textview.setText(mFileInfo.getFileName());
viewHolder.progressBar.setMax(100);
viewHolder.startButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext, DownloadService.class); intent.setAction(DownloadService.ACTION_START);
intent.putExtra("fileInfo", mFileInfo);
mContext.startService(intent);
}
});
viewHolder.stopButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext, DownloadService.class);
intent.setAction(DownloadService.ACTION_STOP);
intent.putExtra("fileInfo", mFileInfo);
mContext.startService(intent);
}
});
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.progressBar.setProgress(mFileInfo.getFinished());
return convertView;
}
最后再新建一個更新進度條的方法促王,在獲得文件ID犀盟,和當前進度之后,直接更新進度條蝇狼,代碼如下:
public void updataProgress(int id, int progress) {
FileInfo info = mFilelist.get(id);
info.setFinished(progress);
notifyDataSetChanged();
}
好了阅畴,整個FileAdapter類就這樣寫完了!下面我們來修改一下MainActivity中的代碼吧迅耘。
修改MainActivity代碼
由于我們在FileAdapter中已經(jīng)將布局寫好了贱枣,而且點擊事件和更新進度也是在FileAdapter中進行的,因此不需要在MainActivity中綁定按鍵了颤专,現(xiàn)在可以將有關(guān)Button和ProgressBar的代碼都刪掉纽哥。然后在配置好ListView控件就可以了,代碼如下:
private ListView listView;
private List<FileInfo> mFileList;
private FileAdapter mAdapter;
private String urlone = "http://www.imooc.com/mobile/imooc.apk";
private String urltwo = "http://www.imooc.com/download/Activator.exe";
private String urlthree = "http://s1.music.126.net/download/android/CloudMusic_3.4.1.133604_official.apk";
private String urlfour = "http://study.163.com/pub/study-android-official.apk";
private UIRecive mRecive;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化控件
listView = (ListView) findViewById(R.id.list_view);
mFileList = new ArrayList<FileInfo>();
// 初始化文件對象
FileInfo fileInfo1 = new FileInfo(0, urlone, getfileName(urlone), 0, 0);
FileInfo fileInfo2 = new FileInfo(1, urltwo, getfileName(urltwo), 0, 0);
FileInfo fileInfo3 = new FileInfo(2, urlthree, getfileName(urlthree), 0, 0);
FileInfo fileInfo4 = new FileInfo(3, urlfour, getfileName(urlfour), 0, 0);
mFileList.add(fileInfo1);
mFileList.add(fileInfo2);
mFileList.add(fileInfo3);
mFileList.add(fileInfo4);
mAdapter = new FileAdapter(this, mFileList);
listView.setAdapter(mAdapter);
mRecive = new UIRecive();
// 注冊廣播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadService.ACTION_UPDATE);
intentFilter.addAction(DownloadService.ACTION_FINISHED);
intentFilter.addAction(DownloadService.ACTION_START);
registerReceiver(mRecive, intentFilter);
}
現(xiàn)在整個視圖終于搞好了栖秕,可以啟動應用春塌,看看視圖是否顯示正常了。當然啦簇捍,下載還沒吧下載的代碼改好只壳,現(xiàn)在我們就來修改下載代碼吧。
修改DownloadTask代碼
既然是多線程下載暑塑,那么我們便要在下載的時候設(shè)置好線程數(shù)吼句,首先添加一個int類型的threadCount的參數(shù)代碼代表線程數(shù),初始值為1事格。然后在DownloadTask的構(gòu)造函數(shù)中添加threadCount變量命辖,這樣在開始下載的時候就能夠確定需要多少個線程下載,代碼如下:
public DownloadTask(Context comtext, FileInfo fileInfo, int threadCount) {
super();
this.mThreadCount = threadCount;
this.mComtext = comtext;
this.mFileInfo = fileInfo;
this.mDao = new ThreadDAOImple(mComtext);
}
然后我們要確認每個線程需要從文件的哪里開始下載分蓖。假設(shè)文件長度為10尔艇,分為3條線程下載,那么0-2是一份么鹤,3-5是一份终娃,6-8是一份(java中從0開始),那么多出的一份怎么辦蒸甜?當然是在計算時棠耕,如果最后多出來余佛,歸最后的拿份,也就是6-9了窍荧。
我們將每個線程需要下載多少長度的文件計算好辉巡,就可以讓每個文件開始自己的下載任務了,代碼如下:
public void download() {
// 從數(shù)據(jù)庫中獲取下載的信息
List<ThreadInfo> list = mDao.queryThreads(mFileInfo.getUrl());
if (list.size() == 0) {
int length = mFileInfo.getLength();
int block = length / mThreadCount;
for (int i = 0; i < mThreadCount; i++) {
// 劃分每個線程開始下載和結(jié)束下載的位置
int start = i * block;
int end = (i + 1) * block - 1;
if (i == mThreadCount - 1) {
end = length - 1;
}
ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), start, end, 0);
list.add(threadInfo);
}
}
mThreadlist = new ArrayList<DownloadThread>();
for (ThreadInfo info : list) {
DownloadThread thread = new DownloadThread(info);
// 使用線程池執(zhí)行下載任務
DownloadTask.sExecutorService.execute(thread);
mThreadlist.add(thread);
// 如果數(shù)據(jù)庫不存在下載信息蕊退,添加下載信息
mDao.insertThread(info);
}
}
需要注意的是啟動下載線程的時候在這里沒有直接使用Thread.start()來啟動郊楣,而是使用了線程池,因為線程過多瓤荔,使用線程池便于管理净蚤。使用線程池非常簡單,只需要在開始的時候定義一個線程池的成員變量:
public static ExecutorService sExecutorService = Executors.newCachedThreadPool();
然后使用
sExecutorService.execute(需要啟動的線程);
這樣就能夠啟動線程了输硝,是一種很簡單的用法今瀑。
然后我們還要定義一個同步方法忘晤,判斷一個文件的全部線程是否都下載完成又谋,如果下載完成就彈出Toast
public synchronized void checkAllFinished() {
boolean allFinished = true;
for (DownloadThread thread : mThreadlist) {
if (!thread.isFinished) {
allFinished = false;
break;
}
}
if (allFinished == true) {
// 下載完成后,刪除數(shù)據(jù)庫信息
mDao.deleteThread(mFileInfo.getUrl());
// 通知UI哪個線程完成下載
Intent intent = new Intent(DownloadService.ACTION_FINISHED);
intent.putExtra("fileInfo", mFileInfo);
mComtext.sendBroadcast(intent);
}
}
最后修改一下run方法中的代碼矾削,前面我們保存斷點下載是整個文件的進度郎逃,現(xiàn)在保存下載是單個線程的進度砾医,同時我們還要判斷是否整個文件的所有線程是否完成的checkAllFinished方法添加進去,所以將部分代碼修改為:
// 定義UI刷新時間
long time = System.currentTimeMillis();
while ((len = is.read(bt)) != -1) {
raf.write(bt, 0, len);
// 累計整個文件完成進度
mFinished += len;
// 累加每個線程完成的進度
threadInfo.setFinished(threadInfo.getFinished() + len);
// 設(shè)置爲500毫米更新一次
if (System.currentTimeMillis() - time > 1000) {
time = System.currentTimeMillis();
// 發(fā)送已完成多少
intent.putExtra("finished", mFinished * 100 / mFileInfo.getLength());
// 表示正在下載文件的id
intent.putExtra("id", mFileInfo.getId());
Log.i("test", mFinished * 100 / mFileInfo.getLength() + "");
// 發(fā)送廣播給Activity
mComtext.sendBroadcast(intent);
}
if (mIsPause) {
mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished());
return;
}
}
}
// 標識線程是否執(zhí)行完畢
isFinished = true;
// 判斷是否所有線程都執(zhí)行完畢
checkAllFinished();
好了衣厘,這樣整個DownloadTask的代碼就修改完了,接下來我們開始修改DownloadService中的代碼了压恒。
DownloadService代碼修改
在之前的代碼中是使用單線程下載影暴,現(xiàn)成我們設(shè)置成可以定義多少條線程下載,因為在Handler中的啟動下載的時候需要添加線程數(shù)探赫。
同時我們要在DownloadService定義一個Map的集合型宙,用于管理下載線程,代碼如下:
private Map<Integer, DownloadTask> mTasks = new LinkedHashMap<Integer, DownloadTask>();
修改Handler代碼伦吠,在啟動下載線程時妆兑,添加進下載集合中,代碼如下:
switch (msg.what) {
case MSG_INIT:
FileInfo fileInfo = (FileInfo) msg.obj;
Log.i("test", "INIT:" + fileInfo.toString());
// 獲取FileInfo對象毛仪,開始下載任務
DownloadTask task = new DownloadTask(DownloadService.this, fileInfo, 3);
task.download();
// 把下載任務添加到集合中
mTasks.put(fileInfo.getId(), task);
break;
}
最后要修改onStartCommand代碼搁嗓,當我們點擊停止的時候,要停止一個文件中每一個正在運行中的線程箱靴,在點擊開始的時候要用線程池啟動下載腺逛,代碼如下:
if (ACTION_START.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.i("test", "START" + fileInfo.toString());
InitThread initThread = new InitThread(fileInfo);
DownloadTask.sExecutorService.execute(initThread);
} else if (ACTION_STOP.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
DownloadTask task = mTasks.get(fileInfo.getId());
if (task != null) {
// 停止下載任務
task.mIsPause = true;
}
}
這樣DownloadService中的代碼也修改完了,只剩下最后修改MainActivity中的代碼了
修改MainActivity代碼
這次我們只需修改廣播接收者的代碼就可以了衡怀,但我們更新進度的時候不能按照單一文件的時候更新了棍矛,我們必須按照文件的id來更新進度安疗,這時我們可以調(diào)用FileAdapter中的updataProgress方法(前面自己定義的)便可以更新。同時我們還要在文件完成時彈出文件已完成的Toast够委,因此要給廣播增加Action荐类。
在onCreate中修改注冊廣播的代碼:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadService.ACTION_UPDATE);
intentFilter.addAction(DownloadService.ACTION_FINISHED);
registerReceiver(mRecive, intentFilter);
修改廣播接收者的代碼:
public void onReceive(Context context, Intent intent) {
if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
// 更新進度條的時候
int finished = intent.getIntExtra("finished", 0);
int id = intent.getIntExtra("id", 0);
mAdapter.updataProgress(id, finished);
} else if (DownloadService.ACTION_FINISHED.equals(intent.getAction())){
// 下載結(jié)束的時候
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
mAdapter.updataProgress(fileInfo.getId(), 0);
Toast.makeText(MainActivity.this, mFileList.get(fileInfo.getId()).getFileName() + "下載完畢", Toast.LENGTH_SHORT).show();
}
}
大功告成!一個多線程多文件下載的項目就這樣解決了茁帽,滿滿的成就感對不_玉罐。
但是且慢,我們還有一個通知欄沒解決脐雪,等我們把通知欄做好再高興也不遲
Notification通知欄的使用
在低版本中厌小,Android使用通知欄是Notification這個API,但是在高版本中使用的是Notification.Builder這個API战秋,兩種區(qū)別不大璧亚,在這里使用的是低版本的Notification。
Notificaiton布局
使用通知欄必須要有個布局脂信,但我們下拉通知欄的時候癣蟋,如播放音樂,我們可以看到有上一首狰闪、下一首等按鍵疯搅。所以就像使用ListView一樣,我們首先要定義自己的通知欄布局埋泵,布局效果如下
這是一個很簡單的布局幔欧,一個TextView,一個ProgressBar丽声,兩個Button就解決了礁蔗。需要說明的是,寫這個布局與普通布局并無不同雁社,布局代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/file_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ProgressBar
android:id="@+id/progressBar2"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2" />
<Button
android:id="@+id/start_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="start" />
<Button
android:id="@+id/stop_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="stop" />
</LinearLayout>
</LinearLayout>
布局寫好了浴井,我們來定義如何操作這個視圖吧!
NotificationUtil工具類
在通知欄中我們要向Activity一樣找到這個布局霉撵,然后操縱它磺浙。但是不同的是通知欄使用的是RemoteViews遠程視圖這個API來控制視圖的。
另外在新建Notification對象的時候徒坡,要設(shè)置好幾個參數(shù)撕氧,這些參數(shù)是顯示在狀態(tài)欄中的。當QQ或者其他來通知的時候喇完,許多時候并不是直接彈出對話框的呵曹,而是在狀態(tài)欄中彈出一個提示,這就是Notification設(shè)置的參數(shù)。
好了Notification介紹到這里奄喂,下面就是NotificationUtil的完整代碼:
public class NotificationUtil {
private Context mContext;
private NotificationManager mNotificationManager = null;
private Map<Integer, Notification> mNotifications = null;
public NotificationUtil(Context context) {
this.mContext = context;
// 獲得系統(tǒng)通知管理者
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// 創(chuàng)建通知的集合
mNotifications = new HashMap<Integer, Notification>();
}
/**
* 顯示通知欄
* @param fileInfo
*/
public void showNotification(FileInfo fileInfo) {
// 判斷通知是否已經(jīng)顯示
if(!mNotifications.containsKey(fileInfo.getId())){
Notification notification = new Notification();
notification.tickerText = fileInfo.getFileName() + "開始下載";
notification.when = System.currentTimeMillis();
notification.icon = R.drawable.ic_launcher;
notification.flags = Notification.FLAG_AUTO_CANCEL;
// 點擊通知之后的意圖
Intent intent = new Intent(mContext, MainActivity.class);
PendingIntent pd = PendingIntent.getActivity(mContext, 0, intent, 0);
notification.contentIntent = pd;
// 設(shè)置遠程試圖RemoteViews對象
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.notification);
// 控制遠程試圖铐殃,設(shè)置開始點擊事件
Intent intentStart = new Intent(mContext, DownloadService.class);
intentStart.setAction(DownloadService.ACTION_START);
intentStart.putExtra("fileInfo", fileInfo);
PendingIntent piStart = PendingIntent.getService(mContext, 0, intentStart, 0);
remoteViews.setOnClickPendingIntent(R.id.start_button, piStart);
// 控制遠程試圖,設(shè)置結(jié)束點擊事件
Intent intentStop = new Intent(mContext, DownloadService.class);
intentStop.setAction(DownloadService.ACTION_STOP);
intentStop.putExtra("fileInfo", fileInfo);
PendingIntent piStop = PendingIntent.getService(mContext, 0, intentStop, 0);
remoteViews.setOnClickPendingIntent(R.id.stop_button, piStop);
// 設(shè)置TextView中文件的名字
remoteViews.setTextViewText(R.id.file_textview, fileInfo.getFileName());
// 設(shè)置Notification的視圖
notification.contentView = remoteViews;
// 發(fā)出Notification通知
mNotificationManager.notify(fileInfo.getId(), notification);
// 把Notification添加到集合中
mNotifications.put(fileInfo.getId(), notification);
}
}
/**
* 取消通知欄通知
*/
public void cancelNotification(int id) {
mNotificationManager.cancel(id);
mNotifications.remove(id);
}
/**
* 更新通知欄進度條
* @param id 獲取Notification的id
* @param progress 獲取的進度
*/
public void updataNotification(int id, int progress) {
Notification notification = mNotifications.get(id);
if (notification != null) {
// 修改進度條進度
notification.contentView.setProgressBar(R.id.progressBar2, 100, progress, false);
mNotificationManager.notify(id, notification);
}
}
}
通知欄的工具類已經(jīng)寫好了跨新,現(xiàn)在就是使用它的時候了富腊。我們要在Activity中點擊下載的時候就彈出通知欄,下面我們就來修改DownloadService和MainActivity中的代碼來啟動通知欄吧域帐。
修改DownloadService
要在點擊開始下載赘被,啟動下載任務的時候彈出通知欄,我們所要知道的是如何收到開始的信號肖揣。
1民假、當點擊開始下載的按鍵時,在FileAdapter的startButton傳出一個ACTION_START的信號龙优,并啟動服務羊异。
2、然后在DownloadService中的onStartCommand方法中接到信號彤断,然后啟動InitThread初始化線程野舶。
3、在InitThread啟動之后會獲得FileInfo的實例宰衙,里面包含所要下載的文件的長度平道,然后InitThread通過Message將FileInfo實例傳遞給Handler。
4供炼、在Hanlder中開啟DownloadTask下載線程任務一屋。
這個下載任務繞來繞去,還挺令人迷惑的袋哼,不過好在我們都知道它是走哪一條路了冀墨。所以在第4步,Handler開啟下載任務的時候先嬉,我們就發(fā)出一個通知,告訴大家:下載已經(jīng)開始啦楚堤!
代碼如下:
Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_INIT:
···
// 發(fā)送啟動下載的通知
Intent intent = new Intent(ACTION_START);
intent.putExtra("fileInfo", fileInfo);
sendBroadcast(intent);
break;
}
};
};
分析了一堆疫蔓,我們終于獲得了開始下載的通知了,然后就能在MainActivity中的廣播接收器中接收到這條廣播身冬,然后彈出通知欄
MainActivity中開啟通知欄
首先我們要接收到開啟下載ACTION_START的這條廣播衅胀,但是之前注冊的廣播接收器并沒有包含這條廣播,因此要添加這條代碼:
intentFilter.addAction(DownloadService.ACTION_START);
然后我們需要一個NotificationUtil成員對象酥筝,在onCreate中初始化它滚躯。
最后我們修改廣播接收者內(nèi)部類的代碼,代碼如下:
class UIRecive extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
// 更新進度條的時候
int finished = intent.getIntExtra("finished", 0);
int id = intent.getIntExtra("id", 0);
mAdapter.updataProgress(id, finished);
mNotificationUtil.updataNotification(id, finished);
} else if (DownloadService.ACTION_FINISHED.equals(intent.getAction())){
// 下載結(jié)束的時候
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
mAdapter.updataProgress(fileInfo.getId(), 0);
Toast.makeText(MainActivity.this, mFileList.get(fileInfo.getId()).getFileName() + "下載完畢", Toast.LENGTH_SHORT).show();
// 下載結(jié)束后取消通知
mNotificationUtil.cancelNotification(fileInfo.getId());
} else if (DownloadService.ACTION_START.equals(intent.getAction())){
// 下載開始的時候啟動通知欄
mNotificationUtil.showNotification((FileInfo) intent.getSerializableExtra("fileInfo"));
}
}
}
這是我們開始下載的時候就能彈出通知欄,來下載進行時能更新通知欄的進度掸掏,最后下載完成能夠自動取消通知欄茁影。
一個多線程多文件外加通知欄顯示的下載器終于完成了,可以直接測試了丧凤。
總結(jié)
一個小小的簡陋的項目終于完成了募闲!但是對于剛?cè)腴T的小伙伴們相信還是廢了不少的功夫。
在這個項目中愿待,我們運用的不再是單一的組件只是浩螺,而是將組件綜合運用起來,如何在listView中操作仍侥,數(shù)據(jù)庫如何增刪改查要出,Service如何與Activity通信,Notification通知欄又是怎樣顯示的····
這些組件我們都刷了一遍农渊,相信下次再次使用的時候就不會像剛開始一樣無從下手了患蹂。
這個項目看上去貌似不錯,但仔細思量仍是有種種的不足之處腿时,還擁有一些BUG待解決况脆。而且在Activity與Service之間的通信用BroadCast廣播,雖然會更簡單些批糟,但對于真正的項目而已可能不是這樣的格了。
因為廣播是系統(tǒng)組件,這樣大材小用是資源的浪費徽鼎,而且效率是偏低的盛末。在一個項目中的單線程多進程中,應該使用Handler加上Messenger進行通信的否淤,這有待于大家學習悄但。
好了,話就說到這里石抡,這個項目的Github地址是:
https://github.com/liaozhoubei/MultiDownload
歡迎大家下載檐嚣,如果發(fā)現(xiàn)有BUG,也可以通知我