Android开发中使用多线程的原因
- 避免ANR(Application is not responding)
- 实现异步,比如从云端获取图片比较费时,不应该使用同步阻塞获取结果,使用异步加载完成一个刷新一个
- 多任务,比如多线程下载
Android事件处理机制
在Android中事件处理的原则是:耗时的操作放到其他线程中处理,Main线程中处理不太耗时的操作,否则事件在5秒内无法得到响应就会出现ANR现象,在Main线程中一般是处理UI,处理Activity事件(OnCreat()等)和事件处理方法(OnClick()等)。
同步和异步的理解
有些事件必须使用同步比如用户的注册,需要得到结果后才能进行下面的操作,有些事件需要异步,比如微博的点赞,只需要点赞完成后提示我一下就行了,没必要在与服务器沟通的过程中采用同步处理不可以干其他的事情。
Java中多线程的创建
通过继承Thread类
Thread类的本质是一个实现了Runnable接口的类,继承Thread类,复写run()方法,最后在通过.start()调用。
通过接口Runnable
Runnable的好处是接口比继承更自由,新建对象后实例一个Thread对象将Runnable传入并调用.start(),如果多个Thread对象传入了同一个Runnable它们将共享数据,比如这个卖火车票的例子。
package info.wujiajun.threadlearning;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void btnOnClickByThread(View view){
ThreadText text1 = new ThreadText();
ThreadText text2 = new ThreadText();
text1.start();
text2.start();
}
public void btnOnClickByRunnable(View view){
RunnableText text1 = new RunnableText();
RunnableText text2 = new RunnableText();
Thread t1 = new Thread(text1);
Thread t2 = new Thread(text2);
t1.start();
t2.start();
}
public void saleTicket(View view){
SaleTicket text = new SaleTicket();
Thread t1 = new Thread(text);
Thread t2 = new Thread(text);
t1.start();
t2.start();
}
SaleTicket.java
package info.wujiajun.threadlearning;
import android.util.Log;
/**
* Email:amojury@outlook.com
* Github:https://github.com/amojury
* Created by Jun on 2017/7/27.
*/
public class SaleTicket implements Runnable {
private int ticket = 20;
@Override
public void run() {
while (true){
if(ticket > 0){
Log.d("TAG",Thread.currentThread().getName()+"卖出了第"+(20-ticket+1)+"张票");
ticket--;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else
break;
}
}
}
线程池的应用
new Thread的弊端
- 性能差
- 缺乏统一管理,无序竞争
- 无法定时定期执行
线程池
Java通过Executors提供了四种线程池
- newCachedThreadPool 可缓存线程池,回收闲置线程
- newFixedThreadPool 定长线程池,有最大并发数
- newScheduledThreadPool 可定时定长线程池
- newSingleThreadExecutor 制定顺序单线程池
实现多线程之间的通讯
Android线程通讯原理
Handle(send方法)
实现两秒后改变button上的文字
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btnHelloWorld)
Button btnHelloWorld;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) { //回调方法,复写handleMessage,处理要放在主线程的事情
if(msg.what == 1)
btnHelloWorld.setText("Not HelloWorld");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btnHelloWorld)
public void onViewClicked() {
new Thread(new Runnable() { //开启新线程
@Override
public void run() {
try {
Thread.sleep(2000); //模拟网络请求
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.sendEmptyMessage(1); //使用send方法发送一个空消息,标识是1
}
}).start();
}
}
send的一些方法
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message,long)
- sendMessageDelayer(Message,long)
Handle(post方法)
post方法允许你排列一个Runnable对象在主线程中
- post(Runnable)
- postAtTime(Runnable,long)
- postDelayer(Runnabld)
三种更新UI的方法
- post
- View.post()
- RunOnUiThread()
AsyncTask类(可以在子线程和UI线程中跳转)
解决使用Handle较为繁琐
AsyncTask是一个抽象类,我们需要一个子类继承它并提供三个泛型参数。
- Params 参数
- Progress 进度
- Result 返回结果
class DownloadTask extend AsyncTask< Void,Integer,Boolean>
Void表示不需要传参数给后台,Integer表示用整形的数来表示过程,Boolean表示用布尔值表示结果。
重写方法
OnPreExecute() 在后台任务开始之前调用,用于初始化UI界面等
doInBackground(Params) 所有代码都在子线程用于耗时操作,完成后返回结果,如果第三个参数是Void,不用返回,如果需要反馈任务进度调用publishProgress()方法
OnProgressUpdate(Progress),当执行publishProgress()方法后被调用用于界面更新(不一定会被调用)
-
OnPostEcecute(Result) 后台任务执行完毕返回参数。
使用AsyncTask实现下载器
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn)
Button btn;
@BindView(R.id.pb)
ProgressBar pb;
private int progress = 0;private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick(R.id.btn) public void onViewClicked() { new DownloadTask().execute(); } class DownloadTask extends AsyncTask<Void,Integer,Boolean>{ @Override protected void onPreExecute() { KLog.d("开始下载"); pb.setVisibility(View.VISIBLE); } @Override protected Boolean doInBackground(Void... voids) { KLog.d("开始下载"); while (true){ try { Thread.sleep(1000); progress += 10; publishProgress(progress); if(progress >= 100) break; } catch (InterruptedException e) { e.printStackTrace(); return false; } } return true; } @Override protected void onProgressUpdate(Integer... values) { KLog.d("正在下载"+values[0]); pb.setProgress(values[0]); } @Override protected void onPostExecute(Boolean aBoolean) { if(aBoolean){ KLog.d("下载成功"); pb.setVisibility(View.GONE); }else{ KLog.d("下载失败"); } } }
}
AsyncTask优点:简单,快捷
缺点:当有多个异步操作时并需要变更ui复杂