Override Context.getSystemService()

jollen 發表於 February 26, 2010 10:12 PM

Context.getSystemService() 是一個很重要的 API,也是「Android 應用程式控制硬體」的起點。在一個開發項目中,如何擴展 getSystemService() 的實作成為一個重要的課題。

幾天前與客戶進行技術討論時,適巧討論到這個議題,因此在這裡做一個簡單的紀錄與大家分享。應用程式要存取手機上的 Sensor 裝置時,須取得 SensorManager 物件,程式寫法如下:

public class mokoidSensor extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         
        SensorManager sensor = (SensorManager)getSystemService(SENSOR_SERVICE);
        sensor.getSensors();
        /* Do something. */
    }
}

如果手機上有一個「馬達」裝置,程式碼的寫法,依此邏輯來推論,設想的程式碼會是這樣:

MotorManager motor = (MotorManager)getSystemService(MOTOR_SERVICE);

只是,AOSP 上的程式碼並無 Motor Service 硬體服務,因此除了加入 MotorManager 與 MotorService 設計外,也要擴充 getSystemService() API。實際能採行的做法很多,這裡討論二種方式:

1. 土方法
2. 符合架構

土方法是一個簡單的方式,直接到 [ApplicationContext.java] 裡把程式碼改掉。這個方式簡單有效,不費勁。

符合架構的方法,是基於「Application Framework 不能修改」的前提下來進行設計。也就是,不要變動 Context 的設計、也不要修改 ApplicationContext 實作。AOSP 的 [Activity.java] 以 override 的方式,展示了這個做法:

    @Override
    public Object getSystemService(String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }
 
        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

因此,改用以下的設計:

public class mokoidActivity extends Activity {
    ...
    @Override
    public Object getSystemService(String name) {
 
        if (MOTOR_SERVICE.equals(name)) {
            MotorManager mMotorManager = new MotorManager();
            return mMotorManager;
        }
        return super.getSystemService(name);
    }	
	...
}

應用程式的部份也要做修改:

public class HelloWorld extends mokoidActivity {
}

目前為止,這只是一個想法,尚未實作驗證。

讀者留言 (1)

  • kyuchan 於 May 25, 2010 16:43:

    老師您好, 我曾經上過老師的課,
    想請問老師, 如果我現在有兩個程式同時需要調用到SensorManager來接收Sensor.TYPE_ACCELEROMETER
    一個是framework下的WindowOrientationListener, 一個是自己寫的App Service
    那藉由Context.getSystemService()所取得的SensorManager物件, 兩個是同一個物件嗎?
    或是兩個程式都各有自己的SensorManager物件?

    因為目前我的service收不到onSensorChanged(),
    但是僅只註冊Service的registerListener(), 就可以收到值了,
    我發現WindowOrientationListener一開始getSystemService(), 會new一個SensorManager的物件,
    但當App Service 載入getSystemService()時, 又new了一個SensorManager的物件,
    不是應該只會有一個SensorManager物件嗎?

留言功能維護中。將於近日重新開放。

連絡作者

Jollen Chen,Moko365(仕橙3G教室)講師,熱愛研究 Linux 與 Android 技術。曾為 Motorola、HTC、Foxconn、LG、OPPO、騰迅、廣達電腦、緯創、仁寶等超過 50 家企業講授課程。目前在 MokoVersity 擔任軟體工程師,撰寫 Node.js 程式,也在幾家科技廠兼任 Android Framework 研發顧問。您可透過電子郵件 <jollen (at) jollen (dot) org> 或這裡與我連絡。