-
單例模式查看全部
-
文件列表控制查看全部
-
ViewHolder 定義成靜態(tài)內(nèi)部類,保證在整個(gè)程序運(yùn)行過程中只加載一次;如果定義成非靜態(tài)內(nèi)部類,則每當(dāng)new 一個(gè)listadapter時(shí)都會(huì)加載一次,到時(shí)內(nèi)存溢出查看全部
-
多線程下載原理查看全部
-
上一個(gè)課程的斷點(diǎn)下載的缺點(diǎn):采用單線程,無法同時(shí)下載多個(gè)文件。 總結(jié)上個(gè)課程內(nèi)容:斷點(diǎn)下載使用到的核心類RandomAccessFile隨機(jī)讀取文件類,HttpUrlConnection類設(shè)置Range請求頭指定開始位置和結(jié)束位置,這里的位置是字節(jié)數(shù)據(jù)的位置查看全部
-
盡量吧對數(shù)據(jù)庫的訪問放到線程外面去,減少對數(shù)據(jù)庫的鎖定的產(chǎn)生查看全部
-
使用線程池啟動(dòng)線程 1.線程池的基本介紹 (如圖) newCachedThreadPool() 帶緩存的線程池,當(dāng)待執(zhí)行的線程減少時(shí),會(huì)將多余的線程池回收放入緩存中;線程多時(shí),會(huì)從緩存中再取出來使用。數(shù)量大小沒有限制 newFixedThreadPool(int)固定線程數(shù)量的線程池 newScheduledThreadPool()大小沒有限制,可以周期性定時(shí)執(zhí)行某個(gè)線程 newSingleThreadExecutor()同一時(shí)間只有一個(gè)線程在執(zhí)行,所有線程會(huì)排隊(duì)等待被執(zhí)行 2.創(chuàng)建一個(gè)靜態(tài)線程池單例,整個(gè)程序中只有一個(gè) public static ExecutorService sExecutorService = Executors.newCachedThreadPool(); //創(chuàng)建一個(gè)線程池 每處執(zhí)行線程的地方都用如下方法執(zhí)行: DownloadTask.sExecutorService.execute(new InitThread(fileInfo)); 3.優(yōu)化點(diǎn): (1)FileLIstAdapter中g(shù)etView()優(yōu)化: 將每個(gè)下載文件對應(yīng)的view在第一次flate成convertView時(shí)就初始化 (2)線程池 (3)進(jìn)度條更新時(shí)間是1s一次 (4)第一次下載一個(gè)文件時(shí),其對應(yīng)的DownloadTask中創(chuàng)建的DownloadThread在第一次開始下載時(shí)就將創(chuàng)建的線程信息保存到數(shù)據(jù)庫中查看全部
-
數(shù)據(jù)庫訪問修改為線程安全 1.將DBHelper extends SQLiteOpenHelper修改為單例模式,這樣整個(gè)程序運(yùn)行只有一個(gè)dataBaseHelper實(shí)例在操作數(shù)據(jù)庫 (1)在這個(gè)類中,實(shí)例是靜態(tài)的,只有一個(gè) private static DBHelper sHelper; (2)單例模式,將構(gòu)造函數(shù)私有化 private DBHelper(Context context) { super(context, DB_NAME, null, VERSION); } (3)單例模式,只有在實(shí)例未創(chuàng)建時(shí)才會(huì)new一個(gè),new過之后會(huì)直接返回它 public static DBHelper geInstance(Context context){ if (sHelper == null){ //sHelper是static,所有它只會(huì)有一個(gè)(單例) sHelper = new DBHelper(context); } return sHelper; } 2.將操作數(shù)據(jù)增刪改的操作都要聲明為同步方法 public synchronized void insertThread(ThreadInfo threadInfo) { }查看全部
-
修改downloadService 1.因?yàn)槭嵌鄠€(gè)文件的下載,每個(gè)文件對應(yīng)一個(gè)DownloadTask,當(dāng)點(diǎn)擊停止按鈕時(shí),downloadService需要知道是哪個(gè)文件對應(yīng)的Task被暫停 這里就創(chuàng)建了一個(gè)Map,key對應(yīng)文件的id,value對應(yīng)DownloadTask private Map<Integer, DownloadTask> mTasks = new LinkedHashMap<Integer, DownloadTask>(); //下載任務(wù)的集合 2.對應(yīng)onStartCommand()如下 public int onStartCommand(Intent intent, int flags, int startId) { if ((intent == null))return 0; if (ACTION_START == intent.getAction()){ FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo"); //啟動(dòng)初始化線程來獲得文件的長度 DownloadTask.sExecutorService.execute(new InitThread(fileInfo)); } else if (ACTION_STOP == intent.getAction()){ FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo"); //從集合中取出點(diǎn)擊文件對應(yīng)的下載,然后讓其停止 DownloadTask task = mTasks.get(fileInfo.getId()); if (task != null){ task.isPause = true; } } }查看全部
-
修改DownloadThread線程run() 1.因?yàn)楝F(xiàn)在是多線程下載了,所以每次停止時(shí)保存進(jìn)數(shù)據(jù)庫中的應(yīng)該是每個(gè)線程完成的進(jìn)度,關(guān)鍵代碼如下: while((len = is.read(buffer)) != -1){ raf.write(buffer, 0, len); //mFinished是全局的,每個(gè)線程下載一點(diǎn)它就會(huì)加上去,所以它表示整個(gè)文件的完成進(jìn)度 mFinished += len; //累加整個(gè)文件的下載進(jìn)度 mThreadInfo.setFinished(mThreadInfo.getFinished() + len); //累加每個(gè)線程的下載進(jìn)度 if () { //1s才會(huì)發(fā)送一個(gè)廣播,發(fā)送內(nèi)容包括文件id(位置)和完成進(jìn)度,這樣界面不卡 intent.putExtra("id", mFileInfo.getId()); intent.putExtra("finished", mFinished); mContext.sendBroadcast(intent); } //在下載暫停時(shí),保存每個(gè)線程當(dāng)前的下載進(jìn)度 if (isPause){ mDao.updateThread(……,mThreadInfo.getFinished()); return; } } 2.在一個(gè)downloadThread執(zhí)行完畢后檢查時(shí)候都已經(jīng)執(zhí)行完 判斷所有線程是否都已執(zhí)行完畢,thread.isFinished初始值是false,只有執(zhí)行完才會(huì)置為true 若都已經(jīng)執(zhí)行完表示文件已經(jīng)下載完畢,發(fā)送一個(gè)廣播給主線程,把已經(jīng)下載完的文件回傳 這是一個(gè)同步方法,因?yàn)橐WC檢查正確性,同一時(shí)間只有一個(gè)線程調(diào)用 private synchronized void checkAllThreadsFinished(){ (代碼如圖) 每個(gè)線程執(zhí)行完都會(huì)調(diào)用這個(gè)方法查看全部
-
多線程下載一個(gè)文件 1.原理如圖,之前在Http通信課程中多線程下載相關(guān)內(nèi)容已經(jīng)學(xué)過了 http://idcbgp.cn/space/notelist/uid/1859625/cid/304 2.修改DownloadTask這個(gè)類的內(nèi)容 (1)構(gòu)造函數(shù)多一個(gè)參數(shù),用來指定線程數(shù) (2)創(chuàng)建一個(gè)線程池,用來執(zhí)行DownloadTask的每個(gè)DownloadThread線程 public static ExecutorService sExecutorService = Executors.newCachedThreadPool(); //創(chuàng)建一個(gè)線程池 (3)重新修改downloadFile()這個(gè)函數(shù) public void downloadFile(){ …… //還沒開始下載,新創(chuàng)建多個(gè)線程信息,分別完成各自的下載長度 int length = mFileInfo.getLength() / mThreadCount; for (int i = 0; i < mThreadCount; i++){ ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), i*length,(i+1)*length - 1, 0); if (i == mThreadCount - 1){ //最后一個(gè)線程 threadInfo.setEnd(mFileInfo.getLength()); } //添加到線程信息集合中 threadInfos.add(threadInfo); …… } } //創(chuàng)建多個(gè)子線程并開始下載(線程數(shù)由DownloadService調(diào)用構(gòu)造函數(shù)指定) 注意:這里的threadInfos線程信息集合可能是數(shù)據(jù)庫中讀出來的(已經(jīng)下載一部分了),也可能是新創(chuàng)建的 for { DownloadTask.sExecutorService.execute(downloadThread); //取出線程放入線程池中執(zhí)行 mThreads.add(downloadThread); } } 代碼看AS,注釋寫的很清楚查看全部
-
顯示多個(gè)下載文件——MainActivity 1.主要完成數(shù)據(jù)源的初始化,然后創(chuàng)建適配器,并設(shè)置給listView (如圖) 2.注意接下來的一點(diǎn)本來是在課程后面講的,在這里就寫出來,下面可能還會(huì)寫。 (1)注冊BroadcastReceiver新的action,用來接收文件下載完的廣播 IntentFilter filter = new IntentFilter(); filter.addAction(DownloadService.ACTION_FINISH); registerReceiver(mReceiver, filter); (2)onReceive() public void onReceive(Context context, Intent intent) { if (ACTION_UPDATE.equals(intent.getAction())){ int finished = intent.getIntExtra("finished", 0); int id = intent.getIntExtra("id", 0); //因?yàn)楝F(xiàn)在是多個(gè)任務(wù),當(dāng)收到相關(guān)廣播時(shí),需要指定是哪個(gè)文件,然后才能設(shè)置其進(jìn)度條,需要在DownloadTask中發(fā)送廣播更新進(jìn)度條時(shí),不僅要發(fā)送已完成進(jìn)度,還要發(fā)送文件的id用以標(biāo)識(shí) mAdapter.updateProgress(id, finished); }else if (ACTION_FINISH){ mAdapter.updateProgress(id, 100); //進(jìn)度直接就是100 } } (3)FileListAdapter中: 更新列表項(xiàng)中對應(yīng)文件的下載進(jìn)度,調(diào)用notifyDataSetChanged()會(huì)使Adapter重新加載,getView()會(huì)重新執(zhí)行,對應(yīng)View進(jìn)度條就會(huì)設(shè)置其完成進(jìn)度 public void updateProgress(int id, int progress){ FileInfo fileInfo = mFileList.get(id); fileInfo.setFinished(progress); notifyDataSetChanged(); //整個(gè)adapter會(huì)被重新加載 }查看全部
-
修改界面顯示多個(gè)下載文件 1.布局修改 (1)原來的每個(gè)item的布局放在一個(gè)新創(chuàng)建的list_item.xml中 (2)主布局只有一個(gè)listView 2.創(chuàng)建listView對應(yīng)的適配器FileListAdapter (1)它需要從MainActivity中傳入的是兩個(gè)參數(shù),在構(gòu)造函數(shù)中初始化 private Context mContext; private List<FileInfo> mFileList; //ListView的數(shù)據(jù)源 (2)實(shí)現(xiàn)關(guān)鍵的getView()方法,需要注意的是在convertView為空進(jìn)行inflate()出來時(shí)就為所有的view控件進(jìn)行初始化,包括button的點(diǎn)擊監(jiān)聽。下次convertView非空時(shí)也不用再設(shè)置了,提高效率。 if (convertView==null){ convertView = mInflater.inflate(R.layout.list_item, null); //將layout布局轉(zhuǎn)換成view視圖 viewHolder = new ViewHolder(); viewHolder.textView = convertView.findViewById(R.id.textView); viewHolder.btToggle = ……(R.id.btToggle); viewHolder.progressBar = ……(R.id.progressBar); //初始化并設(shè)置視圖中的控件 viewHolder.textView.setText(fileInfo.getFileName()); viewHolder.progressBar.setMax(100); //為對應(yīng)的每個(gè)按鈕設(shè)置監(jiān)聽事件 viewHolder.btToggle.setOnClickListener(……); convertView.setTag(viewHolder); } //根據(jù)已經(jīng)完成的進(jìn)度來更新進(jìn)度條 viewHolder.progressBar.setProgress(fileInfo.getFinished()); 3.對應(yīng)ViewHolder要聲明為靜態(tài)內(nèi)部類 在程序運(yùn)行期間只會(huì)加載一次,在創(chuàng)建FileListAdapter時(shí)只會(huì)加載一次,節(jié)省內(nèi)存。查看全部
舉報(bào)
0/150
提交
取消