APP中一種在Java層實現的簡單守護進程方式

MaiB60 8年前發布 | 9K 次閱讀 Java 安卓開發 Android開發 移動開發

CB08F567-698E-46DF-970B-D178FB3BE55B154112-fbbdee0572ec27c6.gif

守護進程是一個黑色地帶的產物,無論是通過native的方式在linux中fork進程達到,還是在java層通過兩個service守護的方式,都是不太友好的做法,據很多人反應,總有一些實際的業務場景中,希望自己的應用保持live狀態, 一種是在native中做:

  • linux中多進程;

  • unix domain套接字實現跨進程通信;

  • linux的信號處理;

  • exec函數族的用法;

把他們組合起來實現了一個雙進程守護,幾個實現雙進程守護時的關鍵點:

父進程如何監視到子進程(監視進程)的死亡?很簡單,在linux中,子進程被終止時,會向父進程發送SIG_CHLD信號,于是我們可以安裝信號處理函數,并在此信號處理函數中重新啟動創建監視進程;

子進程(監視進程)如何監視到父進程死亡?當父進程死亡以后,子進程就成為了孤兒進程由Init進程領養,于是我們可以在一個循環中讀取子進程的父進程PID,當變為1就說明其父進程已經死亡,于是可以重啟父進程。這里因為采用了循環,所以就引出了之前提到的耗電量的問題。

父子進程間的通信有一種辦法是父子進程間建立通信通道,然后通過監視此通道來感知對方的存在,這樣不會存在之前提到的耗電量的問題,在本文的實現中,為了簡單,還是采用了輪詢父進程PID的辦法,但是還是留出了父子進程的通信通道,雖然暫時沒有用到,但可備不時之需!

今天介紹下用兩個service守護的方式作一完整的小案例。僅作學習交流之用。兩個進程互相監視對方,發現對方掛掉就立刻重啟!(實際就是在onDisconnected時,start另一個service)

假設我們的APP中開啟了兩個Service,分別是A和B,那么:如果A守護B,則B掛掉的同時,A就應該把B喚醒起來,反之亦然,也就是說A和B應該是互相守護,無論誰被殺掉,對方就把它喚醒起來。既然提到了兩個Service,那么這兩個Service就不能讓它們同處在一個進程中,否則就會被一次性雙殺。顯然不能在同一個進程中,在android中通常我們可以使用AIDL來實現IPC實現。

原理圖(簡單版):

226BBD66-B17C-4E9C-8849-AE8C6393BB37.png

  • MainActivity

    public class MainActivity extends AppCompatActivity {
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          // 啟動兩個守護服務
          startService(new Intent(this, ServiceA.class));
          startService(new Intent(this, ServiceB.class));
      }
    }
  • manifest

    <service android:name=".ServiceA"></service>
    <service android:name=".ServiceB" android:process="com.guardprocess.remote"></service>
  • aide

interface IBridgeInterface {

      String getName();

}
  • ServiceA

public class ServiceA extends Service {
    private static final String TAG = ServiceA.class.getSimpleName();
    private MyBinder mBinder;
    private PendingIntent mPendingIntent;
    private MyServiceConnection mServiceConnection;

    @Override
    public void onCreate() {
        super.onCreate();
        if (mBinder == null) {
            mBinder = new MyBinder();
        }
        mServiceConnection = new MyServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.bindService(new Intent(this, ServiceB.class), mServiceConnection, Context.BIND_IMPORTANT);
        mPendingIntent = PendingIntent.getService(this, 0, intent, 0);
        Notification.Builder builder = new Notification.Builder(this);
        builder.setTicker("守護服務A啟動中")
               .setContentText("我是來守護B不被殺的!")
               .setContentTitle("守護服務A")
               .setSmallIcon(R.mipmap.ic_launcher)
               .setContentIntent(mPendingIntent)
               .setWhen(System.currentTimeMillis());
        Notification notification = builder.build();
        // 設置service為前臺進程,避免手機休眠時系統自動殺掉該服務
        startForeground(startId, notification);
        return START_STICKY;
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder binder) {
            Log.i(TAG, "ServiceA連接成功");
            Toast.makeText(ServiceA.this, "ServiceA連接成功", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            // 連接出現了異常斷開了,RemoteService被殺掉了
            Toast.makeText(ServiceA.this, "ServiceA被干掉", Toast.LENGTH_LONG).show();
            // 啟動ServiceB
            ServiceA.this.startService(new Intent(ServiceA.this, ServiceB.class));
            ServiceA.this.bindService(new Intent(ServiceA.this, ServiceB.class),
                    mServiceConnection, Context.BIND_IMPORTANT);
        }

    }

    class MyBinder extends IBridgeInterface.Stub {

        @Override
        public String getName() throws RemoteException {
            return "ServiceA";
        }

    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}
  • ServiceB

public class ServiceB extends Service {

    private static final String TAG = ServiceB.class.getSimpleName();
    private MyBinder mBinder;
    private PendingIntent mPendingIntent;
    private MyServiceConnection mServiceConnection;

    @Override
    public void onCreate() {
        super.onCreate();
        if (mBinder == null) {
            mBinder = new MyBinder();
        }
        mServiceConnection = new MyServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.bindService(new Intent(this,ServiceA.class), mServiceConnection, Context.BIND_IMPORTANT);
        mPendingIntent =PendingIntent.getService(this, 0, intent, 0);
        Notification.Builder builder = new Notification.Builder(this);
        builder.setTicker("守護服務B啟動中")
                .setContentText("我是來守護A不被殺的!")
                .setContentTitle("守護服務B")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(mPendingIntent)
                .setWhen(System.currentTimeMillis());
        Notification notification = builder.build();
        //設置service為前臺進程,避免手機休眠時系統自動殺掉該服務
        startForeground(startId, notification);
        return START_STICKY;
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder binder) {
            Log.i(TAG, "ServiceB連接成功");
            Toast.makeText(ServiceB.this, "ServiceB連接成功", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            // 連接出現了異常斷開了,LocalCastielService被殺死了
            Toast.makeText(ServiceB.this, "ServiceB被干掉", Toast.LENGTH_LONG).show();
            // 啟動ServiceA
            ServiceB.this.startService(new Intent(ServiceB.this, ServiceA.class));
            ServiceB.this.bindService(new Intent(ServiceB.this, ServiceA.class), mServiceConnection, Context.BIND_IMPORTANT);
        }

    }

    class MyBinder extends IBridgeInterface.Stub {

        @Override
        public String getName() throws RemoteException {
            return "ServiceB";
        }

    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

最后:如果系統干掉這個服務,還是難逃此劫的。向ROM廠商提出加白名單方式,才是終極最萬全方案。

 

 

來自:http://www.jianshu.com/p/b84efa4d6dc0

 

 本文由用戶 MaiB60 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!