如果程序主线程被阻塞超过5秒,系统会提示 应用程序无响应这就是ANR 。 ANR的全称是Application Not Responding,使用多线程可以避免ANR。但是这里要注意一下不要为了避免ANR而过多的使用多线程,除非万不得已的情况。 比如访问网络服务端返回的过慢、数据过多导致滑动屏幕不流畅、或者I/O读取过大的资源等等。这里可以开启一个新线程来处理这些耗时的操作。 如果过多使用多线程会出现数据同步的问题须要程序员去处理,所以使用多线程的时候尽量保持它的独立不会被其它线程干预。java语言提供了一个线程锁的概 念 synchronized 可以添加对象锁与方法锁专门避免多线程同时访问一个方法或者一个对象导致的问题,有兴趣的朋友可以去看看这里我不罗嗦啦 。
1.Thread与Handler执行多线程
Handler主要用于程序主线程与我们自己创建的线程进行通信。在这个例子中点击按钮后创建一个新的线程去循环的加载100张图片每加载完一张图片在 Thread中使用Handler发送消息通知UI线程更新显示,直到加在完毕通知UI显示加载完成一共耗时多少秒。可见Handler的重要性它就是主 线程与我们自己创建的线程的桥梁啊~~~
001
import java.io.InputStream;
002
003
import android.app.Activity;
004
import android.content.Context;
005
import android.graphics.Bitmap;
006
import android.graphics.BitmapFactory;
007
import android.os.Bundle;
008
import android.os.Handler;
009
import android.os.Message;
010
import android.view.View;
011
import android.view.View.OnClickListener;
012
import android.widget.Button;
013
import android.widget.TextView;
014
015
public class SingleActivity extends Activity {
016
017
/**读取进度**/
018
public final static int LOAD_PROGRESS = 0;
019
020
/**标志读取进度结束**/
021
public final static int LOAD_COMPLETE = 1;
022
023
024
/** 开始加载100张图片按钮 **/
025
Button mButton = null;
026
/** 显示内容 **/
027
TextView mTextView = null;
028
/** 加载图片前的时间 **/
029
Long mLoadStatr = 0L;
030
/** 加载图片后的时间 **/
031
Long mLoadEnd = 0L;
032
033
Context mContext = null;
034
035
//接收传递过来的信息
036
Handler handler = new Handler() {
037
@Override
038
public void handleMessage(Message msg) {
039
switch (msg.what) {
040
case LOAD_PROGRESS:
041
mTextView.setText("当前读取到第" + msg.arg1 + "张图片");
042
break;
043
case LOAD_COMPLETE:
044
mTextView.setText("读取结束一共耗时" + msg.arg1 + "毫秒");
045
break;
046
}
047
super.handleMessage(msg);
048
}
049
};
050
051
@Override
052
protected void onCreate(Bundle savedInstanceState) {
053
setContentView(R.layout.single);
054
mContext = this;
055
056
/** 拿到button 与 TextView 对象 **/
057
mButton = (Button) findViewById(R.id.button0);
058
mTextView = (TextView) findViewById(R.id.textView0);
059
mTextView.setText("点击按钮开始更新时间");
060
mButton.setOnClickListener(new OnClickListener() {
061
@Override
062
public void onClick(View arg0) {
063
//开始读取图片
064
LoadImage();
065
}
066
});
067
068
069
super.onCreate(savedInstanceState);
070
}
071
072
public void LoadImage() {
073
new Thread() {
074
@Override
075
public void run() {
076
//得到加载图片开始的时间
077
mLoadStatr = System.currentTimeMillis();
078
079
for (int i = 0; i < 100; i++) {
080
// 这里循环加载图片100遍
081
ReadBitMap(mContext, R.drawable.bg);
082
083
// 每读取完一张图片将进度甩给handler
084
Message msg = new Message();
085
msg.what = LOAD_PROGRESS;
086
msg.arg1 = i + 1;
087
handler.sendMessage(msg);
088
}
089
090
//得到加载图片结束的时间
091
mLoadEnd = System.currentTimeMillis();
092
093
//100张图片加载完成
094
Message msg = new Message();
095
msg.what = LOAD_COMPLETE;
096
msg.arg1 = (int) (mLoadEnd - mLoadStatr);
097
handler.sendMessage(msg);
098
}
099
}.start();
100
101
}
102
103
/**
104
* 读取本地资源的图片
105
*
106
* @param context
107
* @param resId
108
* @return
109
*/
110
public Bitmap ReadBitMap(Context context, int resId) {
111
BitmapFactory.Options opt = new BitmapFactory.Options();
112
opt.inPreferredConfig = Bitmap.Config.RGB_565;
113
opt.inPurgeable = true;
114
opt.inInputShareable = true;
115
// 获取资源图片
116
InputStream is = context.getResources().openRawResource(resId);
117
return BitmapFactory.decodeStream(is, null, opt);
118
}
119
}
2.TimerTask与Handler延迟多线程
Timer与TimerTask可以构建一个延迟器 就好比开启一个线程每隔一段规定的时间访问一次。可以在这个线程中去关闭这个Timer 与TimerTask ,举个例子比如现在我要做一个网游帐号登录超时客户端的检测 用户输入完帐号密码点击登录这时候我开启一个TimerTask每过1秒检查一下用户是否登录成功,过了10秒如果还没有登录成功提示他登陆超时。这个时 候我就须要在这个检测线程中去关闭Timer 与TimerTask 因为不需要它在循环检测了。 调用cancel()就可以关闭,请同学们阅读下面这个例子。
001
import java.util.Timer;
002
import java.util.TimerTask;
003
import android.app.Activity;
004
import android.content.Context;
005
import android.os.Bundle;
006
import android.os.Handler;
007
import android.os.Message;
008
import android.view.View;
009
import android.view.View.OnClickListener;
010
import android.widget.Button;
011
import android.widget.TextView;
012
013
public class TimerTaskActivity extends Activity {
014
015
/**执行Timer进度**/
016
public final static int LOAD_PROGRESS = 0;
017
018
/**关闭Timer进度**/
019
public final static int CLOSE_PROGRESS = 1;
020
021
/** 开始TimerTask按钮 **/
022
Button mButton0 = null;
023
024
/** 关闭TimerTask按钮 **/
025
Button mButton1 = null;
026
027
/** 显示内容 **/
028
TextView mTextView = null;
029
030
Context mContext = null;
031
032
/**Timer对象**/
033
Timer mTimer = null;
034
035
/**TimerTask对象**/
036
TimerTask mTimerTask = null;
037
038
/**记录TimerID**/
039
int mTimerID = 0;
040
041
042
/**接收传递过来的信息**/
043
Handler handler = new Handler() {
044
@Override
045
public void handleMessage(Message msg) {
046
switch (msg.what) {
047
case LOAD_PROGRESS:
048
mTextView.setText("当前TimerID为" + msg.arg1 );
049
break;
050
case CLOSE_PROGRESS:
051
mTextView.setText("当前Timer已经关闭请重新开启" );
052
break;
053
054
}
055
super.handleMessage(msg);
056
}
057
};
058
059
@Override
060
protected void onCreate(Bundle savedInstanceState) {
061
setContentView(R.layout.timer);
062
mContext = this;
063
064
/** 拿到button 与 TextView 对象 **/
065
mButton0 = (Button) findViewById(R.id.button0);
066
mButton1 = (Button) findViewById(R.id.button1);
067
mTextView = (TextView) findViewById(R.id.textView0);
068
mTextView.setText("点击按钮开始更新时间");
069
070
//开始
071
mButton0.setOnClickListener(new OnClickListener() {
072
@Override
073
public void onClick(View arg0) {
074
//开始执行timer
075
StartTimer();
076
}
077
});
078
079
//关闭
080
mButton1.setOnClickListener(new OnClickListener() {
081
@Override
082
public void onClick(View arg0) {
083
//停止执行timer
084
CloseTimer();
085
}
086
});
087
088
super.onCreate(savedInstanceState);
089
}
090
091
public void StartTimer() {
092
093
if (mTimer == null) {
094
mTimerTask = new TimerTask() {
095
public void run() {
096
//mTimerTask与mTimer执行的前提下每过1秒进一次这里
097
mTimerID ++;
098
Message msg = new Message();
099
msg.what = LOAD_PROGRESS;
100
msg.arg1 = (int) (mTimerID);
101
handler.sendMessage(msg);
102
}
103
};
104
mTimer = new Timer();
105
106
//第一个参数为执行的mTimerTask
107
//第二个参数为延迟的时间 这里写1000的意思是mTimerTask将延迟1秒执行
108
//第三个参数为多久执行一次 这里写1000表示每1秒执行一次mTimerTask的Run方法
109
mTimer.schedule(mTimerTask, 1000, 1000);
110
}
111
112
}
113
114
public void CloseTimer() {
115
116
//在这里关闭mTimer 与 mTimerTask
117
if (mTimer != null) {
118
mTimer.cancel();
119
mTimer = null;
120
}
121
if (mTimerTask != null) {
122
mTimerTask = null;
123
}
124
125
/**ID重置**/
126
mTimerID = 0;
127
128
//这里发送一条只带what空的消息
129
handler.sendEmptyMessage(CLOSE_PROGRESS);
130
}
131
}
3.AsyncTask执行多线程
执行AsyncTask
onPreExecute()///首先执行这个方法,它在UI线程中 可以执行一些异步操作 比如初始化一些东西
doInBackground(Objectarg0) //异步后台执行 ,执行完毕可以返回出去一个结果object对象
onPostExecute(Object result) //可以拿到执行中的进度 当然进度须要在doInBackground中手动调用publishProgress()方法返回
通过例子可以清楚的看到计算出读取100张图片的时间,执行的效率上来说AsyncTask 没有Thread效率块,但是AsyncTask 比Thread更规整,它可是时时的拿到异步线程中进度以及最后的结果集,可以让我们的代码更佳规范。这里说一下 Thread能做到的事AsyncTask 都可以做到 但是AsyncTask 能做到的事Thread 不一定能做到就算勉强做到也很麻烦 。我给大家的建议是如果处理大量的异步操作就用AsyncTask 如果少部分的则使用Thread
001
import java.io.InputStream;
002
import java.util.Timer;
003
import java.util.TimerTask;
004
import android.app.Activity;
005
import android.content.Context;
006
import android.graphics.Bitmap;
007
import android.graphics.BitmapFactory;
008
import android.os.AsyncTask;
009
import android.os.Bundle;
010
import android.view.View;
011
import android.view.View.OnClickListener;
012
import android.widget.Button;
013
import android.widget.TextView;
014
015
public class AsyncTaskActivity extends Activity {
016
017
/**执行Timer进度**/
018
public final static int LOAD_PROGRESS = 0;
019
020
/**关闭Timer进度**/
021
public final static int CLOSE_PROGRESS = 1;
022
023
/** 开始StartAsync按钮 **/
024
Button mButton0 = null;
025
026
027
/** 显示内容 **/
028
TextView mTextView = null;
029
030
Context mContext = null;
031
032
/**Timer对象**/
033
Timer mTimer = null;
034
035
/**TimerTask对象**/
036
TimerTask mTimerTask = null;
037
038
/**记录TimerID**/
039
int mTimerID = 0;
040
041
@Override
042
protected void onCreate(Bundle savedInstanceState) {
043
setContentView(R.layout.async);
044
mContext = this;
045
046
/** 拿到button 与 TextView 对象 **/
047
mButton0 = (Button) findViewById(R.id.button0);
048
mTextView = (TextView) findViewById(R.id.textView0);
049
//开始
050
mButton0.setOnClickListener(new OnClickListener() {
051
@Override
052
public void onClick(View arg0) {
053
//开始执行StartAsync
054
StartAsync();
055
}
056
});
057
058
059
super.onCreate(savedInstanceState);
060
}
061
062
public void StartAsync() {
063
new AsyncTask<Object, Object, Object>() {
064
065
@Override
066
protected void onPreExecute() {
067
//首先执行这个方法,它在UI线程中 可以执行一些异步操作
068
mTextView.setText("开始加载进度");
069
super.onPreExecute();
070
}
071
072
@Override
073
protected Object doInBackground(Object... arg0) {
074
//异步后台执行 ,执行完毕可以返回出去一个结果object对象
075
076
//得到开始加载的时间
077
Long startTime = System.currentTimeMillis();
078
for (int i = 0; i < 100; i++) {
079
// 这里循环加载图片100遍
080
ReadBitMap(mContext, R.drawable.bg);
081
//执行这个方法会异步调用onProgressUpdate方法,可以用来更新UI
082
publishProgress(i);
083
}
084
//得到结束加载的时间
085
Long endTime = System.currentTimeMillis();
086
087
//将读取时间返回
088
return endTime - startTime;
089
}
090
091
@Override
092
protected void onPostExecute(Object result) {
093
//doInBackground之行结束以后在这里可以接收到返回的结果对象
094
095
mTextView.setText("读取100张图片一共耗时" + result+ "毫秒");
096
097
super.onPostExecute(result);
098
}
099
100
101
@Override
102
protected void onProgressUpdate(Object... values) {
103
//时时拿到当前的进度更新UI
104
105
mTextView.setText("当前加载进度" + values[0]);
106
super.onProgressUpdate(values);
107
}
108
}.execute();//可以理解为执行 这个AsyncTask
109
110
}
111
/**
112
* 读取本地资源的图片
113
*
114
* @param context
115
* @param resId
116
* @return
117
*/
118
public Bitmap ReadBitMap(Context context, int resId) {
119
BitmapFactory.Options opt = new BitmapFactory.Options();
120
opt.inPreferredConfig = Bitmap.Config.RGB_565;
121
opt.inPurgeable = true;
122
opt.inInputShareable = true;
123
// 获取资源图片
124
InputStream is = context.getResources().openRawResource(resId);
125
return BitmapFactory.decodeStream(is, null, opt);
126
}
127
}
4.多线程Looper的使用
Looper用来管理线程的消息队列与循环队列,在handler中默认为mainlooper来进行消息循环,如果在handler中开启一个新的线程 那么在这个新的线程中就没有Looper循环,如果想让这个新的线程具有消息队列与消息循环我们须要调用 Looper.prepare();拿到它的loop ,这样就好比在Thread中创建了消息队列与循环 需要调用 Looper.loop(); 它的意思就是执行这个消息循环,下面我给出一个例子希望大家好好阅读。
001
import java.io.InputStream;
002
003
import android.app.Activity;
004
import android.content.Context;
005
import android.graphics.Bitmap;
006
import android.graphics.BitmapFactory;
007
import android.os.Bundle;
008
import android.os.Handler;
009
import android.os.Looper;
010
import android.os.Message;
011
import android.view.View;
012
import android.view.View.OnClickListener;
013
import android.widget.Button;
014
import android.widget.Toast;
015
016
public class LooperActivity extends Activity {
017
/** 发送消息按钮 **/
018
Button mButton = null;
019
/** 加载图片前的时间 **/
020
Long mLoadStatr = 0L;
021
/** 加载图片后的时间 **/
022
Long mLoadEnd = 0L;
023
024
Context mContext = null;
025
026
private Handler handler = new Handler() {
027
public void handleMessage(Message msg) {
028
new Thread() {
029
@Override
030
public void run() {
031
032
//如果handler不指定looper的话
033
//默认为mainlooper来进行消息循环,
034
//而当前是在一个新的线程中它没有默认的looper
035
//所以我们须要手动调用prepare()拿到他的loop
036
//可以理解为在Thread创建Looper的消息队列
037
Looper.prepare();
038
039
040
Toast.makeText(LooperActivity.this, "收到消息",Toast.LENGTH_LONG).show();
041
042
043
//在这里执行这个消息循环如果没有这句
044
//就好比只创建了Looper的消息队列而
045
//没有执行这个队列那么上面Toast的内容是不会显示出来的
046
Looper.loop();
047
048
049
050
051
//如果没有Looper.prepare();与 Looper.loop();
052
//会抛出异常Can't create handler inside thread that has not called Looper.prepare()
053
//原因是我们新起的线程中是没有默认的looper所以须要手动调用prepare()拿到他的loop
054
055
}
056
}.start();
057
}
058
};
059
060
@Override
061
protected void onCreate(Bundle savedInstanceState) {
062
setContentView(R.layout.loop);
063
mContext = this;
064
065
/** 拿到button 与 TextView 对象 **/
066
mButton = (Button) findViewById(R.id.button0);
067
mButton.setOnClickListener(new OnClickListener() {
068
@Override
069
public void onClick(View arg0) {
070
new Thread() {
071
@Override
072
public void run() {
073
074
//发送一条空的消息
075
//空消息中必需带一个what字段
076
//用于在handler中接收
077
//这里暂时我先写成0
078
handler.sendEmptyMessage(0);
079
080
}
081
}.start();
082
}
083
});
084
085
086
super.onCreate(savedInstanceState);
087
}
088
/**
089
* 读取本地资源的图片
090
*
091
* @param context
092
* @param resId
093
* @return
094
*/
095
public Bitmap ReadBitMap(Context context, int resId) {
096
BitmapFactory.Options opt = new BitmapFactory.Options();
097
opt.inPreferredConfig = Bitmap.Config.RGB_565;
098
opt.inPurgeable = true;
099
opt.inInputShareable = true;
100
// 获取资源图片
101
InputStream is = context.getResources().openRawResource(resId);
102
return BitmapFactory.decodeStream(is, null, opt);
103
}
104
}