Android源码适配器模式---Activity类结构

其实适配器模式在Android源码中非常多,而从整体的源码角度上来看Activity的结构就是一种适配器模式。从这个角度上面看Activity,对Activity和应用层框架会有更加深入的理解。

适配器模式

意图

将一个接口转换为用户需要的另外一个接口,适配器模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。

UML图

适配器模式有两种模式,UML分别如下:
这里写图片描述
这里写图片描述

第一种是直接继承已经有的接口适配目标接口,而第二种是引用已有的接口适配目标接口。

示例代码

interface Target{
    void request();
}

class Adaptee{
    public void specialRequest(){
        System.out.println("special from adaptee");
    }
}

class Adapter extends Adaptee implements Target{
    public void request(){
        //do something to implements request
        specialRequest();
    }
}

public static final void main(String args[]){
    Target target = new Adapter();
    target.request();
}

上面是第一种适配器模式的简单代码示例,通过继承已有的类来适配,另外一种组合的方式如下:

interface Target{
    void request();
}

class Adaptee{
    public void specialRequest(){
        System.out.println("special from adaptee");
    }
}

class Adapter implements Target{
    private Adaptee adaptee ;
    public void Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    public void request(){
        //do something to implements request
        adaptee.specialRequest();
    }
}

public static final void main(String args[]){
    Target target = new Adapter(new Adaptee());
    target.request();
}

两种方式类适配器和对象适配器。

Activity与适配器模式

Activity是Android的核心组件,它是负责应用UI的组件,可以说是Android四大组件中最重要,使用最多,最复杂的组建。它的源码也相当地庞大。从适配器的角度上来看,Activity适配了多个接口,先看一下它的类结构图:

这里写图片描述

将Activity看成是适配器模式初看可能会有点牵强。但是ContextThemeWrapper是表示主题的环境类,Context可以翻译为应用环境,但是对于需要显示UI的一个应用组建除了应用环境外,还需要适应其他的内容信息,比如Window,比如KeyEvent等等。

拿窗口系统举例。Android中有Window管理系统,但是窗口系统需要与的Window.Callback接口,但是现在是有了Context,组建需要Window.Callback接口,这样创建Activity(这个是Adapter)实现Window.Callback接口,并且继承ContextThemeWrapper,将ContextWrapper与Window.Callback协作,让Context与Window一起工作。Window.Callback只是Activity适配的其中一个接口,下面分别介绍类结构的每一个部分。

ContextThemeWrapper

这是一个包含主题的Context装饰器,本身ContextWrapper是一个装饰器模式,在Android中,四大组建都是ContextWrapper的子类,四大组建都需要应用环境。关于这部分可以看我这篇文章Android源码装饰模式—ContextWrapper。需要理解的是Context是一个应用环境类型,Context包含了各种跟应用环境相关的信息,可以用来与应用系统打交道的。

Window.Callback, Window.OnWindowDismissedCallback

Window.Callback 这个接口包含了很多接口函数,上面的UML图中只包含了部分接口,全部的接口类可见下面的Outline截图:

这里写图片描述

这个接口是窗口的回调接口,主要分为屏幕事件触发,按键事件触发,Panel相关的View创建与Prepare,Menu的回调,Window的变化回调,SearchRequest的回调,以及ActionMode的回调。

Window.OnWindowDismissedCallback是一个hide类,是无法通过API调用的,是当窗口消失(Window系统移除)的时候的回调接口。Activity的实现也很简单,直接finish掉自己。

/**
 * Called when the main window associated with the activity has been dismissed.
 * @hide
 */
@Override
public void onWindowDismissed() {
    finish();
}

Callback,OnWindowDismissedCallback是Window与Activity交互的回调接口。

初始部分代码为:

//Activity.java
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...

但实际上一个Window并不是只和一个Activity关联,而是一个Window和一个Callback关联,Activity也是Context,Android中Dialog里面也包含了Window,Dialog也实现了Callback接口。一个应用环境中(Context)可能包含多个Window,也就会有多个Callback,只是Activity这种应用环境本身就实现了Callback接口。

KeyEvent.Callback

对应着Key事件的回调接口,当按下按键的时候,会回调该接口。主要是为了适配输入系统。

ComponentCallbacks2

它是ComponentCallbacks的子接口,CompoentCallbacks包含下面两个接口:

void onConfigurationChanged(Configuration newConfig);

void onLowMemory();

