【Android归纳决】回调机制在Android中的应用与实战
回调这种思想在程序中是比较普遍的,有时候可能我们并没有注意到,最近整理了一些对于回调的理解,分享给大家
先上概念......
什么是回调函数?
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
好吧,我们还是先讲一个小故事来缓解一下紧张的氛围:(其实就是举个形象的小例子)
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。其实这就是一种“异步+回调”的编程模式。
java中是不允许对指针进行直接操作的,那我们如何实现回调? 通过接口或者内部类来实现。
我们先看一个小例子吧,就是Android中随处可见的UI事件点击处理,我会从源码角度来分析它是怎么实现接口回调的。
一般在代码中,我们会这样处理Button的点击事件:
可以这样
public class MainActivity extends Activity implements android.view.View.OnClickListener{ private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(this); } @Override public void onClick(View v) { ToastUtils.toast(this, "perform onclick"); }可以这样
public class MainActivity extends Activity { private Button btn; private OnClickListener clickListener = new OnClickListener() { @Override public void onClick(View v) { ToastUtils.toast(getApplication(), "perform onclick"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(clickListener); }}也可以这样
public class MainActivity extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ToastUtils.toast(getApplication(), "perform onclick"); } } }
大家可以仔细看看这三种方式的实现区别。
这里面的OnClickListener即为点击事件的回调接口。我们可以看到View$OnClickListener.class中是这么定义OnClickListener接口的
/** * 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); }
然后,我们会继续查看View中与接口回调相关的方法,原因是
1、Button继承TextView
public class Button extends TextView { public Button(Context context) { this(context, null); } ......}2、TextView继承View
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { ......}在View里面处理了回调
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { /** * 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); } /** * Listener used to dispatch click events. This field should be made * private, so it is hidden from the SDK. {@hide} */ protected OnClickListener mOnClickListener; /** * 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(OnClickListener l) { if (!isClickable()) { setClickable(true); } mOnClickListener = l; } /** * Call this view's OnClickListener, if it is defined. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return false; }}
通过类比我们可以这么理解
1、你到一个商店买东西,刚好你要的东西没有货(尚未触发点击事件,就是你还没有点击按钮btn),
2、于是你在店员那里留下了你的电话
电话号码就是回调接口,即上面的interfaceOnClickListener
你把电话留给店员就叫登记回调函数,即代码中的btn.setOnClickListener(..)
3、过了几天店里有货了(你点击了按钮,触发了回调关联的事件performClick),店员就打了你的电话,店员给你打电话叫做调用回调函数(mOnClickListener.onClick(this);),前提是你的电话可以打通(if(mOnClickListener!=null))
4、然后你接到电话后就到店里去取了货,你到店里去取货叫做响应回调事件,即在Activity中实现了onClick方法
接下来我们可以自己写个小例子总结一下基本的回调过程如何实现:(可以忽视掉代码中跟本章内容无关的东西)
需求:从网络上获取数据,并将数据显示在listview上,这里我们使用AsyncTask完成异步加载数据(通过线程休眠模拟数据加载延迟)
1、首先需要定义一个回调接口,这个回调接口中的方法会在特定的情况下触发,例子当中就是设置了数据获取成功或者失败的回调
public class MyAsyncTask extends AsyncTask<String, Integer, ArrayList<String>> { private QueryResultListener queryResultListener; private ArrayList<String> items; private ListActivity activity; private LoadingDialog mLoadingDialog; private boolean isCompleted; public MyAsyncTask(ListActivity activity) { this.activity = activity; } public void setQueryResultListener(QueryResultListener queryResultListener) { this.queryResultListener = queryResultListener; } @Override protected void onPreExecute() { mLoadingDialog = new LoadingDialog(); mLoadingDialog.show(activity.getFragmentManager(), "LOADING"); super.onPreExecute(); } @Override protected ArrayList<String> doInBackground(String... params) { items = loadData(); return items; } @Override protected void onPostExecute(ArrayList<String> result) { isCompleted = true; if (mLoadingDialog != null) { mLoadingDialog.dismiss(); } if (activity != null) { if (queryResultListener != null) { if (result == null) { queryResultListener.onQueryResultFailed(); } else { queryResultListener.onQueryResultSuccess(result); } } } super.onPostExecute(result); } public ArrayList<String> getItems(){ return items; } private ArrayList<String> loadData() { try { Thread.sleep(5000); } catch (InterruptedException e) { } return new ArrayList<String>(Arrays.asList("接", "口", "回", "调", "详", "解")); } private void notifyActivityTaskCompleted() { if (null != activity) { activity.onTaskCompleted(); } } public interface QueryResultListener { public void onQueryResultFailed(); public void onQueryResultSuccess(ArrayList<String> result); }}
2、在ListActivity中实现回调接口的注册与响应,即成功的话,刷新listview,失败的话,有一些提示等等
MyAsyncTask myAsyncTask = new MyAsyncTask(this); myAsyncTask.execute(); myAsyncTask.setQueryResultListener(new QueryResultListener() { @Override public void onQueryResultSuccess(ArrayList<String> result) { adapter.setItems(result); } @Override public void onQueryResultFailed() { } });
今天的博客就到这里吧!
PS:第一次比较像样的写了点东西,其中也参考了很多资料,感觉写出一篇好博客也是挺费时费力的,有什么问题的话,希望大家轻拍,多多评论,多多交流
demo源码地址:https://github.com/feifei003603/CallBackDemo.git
更多相关文章
- [置顶] android调用第三方库——第三篇——编写库android程序整
- Android6.0 WMS(六) WMS动画管理
- Android查看调用函数名与行号等信息的日志类
- 基于Android(安卓)RIL层实现来电拦截的技术原理(二)
- 详解Android(安卓)Surface系统
- 一个android显示远程txt的代码例子
- Android(安卓)GridView+listener
- android Glide3.7 圆角图片
- android Junit demo