关闭

【Android归纳】回调机制在Android中的应用与实战

标签: javaandroid
1082人阅读 评论(2) 收藏 举报
分类:

回调这种思想在程序中是比较普遍的,有时候可能我们并没有注意到,最近整理了一些对于回调的理解,分享给大家

先上概念......

什么是回调函数?

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

敲打抓狂疑问大哭

好吧,我们还是先讲一个小故事来缓解一下紧张的氛围:(其实就是举个形象的小例子)吐舌头

       你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。其实这就是一种“异步+回调”的编程模式。

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于是你在店员那里留下了你的电话

电话号码就是回调接口,即上面的interface OnClickListener

你把电话留给店员就叫登记回调函数,即代码中的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




1
0
查看评论

Android 回调接口是啥,回调机制详解

首先解决啥是回调: 举个例子:某天,我打电话向你请教问题,当然是个难题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。 C不会自己...
  • a78270528
  • a78270528
  • 2015-07-16 22:45
  • 14026

Android中单例模式应用实战详解

一、单例模式简介 单例模式是对象的创建模式,单例模式能够确保某一个类只有一个单一的实例对象存在,同时能够自行实例化并将单一的实例提供给外界调用的特点,其在项目开发中经常被用到。单例模式是设计模式中最简单的,只有一个单例类,没有其他的层次结构与抽象。该模式需要确保该类只能生成一个对象,通常是该类需要...
  • u012721519
  • u012721519
  • 2016-07-14 22:21
  • 608

《Android源码设计模式解析与实战》读书笔记(十)

第十章、解释器模式 解释器模式是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式。但是它的使用场景确实很广泛,只是因为我们自己很少回去构造一个语言的文法,所以使用较少。
  • qq_17766199
  • qq_17766199
  • 2015-12-23 09:08
  • 2146

《C#开发Android应用实战——使用Mono for Android和.NET/C#》--评

拿到这本书的时候,对书的内容没有感到多少意外,因为之前已经在网上下载了英文版电子书,现在得到这本中文版纸质书感到很亲切。说实在的,作为一个.net开发人员,要想做移动开发,似乎只能选择windows phone平台,而我对windows phone平台目前感到失望,有以下几点:1.windows p...
  • lee576
  • lee576
  • 2013-02-18 11:13
  • 24903

回调机制详解

今天遇到一位码友问我如何在异步加载完毕后获得异步的返回值,当时我思考了下就让他在异步完成后回调一下将值返回……因为对方是初学者我就给他简单解释了下Android的回调……说了一下午他都没弄明白……原因不是我讲得不够详细而是他陷进了他异步的问题……他把他那个问题和我讲的回调混在了一起……一直跳不出那个...
  • aigestudio
  • aigestudio
  • 2014-11-06 22:23
  • 10431

Java回调机制在Android中的应用

java 异步 回调方法 经典例子 android回调 android源码
  • qq_26440221
  • qq_26440221
  • 2017-08-15 10:33
  • 107

设计模式-单例模式(Singleton)在Android中的应用场景和实际使用遇到的问题

介绍在上篇博客中详细说明了各种单例的写法和问题。这篇主要介绍单例在Android开发中的各种应用场景以及和静态类方法的对比考虑,举实际例子说明。单例的思考写了这么多单例,都快忘记我们到底为什么需要单例,复习单例的本质 单例的本质:控制实例的数量 全局有且只有一个对象,并能够全局访问得到。控制实例...
  • Card361401376
  • Card361401376
  • 2016-05-08 19:25
  • 8003

工(程师)欲善其事,必先利其(编译)器——《Android Studio实战——快速、高效地构建Android应用》

用Android Studio搜索“Power Mode II”插件,比HHKB还震撼的编程体验会让你给本文点赞的。
  • dongfeng9ge
  • dongfeng9ge
  • 2016-08-20 16:50
  • 2783

Android进阶:基于okhttp 3 的 Android 网络层架构设计

随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到程序开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了。对于学习有困难不知道如何提升自己可以加扣:1225462853进行交流得到帮助,获取学习资料. CK203...
  • cadn_jueying
  • cadn_jueying
  • 2017-11-25 13:08
  • 157

android 异步回调机制

android 回调机制,如何理解回调方法
  • u010152805
  • u010152805
  • 2013-12-31 19:52
  • 1784
    个人资料
    • 访问:239378次
    • 积分:3434
    • 等级:
    • 排名:第11591名
    • 原创:97篇
    • 转载:0篇
    • 译文:0篇
    • 评论:131条
    其它资源
    友情链接
    最新评论