歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android之service---利用服務實現電話監聽

利用服務實現電話監聽
Service概述                                   

   Service是在一段不定的時間運行在後台,不和用戶交互應用組件。每個Service必須在manifest中 通過<service>來聲明。可以通過contect.startservice和contect.bindserverice來啟動。

   Service和其他的應用組件一樣,運行在進程的主線程中。這就是說如果service需要很多耗時或者阻塞的操作,需要在其子線程中實現。

 

   service的兩種模式(startService()/bindService()不是完全分離的):

l 本地服務 Local Service 用於應用程序內部。它可以啟動並運行,直至有人停止了它或它自己停止。在這種方式下,它以調用Context.startService()啟動,而以調用Context.stopService()結束。它可以調用Service.stopSelf() 或Service.stopSelfResult()來自己停止。不論調用了多少次startService()方法,你只需要調用一次stopService()來停止服務。用於實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。

l 遠程服務 Remote Service 用於Android系統內部的應用程序之間。它可以通過自己定義並暴露出來的接口進行程序操作。客戶端建立一個到服務對象的連接,並通過那個連接來調用服務。連接以調用Context.bindService()方法建立,以調用 Context.unbindService()關閉。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。

 

生命周期

    Service的生命周期並不像Activity那麼復雜,它只繼承了onCreate(),onStart(),onDestroy()三個方法,當我們第一次啟動Service時,先後調用了onCreate(),onStart()這兩個方法,當停止Service時,則執行onDestroy()方法,這裡需要注意的是,如果Service已經啟動了,當我們再次啟動Service時,不會在執行onCreate()方法,而是直接執行onStart()方法。

 

    而啟動service,根據onStartCommand的返回值不同,有兩個附加的模式:

    1. START_STICKY 用於顯示啟動和停止service。

    2. START_NOT_STICKY或START_REDELIVER_INTENT用於有命令需要處理時才運行的模式。

 

服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。
    1. 使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運行。

如果打算采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接著調用onStart()方法。
    如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。
    采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。


    2. 使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。
    onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。
    采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。

 

看看官方給出的比較流程示意圖:

 

    官方文檔告訴我們,一個service可以同時start並且bind,在這樣的情況,系統會一直保持service的運行狀態如果service已經start了或者BIND_AUTO_CREATE標志被設置。如果沒有一個條件滿足,那麼系統將會調用onDestory方法來終止service.所有的清理工作(終止線程,反注冊接收器)都在onDestory中完成。

 

擁有service的進程具有較高的優先級

    官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。

 

1. 如果service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變為前台進程以避免被killed。

2. 如果當前service已經被啟動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.

3. 如果客戶端已經連接到service (bindService),那麼擁有Service的進程則擁有最高的優先級,可以認為service是可見的。

4. 如果service可以使用startForeground(int,Notification)方法來將service設置為前台狀態,那麼系統就認為是對用戶可見的,並不會在內存不足時killed。

 

如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。

 

實驗:利用服務實現電話監聽

清單設置(一個receiver,一個service,若干權限)

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

<uses-permission android:name=

                     "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

<uses-permission android:name=

                     "android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.RECEIVE_SMS"/>

 

<application …>

        <receiver android:name="BootBroadcastReceiver">

            <intent-filter>

                <action android:name=

                           "android.provider.Telephony.SMS_RECEIVED"/>

            </intent-filter>

        </receiver>

        <service android:name="PhoneListenerService" />

 

 

電話監聽器服務類:PhoneListenerService

package cn.class3g.phonelistener;

...

public class PhoneListenerService extends Service {

 

    public IBinder onBind(Intent arg0) {

       return null;

    }

 

    public void onCreate() {

       super.onCreate();

       Log.i("TAG", "服務啟動了");

 

       // 對電話的來電狀態進行監聽

       TelephonyManager telManager = (TelephonyManager) this

              .getSystemService(Context.TELEPHONY_SERVICE);

       // 注冊一個監聽器對電話狀態進行監聽

       telManager.listen(new MyPhoneStateListener(),

              PhoneStateListener.LISTEN_CALL_STATE);

    }

 

    private class MyPhoneStateListener extends PhoneStateListener {

       MediaRecorder recorder;

       File audioFile;

       String phoneNumber;

 

       public void onCallStateChanged(int state, String incomingNumber) {

           switch (state) {

           case TelephonyManager.CALL_STATE_IDLE: /* 無任何狀態時 */

              if (recorder != null) {

                  recorder.stop();// 停止刻錄

                  recorder.reset();// 重設

                  recorder.release();// 刻錄完成一定要釋放資源

              }

              break;

           case TelephonyManager.CALL_STATE_OFFHOOK: /* 接起電話時 */

 

              try {

                  recorder = new MediaRecorder();

                  recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 設置音頻采集原

                  recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);// 內容輸出格式

                  recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 音頻編碼方式

 

                  // recorder.setOutputFile("/sdcard/myvoice.amr");

                  audioFile = new File(

                         Environment.getExternalStorageDirectory(),

                         phoneNumber + "_" + System.currentTimeMillis()

                                + ".3gp");

                  recorder.setOutputFile(audioFile.getAbsolutePath());

                  Log.i("TAG", audioFile.getAbsolutePath());

 

                  recorder.prepare(); // 預期准備

                  recorder.start();

 

              } catch (IllegalStateException e) {

                  e.printStackTrace();

              } catch (IOException e) {

                  e.printStackTrace();

              }

 

              break;

           case TelephonyManager.CALL_STATE_RINGING: /* 電話進來時 */

              phoneNumber = incomingNumber;

               break;

           default:

              break;

           }

           super.onCallStateChanged(state, incomingNumber);

       }

    }

}

 

利用開機啟動廣播啟動服務(實驗環境中為了方便起見可以先改用短消息廣播)

開機啟動完成廣播action

<action android:name="android.intent.action.BOOT_COMPLETED"/>

 

package cn.class3g.phonelistener;

...

public class BootBroadcastReceiver extends BroadcastReceiver {

 

       public void onReceive(Context context, Intent intent) {

              Log.i("TAG", "廣播被接收了");

             

              Intent serviceIntent = new Intent(context, PhoneListenerService.class);

              context.startService(serviceIntent);

       }

}

 

測試:

1.        啟動模擬器,部署應用

2.        利用模擬器控制器發送短信啟動服務(查看日志輸出判斷是否成功)

3.        向模擬器撥打電話,並接聽,掛斷電話後,利用文件管理查看對應的cache目錄或者sdcard中生成了3gp文件,並將其復制到pc中播放以驗證。

Copyright © Linux教程網 All Rights Reserved