标签:
本章讲述在android开发中,多线程的应用。多线程能够处理耗时的操作并优化程序的性能。本章主要介绍知识点,AsyncTask,Java线程池,ThreadPoolExecutor线程池类。本章案例只是抛砖引玉,在多线程应用方面,推荐研究大文件断点续传下载文件方面的应用。
用AsyncTask来实现文件下载,要求:
图1 AsyncTask下载文件
图2 导航界面
图3 线程池下载
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程.每个线程都使用默认的堆栈大小, 以默认的优先级运行,并处于多线程单元中.如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙. 如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值.超过最大值的线 程可以排队,但他们要等到其他线程完成后才启动。
1.参数说明
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。当一个任务通过execute(Runnable)方法欲添加到线程池时:
处理任务的优先级为:
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
线程池原生使用案例代码如下:
1 int corePoolSize = 1; 2 int maximumPoolSize = 2; 3 int keepAliveTime = 10; 4 BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(5); 5 ThreadFactory threadFactory = Executors.defaultThreadFactory(); 6 //线程池和队列满了之后的处理方式 7 //1.抛出异常 8 RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); 9 RejectedExecutionHandler handler2 = new ThreadPoolExecutor.CallerRunsPolicy(); 10 RejectedExecutionHandler handler3 = new ThreadPoolExecutor.DiscardPolicy(); 11 RejectedExecutionHandler handler4 = new ThreadPoolExecutor.DiscardOldestPolicy(); 12 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, threadFactory, handler2); 13 for (int j = 1; j < 15; j++) { 14 threadPoolExecutor.execute(new Runnable() { 15 public void run() { 16 17 try { 18 System.out.println(Thread.currentThread().getName()); 19 TimeUnit.SECONDS.sleep(1); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 24 25 } 26 }); 27 } 28 29 System.out.println(threadPoolExecutor); 30 31 }
1 public static ExecutorService newFixedThreadPool(int nThreads) { 2 return new ThreadPoolExecutor(nThreads, nThreads, 3 0L, TimeUnit.MILLISECONDS, 4 new LinkedBlockingQueue<Runnable>()); 5 } 6 public ThreadPoolExecutor(int corePoolSize, 7 int maximumPoolSize, 8 long keepAliveTime, 9 TimeUnit unit, 10 BlockingQueue<Runnable> workQueue) { 11 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 12 Executors.defaultThreadFactory(), defaultHandler); 13 }
newCachedThreadPool比较适合没有固定大小并且比较快速就能完成的小任务,没必要维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已创建的线程。
1 public static ExecutorService newCachedThreadPool() { 2 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 3 60L, TimeUnit.SECONDS, 4 new SynchronousQueue<Runnable>()); 5 } 6 public ThreadPoolExecutor(int corePoolSize, 7 int maximumPoolSize, 8 long keepAliveTime, 9 TimeUnit unit, 10 BlockingQueue<Runnable> workQueue) { 11 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 12 Executors.defaultThreadFactory(), defaultHandler); 13 }
newSingleThreadExecutor单线程原理:
1 public static ExecutorService newSingleThreadExecutor() { 2 return new FinalizableDelegatedExecutorService 3 (new ThreadPoolExecutor(1, 1, 4 0L, TimeUnit.MILLISECONDS, 5 new LinkedBlockingQueue<Runnable>())); 6 }
Java通过Executors提供四种线程池,分别为:
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调 用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。示例代码如下:
1 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 2 for (int i = 0; i < 10; i++) { 3 final int index = i; 4 try { 5 Thread.sleep(index * 1000); 6 } catch (InterruptedException e) { 7 e.printStackTrace(); 8 } 9 10 cachedThreadPool.execute(new Runnable() { 11 12 @Override 13 public void run() { 14 System.out.println(index); 15 } 16 }); 17 }
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失 败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。案例代码如下:
1 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); 2 for (int i = 0; i < 10; i++) { 3 final int index = i; 4 fixedThreadPool.execute(new Runnable() { 5 6 @Override 7 public void run() { 8 try { 9 System.out.println(index); 10 Thread.sleep(2000); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 } 16 }); 17 }
因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:
1 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 2 scheduledThreadPool.schedule(new Runnable() { 3 @Override 4 public void run() { 5 System.out.println("delay 3 seconds"); 6 } 7 }, 3, TimeUnit.SECONDS);
表示延迟3秒执行。
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行 后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。示例代码如下:
1 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); 2 for (int i = 0; i < 10; i++) { 3 final int index = i; 4 singleThreadExecutor.execute(new Runnable() { 5 @Override 6 public void run() { 7 try { 8 System.out.println(index); 9 Thread.sleep(2000); 10 } catch (InterruptedException e) { 11 // TODO Auto-generated catch block 12 e.printStackTrace(); 13 } 14 } 15 }); 16 }
结果依次输出,相当于顺序执行各个任务。
AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。
AsyncTask定义了三种泛型类型 Params,Progress和Result。
使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:
有必要的话你还得重写以下这三个方法,但不是必须的:
使用AsyncTask类,以下是几条必须遵守的准则:
1.AsyncTask文件下载逻辑函数:
1 private class DownloadByAsyncTask extends AsyncTask<String, Integer, String> { 2 //onPreExecute方法用于在执行后台任务前做一些UI操作 3 @Override 4 protected void onPreExecute() { 5 Log.i(TAG, "onPreExecute() called"); 6 progressDialog.show(); 7 result.setText("loading..."); 8 } 9 10 //doInBackground方法内部执行后台任务,不可在此方法内修改UI 11 @Override 12 protected String doInBackground(String... params) { 13 Log.i(TAG, "doInBackground(Params... params) called"); 14 String mark=""; 15 try 16 { 17 URL url = new URL(params[0]); 18 URLConnection urlConnection = url.openConnection(); 19 InputStream inputStream = urlConnection.getInputStream(); 20 int contentLength = urlConnection.getContentLength(); // 要下载的文件的大小 21 Log.i(TAG, "the download file‘s content length: " + contentLength); 22 // File.separator 表示文件路径分割线// 23 String downloadFoldersName = Environment.getExternalStorageDirectory() + File.separator + GEEK_BAND + File.separator; 24 File file = new File(downloadFoldersName); 25 if (!file.exists()) { 26 file.mkdir(); 27 } 28 29 String fileName = downloadFoldersName + "test.apk"; 30 File apkFile = new File(fileName); 31 if (apkFile.exists()) { 32 apkFile.delete(); 33 } 34 int downloadSize = 0; 35 byte[] bytes = new byte[1024]; 36 int length = 0; 37 OutputStream outputStream = new FileOutputStream(fileName); 38 while ((length = inputStream.read(bytes)) != -1) { 39 outputStream.write(bytes, 0, length); 40 downloadSize += length; 41 int progress = downloadSize * 100 / contentLength; 42 mark=String.valueOf(progress); 43 publishProgress(progress); 44 Log.i(TAG, "download progress: " + progress); 45 } 46 Log.i(TAG, "download success"); 47 inputStream.close(); 48 outputStream.close(); 49 } catch (IOException e) { 50 e.printStackTrace(); 51 Log.i(TAG, "download failure"); 52 } 53 return mark; 54 } 55 56 //onProgressUpdate方法用于更新进度信息 57 @Override 58 protected void onProgressUpdate(Integer... progresses) { 59 Log.i(TAG, "onProgressUpdate(Progress... progresses) called"); 60 // 更新ProgressDialog的进度条 61 progressDialog.setProgress(progresses[0]); 62 result.setText("loading..." + progresses[0] + "%"); 63 } 64 65 //onPostExecute方法用于在执行完后台任务后更新UI,显示结果 66 @Override 67 protected void onPostExecute(String results) { 68 Log.i(TAG, "onPostExecute(Result result) called"); 69 progressDialog.dismiss(); 70 result.setText("下载进度:"+results); 71 } 72 73 //onCancelled方法用于在取消执行中的任务时更改UI 74 @Override 75 protected void onCancelled() { 76 Log.i(TAG, "onCancelled() called"); 77 result.setText("cancelled"); 78 } 79 }
2.线程池定义及执行代码:
1 newFixedThreadPool = Executors.newFixedThreadPool(1); 2 download.setOnClickListener(new View.OnClickListener() { 3 @Override 4 public void onClick(View arg0) { 5 if (HttpUtil.IsOnline(mContext)) { 6 result.setText("开始文件下载"); 7 progressDialog.show(); 8 newFixedThreadPool.execute(new Runnable() { 9 @Override 10 public void run() { 11 download(url_text.getText().toString()); 12 } 13 }); 14 } else { 15 result.setText("网络未连接"); 16 } 17 } 18 });
本项目源代码在360云盘上,开发环境为 Android Studio 2.0 。
https://yunpan.cn/cYamkZG3sq9jf 访问密码 f3d5。文件名称:android多线程操作。
标签:
原文地址:http://www.cnblogs.com/wlandwl/p/android_8.html