为了账号安全,请及时绑定邮箱和手机立即绑定

Android-Service系列之多线程断点续传下载

XRay_Chen 移动开发工程师
难度中级
时长 1小时32分
学习人数
综合评分9.83
28人评价 查看评价
9.9 内容实用
9.9 简洁易懂
9.7 逻辑清晰
  • 数据库访问修改为线程安全 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) { }
    查看全部
  • 学习内容
    查看全部
  • 截图重要。需要小心,是重要的经验。 通过 单例模式 + (同一个对象的)同步方式 = 实现对数据操作的线程安全。不然多个对象的话,其同步方法就没有效果了。
    查看全部
  • Adaptor中的getView方法就是视图和内容的融合,有一个ViewHolder的类比较方便做这个工作。
    查看全部
  • 把java的修饰再过一遍: 如static , final, private等等。这很重要。
    查看全部
  • 尽量吧对数据库的访问放到线程外面去,减少对数据库的锁定的产生
    查看全部
  • 使用线程池启动线程 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在第一次开始下载时就将创建的线程信息保存到数据库中
    查看全部
  • 修改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.imooc.com/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时只会加载一次,节省内存。
    查看全部
  • 尽量把对数据库的操作放到线程外去执行 这样比较容易对数据库进行锁定?
    查看全部
  • 多个线程访问数据库导致数据库锁定
    查看全部
  • notifyDataSetChanged()方法
    查看全部
首页上一页1234下一页尾页

举报

0/150
提交
取消
课程须知
适合有一定Android开发基础的同学,要求学生能够有很强的java中多线程以及线程池的基础,灵活使用service和多线程结合实现断点续传功能。
老师告诉你能学到什么?
Activity和Service的通信方法 ListView和BaseAdapter的使用 数据库操作 多线程分段下载的原理 线程池的使用 线程同步的应用 单例模式的应用 Notification的使用 Messenger的使用

微信扫码,参与3人拼团

意见反馈 帮助中心 APP下载
官方微信
友情提示:

您好,此课程属于迁移课程,您已购买该课程,无需重复购买,感谢您对慕课网的支持!