思考ANDROID架构(二):像ANDROID框架,如何(HOW-TO)吸引开发者来使用它呢?

本文深入探讨Android应用框架如何在抽象类中引入默认行为,并通过子类的覆写函数实现反向沟通,减少开发者负担,提高开发效率。通过具体案例分析,展示了如何在不同层次上复用或修改默认行为,以及Android框架如何流畅地反向调用子类的自定义行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:Android平台的核心是C/C++,为了吸引更多App开发者来使用它,就在C/C++之上,增添一层Java框架,这框架的基类一方面提供接口(即抽像函数)来支持App开发者撰写子类。这项接口的幕后,隐藏了默认函数(Default Function)及其复杂代码,让App开发者只要撰写简单的子类,就能继承而调用这些默认函数,藉由简单的接口调用,就能轻易叫出复杂的默认行为(Default Behavior)。

     抽象类的焦点在于抽象函数,它们的实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。这抽像函数是基类与子类两者相遇的地方,也就成为两者的接口,又称为基类的API。不过,在上述文章里,偏重于API的角色及其设计要点。但并未碰触到应用开发者的心理层面:为何应用开发者愿意来使用框架及其API呢? 也就是说,框架开发者必须关心如何去吸引应用开发者来使用框架及其API。其解答之一是:在基类里提供默认行为。

 

1.   基类接口隐藏了默认函数

      刚才提过了,抽象类里的抽象函数,其实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。可知,子类开发者的负担是蛮重的,框架设计可以透过默认行吸引应用子类开发者。想一想,如果将一些默认(Default)的实作部份加到抽象类里,则其子类就可选择要不要修正这个默认实作部份,这样可以减轻子类开发者的负担。例如下述之范例: 

其程序代码如下:

/*   Shape.java    */

import java.awt.Color;

import java.awt.Graphics; 

public abstract class Shape {

     Graphics m_gr;

     public Shape(Graphics gr) {   m_gr = gr;   }

     public void onPaint(){  

             // drawing background

             m_gr.setColor(Color.black);

             m_gr.fillRect(10,30, 200,100); 

     }

    public void paint()  {  

                     onPaint();

}}

/*   Bird.java    */

import java.awt.Color;

import java.awt.Graphics;

public class Bird extends Shape {

           Graphics m_gr;

           public Bird(Graphics gr) {

                     super(gr);

                     m_gr = gr;

             }

           public void onPaint(){

                     super.onPaint();

                     // drawing a bird

                     m_gr.setColor(Color.cyan);

                     m_gr.drawArc(30,80,90,110,40,100);

                     m_gr.drawArc(88,93,90,100,40,80);

                     m_gr.setColor(Color.white);

                     m_gr.drawArc(30,55,90,150,35,75);

                     m_gr.drawArc(90,80,90,90,40,80);

  }} 

/*   JMain.java    */

import java.awt.Graphics;

import javax.swing.JFrame;

import javax.swing.JPanel;

class JP extends JPanel {

            public void paintComponent(Graphics gr){

                      super.paintComponents(gr);

                      Shape bird = new Bird(gr);

                      bird.paint();

     }}

public class JMain extends JFrame{

           public JMain(){ 

                    setTitle(""); 

                    setSize(350, 250); 

      }

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的paint()和onPaint()函数都含有默认的实作指令。具体类Bird的设计者选择如下:

  • 不覆写(即不修正)paint()函数。直接使用默认行为,所以减轻了负担。
  • 覆写(即修正)onPaint()函数。重用(Reuse)了默认行为,也减轻了部分的负担。

