您能够经过点击 右下角 的按钮 来对文章内容做出评价, 也能够经过左下方的 关注按钮 来关注个人博客的最新动态。 若是文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 若是您对文章内容有任何疑问, 能够经过评论或发邮件的方式联系我: 609025984@qq.com 若是须要转载,请注明出处,谢谢!!
本篇随笔将讲解一下Android的多线程的知识,以及如何经过AsyncTask机制来实现线程之间的通讯。android
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当中完成的话,当咱们点击Button的时候,UI线程就会处于阻塞的状态,此时,咱们的系统不会调度任何其它的事件,更糟糕的是,当咱们的 整个现场若是阻塞时间超过5秒钟(官方是这样说的),这个时候就会出现 ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来讲,出现ANR的现象是绝对不能被容许 的。多线程
另外,因为咱们的Android UI控件是线程不安全的,因此咱们不能在UI Thread以外的线程当中对咱们的UI控件进行操做。所以在Android的多线程编程当中,咱们有两条很是重要的原则必需要遵照:app
绝对不能在UI Thread当中进行耗时的操做,不能阻塞咱们的UI Thread框架
不能在UI Thread以外的线程当中操纵咱们的UI元素异步
3、如何处理UI Thread 和 Worker Thread之间的通讯
既然在Android当中有两条重要的原则要遵照,那么咱们可能就有疑问了?咱们既不能在主线程当中处理耗时的操做,又不能在工做线程中来访问咱们 的UI控件,那么咱们好比从网络中要下载一张图片,又怎么能将其更新到UI控件上呢?这就关系到了咱们的主线程和工做线程之间的通讯问题了。在 Android当中,提供了两种方式来解决线程直接的通讯问题,一种是经过Handler的机制(这种方式在后面的随笔中将详细介绍),还有一种就是今天 要详细讲解的 AsyncTask 机制。
4、AsyncTask
AsyncTask:异步任务,从字面上来讲,就是在咱们的UI主线程运行的时候,异步的完成一些操做。AsyncTask容许咱们的执行一个异步 的任务在后台。咱们能够将耗时的操做放在异步任务当中来执行,并随时将任务执行的结果返回给咱们的UI线程来更新咱们的UI控件。经过AsyncTask 咱们能够轻松的解决多线程之间的通讯问题。
怎么来理解AsyncTask呢?通俗一点来讲,AsyncTask就至关于Android给咱们提供了一个多线程编程的一个框架,其介于 Thread和Handler之间,咱们若是要定义一个AsyncTask,就须要定义一个类来继承AsyncTask这个抽象类,并实现其惟一的一个 doInBackgroud 抽象方法。要掌握AsyncTask,咱们就必需要一个概念,总结起来就是: 3个泛型,4个步骤。
3个泛型指的是什么呢?咱们来看看AsyncTask这个抽象类的定义,当咱们定义一个类来继承AsyncTask这个类的时候,咱们须要为其指定3个泛型参数:
AsyncTask <Params, Progress, Result>
Params: 这个泛型指定的是咱们传递给异步任务执行时的参数的类型
Progress: 这个泛型指定的是咱们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型
咱们在定义一个类继承AsyncTask类的时候,必需要指定好这三个泛型的类型,若是都不指定的话,则都将其写成Void,例如:
AsyncTask <Void, Void, Void>
4个步骤:当咱们执行一个异步任务的时候,其须要按照下面的4个步骤分别执行
onPreExecute(): 这个方法是在执行异步任务以前的时候执行,而且是在UI Thread当中执行的,一般咱们在这个方法里作一些UI控件的初始化的操做,例如弹出要给ProgressDialog
doInBackground(Params... params): 在onPreExecute()方法执行完以后,会立刻执行这个方法,这个方法就是来处理异步任务的方法,Android操做系统会在后台的线程池当中开 启一个worker thread来执行咱们的这个方法,因此这个方法是在worker thread当中执行的,这个方法执行完以后就能够将咱们的执行结果发送给咱们的最后一个 onPostExecute 方法,在这个方法里,咱们能够从网络当中获取数据等一些耗时的操做
onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,咱们在异步任务执行的时候,有时候须要将执行的进度返回给咱们的UI界面,例以下载一张网络图片,咱们须要时刻显示其下载的进 度,就可使用这个方法来更新咱们的进度。这个方法在调用以前,咱们须要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将咱们的进度时时刻刻传递给 onProgressUpdate 方法来更新
onPostExecute(Result... result): 当咱们的异步任务执行完以后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,咱们能够将返回的结果显示在UI控件上
为何咱们的AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??缘由是,咱们若是要作一个异步任务,咱们必需要为其开辟一个新的Thread,让其完成一些操做,而在完成这个异步任务时,我可能并不需 要弹出要给ProgressDialog,我并不须要随时更新个人ProgressDialog的进度条,我也并不须要将结果更新给咱们的UI界面,因此 除了 doInBackground 方法以外的三个方法,都不是必须有的,所以咱们必需要实现的方法是 doInBackground 方法。
5、经过AsyncTask来从网络上下载一张图片
下面咱们就经过两个代码示例,来看看如何经过AsyncTask来从网络上下载一张图片,并更新到咱们的ImageView控件上。
①下载图片时,弹出一个ProgressDialog,可是不显示实时进度
咱们来看看布局文件:
<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" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="200dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:scaleType="fitCenter"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/imageView" android:layout_centerHorizontal="true" android:layout_marginTop="41dp" android:text="从网络上下载一张图片" /></RelativeLayout>
就是很简单的一个ImageView控件和一个Button控件,当点击Button控件时,弹出一个ProgressDialog,而后开启一个 异步任务,从网络中下载一张图片,并更新到咱们的ImageView上。这里还要注意一点,若是咱们要使用手机访问网络,必须还要给其受权才行,在后续的 学习当中,将会详细讲解Android当中的受权的知识。咱们来看看
AndroidManifest.xml文件:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xiaoluo.android_asynctast" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <!-- 受权手机可以访问网络 --> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.xiaoluo.android_asynctast.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
接下来咱们来看看咱们的Activity代码:
MainActivity String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg" == progressDialog = ProgressDialog(MainActivity."提示信息""正在下载中,请稍后......" progressDialog.setCancelable( MyAsyncTask AsyncTask<String, Integer, []> HttpClient httpClient = = HttpGet(params[0[] image = ==(httpEntity != && httpResponse.getStatusLine().getStatusCode() === onPostExecute( Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0
咱们来看看效果图:
②带有进度条更新的下载一张网络图片
下面这个代码示例,将会在下载图片的时候,显示进度条的更新,配置文件都不变,咱们来看看Activity代码:
public class MainActivity extends Activity { private Button button; private ImageView imageView; private ProgressDialog progressDialog; private final String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";// private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button); imageView = (ImageView)findViewById(R.id.imageView); // 弹出要给ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setTitle("提示信息"); progressDialog.setMessage("正在下载中,请稍后......"); // 设置setCancelable(false); 表示咱们不能取消这个弹出框,等下载完成以后再让弹出框消失 progressDialog.setCancelable(false); // 设置ProgressDialog样式为水平的样式 progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(IMAGE_PATH); } }); } /** * 定义一个类,让其继承AsyncTask这个类 * Params: String类型,表示传递给异步任务的参数类型是String,一般指定的是URL路径 * Progress: Integer类型,进度条的单位一般都是Integer类型 * Result:byte[]类型,表示咱们下载好的图片以字节数组返回 * @author xiaoluo * */ public class MyAsyncTask extends AsyncTask<String, Integer, byte[]> { @Override protected void onPreExecute() { super.onPreExecute(); // 在onPreExecute()中咱们让ProgressDialog显示出来 progressDialog.show(); } @Override protected byte[] doInBackground(String... params) { // 经过Apache的HttpClient来访问请求网络中的一张图片 HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(params[0]); byte[] image = new byte[]{}; try { HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); InputStream inputStream = null; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // 获得文件的总长度 long file_length = httpEntity.getContentLength(); // 每次读取后累加的长度 long total_length = 0; int length = 0; // 每次读取1024个字节 byte[] data = new byte[1024]; inputStream = httpEntity.getContent(); while(-1 != (length = inputStream.read(data))) { // 每读一次,就将total_length累加起来 total_length += length; // 边读边写到ByteArrayOutputStream当中 byteArrayOutputStream.write(data, 0, length); // 获得当前图片下载的进度 int progress = ((int)(total_length/(float)file_length) * 100); // 时刻将当前进度更新给onProgressUpdate方法 publishProgress(progress); } } image = byteArrayOutputStream.toByteArray(); inputStream.close(); byteArrayOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } finally { httpClient.getConnectionManager().shutdown(); } return image; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // 更新ProgressDialog的进度条 progressDialog.setProgress(values[0]); } @Override protected void onPostExecute(byte[] result) { super.onPostExecute(result); // 将doInBackground方法返回的byte[]解码成要给Bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); // 更新咱们的ImageView控件 imageView.setImageBitmap(bitmap); // 使ProgressDialog框消失 progressDialog.dismiss(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } }
咱们来看看效果图:
这样咱们就可以经过AsyncTask来实现从网络中下载一张图片,而后将其更新到UI控件中,并时时刻刻的更新当前的进度这个功能了。
6、AsyncTask的重要知识点
在上面两节已经详细讲解了AsyncTask的工做原理了,这里咱们还要补充一下AsyncTask的一些其余知识点:
1.Cancelling a Task
咱们能够在任什么时候刻来取消咱们的异步任务的执行,经过调用 cancel(boolean)方法,调用完这个方法后系统会随后调用 isCancelled() 方法而且返回true。若是调用了这个方法,那么在 doInBackgroud() 方法执行完以后,就不会调用 onPostExecute() 方法了,取而代之的是调用 onCancelled() 方法。为了确保Task已经被取消了,咱们须要常常调用 isCancelled() 方法来判断,若是有必要的话。
2.在使用AsyncTask作异步任务的时候必需要遵循的原则:
AsyncTask类必须在UI Thread当中加载,在Android Jelly_Bean版本后这些都是自动完成的
AsyncTask的对象必须在UI Thread当中实例化
execute方法必须在UI Thread当中调用
不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自动调用的
AsyncTask任务只能被执行一次
到此,有关AsyncTask的总结就到此为止了,本篇随笔主要讲解了Android中的多线程知识,而且详细地讲解了 AsyncTask 异步任务的概念和实现机制,并经过实例来了解 AsyncTask 的执行过程,最后还补充了 AsyncTask 的一些重要知识点,包括如何取消一个 AsyncTask 以及,咱们在使用 AsyncTask 时所必须遵循的规则。