By 高焕堂  

ee                                                                   ee 

【思考Android技术】 

 請參考:Android从程序员到架构师之路课程(在线视频学习) 

 请进入:ADT架构师学苑  

ee                                                                   ee

Android"强势型"API幕后的IoC机制

 

 

1. 前言

    顾名思义,IoC就是「反向控制」(Inversion of Control)之意思;又称为「控制反转」。而它是相对于「正向控制」一词,所以先了解「正向控制」之涵义,就能理解「反向控制」之涵义了。IoC观念和机制源自于OOP语言(如C++、Java等)的类别继承(Class Inheritance)机制,例如Java语言中,基类(Super-class)的函数可以主动调用子类(Subclass)之函数,这就是最基本的IoC机制,称为「继承型IoC」。在现代云平台里,云主(即强龙)通常在其框架(Framework)里提供许多基类,让第三方开发者(即地头蛇)撰写子类来继承之。而这IoC机制就在幕后支持着主动型API,来让强龙的基类调用地头蛇的子类。

    

2. 从被动型API谈起

2.1  以函数调用(Function Calling)为例

     强龙可以提供传统的链接库(Function Library),它含有许多函数,让地头蛇的过程调用之。例如,

       public myFunction(){

                int x = abs( y ); 

                //……        

           } 

       abs()函数就是强龙提供的库存函数,强龙撰写abs()函数在先,是前辈;而地头蛇则撰写myFunction()在后,是晚辈。这是传统调用法:晚辈调用前辈,也就是地头蛇调用强龙,属于被动型API。 

2.2  以类别继承(Class Inheritance)为例

      强龙可以提供一般类别库(Class Library),其类别里含有许多函数,让地头蛇的类别来调用之。例如,Google强龙在Android框架里撰写了一个MediaPlayer基类如下:

             public class MediaPlayer {

                      //……..

              } 

接着,地头蛇可以写个子类myMediaPlayer去继承它,并且调用它的函数,如下:

 

// myMediaPlayer.java

package com.misoo.pkzz;

import android.content.Context;

import android.media.MediaPlayer;

 

