Android中的线程问题:
- 主线程(Main Thread),UI线程。直接写在onCreate等方法中的代码都是写在主线程的,主线程用于负责UI页面的显示效果以及逻辑顺序的处理功能
- 子线程(Worker Thread),工作线程, 凡是写在自己new Thread中的代码均是运行在子线程中的代码
通常情况下,会在子线程中进行耗时操作
为什么要在子线程中进行耗时操作??
原因:Android系统为单线程模型。即同一时刻只能处理一件事,比如:有2个按钮,单击按钮1后,进行按钮1点击事件的处理,如果此时按钮1的点击事件需要很长事件的处理,那么,在按钮1的时间没处理完的过程中,无法处理其他的用户交互,如:按钮2的点击,返回键的点击等
当主线程处理一件事儿超过5秒以上时,会显示一个ANR对象框ANR: Application Not Responseding 程序无响应.因此为了避免ANR的问题,会将耗时操作写在子线程中.
在Android应用中实现连网操作时,注意以下问题:
- Android4.0以上的设备中,不能够在主线程写连网操作的,如果倔强的在主线程中写了连网的代码,那么会爆出NetWorkOnMainThreadException。
- 要进行连网操作,必须添加连网权限:
<!-- 添加连网权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
3.只有主线程中才能进行更改UI的操作,即子线程不能更新UI
为了解决在子线程中连网操作执行完成后,进行更新UI的操作,解决方式有以下3种:
方式一: 使用封装好的AsyncTask(异步任务)完成此目标
方式二 : 使用Thread+Hanlder完成此目标
方式三: 使用封装好的Loader完成此目标
AsyncTask异步任务
- 特点:此类中有2个特别主要的方法,其中一个方法(doInBackground)中的代码默认运行在子线程中,另一个方法(onPostExecute)会在doInBackground中的代码执行完成后自动调用此方法,并且这个onPostExecute方法中的代码时运行在主线程的
AsyncTask的使用:
- 创建一个AsyncTask的子类
- 设置AsyncTask的泛型
- 按报错要求添加doInBackground方法,并在方法中进行耗时操作(如连网操作)
- 手动添加onPostExecute方法,在方法中根据方法参数中的下载结果更新UI
样例如下:
/*
* 泛型1:用于限制当启动异步任务时,传递的数据的类型,如果不需要传递数据,在填写Void即可
* 显示了doInBackground方法以及execute方法的参数类型
* 泛型3: 用于限制本次连网操作的下载结果的类型
* 限制了doInBackground方法返回值的类型以及onPostExecute方法的参数类型
* */
class MyTask extends AsyncTask<String, Void, Bitmap> {
/**
* 此方法中的代码默认运行在子线程中
* 当此方法中的子线程执行完毕后,会自动调用onPostExecute方法
* 并且会通过返回值将下载结果传递给onPostExecute方法
*
* 参数:String...代表可变参数,即当调用此方法时(当启动异步任务时),可以传递任意个数的String对象
*/
@Override
protected Bitmap doInBackground(String... params) {
// TODO Auto-generated method stub
Log.i("oye", "doInBackground中: 当前线程名称为: "+Thread.currentThread().getName());
//下载图片并显示
try {
HttpURLConnection conn = (HttpURLConnection) new URL(params[0]).openConnection();
conn.setRequestMethod("GET");
// conn.setConnectTimeout(timeoutMillis)
conn.connect();
//连接成功
if (conn.getResponseCode() == 200) {
// 获取连接的网址返回的数据
InputStream is = conn.getInputStream();
/**
* 当想要将is流中的数据显示到imageview上时
* 1. 要根据is流生成Bitmap对象 (Bitmap代表图片对象)
* 2. 让iv显示Bitmap对象即可
*/
bitm = BitmapFactory.decodeStream(is);
Log.i("oye", "获取到的应答结果的bitm为: "+bitm);
return bitm;
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (result != null) {
iv.setImageBitmap(result);
}
}
}
5 通过子类对象调用execute方法启动异步任务
public void click (View v) {
//启动异步任务
new MyTask().execute(imgUrl);
}
泛型2的作用:
泛型2:
* 显示publishProgress和onPublishProgress方法的参数类型
- publishProgress和onProgressUpdate方法的作用:
//当子线程下载过程中需要更新UI显示时,调用publishProgress方法,
// 调用此方法后,系统会自动调用异步任务子类中onProgressUpdate 方法
publishProgress(total,current);
/*
* 当在doInBackground中调用publishProgress方法时,会运行此方法
* 此方法用于在下载的过程中更新UI页面
* */
//@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
dialog.setMax(values[0]);
dialog.setProgress(values[1]);
dialog.setTitle(values[1] +"/"+values[0]);
}
使用异步任务时的注意事项:
- 当需要多次启动异步任务时,必须每次新建对象然后通过execute方法启动
即,以下写法为错误写法:
MyTask mt=new MyTask();
mt.execute(params);
mt.execute(params);
2 多个异步任务同时执行的问题:
public void click (View v) {
/**
* 当同时启动多个异步任务时,多个异步任务之间默认是依次排队执行
* 即只有当第一个异步任务执行完毕之后,第二个异步任务才会开始执行
*/
// new MyTask(pb1,iv1).execute(imgUrl);
// new MyTask(pb2,iv2).execute(imgUrl2);
/**
* 如果需要让多个异步任务同时执行的话:
* 创建Executor对象,线程池对象,用于控制当有多个异步任务需要启动下载时,同时可以执行几个异步任务
* 使用executeOnExceuter方法替代execute方法启动
*/
Executor exec = Executors.newFixedThreadPool(2);
new MyTask(pb1,iv1).executeOnExecutor(exec, imgUrl);
new MyTask(pb2,iv2).executeOnExecutor(exec, imgUrl2);
}