线程的概念
手机客户端有个概念,即UI主线程,我们能看到的按钮控件等其实都是有一条UI的主线程在运行着。如果有一条UI主线程在运行,在UI的主线程中我们去访问网络资源(存数据或者取数据),我们会遇到阻塞的现象。学习线程的好处:UI主线程是用于负责展示UI控件的,我们要完成耗时任务的操作,比如提交数据或者存数据应该在另外一个线程完成。
如果我们让不同的组件在这里运行,我们应该让他运行在独立的线程中,比如说,UI主线程在这运行着,同时我又下文件,又上传数据,又看电影,这些分别不同的操作应该放在独立的线程操作,不应该放在一个线程中操作。
系统不会给我们创建一个独立的线程给每一个组件,也就是说,当我们点击一个按钮,访问网络图片时(这是在主线程中操作的),我们期望的是不改在主线程中操作,系统不会帮我们创建的。如果不从主线程中分离开,这样就会有好多组件的操作在主线程中操作,会导致主线程任务繁忙,尤其是耗时操作的时候(导致阻塞),反应超过5s就会可能产生ANR(application not responding)。
所以,UI主线程不能当做工作线程,Android中的线程模式应该是单线程的模式。
①不要阻塞UI主线程(不在UI线程访问网络等)。
②不在UI以外的线程当中访问UI(UI的概念就是在主线程中呈现的)。
异步和同步的区别:
看下图(参考别人的)
处理复杂的工作线程两种机制:Handler或者AsyncTask
AsyncTask(异步任务)允许我们在用户UI上执行异步的工作,这个执行可能是个阻塞的操作,它可以把结果发布给UI。举个例子,当我们下载一个文件时,刻度条就是UI,我们一边下载,一边让刻度条走动来达到更新UI的效果。
异步任务操作的方法:
声明一个类MyTask继承(extends)AsyncTask(抽象类)->实现doInBackground()方法->为了更新UI实现onPostExecute()方法->把这些结果从doInBackground()方法中传递到UI上->执行这个异步任务,只需在UI线程中调用execute()方法。
AsyncTask异步任务分为三个参数Params,Progress和Result,四个步骤调用onPreExecute,doInBackground,onProgressUpdate和onPostExecute。
三个参数:
①Params:这个参数类型表示我们执行异步任务要发送的参数,也就是说,这个异步任务要执行什么东西,比如访问网络这里就是一个网址。
②Progress:这个参数是一个进度单位的类型,这个进度单位是在后台计算完成之后被发布的单位。
③Result:表示后台执行的返回结果。
并非所有的异步任务都要用到三个参数,如果不用就用Void类型代替。
四个步骤:
执行一个完整的异步任务,要依次经历以下四个步骤(步骤是有顺序的①②③④顺序执行),其中,doInBackground()方法是必须要执行的,即继承了AsyncTask方法就一定要实现doInBackground方法,也就是说你得做一件事情,否则异步任务没有意义:
①onPreExecute(),异步任务被执行之前执行,UI展示的时候执行。这个步骤通常用来构建一个异步任务,比如,我们可以显示一个进度条给用户看。
②doInBackground(Params...),在onPreExecute()之后doInBackground()会立即执行,这个步骤通常用来执行后台的操作(耗时操作)。Params...这个参数表示我们要执行的参数。参数的结果会被计算出来,并且通过这个步骤返回给最后一个步骤onPostExecute()。在这个步骤中间我们可以用publicProgress(Progress...)来发布一个或多个的进度单位,在第三个步骤onProgressUpdate()被调用时,把这个值发布给UI线程。
③onProgressUpdate(Progress...)
④onPostExecute(Result),后台计算出结果后会执行在UI上面,UI上的结果通常会来自doInBackground(Params...)。
注意:假设我不使用提示框第①个步骤不需要调用,如果我不需要把这个结果更新给UI第④个步骤不需要调用,如果我在下载的时候不更新进度条,第③个步骤不需要,这就是为什么一个类继承了AsyncTask方法的时候仅仅需要doInBackground方法。
下面举个例子:点击下载网络图片按钮,实现下载网络图片功能
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="com.example.android_asynctask_downloadimage.MainActivity" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginLeft="60dp"
android:layout_marginTop="40dp"
/>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="下载网络图片" />
</RelativeLayout>
MainActivity.java
package com.example.android_asynctask_downloadimage;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
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.support.v7.app.ActionBarActivity;
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.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends ActionBarActivity {
private Button btn1;
private ImageView imageView1;
private final String IMAGE_PATH="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1=(Button) this.findViewById(R.id.button1);
imageView1=(ImageView) this.findViewById(R.id.imageView1);
dialog=new ProgressDialog(MainActivity.this);
dialog.setTitle("提示");
dialog.setCancelable(false);
dialog.setMessage("正在下载图片,请等待...");
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new MyTask().execute(IMAGE_PATH);//执行异步任务
}
});//点击这个按钮下载图片,需要访问网络,记得在清单文件AndroidManifest.xml中加授权android.permission.INTERNET
}
/**
* 异步任务执行网络下载图片
* @author liuya
*
*/
public class MyTask extends AsyncTask<String, Integer, byte[]>{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
dialog.show();
}
/**
* 后台访问网络
*/
@Override
protected byte[] doInBackground(String... params) {
// TODO Auto-generated method stub
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(params[0]);//可变参数,三个点代表可变参数
byte[] result=null;
try {
HttpResponse httpResponse=httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==200){
result=EntityUtils.toByteArray(httpResponse.getEntity());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
httpClient.getConnectionManager().shutdown();
}
return result;
}
/**
* 更新UI
*/
@Override
protected void onPostExecute(byte[] result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
Bitmap bitmap=BitmapFactory.decodeByteArray(result, 0, result.length);
imageView1.setImageBitmap(bitmap);
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;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
效果图如下