多线程特性:
原子性:同一时间只有一个操作,保证数据的正确性,所以需要synchronized或锁
可见性:一个线程对共享变量的修改,其它线程能够立即看到,所以需要volatile,场合单线程写,多线程读数据,只能修饰变量
顺序性:程序按照代码的先后顺序执行
synchronized保证原子性,可见性,可修饰方法,变量,类,可能造成线程的阻塞
volatile保证可见性,可修饰变量
Android多线程的方式:
1 Handler+Thread 多任务
2 AsyncTask 单任务
3 ThreadPoolExecutor 批处理任务
4 IntentService 处理异步任务的简单Service
线程池概念:
1 FixedThreadPool 定长线程池,固定数量不被回收,能快速响应外界请求,比如服务器端负载
// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
fixedThreadPool.execute(task);
// 4. 关闭线程池
fixedThreadPool.shutdown();
2 SingleThreadPool 单线程线程池,所有任务只在一个线程中顺序执行
// 1. 创建单线程化线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
singleThreadExecutor.execute(task);
// 4. 关闭线程池
singleThreadExecutor.shutdown();
3 CachedThreadPool 可缓存线程池,无固定数量自动60s回收
// 1. 创建可缓存线程池对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
cachedThreadPool.execute(task);
// 4. 关闭线程池
cachedThreadPool.shutdown();
//当执行第二个任务时第一个任务已经完成
//那么会复用执行第一个任务的线程,而不用每次新建线程。
4 ScheduledThreadPool 定时线程池,固定数量并周期执行
// 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:schedule()
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
// 4. 关闭线程池
scheduledThreadPool.shutdown();
多线程实例:
0:Asyctask的使用
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new TessTwoTask().execute();
}
});
public class TessTwoTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
bitmap = BitmapUtils.cutBitmap(BitmapUtils.getBitmapByAssets(getApplicationContext(), "i2.jpg"), mRect);
baseApi.setImage(bitmap);
return baseApi.getUTF8Text();
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//background
image.setBackground(new BitmapDrawable(bitmap));
//info
Log.i("result", result);
textView.setText(result);
}
}
1:handler/thread,更新UI
//工作线程,在HandlerActivity doClick()方法中,获取数据后通过message发送数据Bitmap
new Thread() {
public void run() {
try {
HttpEntity entity = service.getEntity("http://172.60.5.68:8080/time/1.jpg",
null, HttpService.METHOD_GET);
InputStream is = service.getStream(entity);
Bitmap bm = BitmapFactory.decodeStream(is);
// 发消息回主线程
Message msg = Message.obtain();
msg.obj = bm;
handler.sendMessage(msg);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
};
}.start();
//主线程,在HandlerActivity onCreate()方法中,接收图片数据Bitmap并更新UI
service = new HttpService();
handler = new Handler() {
public void handleMessage(Message msg) {
// 获取Bitmap
Bitmap bm = (Bitmap) msg.obj;
// 更新Imageview
if (bm != null) {
ivPic.setImageBitmap(bm);
} else {
ivPic.setImageResource(R.drawable.ic_launcher);
}
}
};
2:handler/runnable,更新UI
//工作线程,在Handler_PostActivity中创建工作线程Runnable,完成耗时任务获取数据后runable直接更新UI
Runnable runnable = new Runnable() {
public void run() {
try {
HttpEntity entity = service.getEntity("http://172.60.5.68:8080/time/1.jpg",
null, HttpService.METHOD_POST);
InputStream is = service.getStream(entity);
final Bitmap bm = BitmapFactory.decodeStream(is);
//直接更新UI
if (bm != null) {
ivPic.setImageBitmap(bm);
} else {
ivPic.setImageResource(R.drawable.ic_launcher);
}
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
//主线程:在Handler_PostActivity doClick()方法中发送post runnable
Handler handler = new Handler();
handler.post(runnable);
3:handler/AsyncTask:
AsyncTaskActivity类中:
工作线程:
public static final String URI = "http://172.60.5.68:8080/time/1.jpg";
class MyTask extends AsyncTask<String, String, Bitmap> {
protected void onPreExecute() {
iv.setImageResource(R.drawable.ic_launcher);
}
protected Bitmap doInBackground(String... params) {
Bitmap bm = null;
try {
publishProgress("准备下载");
HttpEntity entity = HttpUtils.getEntity(params[0], null,
HttpUtils.METHOD_GET);
InputStream is = HttpUtils.getStream(entity);
bm = BitmapFactory.decodeStream(is);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bm;
}
protected void onProgressUpdate(String... values) {
}
protected void onPostExecute(Bitmap result) {
if (result != null) {
iv.setImageBitmap(result);
} else {
Toast.makeText(Day14_02_AsyncTaskActivity.this, "获取图片失败",
Toast.LENGTH_LONG).show();
}
}
protected void onCancelled() {
super.onCancelled();
cancel(true);
}
}
主线程:
public void doClick(View v) {
MyTask task = new MyTask();
task.execute(URI);
}
4:handler.sendMessage(),更新UI
//工作线程,在StuDemoActivity setupView()方法中,获取数据后通过message发送数据students
new Thread() {
public void run() {
try {
InputStream is = service.execute(HttpService.BASE_URL+ "stu_server/student", null, HttpUtils.METHOD_GET);
ArrayList<Student> students = parser.parse(is, "utf-8");
// 加载解析完成,发消息回主线程
Message msg = Message.obtain();
msg.what = 1;
msg.obj = students;
handler.sendMessage(msg);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
handler.sendEmptyMessage(0);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
//主线程,在StuDemoActivity onCreate()方法中,接收消息数据students并更新UI
this.handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:// 更新listView
ArrayList<Student> students = (ArrayList<Student>) msg.obj;
adapter.changeData(students);
break;
case 0:// 连接超时
Log.i("info", "连接超时异常提示。");
Toast.makeText(Day11_04_StuDemoActivity.this, "网络连接超时",3000).show();
break;
}
};
};
5:handler.sendMessage(),耗时任务
//工作线程,在AsyncImageLoader AsyncImageLoader()构造方法中,获取数据后通过message发送数据ImageLoadTask
isLoop = true;
tasks = new ArrayList<ImageLoadTask>();
workThread = new Thread() {
public void run() {
Log.i("info", Thread.currentThread().getName() + " is started");
while (isLoop) {
while (tasks.size() > 0 && isLoop) {
ImageLoadTask task = tasks.remove(0);
try {
byte[] data = service.getBytes(HttpService.BASE_URL
+ task.path, null, HttpUtils.METHOD_GET);
task.bitmap = BitmapUtils.getBitmap(data, 100, 100);
// 发送消息回主线程
Message msg = Message.obtain();
msg.what = 0;
msg.obj = task;
handler.sendMessage(msg);
// 缓存图片
caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
File savePath = new File("/mnt/sdcard/" + task.path);
BitmapUtils.save(task.bitmap, savePath);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
if (isLoop == false)
break;
// 如果任务集合中的任务都执行完成,线程等待
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Log.i("info", Thread.currentThread().getName()+ " is finished");
};
};
workThread.start();
class ImageLoadTask {
private String path;
private Bitmap bitmap;
private Callback callback;
public boolean equals(Object o) {
ImageLoadTask task = (ImageLoadTask) o;
return this.path.equals(task.path);
}
}
public interface Callback {
void imageLoaded(String path, Bitmap bm);
}
//主线程,在AsyncImageLoader AsyncImageLoader()构造方法中,接收ImageLoadTask并加载
service = new HttpService();
caches = new HashMap<String, SoftReference<Bitmap>>();
handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:// 图片加载成功
ImageLoadTask task = (ImageLoadTask) msg.obj;
task.callback.imageLoaded(task.path, task.bitmap);
break;
}
}
};
6:handler.sendMessage(),耗时任务
//工作线程----在MusicService(继承IntentService) onHandleIntent()方法中,该方法运行于工作线程,通过message发送savePath
String path = intent.getStringExtra("path");
File savePath = new File("/mnt/sdcard/" + path);
if (savePath.exists()) {
Message msg = Message.obtain();
msg.what = MSG_TAG_FILE_EXISTS;
msg.obj = savePath;
handler.sendMessage(msg);
return;
}
try {// 连接服务端
HttpEntity entity = HttpUtils.getEntity(
HttpService.BASE_URL + path, null, HttpUtils.METHOD_GET);
// 获取文件名和文件长度
fileName = savePath.getName();
fileLength = HttpUtils.getLength(entity);
// 发送消息到主线程 ,开始下载
handler.sendEmptyMessage(MSG_TAG_STARTED);
// 下载
InputStream is = HttpUtils.getStream(entity);
StreamUtils.save(is, savePath, handler);
// 发送消息到主线程 下载成功
handler.sendEmptyMessage(MSG_TAG_SUCCESSED);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
handler.sendEmptyMessage(MSG_TAG_FAILED);
} catch (FileNotFoundException e) {
e.printStackTrace();
handler.sendEmptyMessage(MSG_TAG_FAILED);
} catch (IOException e) {
e.printStackTrace();
handler.sendEmptyMessage(MSG_TAG_FAILED);
}
//主线程----在MusicService onCreate()方法中接收savePath并处理消息
handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TAG_FILE_EXISTS:
String info = "文件已存在,请勿重复下载:"+ ((File) msg.obj).getAbsolutePath();
Toast.makeText(MusicService.this, info, 3000).show();
break;
case MSG_TAG_STARTED:// 开始下载,发通知到通知栏
noti.contentView.setTextViewText(R.id.tvName_Noti, fileName);
noti.contentView.setTextViewText(R.id.tvProgress_Noti,fileSizeFormat(0));
noti.contentView.setTextViewText(R.id.tvTotalLength_Noti,fileSizeFormat(fileLength));
noti.contentView.setProgressBar(R.id.pbDownload_Noti,(int) fileLength, 0, false);
manager.notify(NOTI_ID, noti);
break;
case StreamUtils.MSG_TAG_CURRENT_PROGRESS:// 更新通知的下载进度
int loadedLength = msg.arg1 * 1024;
noti.contentView.setTextViewText(R.id.tvProgress_Noti,fileSizeFormat(loadedLength));
noti.contentView.setProgressBar(R.id.pbDownload_Noti,(int) fileLength, loadedLength, false);
manager.notify(NOTI_ID, noti);
break;
case MSG_TAG_FAILED:// 下载失败
Notification noti1 = new Notification(android.R.drawable.ic_dialog_alert, "警告", System
.currentTimeMillis());
noti1.setLatestEventInfo(MusicService.this, "下载失败","音乐下载失败", contentIntent);
noti1.flags = Notification.FLAG_AUTO_CANCEL;
noti1.defaults = Notification.DEFAULT_LIGHTS;
manager.notify(0, noti1);
break;
case MSG_TAG_SUCCESSED:// 下载成功
manager.cancel(NOTI_ID);
}
}
};
7:工作线程是独立一个类:
protected void updateUserSetting() {
handler = null;
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ConstantInfo.GET_DATA_OK01:
user = (User) msg.obj;
HuhangApplication.getHuhangApplication().setUser(user);
break;
case ConstantInfo.NET_WRONG:
Toast.makeText(getBaseContext(), "网络异常 请检查您的网络",
Toast.LENGTH_SHORT).show();
break;
case ConstantInfo.TOKEN_EXPRISSED:
TokenExprissed exprissed = new TokenExprissed(
FSPointActivity.this);
exprissed.toLogin();
break;
default:
break;
}
dismissPopuBar();
}
};
new UpdateSettingThread(this, "0", settingValue + "", sp, handler)
.start();
}
8:获取验证码方法供调用:
private void GetCode() {
getCodeHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case GET_CODE_CODE_OK:
if (mobileVerifyCodeBean.getReturnCode().equals("0")
&& mobileVerifyCodeBean.getErrorCode().equals("0")) {
Log.i("info", "服务器已发送验证码!");
Toast.makeText(RegisterCheckMobileActivity.this,
"验证短信已发送 请注意查收", Toast.LENGTH_SHORT).show();
getCodeButton.setClickable(false);
getCodeButton.setTextColor(getResources().getColor(
R.color.lightGray));
// 延时1分钟,才可再次获取验证码
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
getCodeHandler
.sendEmptyMessage(BUTTON_CLICK_TRUE);
}
}, 1000 * 60);
// 设置提示语可见
// noteText.setVisibility(EditText.VISIBLE);
Editor editor = sp.edit();
editor.putString("accessToken",
mobileVerifyCodeBean.getAccessToken());
editor.putString("timestamp",
mobileVerifyCodeBean.getTimestamp());
editor.putString("mobileNumber",
mobileVerifyCodeBean.getMobileNumber());
editor.commit();
} else if (mobileVerifyCodeBean.getReturnCode().equals("3")
&& mobileVerifyCodeBean.getErrorCode().equals(
"30123")) {
editTextChange.changeToRed(registerMobileNumber);
Toast.makeText(RegisterCheckMobileActivity.this,
"该手机号已经被注册 请更换其他号码", Toast.LENGTH_SHORT).show();
getCodeButton.setClickable(true);
getCodeButton.setTextColor(getResources().getColor(
R.color.darkGray));
// nextButton.setClickable(false);
// nextButton.setTextColor(android.R.color.darker_gray);
}
break;
case BUTTON_CLICK_FALSE:
getCodeButton.setClickable(false);
getCodeButton.setTextColor(getResources().getColor(
R.color.lightGray));
break;
case BUTTON_CLICK_TRUE:
getCodeButton.setClickable(true);
getCodeButton.setTextColor(getResources().getColor(
R.color.darkGray));
break;
default:
Toast.makeText(RegisterCheckMobileActivity.this,
"网络连接异常,请连接您的网络!", Toast.LENGTH_SHORT).show();
getCodeButton.setClickable(true);
getCodeButton.setTextColor(getResources().getColor(
R.color.darkGray));
break;
}
dismissPopuBar();
};
};
// 获取验证码的线程
new Thread() {
public void run() {
getCodeHandler.sendEmptyMessage(BUTTON_CLICK_FALSE);
// 发送请求,获取验证码
if (netTool.checkNetIsAvailable()) {
try {
// 获取验证码的请求的返回信息
getCodeResult = HttpConnectionManager
.GetMoobileVerifyCode(mobileNumber, 1);
Log.i("info", "获取验证码结果" + getCodeResult);
// 创建一个验证码返回信息对象
mobileVerifyCodeBean = gson.fromJson(getCodeResult,
MobileVerifyCodeBean.class);
if (mobileVerifyCodeBean != null) {
// 向主线程发消息,获取验证码返回信息成功
Message msg = new Message();
msg.what = GET_CODE_CODE_OK;
getCodeHandler.sendMessage(msg);
}
} catch (Exception e) {
// 获取失败
e.printStackTrace();
getCodeHandler.sendEmptyMessage(0);
}
} else {
// 获取失败
getCodeHandler.sendEmptyMessage(0);
}
};
}.start();
}
9:保存用户数据工作线程方法:
private void SaveInfo() {
titleRightButton.setClickable(false);
final Handler saveHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
dismissPopuBar();
UserBean ub = (UserBean) msg.obj;
switch (msg.what) {
case SAVE_USER_INFO_OK:
user = ub.getUser();
HuhangApplication.getHuhangApplication().setUser(user);
showUserInfo();
updateInfo();
Toast.makeText(UserInfoActivity.this, "保存成功",
Toast.LENGTH_SHORT).show();
break;
case SAVE_USER_INFO_FAIL:
Toast.makeText(UserInfoActivity.this, "保存信息失败",
Toast.LENGTH_SHORT).show();
break;
case USER_NOT_FOUND:
Toast.makeText(UserInfoActivity.this, "该账户已被注销",
Toast.LENGTH_SHORT).show();
break;
case NET_WRONG:
Toast.makeText(UserInfoActivity.this, "网络连接异常 请检查您的网络",
Toast.LENGTH_SHORT).show();
break;
case ConstantInfo.TOKEN_EXPRISSED:
TokenExprissed exprissed = new TokenExprissed(
getApplicationContext());
exprissed.toLogin();
default:
break;
}
titleRightButton.setClickable(true);
};
};
new Thread() {
String result = null;
public void run() {
List<NameValuePair> postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair("accessToken", sp
.getString("accessToken", "")));
postParameters.add(new BasicNameValuePair("timestamp", sp
.getString("timestamp", "")));
postParameters.add(new BasicNameValuePair("appId",
ConstantUrl.APPID));
postParameters.add(new BasicNameValuePair("accountId", sp
.getString("accountId", "")));
if (firstName.isShown()) {
postParameters.add(new BasicNameValuePair("lastName",
getTextValue(secondName)));
postParameters.add(new BasicNameValuePair("firstName",
getTextValue(firstName)));
}
postParameters.add(new BasicNameValuePair("email",
getTextValue(email)));
postParameters.add(new BasicNameValuePair("province",
(province) + ""));
postParameters.add(new BasicNameValuePair("address",
toUTF8(getTextValue(address))));
postParameters.add(new BasicNameValuePair("zipCode",
getTextValue(zipCode)));
postParameters.add(new BasicNameValuePair("homePhone",
getTextValue(phone)));
postParameters.add(new BasicNameValuePair("timeZone", timeZone
+ ""));
try {
result = HttpConnectionManager.getDataFromHttp(
ConstantUrl.UPDATE_USER_URL, postParameters);
Log.i("info", "saveUserInfo:" + result);
} catch (Exception e) {
saveHandler.sendEmptyMessage(NET_WRONG);
e.printStackTrace();
}
UserBean ub = gson.fromJson(result, UserBean.class);
if (ub.getReturnCode().equals("0")
&& ub.getErrorCode().equals("0")) {
Message msg = new Message();
msg.obj = ub;
msg.what = SAVE_USER_INFO_OK;
saveHandler.sendMessage(msg);
} else if (ub.getReturnCode().equals("1")
&& ub.getErrorCode().equals("10100")) {
saveHandler.sendEmptyMessage(USER_NOT_FOUND);
} else if (ub.getReturnCode().equals("3")
&& ub.getErrorCode().equals("30303")) {
saveHandler.sendEmptyMessage(ConstantInfo.TOKEN_EXPRISSED);
} else {
saveHandler.sendEmptyMessage(SAVE_USER_INFO_FAIL);
}
};
}.start();
}