      此程序执行时,主程序的指令:bird.paint()呼叫子类Bird的paint(),但是Bird并没有paint()函数,于是采用基类Shape的默认paint()函数。此默认函数呼叫onPaint(),就反向呼叫了子类的onPaint()。请注意,是基类paint()呼叫子类的onPaint();并不是基类onPaint()来呼叫子类的onPaint()。反而是子类onPaint()呼叫基类的onPaint()。[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

 

2.  Android框架的默认函数之例

       Android是应用框架,而在抽象类里摆上一些默认行为又是应用框架的主要技俩,所以Android里不仅含有各式各样的抽象类,而且也有各式各样的默认行为。例如在View基类(或称抽象类)里就定义了onDraw()函数。Button又从View类继承而得到onDraw()函数。最后,我们还可以撰写新类去继承Button类,仍然继承而得onDraw()函数。在这个继承体系里的每一层级皆可覆写这继承而来的onDraw()函数,形成默认行为了。例如下述的范例程序:

 

2.1 操作情境

  • 此程序一开始,画面出现两个按钮如下:

  

  • 如果按下<Exit>按钮,程序就结束了。 

2.2 撰写步骤

Step-1: 建立Android项目:Fx02。

 

Step-2: 撰写Activity的子类:ac01,其程序代码如下:

 

/*   ac01.java   */

package com.misoo.pkaz;

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.LinearLayout;

 

public class ac01 extends Activity implements OnClickListener {

        private okButton ok_btn;

        private exitButton exit_btn;

        @Override public void onCreate(Bundle savedInstanceState) {

                 super.onCreate(savedInstanceState);

                 setContentView(R.layout.main);    

                 LinearLayout layout = new LinearLayout(this);

                 layout.setOrientation(LinearLayout.VERTICAL);

                 ok_btn = new okButton(this);

                 ok_btn.setOnClickListener(this);

                 LinearLayout.LayoutParams param = 

                                new LinearLayout.LayoutParams(ok_btn.get_width(),

                 ok_btn.get_height());

                 layout.addView(ok_btn, param);

                 exit_btn = new exitButton(this);     exit_btn.setOnClickListener(this);

                 exit_btn.setText("Exit");  exit_btn.setTextColor(Color.BLUE);

                 exit_btn.setTextSize(25);  exit_btn.setBackgroundResource(R.drawable.icon2);

                 LinearLayout.LayoutParams param2 = 

                               new LinearLayout.LayoutParams(80, 55);

                 param2.topMargin = 5;   param2.leftMargin = 5;

                 layout.addView(exit_btn, param2);

                 setContentView(layout);   

             }

             public void onClick(View v) {

                     if(v == ok_btn)   setTitle("OK");

                     else if(v == exit_btn)   finish();

}} 

 

Step-3: 撰写Button的子类:okButton,其程序代码如下: 

/*   okButton.java   */

package com.misoo.pkaz;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.widget.Button;

public class okButton extends Button{

          public okButton(Context ctx){

                      super(ctx);   super.setText("  K");

                      super.setTextSize(30);   super.setTextColor(Color.RED);    

                  }

          @Override protected void onDraw(Canvas canvas) {

                super.onDraw(canvas);

                Paint pa = new Paint();

                pa.setColor(Color.YELLOW);

                canvas.drawCircle(30, 25, 17, pa);

                pa.setColor(Color.RED);

                canvas.drawCircle(30, 25, 15, pa);

                pa.setColor(Color.YELLOW);

                canvas.drawCircle(30, 25, 12, pa);

               }

           public int get_width(){  return 90;  }

           public int get_height(){  return 60;  }

Step-4: 撰写Button的子类:exitButton,其程序代码如下:

 

/*   exitButton.java   */

package com.misoo.pkaz;

import android.content.Context;

import android.graphics.Canvas;

import android.widget.Button; 

public class exitButton extends Button {

          public exitButton(Context ctx){  super(ctx);   }

          @Override protected void onDraw(Canvas canvas) {

                        super.onDraw(canvas);

}} 

 

2.3  说明:

1)  我们在撰写okButton类时,面对Button的默认行为,可重复利用(Reuse)它,也可以不用它。

2)  因之,我们有三种选择:

  • 并不覆写onDraw()函数。这表示onDraw()函数的默认行为适合于okButton类,直接继承Button基类的onDraw()函数就行了。
  • 完全覆盖掉基类的默认行为。这表示onDraw()函数的默认行为不适合于okButton类,而且不想去重复利用(Reuse)它。
  • 重复利用(Reuse)它,并且修正它。呼叫基类的默认行为(如画出图画的背景),在增添新的行为,达到修正默认行为之目的。

3) 例如,okButton类采取上述的第(三)种做法。既呼叫默认的行为绘出一个白色按钮,而且自己画出彩色的图案。

