By 高焕堂   

ee                                                                   ee  

【思考Android技术】  

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

 请进入:ADT架构师学苑   

ee                                                                   ee

 框架创建App子类对象的范例说明

 -- 由基类创建亲生子类的对象   

         

1.  以MediaPlayer播放mp3音乐为例

    大家对于拿MediaPlayer来播放mp3音乐的应用程序,应该不会陌生。最简单的架构如下图所示: 

 

 图1、由myActivity来创建MediaPlayer的对象 

        你也可以设计一个包容器(Wrapper)类别来包装MediaPlayer类别,也就是由这个Wrapper来帮忙创建MediaPlayer对象,如下图所示: 

 

  图2、由Wrapper协助来创建MediaPlayer的对象 

      无论是在上图1或图2的架构里,你都是扮演一个传统的角色:AP开发者。于此,就来换个新鲜的角色:框架开发者。在上一篇文章里,已经说明了,必须让框架基类去创建应用子类的对象,才能让框架拥有高度的制约力量。在上一篇文章的范例里(即Ex04_01程序范例)已经实践了这个目标,让框架基类(如GraphView)来创建子类别(如myDrawing)的对象。其中,值得留意的是:上一篇文章范例里的基类GraphView并不是myDrawing的直系父类别(基类)。简而言之,myDrawing并不是继承GraphView基类。亦即,myDrawing并不是GraphView的亲生应用子类。

    于是,在本文里,我们来看看一个基类如何创建亲生子类别的对象。如下图所示: 

 

 图3、由基类Player来创建亲生子类mp3Player的对象 

      大家都知道一个类别本身就是一个对象,我们常常称之为「母对象」(Meta-object),而类别的静态函数(static function)就是这个母对象的函数,在这种函数里可以(由该母对象)创建自己类别的对象。在上图的架构里,由myActivity创建Player基类的init()静态函数,这init()创建其亲生子类别mp3Player的对象。请留意,这init()会将mp3Player的IPlay接口传回给myActivity。一旦myActivity接到mp3Player的IPlay接口,就能进行实际的创建动作。也就是,myActivity透过IPlay接口,而创建到mp3Player的play()函数,并进而创建MediaPalyer的start()函数,展开了播放mp3音乐的动作了。 [歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

 

2.  框架的设计与实践

2.1 复习:擅用配置文文件

    在上一篇文章里,运用了配置(Configuration)文件的机制来实践由框架(的基类)来创建应用(AP或App)子类的对象的任务,这是框架开发的基本技巧。其主要面对的情境是:框架基类由强龙设计在先,而应用子类则由地头蛇开发在后。也就是说,当强龙撰写框架基类时,地头蛇(和客户)都尚未出现,基类开发者还不知道AP子类的名称,那么又如何去<<new>>一个AP子类的对象呢? 其实很简单,因为在程序的执行时间( Run-time)才会真正创建对象,这执行时间已经是在地头蛇撰写子类的时间之后了,如下图所示:    

 

  图4、由子类开发者撰写配置(Configuration)文件 

      因此,解决之道是:请地头蛇撰写完AP子类时,也将子类的名称字符串(classname string)写入一个特定的配置(Configuration)文件里,然后于程序执行时,才去读取配置文文件里的子类的名称字符串,然后透过Java的指令: 

         Class.forName(classname).newInstance();

就能创建该AP子类的对象了。于是,实践了由框架创建AP子类对象的任务了。

2.2 设计架构图

     基于上述配置文文件的机制,也依循上一篇文章范例的架构,兹绘制架构图如下: 

 

图6、由基类创建子类对象的过程

     创建完成子类对象之后,就能够透过接口来调用该子类对象的函数了。例如,myActivity就透过IPlay而调用mp3Player的play()函数,此刻myActivity就调用MediaPlayer的start()函数,开始播放了。如下图: 

  

  图7、调用所创建的新对象 

       结束播放时,myActivity就透过IPlay而调用了mp3Player的stop()函数,此刻myActivity就调用MediaPlayer的stop()函数了。 

2.3  撰写代码:<创建对象>的实践途径

      兹将上图落实为Android代码,其执行画面如下: 

  

     首先建立一个Android的Ex04_02项目(Project),如下:

  

    背景图像sunrise.png存在/res里。 

  • 撰写你的框架基类和API 

// Player.java

package myFramework;

import android.content.Context; 

public abstract class Player {

       static private Player player = null;

       public static IPlay init(Context context){

                String pkclassName = ((PActivity)context).getPlayerClass();

                try {

                       player = (Player)Class.forName(pkclassName).newInstance();

                       return (IPlay)player;

                     } catch (Exception e) 

                            { e.printStackTrace(); 

               return null;

           }

 } 

 

// PActivity.java

package myFramework;

import com.misoo.pk003.R;

import android.app.Activity; 

public abstract class PActivity extends Activity{

       public IPlay init(){

             return Player.init(this);

        }

       public String getPlayerClass(){

                return this.getResources().getString(R.string.playerclass);

      }

}

 

//  IPlay.java

package myFramework;

import android.content.Context; 

public interface IPlay {

    void play(Context context);

    void stop();

} 

  • 把基类和API送人,协助别人去开发应用子类

// mp3Player.java

package com.misoo.pk003;

import myFramework.IPlay;

import myFramework.Player;

import android.content.Context;

import android.media.MediaPlayer;

import android.util.Log; 

public class mp3Player extends Player implements IPlay{

private MediaPlayer mPlayer = null; 

public void play(Context context) {

     try{

            mPlayer = MediaPlayer.create(context, R.raw.dreamed);

            mPlayer.start();

           } catch (Exception e) {

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

              }

        }

 public void stop(){

      if (mPlayer != null) {

           mPlayer.stop();      mPlayer.release();         mPlayer = null;

       }

  }}

 

// myActivity.java

package com.misoo.pk003;

import myFramework.IPlay;

import myFramework.PActivity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

 

public class myActivity extends PActivity implements OnClickListener{

private Button ibtn1, ibtn2;

private IPlay player;

 

@Override protected void onCreate(Bundle icicle) {

        super.onCreate(icicle);  

        LinearLayout layout = new LinearLayout(this);

        layout.setOrientation(LinearLayout.VERTICAL);        

        ibtn1 = new Button(this);        ibtn1.setOnClickListener(this);

        ibtn1.setText("Play");              ibtn1.setBackgroundResource(R.drawable.heart);

        LinearLayout.LayoutParams param1 = 

                  new LinearLayout.LayoutParams(150, 65);

        param1.topMargin = 10;    param1.leftMargin = 10;

        layout.addView(ibtn1, param1);        

        ibtn2 = new Button(this);        ibtn2.setOnClickListener(this);

        ibtn2.setText("Exit");              ibtn2.setBackgroundResource(R.drawable.mountain);

        layout.addView(ibtn2, param1);

        setContentView(layout);

        //-----------------------------------------------

        player = init();

    }

public void onClick(View v) {

        if(v == ibtn1) {

                  setTitle("playing...");

                  player.play(this);

             } 

        else { 

                  player.stop();

                  finish();

             }  

} 

 

3.  结语

      由框架(的基类)来创建应用(AP或App)子类的对象是框架开发的基本技巧。因为框架必需掌握重要对象的运行生命周期(Life-cycle)才能有效控制对象的行为,以及取重要的信息传递机制和内容。如此,框架就能有效<框住>数以万计的应用程序,而不会失控了 

[Go Back]