首先,什么是异步呢?异步其实是和同步相对应的一个概念,他们是一种消息通讯机制,关注的是执行过程和结果返回方式。同步的话,在没有得到结果之前,始终是不返回请求的,但是一旦请求返回,就得到返回值了。异步的话则相反,当你发送一个请求后,请求就返回了,但是并没有立刻得到返回结果,需要被请求者通过某种方式(比如回调)来告诉请求者结果。编程都是源于生活,我们举个例子:
假设我要买一部可以当炸弹的手机,我打电话问sam啥手机店的老板,如果有的话,老板会立刻告诉我,有啊noo7就行了;不清楚的时候,他可能告诉我,你等会我查下,然后查啊查,可能查了四五秒,也可能查了四五分钟,然后就告诉我结果(请求返回,立刻有返回结果)。这就是同步了。
同样是上述的情况,我问老板有可以当炸弹的手机吗,老板不确定,就和我说,我待会查下,查到了再打电话告诉你,然后电话挂断了(不返回结果)。第二天,老板打电话告诉我,这手机给禁了,买不到了(回调)。这就是异步了。
PS:从上面例子也可以看出,同步和异步是不能和阻塞和不阻塞混为一谈的,我们同步阻塞的时候就会出现ANR了。
好,为了更好的理解,那么我们现在来写代码。首先是同步机制,书店老板有一个checkPhone()方法可以检查是否有我们要的手机。
public class PhonestoreService {
public boolean ckeckPhone(String phoneName) {
boolean result = "noo7".equals(phoneName);
return result;
}
}
接下来我们要打电话给sam啥手机店老板,我们在MainActivity的布局文件中定义了一个按钮,点击时打电话给书店老板询问是否有手机,然后打印结果:
@OnClick(R.id.btnCall)
public void onClick() {
// 打了电话给手机店老板,要老板告诉是否有手机
boolean result = service.ckeckPhone("noo7");
Log.i("result: ", result +"");
}
我们运行一下,得到下面的结果:
这时大家就要吐槽了,这谁不会,我刚学java基础的时候就会了,别急,后面完全看懂了才叫做会。
假设手机店老板手机型号太多了,不知道有没有你要的手机,他需要打开电脑的数据库查一下,这可能需要花费一点儿时间,这时候我们在checkPhone()方法里面加上一点代码:
public boolean ckeckPhone(String phoneName) {
SystemClock.sleep(2000);
boolean result = "noo7".equals(phoneName);
return result;
}
相信大家看到代码都知道点击按钮时,按钮有两秒变成不可点击的状态,如果把sleep()时间变为5000毫秒或者更久,这里还会出现一个ANR的错误。这时大家要说了,那还不简单,开个线程呗,于是有了下面的代码(下面代码有内存泄露的风险哦,同学们可以自己分析下):
/**
* 异步实现
* @param phoneName
* @return
*/
public boolean checkPhone1(final String phoneName) {
new Thread() {
@Override
public void run() {
SystemClock.sleep(2000);
result = "noo7".equals(phoneName);
}
}.start();
return result;
}
这时候我们看下Log日志会发现,咦,第一次请求结果是false,2秒后再请求一次结果是true了(下面是连续点击的效果):
其实到这里,我们就开启了异步请求了,简单分析下:我们在开启线程的时候,第一次返回的是result的默认值,而第二次发起请求时,实际上是返回给我们的结果是第一次请求时线程里面2秒后赋值的result,为true。那么现在我们想请求后,对方给我们返回正确的结果。怎么办呢?这时候就要用到监听回调了。
好了,这时候我们给店主留了号码,然后让他查到结果以后再告诉我们,接下来我们就去做自己的事情了。这里我们在MainActivity中定义了一个方法onResultGet(),店主查到结果就会调用这个方法(打电话过来):
/**
* 有结果了,接电话
*
* @param result
* @return
*/
public boolean onResultGet(boolean result) {
return result;
}
而在店主那里,要有我的联系方式(占有了我的引用):
/**
* 异步回调
* @param phoneName
* @param activity 我的引用
*/
public void ckeckPhoneAsyn(final String phoneName,
final MainActivity activity) {
new Thread() {
@Override
public void run() {
SystemClock.sleep(2000);
boolean resultAsyn = "noo7".equals(phoneName);
// 查到结果了,要告诉顾客,等顾客接电话
activity.onResultGet(resultAsyn);
}
}.start();
}
这时候我们再运行一下看看结果:
但是,这依然不是最好的。相信用过框架的同学都知道,我们不会直接在ckeckPhoneAsyn()中直接传个当前对象this(即固定的对象)进来,因为这里一旦我们变成了别的对象,这个方法就废了。这时候我们就要有一种面向接口编程的思想了!我们直接在PhonestoreService中定义一个接口:
public interface onResultGetListener {
void onSuccess(boolean result);
void onFail();
}
接口里面有两个方法,分别在成功返回结果和返回结果失败的时候进行相应的动作(比如老板因为店里有太多sam啥手机,炸了,没办法告诉你结果)。这时候,只要我们的Mainactivity实现这个接口,然后在这两个方法里面做相应的业务处理就可以了。下面是面向接口编程思想后的形式,第二种看着是不是很像onClickListener()呢?其实道理是一样的。
service.ckeckPhoneAsynCallback("not7", MainActivity.this);
service.ckeckPhoneAsynCallback("noo7", new PhonestoreService.onResultGetListener() {
@Override
public void onSuccess(boolean result) {
}
@Override
public void onFail() {
}
});