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,对于整个应用层结构会更清晰。

时间: 2024-10-19 07:00:27

Android源码适配器模式---Activity类结构的相关文章

Activity生命周期的回调,你应该知道得更多!--Android源码剖析(上)

转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/46909173[yalinfendou的博客] 学习Android近一年,最近几天总算把Activity启动的生命周期回调流程走通了,因为所涉及的知识点太多,赶快做了笔记,不然过几天就忘了.强烈推荐<Android内核剖析>这本书,虽然随着Android版本的不断迭代更新,代码变化了不少,仍具有很强的参考价值. 本篇所涉及知识点: Android 从开机到第一个Activit

谈谈23种设计模式在Android源码及项目中的应用

本文首发于个人博客:Lam's Blog - 谈谈23种设计模式在Android源码及项目中的应用,文章由MarkDown语法编写,可能不同平台渲染效果不一,如果有存在排版错误图片无法显示等问题,烦请移至个人博客,如果个人博客无法访问可以留言告诉我,转载请声明个人博客出处,谢谢. 前言 本文将结合实际谈谈23种设计模式,每种设计模式涉及 * 定义:抽象化的定义与通俗的描述,尽量说明清楚其含义与应用场景 * 示例:如果项目中有使用过该模式,则会给出项目中的代码,否则会给出尽可能简单好理解的java

《Android源码设计模式解析》读书笔记——Android中你应该知道的设计模式

断断续续的,<Android源码设计模式解析>也看了一遍,书中提到了很多的设计模式,但是有部分在开发中见到的几率很小,所以掌握不了也没有太大影响. 我觉得这本书的最大价值有两点,一个是从设计模式的角度去理解Android源码,结合着日常开发中的常用类,对设计模式的理解会更加的深刻:另外一个好处就是了解常用模式,再看其他人写的代码的时候,更容易理解代码思路.下面是我的读书笔记和一些思考,设计模式只整理我认为重要的部分. 建造者模式 建造者模式最明显的标志就是Build类,而在Android中最常

Cordova Android源码分析系列一(项目总览和CordovaActivity分析)

PhoneGap/Cordova是一个专业的移动应用开发框架,是一个全面的WEB APP开发的框架,提供了以WEB形式来访问终端设备的API的功能.这对于采用WEB APP进行开发者来说是个福音,这可以避免了原生开发的某些功能.Cordova 只是个原生外壳,app的内核是一个完整的webapp,需要调用的原生功能将以原生插件的形式实现,以暴露js接口的方式调用. Cordova Android项目是Cordova Android原生部分的Java代码实现,提供了Android原生代码和上层We

android源码大放送(实战开发必备),免费安卓demo源码,例子大全文件详细列表

免费安卓demo源码,例子大全文件详细列表 本列表源码永久免费下载地址:http://www.jiandaima.com/blog/android-demo 卷 yunpan 的文件夹 PATH 列表 卷序列号为 0000-73EC E:. │ jiandaima.com文件列表生成.bat │ 例子大全说明.txt │ 本例子永久更新地址~.url │ 目录列表2016.03.10更新.txt │ ├─前台界面 │ ├─3D标签云卡片热门 │ │ Android TagCloudView云标签

Android源码学习之装饰模式应用

主要内容: 装饰模式定义 装饰模式优势 装饰模式在Android源码中的应用 一.装饰模式定义 装饰模式定义: Attach additional responsibilities to an object dynamically keeping the same interface. Decoators provide a flexible alternative to subclassing for extending functionality. 动态地给一个对象添加一些额外的职责.就增加

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

第十八章.代理模式 代理模式也称委托模式,是结构型设计模式之一.是应用广泛的模式之一. 1.定义 为其他对象提供一种代理以控制对这个对象的访问. 2.使用场景 当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口. 3.UML类图 (1)Subject:抽象主题类,声明真实主题与共同接口方法,该类可以是抽象类或接口. (2)RealSubject:真实主题类(被委托类),尤其执行具体的业务逻辑方法.

Android学习进阶路线Android源码分享)

毕业8月有余,同时伴随着从事的Android经验的提升.仔细思量过去的工作内容,掐指算来也是少得可怜---主要维护FM收 音机模块,间或看看Lancher模块的代码.尽管这样,总的来说,在这八个月事件的学习里,我自我感觉收获还是很大的---自学了很多 Android相关方面的知识.虽然里真正的大牛还有很远的一段路程要走,自己也会坚持学习.坚持走下去. 再次,把自己学习过程中总结的一些经验总结出来,帮助那些刚刚步入Android的网友以及有了取得了一些经验但不知如何 继续前行的朋友,希望你们能够早

当观察者模式和回调机制遇上Android源码

上一篇博客跟大家分享了Android源码中的装饰者模式,有点意犹未尽,今天跟大家分享下Android中的观察者模式,顺便说一说观察者模式和回调机制的关系,欢迎大家拍砖. 观察者模式 定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观察者模式的结构 观察者模式所涉及的角色有: 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对