首页 > 编程知识 正文

安卓的回调方法,android 接口回调

时间:2023-05-04 19:19:00 阅读:268486 作者:4523

最近看书或者博客的时候老是遇到回调函数、回调接口,在没有搞懂的情况下继续学习,头都是大的。于是看了很多博客,现在总算是弄明白了一点,以下写出自己对Android中“回调”的很粗浅的理解。

首先说一下最抽象的形式——2个类,A类和B类。A类含有1个接口、1个接口变量、(可能含有)1个为接口变量赋值的方法以及1个会使用接口变量的“地方”;B类实现A中的接口,(可能)含有1个A类实例的引用,并且(可能用A类中为接口变量赋值的方法)将“自己”传递给A类的接口变量。

这么说相当的抽象,用一个稍微具体一点的例子来说明一下:

首先是A类

public class A{ ... //1个接口变量 private Callback mCallback; ... //1个接口 public interface Callback{ void doSomething(); } ... //1个给接口变量赋值的方法 public void setCallback(Callback callback){ mCallback = callback; } ... //1个使用接口变量的地方 public void onExecute(){ ... mCallback.doSomething(); ... } ...}

然后是B类

public class B implements A.Callback{ ... //A类的实例的引用 private A mAInstance; ... //B类实现了A类的接口 public void doSomething(){ Log.d("TAG","will do something"); } ... //B类将自己(实际上是接口的实现)传给A类实例的接口变量 mAInstance.setCallback(this); }

总结一下就是以下的几个点:

A类中有一个接口变量和接口。B类实现A类的接口(这个接口就是所谓的回调接口)。A类的接口变量会(通过过某种方法)获得靠B类实现的接口。A类在一个适当的时机“使用”这个接口变量,即调用接口中的函数(这个函数就是所谓的回调函数)。

用生活中的事情打比方,其实很像是某人甲买杀手去杀死淡定的皮皮虾= =,甲只是告诉杀手杀人这个目的,具体怎么杀死甲的淡定的皮皮虾,由杀手去决定。这里甲是A类,杀手是B类,甲在某时刻告诉杀手杀人是A类调用回调接口里面的回调函数,杀手杀人的方法是B类实现A类的回调接口…

作为Android中更加具体的例子,我们来看看以下的场景:

场景一:
在Android中,听到最多的大概就是就是onclick回调方法,我们对比着A类和B类来看。

首先我们来看B类,在这里一般为一个Activity。

public class MainActivity extends Activity implements View.OnClickListener{ ... //Button的引用(A类的引用) private Button mButton; ... protected void onCreate(Bundle savedInstanceState){ ... //Activity将自己传给Button的OnClickListener接口变 //量(B类将自己传给A类实例的接口变量) mButton.setOnClickListener(this); ... } //Activity实现了Button的OnClickListener接口(B类实现 //了A类的接口) public void onClick(){ Log.d("TAG", "点击了"); } ...}

这是一个很常见的Activity中的点击了Button之后我们进行一系列逻辑操作的例子。

然后来看一下A类,这里为1个Button或者说是View,我们在Button以及它的父类TextView中都没有看到类似接口变量的东西,我们继续找TextView的父类View,Ctrl+F我们找到了想要的东西。

1个接口变量

/** * Listener used to dispatch click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ public OnClickListener mOnClickListener;

1个接口

/** * Interface definition for a callback to be invoked when a view is clicked. */ public interface OnClickListener { /** * Called when a view has been clicked. * * @param v The view that was clicked. */ void onClick(View v); }

1个为接口变量赋值的方法

/** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */ public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }

1个使用这个接口的地方

/** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing * a sound, etc. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; }

至于performClick这个方法什么时候执行,这涉及到Touch事件分发的一些知识,我们大可以理解为“在某个时候执行了回调函数onClick”

看了这个具体的例子以后,有这样一种感觉——“Button(View)只是知道在某个时刻要去执行onClick,而具体的细节要交给实现了接口的Activity去决定”,这让人想起面向对象的思想:每一个Button都可以被点击,但是每个Button被点击后执行的逻辑可能不相同,Button只对外暴露一个接口,具体的实现细节谁实现谁负责。

场景二:
这里参考的是《Android权威指南》
假如在平板上,有1个Activity管理着2个Fragment,左半边的是笔记列表NotesFragment,有半边是笔记内容DetailFragment。我们点击左边的列表的note,在右边相应显示出note的detail。我们马上会想到在NotesFragment的点击回调函数实现里,获取FragmenManager,然后用FragmenManager实例执行事务将1个DetailFragment添加到Activity右半边的布局,设想的代码如下:

public void onListItemClick(ListView l, View v, int position, long id){ ... Fragment fragment = DetailFragment.newInstance(position); FragmentManager fm = getActivity().getSupportFragmentManager(); fm.begainTransaction() .add(R.id.detailFragmentContainer, fragment) .commit();}

但是这样的做法很老套,Fragment天生是一种独立开发的构件,它不需要知道托管它的Activity是如何工作的,它们之间应该独立,Activity添加DetailFragment的工作是Activity应该处理的事情。我们在NotesFragment中定义回调接口,托管它的Activity实现回调接口。NotesFragment只是在某个时刻命令托管它的Activity去完成添加DetailFragment的任务。大概的代码如下:

public class NotesFragment extends ListFragment{ ... //1个接口变量 private Callback mCallback; ... //1个接口 public interface Callback{ void onNoteSelected(int position); } ... //给接口变量赋值的地方 public void onAttach(Activity activity){ super.onAttach(); mCallback = (Callback)activity; } public void onDtach(){ super.onDetach(); mCallback = null; } ... public void onListItemClick(ListView l, View v, int position, long id){ ... //使用接口变量的地方 mCallback.onNoteSelected(position); ... }} public class NotesActivity extends Activity implements NotesFragment.Callback{ ... //实现NotesFragment的接口 public void onNoteSelected(int position){ //添加DetailFragment到右半屏幕 ... } ...}

我们可以看到,NotesFragment中在点击的回调函数中调用了自己的回调函数,Activity实现了具体添加操作。这里和场景一中略有不同的是NotesFragment中没有一个专门为接口变量赋值的方法,托管它的Activity中也没有获得NotesFragment实例的引用,并把自己传回去,因为这一切都可以在系统的回调函数onAttach中完成。

好了,现在我们对“回调”应该有一个还算清晰的认识了,我们再来看看《Thinking in Java》中对接口的描述:接口为我们提供了一种将接口与实现分离的更加结构化的方法,所有实现了接口的类看起来都像这样。我理解的是一个类实现了一个接口,那么它就具有某种“能力”或“特性”去做某些事情,回调接口,就是A类让“有能力”的B类去完成某些事情,A类只负责命令,B类负责具体实施。上面两个场景都是让Activity去处理不同的细节。这也很契合面向对象的思想,我们把普通事物的共性抽取出来,而这些共性之中又充斥着特性,每个不同的特性就需要交给特定的情况处理,通过暴露接口方法可以减少很多重复,代码更加优雅。我们再看到回调函数,应该有这样的意识——一定有某个类在某个时刻调用了这个回调函数。

最后再看一下百度百科里对于回调函数的定义(虽然好像是针对C语言写的,但是思想是一样的),你可能就更加明白了:

定义:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
机制:
⑴定义一个回调函数;
⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

以上就是大意的香菇对Android中“回调”的拙见,如果有错误或需要补充的地方我会及时更新,谢谢!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。