4) 再如,exitButton类采取上述的第(一)种做法。虽然表面上有覆写的形式,但也只是呼叫基类的默认行为而已。所以,exitButton的onDraw()函数是多余的、可删去之。

  

3. Android的<IoC + 默认函数>

      Android是应用框架,不但在抽象类里摆上一些默认行为,而且能进行反向呼叫具体类的覆写函数。抽象类、默认行为和反向呼叫是应用框架的主要元素,所以Android里不仅含有各式各样的抽象类,而且也有各式各样的默认行为,还能顺畅地反向呼叫。例如在View类体系里各类皆能覆写onDraw()函数,让View类继承的基类能顺利反向呼叫子类的onDraw()函数。例如下述的范例程序:

 

3.1 操作情境

1) 此程序执行时,呈现如下的画面:

  

2) 如果按下RadioButton,画面就变换如下:

  

3) 若按下<Exit>按钮,程序就结束了。 

 

3.2 撰写步骤:

Step-1: 建立Android应用程序项目:Fx03。

 

Step-2: 撰写Activity的子类ac01,其程序代码如下:

/*    ac01.java    */

package com.misoo.ppxx;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.ViewGroup;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

import android.widget.RadioButton; 

public class ac01 extends Activity implements OnClickListener {

       private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT; 

       private MyView mv;

       private RadioButton ra;

       private Button btn;

 

       @Override public void onCreate(Bundle icicle) {

            super.onCreate(icicle);

            LinearLayout layout = new LinearLayout(this);

            layout.setOrientation(LinearLayout.HORIZONTAL);                     

            LinearLayout.LayoutParams param = 

                         new LinearLayout.LayoutParams(115, 250);

            param.leftMargin = 1;

            mv = new MyView(this);

            layout.addView(mv, param);           

            ra = new RadioButton(this);    ra.setOnClickListener(this);

            param = new LinearLayout.LayoutParams(WC, WC);

            param.topMargin = 40;

            layout.addView(ra, param);

            btn = new Button(this);            btn.setText("Exit");

            btn.setOnClickListener(this);      btn.setBackgroundResource(R.drawable.gray);

            param.leftMargin = 30;

            layout.addView(btn, param);

            setContentView(layout);

         }

         public void onClick(View v) {

                if( v == ra)            mv.ReDraw();

                else if(v == btn)     finish();

}}

Step-3: 撰写View的子类MyView,其程序代码如下:

/*   MyView.java   */

package com.misoo.ppxx;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.view.View;

 

public class MyView extends View {

    private Paint pa = new Paint();

    private boolean yn = false;   

    public MyView(Context context) {   super(context);     }

    public void ReDraw(){

                this.invalidate();

    }

    @Override protected void onDraw(Canvas canvas) {

              yn = !yn;

              pa.setColor(Color.WHITE);

              canvas.drawRect(10, 10, 100, 100, pa);

              pa.setColor(Color.YELLOW);

              if(yn){  pa.setColor(Color.BLUE);

                          canvas.drawCircle(55, 55, 15, pa);  }

              else {   pa.setColor(Color.RED);

                          canvas.drawRect(40, 40, 70, 70, pa);

                      }

  }} 

Step-4: 执行之。 

 

3.3  说明:

   1) 这MyView子类覆写了onDraw()函数,创造了反向呼叫的机会。

   2) 当我们按下RadioButton时,就呼叫到MyView类的ReDraw()函数,进而呼叫到View的invalidate()函数。

   3) 接着,就反向呼叫MyView子类的onDraw()函数,在UI画面上绘出图形来。这就是典型的「反向沟通」了。

   4) 如果你没有定义onDraw()函数的话,会执行View基类默认的onDraw()函数,而依据框架默认之惯例而行了。

 

ee                                                                             ee

【思考Android技术】

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

请进入:ADT架构师学苑

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值