ComponentCallbacks2新增了onTrimMemory接口。
ComponentCallbacks是专门为Android组件使用的回调接口,Android组件都会实现该接口(目前变成了ConponentCallbacks2),当配置信息变化,内存变化的时候,这些接口会被调用。调用这些接口的是ActivityThread(消息循环中,收到变化消息时),ViewRootImpl(在Window有变化的时候,ViewRootImpl负责与WindowManagerService通信)等。该接口是为了适配系统信息管理部分。

这里有两个跟内存相关的接口,这其实是为帮应用应对Android内存满负荷,提醒应用程序做一些释放内存处理,如果占用内存过大,应用将会更容易被杀死。具体可以看LowMemoryKiller的介绍。

OnCreateContextMenuListener

Android上下文菜单: 当给一个View注册了上下文菜单后,对这个View长按2秒,会弹出一个浮动的菜单。OnCreateContextMenuListener 它只有一个接口函数:

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    }

当View的Context menu被创建的时候,该接口的会被调用,用于获取Menu(作为实现改接口的Activity来讲,是设置Menu)。在Activity中,与这个接口函数对应的函数是onContextItemSelected,而该函数是继承自Window.Callback接口的onMenuItemSelected函数:

    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        CharSequence titleCondensed = item.getTitleCondensed();

        switch (featureId) {
            case Window.FEATURE_OPTIONS_PANEL:
                if(titleCondensed != null) {
                        EventLog.writeEvent(50000, 0, titleCondensed.toString());
                }
                if (onOptionsItemSelected(item)) {
                    return true;
                }
            ...
            case Window.FEATURE_CONTEXT_MENU:
                if(titleCondensed != null) {
                    EventLog.writeEvent(50000, 1, titleCondensed.toString());
                }
                if (onContextItemSelected(item)) { //这里
                    return true;
                }
                return mFragments.dispatchContextItemSelected(item);

            default:
                return false;
        }
    }

我们平时监听普通的Menu的函数onOptionsItemSelected也是由onMenuItemSelected调用的。

另外一边View中显示ContextMenu的函数是showContextMenu:

public boolean showContextMenu() {
    return getParent().showContextMenuForChild(this);
}

ViewGroup的showContextMenuForChild为:

public boolean showContextMenuForChild(View originalView) {
    return mParent != null && mParent.showContextMenuForChild(originalView);
}

getParent()最终会到DecorView,DecorView中创建了ContextMenu。然后调用View的createContextMenu方法,最终使用mOnCreateContextMenuListener获取Menu:

public void createContextMenu(ContextMenu menu) {
    ContextMenuInfo menuInfo = getContextMenuInfo();

    // Sets the current menu info so all items added to menu will have
    // my extra info set.
    ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);

    onCreateContextMenu(menu);
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnCreateContextMenuListener != null) {
        li.mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
    }

    // Clear the extra information so subsequent items that aren't mine don't
    // have my extra info.
    ((MenuBuilder)menu).setCurrentMenuInfo(null);

    if (mParent != null) {
        mParent.createContextMenu(menu);
    }
}

DecorView在PhoneWindow中,Menu其实会由Window统一管理,响应Item的点击事件的接口是一致的(Window.Callback.onMenuItemSelected),另外ContextMenu实际上显示出来的就是一个Dialog。但由于ContextMenu是跟View对应的,所以有了OnCreateContextMenuListener接口,它是用于当View需要创建ContextMenu的时候,方便指定ContextMenu的内容。

LayoutInflater.Factory2

这个接口只有一个接口函数:

public View onCreateView(View parent, String name, Context context, AttributeSet attrs);

它继承自Factory:

   public interface Factory {
        /**
         * Hook you can supply that is called when inflating from a LayoutInflater.
         * You can use this to customize the tag names available in your XML
         * layout files.
         * 
         * <p>
         * Note that it is good practice to prefix these custom names with your
         * package (i.e., com.coolcompany.apps) to avoid conflicts with system
         * names.
         */
        public View onCreateView(String name, Context context, AttributeSet attrs);
    }

用于跟LayoutInflater系统交互,为了适配LayoutInflater系统。实现改接口,可以在Inflater的时候,解析XML中自定义的Tag。该接口为LayoutInflater调用,而LayoutInflater的实现为PhoneLayoutInflater。对于Window和LayoutInflater结构可以看这篇Android源码抽象工厂—IPolicy

