Android中的接口回调机制
文章目录
- 1.回调的含义和用途
- 2.java实现接口回调
- 3.Android中接口回调的体现
- 4.接口回调在异步任务中的体现
1.回调的含义和用途
一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类同步调用、异步调用和回调。同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。异步调用是一种类似消息或事件的机制解决了同步阻塞的问题,例如A通知B后,他们各走各的路,互不影响,不用像同步调用那样,A通知B后,非得等到B走完后,A才继续走。回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,例如A要调用B,B在执行完又要调用A。
回调的思想是:
类A的a()方法调用类B的b()方法
类B的b()方法执行完毕主动调用类A的callback()方法
通俗而言: 就是A类中调用B类中的某个方法C, 然后B类中反过来调用A类中的方法D, D这个方法就叫回调方法, 这样子说你是不是有点晕晕的, 其实我刚开始也是这样不理解, 看了人家说比较经典的回调方式:
1.class A实现接口CallBack callback——背景1
2.class A中包含一个class B的引用b ——背景2
3.class B有一个参数为callback的方法f(CallBack callback) ——背景3
4.A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C
5.然后b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
2.java实现接口回调
在C/C++中,要实现回调函数,被调用函数要告诉调用者自己的指针地址。但是Java没有指针地址,不能传递方法的地址,一般采用接口回调的方法来实现:把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被调用类实现的接口的方法。
原理:首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象,控制器对象负责检查某个场景是否出现或某个条件是否满足,当满足时,自动调用回调对象的方法。
package Demon;
//创建回调接口实现类
public class Boss implements AcceptListener{
public void acceptEvent(String result){
if(result.equals("ok")){
System.out.println("接受到了工作结果,老板很满意");
}else {
System.out.println("接受到了工作结果,老板很生气!");
}
}
}
class Manager implements AcceptListener{
public void acceptEvent(String result){
if(result.equals("ok")){
System.out.println("接受到了工作结果,经理很满意");
}else {
System.out.println("接受到了工作结果,经理很生气!");
}
}
}
class Tester implements AcceptListener{
public void acceptEvent(String result){
if(result.equals("ok")){
System.out.println("接受到了工作结果,测试很满意");
}else {
System.out.println("接受到了工作结果,测试很生气!");
}
}
}
//创建回调接口
interface AcceptListener {
public void acceptEvent(String result);
}
//创建控制类,要持有回调接口
class Employee{
AcceptListener listener;
public void setListener(AcceptListener listener) {
this.listener = listener;
}
public void doWork(){
System.out.println("玩命工作中...");
if(listener!=null){
listener.acceptEvent("ok");
}
}
public void haveRest(){
System.out.println("工作太累了,需要休息一天");
if(listener!=null){
listener.acceptEvent("休息");
}
}
}
//测试类
class TestMain{
public static void main(String[] args) {
Employee employee = new Employee();
//创建控制器对象,将提供给他的回调对象传入
employee.setListener(new Boss());
//启动控制器对象运行
employee.doWork();
}
}
设置接口就相对于设置一个用途,相当于可以将这个用途抽取出来供多个人可以使用,比较灵活
3.Android中接口回调的体现
package com.example.intenttest;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
public class MyDialog extends Dialog implements View.OnClickListener{
Button btn1,btn2;
EditText et;
TextView waimainTv;
interface OnEnsureListener{
public void onEnsure(String msg);
}
OnEnsureListener onEnsureListener;
public void setListener(OnEnsureListener listener) {
this.onEnsureListener = listener;
}
public MyDialog(@NonNull Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mydialog);
//只要是调用的本类的方法或变量加不加this都一样。因为findViewById是MyDialog继承的方法
btn1=findViewById(R.id.dg_btn1);
btn2=findViewById(R.id.dg_btn2);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
et=findViewById(R.id.dg_et);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.dg_btn1:
String msg = et.getText().toString();
if(!TextUtils.isEmpty(msg)){
if(onEnsureListener !=null){
onEnsureListener.onEnsure(msg);
}
}
break;
case R.id.dg_btn2:
cancel();
break;
}
}
public void setTextView(TextView tv) {
waimainTv=tv;
}
}
OnEnsureListener就是android系统所约好的接口,然后在我们写的应用程序中传入回调对象,这样就可以达到接口统一,实现不同的效果。
package com.example.intenttest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MainActivity2 extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv=findViewById(R.id.tv);
}
public void onClick(View view){
switch (view.getId()){
case R.id.tv:
MyDialog dialog = new MyDialog(this);
dialog.show();
dialog.setListener(new MyDialog.OnEnsureListener() {
@Override
public void onEnsure(String msg) {
Log.i("=====", "onEnsure: "+msg);
tv.setText(msg);
}
});
break;
}
}
}
点击确定后,点一下会变成你输入的内容
4.接口回调在异步任务中的体现
package com.example.intenttest;
import android.os.AsyncTask;
public class MyTask extends AsyncTask<String,Void,byte[]> {
public interface CallBack{
public void onSuccess(byte[] bytes);
public void onError();
}
CallBack mCallBack;
public void setCallBack(CallBack callBack) {
mCallBack = callBack;
}
@Override
protected byte[] doInBackground(String... params) {
//进行子线程操作的函数
byte[] content = HttpUtils.getByteContent(params[0]);
return content;
}
@Override
protected void onPostExecute(byte[] bytes) {
super.onPostExecute(bytes);
//将字节数组转换为位图
if(bytes!=null&&bytes.length!=0){
mCallBack.onSuccess(bytes);
}else {
mCallBack.onError();
}
}
}
package com.example.intenttest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtils {
public static byte[] getByteContent(String path){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
try {
HttpURLConnection conn=(HttpURLConnection) new URL(path).openConnection();
InputStream is = conn.getInputStream();
int hasRead=0;
byte[] buf = new byte[1024];
while (true){
hasRead=is.read(buf);
if(hasRead==-1){
//读完了,跳出循环,不读了
break;
}else {
baos.write(buf,0,hasRead);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}catch (Exception e){
e.printStackTrace();
}
return baos.toByteArray();
}
}
package com.example.intenttest;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
public class MainActivity2 extends AppCompatActivity {
TextView tv,tv2;
ImageView iv;
String url="https://img1.baidu.com/it/u=378860652&size=w500&n=0&g=0n&f=jpeg?sec=1646658692&t=cbeca35343cf07495a91f56c444974ff";
String url2="https://www.163.com/news/article/GVE6NVOQ0001899N.html?clickfrom=w_lb_1_big";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv=findViewById(R.id.tv);
tv2=findViewById(R.id.tv2);
iv=findViewById(R.id.iv);
}
public void onClick(View view){
switch (view.getId()){
case R.id.tv:
showDialog();
break;
case R.id.tv2:
MyTask myTask = new MyTask();
myTask.setCallBack(new MyTask.CallBack() {
@Override
public void onSuccess(byte[] bytes) {
try {
String s = new String(bytes, 0, bytes.length, "UTF-8");
tv2.setText(s);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void onError() {
tv2.setText("加载不到啊");
}
});
myTask.execute(url2);
break;
case R.id.iv:
showImage();
break;
}
}
private void showImage() {
MyTask task = new MyTask();
task.setCallBack(new MyTask.CallBack() {
@Override
public void onSuccess(byte[] bytes) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
iv.setImageBitmap(bitmap);
}
@Override
public void onError() {
iv.setImageResource(R.drawable.pc1);
}
});
task.execute(url);
}
private void showDialog() {
MyDialog dialog = new MyDialog(this);
dialog.show();
dialog.setListener(new MyDialog.OnEnsureListener() {
@Override
public void onEnsure(String msg) {
Log.i("=====", "onEnsure: "+msg);
tv.setText(msg);
}
});
}
}
上面通过接口回调很灵活,不仅通过该接口可以实现图片的加载也可以实现网页数据的加载。
因为在到点击事件的时候设置接口(是new的),当传入到Mytask类里的时候,通过传过来的对象给当前的接口赋值,然后通过当前接口调用的方法( mCallBack.onSuccess(bytes); mCallBack.onError();),从而去执行实现了的接口方法。