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