【声明】html
欢迎转载,但请保留文章原始出处→_→java
生命壹号:http://www.cnblogs.com/smyhvae/android
文章来源:http://www.cnblogs.com/smyhvae/p/3866570.html
数据库
【正文】apache
本文将讲解一下Android的多线程的知识,以及如何经过AsyncTask机制来实现线程之间的通讯。编程
1、Android当中的多线程:安全
在Android当中,当一个应用程序的组件启动的时候,而且没有其余的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行。默认的状况下,在一个相同Android应用程序当中,里面的组件都是运行在同一个线程里的,这个线程称之为Main线程。当咱们经过某个组件来启动另外一个组件的时候,这个时候默认都是在同一个线程当中完成的。固然,咱们能够本身来管理咱们的Android应用的线程,咱们能够根据咱们本身的须要来给应用程序建立额外的线程。微信
2、Main Thread 和 Worker Thread:网络
在Android当中,一般将线程分为两种,一种叫作Main Thread,除了Main Thread以外的线程均可称为Worker Thread。多线程
当一个应用程序运行的时候,Android操做系统就会给该应用程序启动一个线程,这个线程就是咱们的Main Thread,这个线程很是重要,它主要用来加载UI界面,完成系统和用户之间的交互,并将交互后的结果又展现给用户,因此Main Thread又被称为UI Thread。
Android系统默认不会给应用程序组件建立一个额外的线程,全部的这些组件默认都是在同一个线程中运行。然而,某些时候当咱们的应用程序须要完成一个耗时操做的时候(如访问网络或者是对数据库进行查询),此时UI Thread就会被阻塞。例如,当咱们点击一个Button,而后但愿其从网络中获取一些数据,若是此操做在UI Thread当中完成的话,UI线程就会处于阻塞的状态,此时,咱们的系统不会调度任何其它的事件,更糟糕的是,当咱们的整个现场若是阻塞时间超过5秒钟(官方描述),这个时候就会出现 著名的ANR (Application Not Responding)的问题,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来讲,出现ANR的现象是绝对不能被容许的。
另外,因为Android UI控件不是线程安全的,因此咱们不能在UI Thread以外的线程当中对UI控件进行操做。所以在Android的多线程编程当中,有两条很是重要的原则必须遵照:
3、如何处理UI Thread 和 Worker Thread之间的通讯:
咱们既不能在主线程当中处理耗时的操做,又不能在工做线程中来访问咱们的UI控件,那么咱们好比从网络中要下载一张图片,又怎么能将其更新到UI控件上呢?这就关系到了主线程和工做线程之间的通讯问题了。在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通讯问题,一种是通过Handler的机制(这种方式在后面的博客中将详细介绍),还有一种就是今天要详细讲解的 AsyncTask 机制。
4、AsyncTask:
AsyncTask:异步任务,从字面上来讲,就是在UI主线程运行的时候,异步完成一些操做。AsyncTask容许咱们在后台执行一个异步任务。咱们能够将耗时的操做放在异步任务当中来执行,并随时将任务执行的结果返回给UI线程来更新UI控件。经过AsyncTask咱们能够轻松解决多线程之间的通讯问题。
怎么来理解AsyncTask呢?通俗来讲,AsyncTask就至关于Android给咱们提供了一个多线程编程的一个框架,其介于Thread和Handler之间,咱们若是要定义一个AsyncTask,就须要定义一个类来继承AsyncTask这个抽象类,并实现其惟一的一个 doInBackgroud 抽象方法。
要掌握AsyncTask,咱们就必需要一个概念,总结起来就是: 3个泛型,4个步骤。
咱们来看看AsyncTask这个抽象类的定义,当咱们定义一个类来继承AsyncTask这个类的时候,须要为其指定3个泛型参数:
AsyncTask <Params, Progress, Result>
咱们在定义一个类继承AsyncTask类的时候,必须指定好这三个泛型的类型,若是都不指定的话,则都将其写成Void,例如:
AsyncTask <Void, Void, Void>
4个步骤:当咱们执行一个异步任务时,须要按照下面的4个步骤分别执行:
为何AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??缘由是,咱们若是要作一个异步任务,咱们必需要为其开辟一个新的Thread,让其完成一些操做,而在完成这个异步任务时,我可能并不须要弹出ProgressDialog,并不须要随时更新ProgressDialog的进度条,也并不须要将结果更新给UI界面,因此除了 doInBackground 方法以外的三个方法,都不是必须有的,所以必需要实现的方法是 doInBackground 方法。
4个步骤简洁版描述以下:
第一步:表示任务执行前的操做
第二步:主要完成耗时操做
第三步:主要是更新UI操做
第四步:产生最终结果
如下实例中表明的含义为:
第一步:显示进度条
第二步:(此任务必不可少)在后台执行任务,将进度值传给第三步,将结果传给第四步;
第三步:进度值更新
第四步:产生最终结果
5、【实例】经过AsyncTask来从网络上下载一张图片:
下面经过两个代码示例,来看看如何经过AsyncTask来从网络上下载一张图片,并更新到ImageView控件上。
在这以前,必需要执行的操做是:添加网络受权。下面的例子将有详细的操做描述。
【实例一】从网络下载图片,弹出一个ProgressDialog,但不显示实时进度:
1、添加网络受权:
由于手机默认不能访问网络,因此首先要在清单文件 AndroidManifest.xml中添加网络受权。
方法以下:
打开AndroidManifest.xml文件,点击"Permissions"按钮,而后点击“Add”:
弹出对话框后,选择最后一项:Users Permission:
接着选择其中的Internet选项:
按Ctrl+S保存便可。
紧接着,咱们在清单文件中就能看到对应自动生成的代码:
其实,直接在AndroidManifest.xml文件中添加上这一行代码也是能够的。
二、完整版代码以下:
activity_main.xml的代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" >
<ImageView android:id="@+id/imageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" />
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="136dp" android:text="下载网络图片" />
</RelativeLayout>
MainActivity.java的代码:
package com.example.downloadimage01; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView imageView ; private Button button ; private ProgressDialog dialog ; //来自网络的图片
private String image_path = "http://imgsrc.baidu.com/forum/pic/item/7c1ed21b0ef41bd51a5ac36451da81cb39db3d10.jpg" ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //添加弹出的对话框
dialog = new ProgressDialog(this) ; dialog.setTitle("提示") ; dialog.setMessage("正在下载图片,请稍后···") ; imageView = (ImageView)findViewById(R.id.imageView1) ; button = (Button)findViewById(R.id.button1) ; button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //点击按钮时,执行异步任务的操做
new DownTask().execute(image_path) ; } }) ; //注意,这个地方的分号容易遗忘
} /* * 异步任务执行网络下载图片 * */
public class DownTask extends AsyncTask<String, Void, Bitmap> { //上面的方法中,第一个参数:网络图片的路径,第二个参数的包装类:进度的刻度,第三个参数:任务执行的返回结果
@Override //在界面上显示进度条
protected void onPreExecute() { dialog.show() ; }; protected Bitmap doInBackground(String... params) { //三个点,表明可变参数 //使用网络连接类HttpClient类完成对网络数据的提取
HttpClient httpClient = new DefaultHttpClient() ; HttpGet httpget = new HttpGet(params[0]) ; Bitmap bitmap = null ; try { HttpResponse httpResponse = httpClient.execute(httpget) ; if(httpResponse.getStatusLine().getStatusCode()==200){ HttpEntity httpEntity = httpResponse.getEntity() ; byte[] data = EntityUtils.toByteArray(httpEntity); bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } } catch (Exception e) { // TODO Auto-generated catch block
e.printStackTrace(); } return bitmap; } //主要是更新UI
@Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); imageView.setImageBitmap(result) ;//更新UI
dialog.dismiss() ; } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu); return true; } }
【实例二】从网络下载图片,弹出能显示进度值的对话框:
注:既然要显示进度值,因此此处的进度条风格要设置为水平。
一、添加网络受权(同上)
二、完整版代码以下:
activity_main.xml的代码和【实例一】中的同样;
MainActivity.java的代码:
1 package com.example.smyh001downloadimage01; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.InputStream; 5 6 import org.apache.http.HttpEntity; 7 import org.apache.http.HttpResponse; 8 import org.apache.http.client.HttpClient; 9 import org.apache.http.client.methods.HttpGet; 10 import org.apache.http.impl.client.DefaultHttpClient; 11 12 import android.app.Activity; 13 import android.app.ProgressDialog; 14 import android.graphics.Bitmap; 15 import android.graphics.BitmapFactory; 16 import android.os.AsyncTask; 17 import android.os.Bundle; 18 import android.view.Menu; 19 import android.view.View; 20 import android.view.View.OnClickListener; 21 import android.widget.Button; 22 import android.widget.ImageView; 23 24 public class MainActivity extends Activity { 25 26 private ImageView imageView ; 27 private Button button ; 28 private ProgressDialog dialog ; 29 //来自网络的图片 30 private String image_path = "http://imgsrc.baidu.com/forum/pic/item/7c1ed21b0ef41bd51a5ac36451da81cb39db3d10.jpg" ; 31 @Override 32 protected void onCreate(Bundle savedInstanceState) { 33 super.onCreate(savedInstanceState); 34 setContentView(R.layout.activity_main); 35 36 //添加弹出的对话框 37 dialog = new ProgressDialog(this) ; 38 dialog.setTitle("提示") ; 39 dialog.setMessage("正在下载图片,请稍后···") ; 40 //将进度条设置为水平风格,让其可以显示具体的进度值 41 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL) ; 42 dialog.setCancelable(false) ; //用了这个方法以后,直到图片下载完成,进度条才会消失(即便在这以前点击了屏幕) 43 44 imageView = (ImageView)findViewById(R.id.imageView1) ; 45 button = (Button)findViewById(R.id.button1) ; 46 button.setOnClickListener(new OnClickListener() { 47 48 @Override 49 public void onClick(View v) { 50 //点击按钮时,执行异步任务的操做 51 new DownTask().execute(image_path) ; 52 } 53 }) ; //注意,这个地方的分号容易遗忘 54 55 } 56 57 58 /* 59 * 异步任务执行网络下载图片 60 * */ 61 public class DownTask extends AsyncTask<String, Integer, byte[]> { 62 //上面的方法中,第一个参数:网络图片的路径,第二个参数的包装类:进度的刻度,第三个参数:任务执行的返回结果 63 @Override 64 //在界面上显示进度条 65 protected void onPreExecute() { 66 dialog.show() ; 67 }; 68 69 protected byte[] doInBackground(String... params) { //三个点,表明可变参数 70 //使用网络连接类HttpClient类完成对网络数据的提取,即完成对图片的下载功能 71 HttpClient httpClient = new DefaultHttpClient() ; 72 HttpGet httpget = new HttpGet(params[0]) ; 73 byte[] result = null ; 74 ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ; 75 InputStream inputStream = null ; 76 77 try { 78 HttpResponse httpResponse = httpClient.execute(httpget) ; 79 if(httpResponse.getStatusLine().getStatusCode()==200){ 80 81 HttpEntity httpEntiry = httpResponse.getEntity(); 82 inputStream = httpEntiry.getContent(); 83 // 先要得到文件的总长度 84 long file_length = httpResponse.getEntity().getContentLength() ; 85 int len = 0 ; 86 // 每次读取1024个字节 87 byte[] data = new byte[1024] ; 88 // 每次读取后累加的长度 89 int total_length = 0 ; 90 while ((len = inputStream.read(data))!=-1) { 91 // 每读一次,就将total_length累加起来 92 total_length+=len ; 93 // 获得当前图片下载的进度 94 int progress_value = (int) ((total_length / (float)file_length)*100); 95 // 时刻将当前进度更新给onProgressUpdate方法 96 publishProgress(progress_value) ; 97 outputStream.write(data, 0, len); 98 } 99 // 边读边写到ByteArrayOutputStream当中 100 result = outputStream.toByteArray(); 101 //bitmap = BitmapFactory.decodeByteArray(result, 0, result.length) ; 102 } 103 } catch (Exception e) { 104 // TODO Auto-generated catch block 105 e.printStackTrace(); 106 } finally { 107 httpClient.getConnectionManager().shutdown(); 108 } 109 return result; 110 } 111 112 @Override 113 protected void onProgressUpdate(Integer... values) { 114 // TODO Auto-generated method stub 115 super.onProgressUpdate(values); 116 // 更新ProgressDialog的进度条 117 dialog.setProgress(values[0]); 118 } 119 120 //主要是更新UI 121 @Override 122 protected void onPostExecute(byte[] result) { 123 super.onPostExecute(result); 124 // 将doInBackground方法返回的byte[]解码成要给Bitmap 125 Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length) ; 126 // 更新咱们的ImageView控件 127 imageView.setImageBitmap(bitmap) ;//更新UI 128 // 使ProgressDialog框消失 129 dialog.dismiss() ; 130 } 131 } 132 133 @Override 134 public boolean onCreateOptionsMenu(Menu menu) { 135 // Inflate the menu; this adds items to the action bar if it is present. 136 getMenuInflater().inflate(R.menu.main, menu); 137 return true; 138 } 139 140 }
运行后,显示结果以下:
【工程文件】
连接:http://pan.baidu.com/s/1dDvhXkt
密码:47ce
6、AsyncTask的重要知识点:
明白了AsyncTask的工做原理后,继续补充一下AsyncTask的一些其余知识点:
一、Cancelling a Task
咱们能够在任什么时候刻来取消异步任务的执行,经过调用 cancel(boolean)方法,调用完这个方法后系统会随后调用 isCancelled() 方法而且返回true。若是调用了这个方法,那么在 doInBackgroud() 方法执行完以后,就不会调用 onPostExecute() 方法了,取而代之的是调用 onCancelled() 方法。若是有必要的话,为了确保Task已经被取消了,咱们须要常常调用 isCancelled() 方法来判断。
二、在使用AsyncTask作异步任务的时候必需要遵循的原则:
【总结】
到此,有关AsyncTask的总结就到此为止了,本文主要讲解了Android中的多线程知识,而且详细地讲解了 AsyncTask 异步任务的概念和实现机制,并经过实例来了解 AsyncTask 的执行过程,最后还补充了 AsyncTask 的一些重要知识点,包括如何取消一个 AsyncTask 以及在使用 AsyncTask 时所必须遵循的规则。建议初学者(包括我)在理解这方面的问题时能够多参考官方的API文档。
想学习代码以外的软技能?不妨关注个人微信公众号:生命团队(id:vitateam)。
扫一扫,你将发现另外一个全新的世界,而这将是一场美丽的意外: