多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销。
这些开销反而会导致下载速度降低。因此需要避免在代码中直接开启大量线程执行下载。
主要实现步奏:
1、定义一个DownUtil类,下载工作基本在此类完成,在构造器中初始化UI线程的Handler。用于子线程和UI线程传递下载进度值。
2、所有的下载任务都保存在LinkedList。在init()方法中开启一个后台线程,不断地从LinkedList中取任务交给线程池中的空闲线程执行。
3、每当addTask方法添加一个任务,就向 mPoolThreadHandler发送条消息,就从任务队列中取出一个任务交给线程池执行。这里使用了使用了Semaphore信号量,也就是说只有当一个任务执行完成之后,release()一个信号量,才能从LinkedList中取出一个任务再去执行,否则acquire()方法会一直阻塞线程,直到上一个任务完成。
public class DownUtil { //定义下载资源的路径 private String path; //指定下载文件的保存位置 private String targetFile; //定义下载文件的总大小 private int fileSize; //线程池 private ExecutorService mThreadPool; //线程数量 private static final int DEFAULT_THREAD_COUNT = 5; //任务队列 private LinkedListmTasks; //后台轮询线程 private Thread mPoolThread; //后台线程的handler private Handler mPoolThreadHandler; //UI线程的Handler private Handler mUIThreadHandler; //信号量 private Semaphore semaphore; private Semaphore mHandlerSemaphore = new Semaphore(0); //下载线程数量 private int threadNum; public DownUtil(String path , String targetFile , int threadNum , final ProgressBar bar) { this.path = path; this.targetFile = targetFile; this.threadNum = threadNum; init(); mUIThreadHandler = new Handler() { int sumSize = 0; @Override public void handleMessage(Message msg) { if (msg.what == 0x123) { int size = msg.getData().getInt("upper"); sumSize += size; Log.d("sumSize" , sumSize + ""); bar.setProgress((int) (sumSize * 1.0 / fileSize * 100)); } } }; } private void init() { mPoolThread = new Thread() { public void run() { Looper.prepare(); mPoolThreadHandler = new Handler() { public void handleMessage(Message msg) { if (msg.what == 0x111) { mThreadPool.execute(getTask()); try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } } } }; mHandlerSemaphore.release(); Looper.loop(); } }; mPoolThread.start(); mThreadPool = Executors.newFixedThreadPool(DEFAULT_THREAD_COUNT); mTasks = new LinkedList<>(); semaphore = new Semaphore(DEFAULT_THREAD_COUNT); } public void downLoad() { try { URL url = new URL(path); HttpURLConnection cOnn= (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); conn.setRequestProperty( "Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " + "application/x-ms-application, application/vnd.ms-excel, " + "application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Connection", "Keep-Alive"); //得到文件的大小 fileSize = conn.getContentLength(); conn.disconnect(); int currentPartSize = fileSize / threadNum + 1; RandomAccessFile file = new RandomAccessFile(targetFile , "rw"); file.setLength(fileSize); file.close(); for (int i = 0 ; i 0) { currentPart.write(buffer , 0 , hasRead); //累计该线程下载的总大小 length += hasRead; } Log.d("length" , length + ""); //创建消息 Message msg = new Message(); msg.what = 0x123; Bundle bundle = new Bundle(); bundle.putInt("upper" , length); msg.setData(bundle); //向UI线程发送消息 mUIThreadHandler.sendMessage(msg); semaphore.release(); currentPart.close(); inStream.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void skipFully(InputStream in , long bytes) throws IOException { long remaining = bytes; long len = 0; while (remaining > 0) { len = in.skip(remaining); remaining -= len; } } }
以下是MainActivity的代码:
public class MainActivity extends Activity { EditText url; EditText target; Button downBn; ProgressBar bar; DownUtil downUtil; private String savePath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //获取界面中的四个界面控件 url = (EditText) findViewById(R.id.address); target = (EditText) findViewById(R.id.target); try { File sdCardDir = Environment.getExternalStorageDirectory(); savePath = sdCardDir.getCanonicalPath() + "/d.chm"; } catch (Exception e) { e.printStackTrace(); } target.setText(savePath); downBn = (Button) findViewById(R.id.down); bar = (ProgressBar) findViewById(R.id.bar); downBn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { downUtil = new DownUtil(url.getText().toString() , target.getText().toString() , 7 , bar); new Thread() { @Override public void run() { try { downUtil.downLoad(); } catch (Exception e) { e.printStackTrace(); } } }.start(); } }); } }
页面布局比较简单这里一并贴出:
此例主要是在李刚老师的《疯狂Java的讲义》的多线程的例子上修改,感谢李刚老师,如有不足之处,欢迎批评指正。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。