public class myMediaPlayer extends MediaPlayer {

public static MediaPlayer create(Context context){

      return MediaPlayer.create(context, R.raw.test_cbr);

}}

 

    强龙撰写MediaPlayer.create()函数在先,是前辈;而地头蛇则撰写myMediaPlayer.create()在后,是晚辈。这也是晚辈调用前辈的情形。由于大家已习惯了这种晚辈调用前辈的用法,就通称为「正向」(Forward) 调用法。这种机制又称为「正向控制」。然而,由于地头蛇(晚辈)拥有主控权,而强龙则处于被动地位,所以这种正向控制所支持的API,是属于被动型的API[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

 

2.3   以对象组合(Object Composition)为例 

 

        对象A把外界传来的讯息「转送」给对象B,由B 处理之,我们称对象A 委托(Delegate)对象B。当一群对象互相沟通分工合作时常用委托观念。由于类别继承(Class Inheritance)和对象组合(Object Composition)是OOP软件设计的两个主要方法;在不便使用继承的场合里,常使用对象组合,此时也常用委托观念。例如,强龙提供了一个MediaPlayer具象类别;而地头蛇则撰写mp3Player类别,然后将播放音乐的工作委托强龙的MediaPlayer来执行,如下述代码:

 

/*    mp3Player.java   */

package com.misoo.pkcc;

import android.app.Activity;

import android.content.Context;

import android.media.MediaPlayer;

import android.util.Log;

 

public class mp3Player {

      private MediaPlayer mPlayer;

      private Context ctx; 

 

      public mp3Player(Context context){

           ctx = context;

           mPlayer = MediaPlayer.create(ctx, R.raw.test_cbr);

}

public void start(){

      try{

              mPlayer.start();

              ((ac01)ctx).tv.setText("Playing audio...");

              ((Activity)ctx).setTitle("MP3 Music");

          } catch (Exception e) 

                 {  Log.e("StartPlay", "error: " + e.getMessage(), e);  }

      }

     public void stop(){

        if (mPlayer != null) {

        ((ac01)ctx).tv.setText("Stop");

             mPlayer.stop();

             mPlayer.release();

             mPlayer = null;

         }

  }}  

  

       强龙撰写MediaPlayer.java具象类别在先,是前辈;而地头蛇则撰写mp3Player.java类别在后,是晚辈。这也是晚辈调用前辈的情形。由于地头蛇(晚辈)拥有主控权,而强龙则处于被动地位,所以这种正向控制所支持的API,是属于被动型的API。 

 

3. IoC如何支持框架的主动型API

3.1 基本概念

    与正向控制相反的就是「正向控制」,也就是IoC(Inversion of Control)。当子类继承基类时,基类之函数可以调用子类之函数。虽然基类(前辈)诞生时,子类(晚辈)常常尚未诞生﹔但是前辈有时候可预知晚辈中的函数,就可调用它。例如:有了一个基类:          

  

    图1. 从一个简单基类出发 

    设计者预期子类将会定义一个Draw()函数,于是让Paint()调用子类的Draw()函数。于是子类(晚辈)提供Draw()给基类(前辈)来调用之,如下: 

        

    图2. 子类提供Draw()给基类来调用 

兹以Java程序表达如下: 

// Shape.java

package _framework;

public class Shape {

          public void Paint(){

                    for(int i=0; i<3; i++)

                           this.Draw(30 + i*10);

               }

          public void Draw(int radius ){}

 } 

// Circle.java

package _objects;

import java.awt.Color;

import java.awt.Graphics;

import _framework.*; 

public class Circle extends Shape {

            private Graphics m_gr;

            private int x, y;

            public Circle(Graphics gr)

                      { m_gr = gr;  }

            public void SetValue(int x0, int y0)

                      { x = x0;    y = y0; }

            public void Draw(int radius){

                               //画圆

                               m_gr.setColor(Color.BLACK);

                               m_gr.drawOval(x-radius, y-radius, 2*radius, 2*radius);

                        }

} 

// JMain.java

import java.awt.*;

import javax.swing.*;

import _framework.Shape;

import _objects.*;

 

class JP extends JPanel {

public void paintComponent(Graphics gr) {

super.paintComponents(gr);

         Circle cir = new Circle(gr);     cir.SetValue(160, 100);

         Shape sp = cir;     sp.Paint();

    }}

public class JMain extends JFrame {

                 public JMain()

                          { setTitle(""); setSize(400, 300); }

                  public static void main(String[] args) {

                  JMain frm = new JMain();

                  JP panel = new JP();

                  frm.add(panel);

                  frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                  frm.setVisible(true);

}}

 

此程序执行到指令:

             Shape sp = cir;

             sp.Paint();

就调用到基类的Paint()函数,接着执行到指令:

      public class Shape {

             public void Paint()

                      {  for(int i=0; i<3; i++)

                                  this.Draw(30 + i*5);  

                       }

             public void Draw(int radius){}

 }

 

此时,就调用到子类里的Draw()函数了,画出下述图形: 

     

这种前辈调用晚辈的用法,就通称为「反向」(Inversion) 调用法。由于前辈拥有主控权,所以这种机制又称为「反向控制」(Inversion of Control)。

    回想一下,您写传统程序时,使用「正向控制」,主控权掌握在您的手中,您决定如何调用库存函数﹔就像棒球比赛的「投手」一样。反之,在上述例子里,使用「反向控制」,您的子类则担任「捕手」之角色[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

    于是,我们可进一步将上述的Shape基类放入框架里,并且将子类Circle归入AP里。如下图3所示。

 

 图3. 将Shape基类放入框架里

       由于基类内含不完整之抽象函数,必须由AP里的子类提供具体函数来补充之。接着,运用「外注相依性」机制,让框架能拿完整的子类来诞生对象,并且建立这些对象之间的合作或互动关系。然后,在执行时(Run-time),整个程序的主控权就掌握在Shape的Paint()函数手中,这就是典型的(IoC)机制了。 

3.2  微软.NET框架之例

   请看看微软.NET框架里的StreamFactory基类:

              public class abstract class StreamFactory {

                                   public Stream CreateStream();

                           }

 

这基类就反向调用AP里的myStreamFactory子类之CreateStream()具象函数:

               public class myStreamFactory : StreamFactory {

                                public Stream CreateStream(){

                                                          ………

                           } 

                }

如下图所示:

 

  图4. 基类反向调用子类的CreateStream()具象函数

 

3.3  Google的Android框架之例

       请看看Android框架里的Binder基类:

public class abstract class Binder {

         public boolean onTransact();

         ...............

}

这基类就反向调用AP里的myBinder子类之onTransact()具象函数: 

public class myBinder extends Binder {

             public boolean onTransact(){

                  ………

 } 

 

如下图所示: 

 

     图5. 基类反向调用子类的 onTransact()具象函数

 

4. 结语

      IoC是一个重要的框架实践技术,泛指开发在先的框架掌握了系统的主控权,反向调用开发在后的AP类别(或对象)。无论是继承IoC、委托IoC、或是外注相依IoC机制都是藉由框架里的预先规划的基类和抽象函数来提供更美好接口,以提升AP类别之抽换性,促进系统的新代谢,确保系统之整体和谐性。于是,成为框架设计师惯用的技术。 

[Go Back]