一、为什么会有接口回调?什么是接口回调?
其实这两个问题是一个问题,知道了接口回调的原理自然就知道了为什么会有这么个东西。我们知道java中接口是不可以直接创建实例的,那么问题来了,假如我把一个接口声明为一个变量,那么我执行这个接口中的方法,接口没有实例它该怎么办呢?啊哈,这里自然又改出现java中的另一个特性---“多态”,这时java虚拟机自然会去找其子类,调用其子类中已经重载的该方法,这里就是接口回调的本质!!我们只需要给该变量指向其子类的地址就可以在调用的时候知道调用子类的方法。那么我们就可以在A类中创建接口的子类实例,在B类中创建一个接口的变量,把A类的地址传给B类的变量,在变量执行接口中的方法的时候就会调用A类中重写的方法,这就是接口回调的执行步骤。我们在网络请求等耗时的操作的时候会使用到该机制,用来把得到的数据传回主线程中。
二、使用实例
下面我们就来用一个网络请求的实例来演示如何使用该机制,为了尽量的减少代码,使主要代码能够突出,里面的一些不必要的容错代码就没有写了,大家在正式的项目中希望可以做好容错处理。
我们用一个按钮和一个imageView来演示,当点击按钮的时候去下载数据,在数据下载完成之后使用接口回调把数据传回来显示在imageview中。效果如下:
布局文件很简单就不在贴出来了,我们有3个类,一个是回调的接口,一个是主activity,一个为执行下载并且把数据回调回来的Callee类,下面来看接口类:
import android.graphics.Bitmap;
/**
* Created by JimLv on 2016/5/31.
*/
public interface CallBackInterface {
void result(Bitmap bm);
}
定义了一个用于传递数据的方法,来看看Callee类:
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by JimLv on 2016/5/31.
*/
public class Callee {
private final String IMGURL = "http://f.hiphotos.baidu.com/zhidao/pic/item/b21bb051f8198618a323ac464bed2e738ad4e688.jpg";
//持有接口变量
CallBackInterface mInterface;
Callee(CallBackInterface theInterface) {
//这里是关键,把外部的接口实例引用到该类,给变量赋值
mInterface = theInterface;
//创建对象的时候执行下载
executeDown();
}
public void executeDown() {
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(IMGURL).openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
mInterface.result(BitmapFactory.decodeStream(conn.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
在这里我们声明了一个接口变量,在类的初始化方法中把接口的子类的地址赋给该变量并且调用下载数据的方法,在数据下载完成之后调用接口变量的方法把数据传给该方法,执行该方法实际是执行子类的该方法,这就是接口回调真正做的事。
最后我们来看MainActivity类:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView imgv;
private Button downImgBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgv = (ImageView) findViewById(R.id.imgv);
downImgBtn = (Button) findViewById(R.id.downImgBtn);
//点击按钮去下载
downImgBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//从接口回调得到数据,因为下载是在子线程中,所以这里要变到主线程中设置图片
new Callee(new CallBackInterface() {
@Override
public void result(final Bitmap bm) {
runOnUiThread(new Runnable() {
@Override
public void run() {
imgv.setImageBitmap(bm);
}
});
}
});
}
}
在这里我们用匿名内部类创建该接口的子类实例,子类重载的方法中因为是运行在子线程中的,我们无法操作UI,所有调用了runOnUiThread方法,把数据更新到imageview中,整个的demo就这么点是不是很简单呢?
扫描关注我的微信公众号:
三、总结
哈哈,整个回调机制是不是很简单呢?其实以前不懂这个的时候也是很头疼,理不清里面的逻辑,百度上面的文章又是一大堆,可是里面内容啰啰嗦嗦不知所云,真是肯爹不浅啊。“”该死的程序狗“”~~~~~~~~~~好啦,其实我也很讨厌别人这么说,不过无所谓啦,我们是有上进心的工程师,不是和你家那位一样的程序狗!弄懂了一个技术的本质是什么之后使用起来才不会无从下手,遇到问题才会知道如何解决。如果你觉得这篇文章写的还不错希望你点个赞,如果想看我以后写的文章请关注我,祝步步高升,天天开心!
最后附上demo:点击打开链接