[筆記] SIP VoIP in Android background 避免被系統殺掉

        在前面一文SIP Voip 在 Android 系統的開發中,我設計了一個 SIP VoIP APP,但是遇到一個大問題 ---- 就是 APP 會被系統殺掉。為了解決這個問題,在網路上尋找各種方法,首先我們要先把 APP 拆成 Activity 和 Service 兩個部份,如下圖一所示。Service 負責透過 JNI 介面與底層的 SIPMedia Codec...等通訊,而 Activity 則負責使用者顯示的介面,一旦 Activity 關閉後,至少還有 Service 可以在後台繼續運行。

圖一:SIP VoIP 系統架構圖

Activity 與 Service 間的通訊
        兩者之間的通訊分成 Activity 到 Service 的方向,以及 Service 到 Activity 的方向。先說 A 到 S 的方向,第一步就是 Activity bind Service,如下面的代碼。接著,建立一個與 Service 的連線,取得 mService 之後,便可以在 Activity 呼叫 Service 的函式了。可以參考網路上其他人的文章與範例說明。

        Intent intent = new Intent("com.altigen.siptalk.SipService");
bindService(intent, sc, Context.BIND_AUTO_CREATE);

private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBound = true;
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
}

@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};

第二步是 Service 到 Activity 的通訊方式,它不是用 bind 的方式,而用一種 Android廣播器的方法。從設計觀念來說,Service 端發出一個特定的廣播,然後 Activity 收到這個廣播,再從該廣播中取得資料,完成通訊。廣播器的設計, Activity 代碼中必須先註冊一個 Receiver 的廣播器,然後做一個 OnReceiver 的接收器,如下:

        receiver = new ActivityReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.hanya.siptalk.activity");
        this.registerReceiver(receiver, filter);

public class ActivityReceiver extends BroadcastReceiver {
          @Override
           public void onReceive(Context context, Intent intent) {
                Bundle bundle = intent.getExtras();
                String a = bundle.getString("Sip_LCD");
                mTextView.setText(a);
        }

而在 Service 這邊只要產生一個廣播的通訊包 (其實是一個Intent),名為 "com.hanya.siptalk.activity",如此一來 Activity 就能夠收到了,其代碼如下:

        {
            Intent intent = new Intent();
            String str = new String( LcdBuf );
            intent.putExtra("Sip_LCD", "" + str);
            intent.setAction("com.hanya.siptalk.activity");
            sendBroadcast( intent );
        }


Service 不被系統殺掉的方法
        雖然 Service 已經在後台運行了,但是只要 APP 關閉或者經過一段時間後,Service 還是會被系統移除。另外,除了被系統殺掉之外,執行記憶體清除時,後台的 Service 也容易被一起清除掉。

第一個:提高 service 的優先等級
        修改這個 AndroidManifest.xml 檔案,如下的內容。新增欄位 android:priority="1000",
                 
                 <service android:name=".SipService" android:enabled="true">
                         <intent-filter android:priority="1000">
                                <action android:name="com.hanya.siptalk.SipService"/>
                         </intent-filter>
                 </service>


第二個:修改 onStartCommand 回傳值
        雖然第一個的方法提高優先等級,但是測試後的結果,service 仍然可能被系統殺掉。因此,再使用第二個方法,START_STICKY 表示 service 被殺掉後,幾秒後便自動再啟動運行,如下:

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {

                           Log.i(TAG, "Service onStartCommand");
                           return Service.START_STICKY;
                 }

第三個:將 service 改為前台
        雖然同時採用前兩個方法,但是測試後的結果,service 仍然可能被系統殺掉。因此,使用最後一個方法,就是把 service 改成前台,如下:

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
                           Log.i(TAG, "Service onStartCommand");
                           startForeground(startId, new Notification());
                           return Service.START_STICKY;
                 }

參考資料
[1] Android Service-幾種應用範例
[2] android如何讓service不被殺死-提高程序優先級
[3] 防止Android Service被系统回收的4个方法

留言

此網誌的熱門文章

[筆記] Raspberry Pi 樹莓派的軟體開發

[應用] 在 ESP32 Audio 開發板的 VoIP 範例

[筆記] Android APP 藍芽範例說明 -- BluetoothChat

[筆記] ESP32 在 VS Code 開發環境的編譯與除錯

[筆記] Visual Studio 遠端偵錯的設定步驟

[筆記] Android APP BLE範例程式 -- BluetoothLeGatt

[應用] 藍芽 BLE client/server 架構:BLE remote controller