除了Activity外,Application和Service都实现了ComponnentCallbacks接口,继承了ContextWrapper,其实都可以用类适配器模式看待。

设计思考

本身应用组件都应该是一种应用环境(Context),但是又需要满足Window等系统的回调需求,我们平时可能直接单独实现Window.Callback接口,但是将Activity实现Window.Callback接口,那么Activity会更加具有整体性,不过设计意图在这里思考过多感觉有点太牵强。

总结

从Android应用层源码整理来看,Activity的类结构完全可以看成是一种适配器模式,在基于应用环境(Context)的情况下,去满足LayoutInflater系统(LayoutInflater.Factory2),Window系统(Window.Callback,Window.OnWindowDismissedCallback),输入系统(KeyEvent.Callback)的接口需求,另外ComponnentCallbacks更是ActivityThread和ViewRootImpl需要的接口。通过适配器模式来看Activity,对于Activity,对于Activity与其他部分的交互,对于应用层框架会有更好的理解。另外再有装饰模式看Context,对于整个应用层结构会更清晰。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个Android 身份证号码,手机号码归属地查询模块源码包,实例中一共有24个源代码文件,各个文件功能说明:   BaseActivity 头部bar的布局   BaseLayout 头部bar的布局   DataListHolder 就两个成员 ImageView TextView 你说干嘛呢   ItemAdapter 继承适配器   QueryAddress 继承BaseActivity 实现地址查询   QueryIDCard 继承BaseActivity 实现身份证归属地查询   QueryPhone 继承BaseActivity 实现手机号归属地查询   SmallToolsActivity继承BaseActivity 实现主界面布局   Splash 继承Activity 实现闪烁延时效果   UpdateApp 一个确定版本信息升级的   ApiImpl Api接口 实现了地址查询 身份证查询 手机信息查询 更新信息查询 网络请求的功能   AddressInfo 定义了一个地址信息结构   AppUpdateInfo定义了一个更新信息结构   IDCardInfo 定义了一个身份证结构   NumberItem定义了一个帐号结构   PhoneInfo 定义了一个手机信息结构   AppInfoHelpr 定义了一个程序信息 得到版本信息,版本名   Constants 定义了一个公用静态变量存放   DeviceHelper 定义了一个设备信息 主要功能有获取尺寸 读取手机号   HttpClientHelper 定义了一个服务请求,实现与服务器的交流。   HttpException 继承 Exception 实现了输出异常信息   HttpPostParameter 实现了参数信息的设置   HttpResponse 消息的响应   InternetHelper 内部帮助 可以湖的网络是否可用   从源代码文件可以看出,App只是实现了与服务器之间请求信息,以及信息显示,更适合android新手参考 。
本项目是一个基于安卓的框架项目源码 Loonandroid是一个注解框架,不涉及任何UI效果,目的是一个功能一个方法,以方法为最小颗粒度对功能进行拆解。把功能傻瓜化,简单化,去掉重复性的代码,隐藏复杂的实现。以便团队合作或者后期修改变得简单。说框架是夸大了,主要是因为我比较喜欢偷懒,对于一个码农来说,能够偷懒,并且在不影响项目质量的情况下,是不容易的。 很多朋友看到注解就就要吐槽,会影响性能什么的。注解,确实会影响性能。通过注解自动注入,反射会让程序变慢50~100毫秒左右,从体验感基本感觉不出来.硬件性能好的手机可以忽略,经过测试无需太大的担心。我是做外包的,初衷是在不影响项目质量的前提下减少我的工作量,而且BUG其他人改起来相对比较容易,本工具专属外包码农,如果你想做精细,很在意性能数据,请看看就好。 1、基本功能 InLayer注解 InPlayer 注解 Activity生命周期注解 InView注解 InSource注解 InAll注解 后台进程注解 方法点击事件注解 基注解 自动Fragment注解 手动Fragment注解 2、适配器功能 无适配器 无参baseAdapter 自定义一adapter 自定义二adapter 自动绑定一adapter 自动绑定二adapter 通用适配器 3、综合功能集合 网络请求模块 输入验证 跨进程通讯 Json格式化 倒计时 4、傻瓜式下拉刷新 Listview Grid 横向Scrollview 纵向Scrollview 横向ViewPage 纵向ViewPage WebView 5、自定义模块 自定义模块XML中使用 自定义模块变量使用 6、傻瓜式组件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值