關於防止App被第三方應用Kill掉的問題


  由於一些需求的原因需要讓自己App長時間在后台。雖然這樣的做法是很Orz的,但是由於項目需求有時候不得不這樣做。QQ、微信之所以沒被第三方應用直接給kill掉,從市場原因騰訊的軟件已經深入人心,很多廠家或是軟件早就把這些軟件加入白名單中!

  但是作為App的開發人員來說肯定是強烈建議不要這么做,不僅僅從用戶角度考慮,作為Android開發者也有責任去維護Android的生態環境就是因為越來越多的這樣的占用系統內存的進程越來越多才使得android手機越來越卡,android碎片化的原因越來越多的第三方App可以通過清理內存的方式將后台進程給直接Kill掉來增加run可用內存。

  但是通過技術層面如何解決這個不被kill掉進程的問題呢?

  • Service設置成START_STICKY,kill 后會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣。(PM1中提供參考)
  • ​通過 startForeground將進程設置為前台進程,做前台服務,優先級和前台應用一個級別​,除非在系統內存非常缺,否則此進程不會被 kill
  • 雙進程Service:讓2個進程互相保護,其中一個Service被清理后,另外沒被清理的進程可以立即重啟進程
  • QQ黑科技:在應用退到后台后,另起一個只有 1 像素的頁面停留在桌面上,讓自己保持前台狀態,保護自己不被后台清理工具殺死
  • 在已經root的設備下,修改相應的權限文件,將App偽裝成系統級的應用(Android4.0系列的一個漏洞,已經確認可行)
  • Android系統中當前進程(Process)fork出來的子進程,被系統認為是兩個不同的進程。當父進程被殺死的時候,子進程仍然可以存活,並不受影響。鑒於目前提到的在Android-Service層做雙守護都會失敗,我們可以fork出c進程,多進程守護死循環在那檢查是否還存在,具體的思路如下(Android5.0以下可行)
  1. 用C編寫守護進程(即子進程),守護進程做的事情就是循環檢查目標進程是否存在,不存在則啟動它。
  2. 在NDK環境中將1中編寫的C代碼編譯打包成可執行文件(BUILD_EXECUTABLE)。
  3. 主進程啟動時將守護進程放入私有目錄下,賦予可執行權限,啟動它即可。
  • 聯系廠商,加入白名單

  PM1:

在基類service的oncreate中定義了一個notification,重要屬性:
notification.flags = Notification.FLAG_NO_CLEAR|Notification.FLAG_ONGOING_EVENT;然后 startForeground(setClass().hashCode(), notification);使得服務能掛在通知欄,感謝1、3樓提供的思路。接着我監聽了一個系統廣播Intent.ACTION_TIME_TICK,這個廣播每分鍾發送一次,我們可以每分鍾檢查一次Service的運行狀態,如果已經被結束了,就重新啟動Service。關鍵代碼:
boolean isServiceRunning = false;
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
  for (RunningServiceInfo service :manager.getRunningServices(Integer.MAX_VALUE)) {
    if("com.XXX.XXX.XXXService".equals(service.service.getClassName())){
    isServiceRunning = true;
    }
  }
  if (!isServiceRunning ) {
 Intent i = new Intent(context, com.XXX.XXX.XXXService.class);
  i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  context.startService(i);
  } 
  
PM2:
Notification notification = new Notification(R.drawable.icon, "服務開啟" , System.currentTimeMillis());
notification.flags|= Notification.FLAG_NO_CLEAR;
notification.flags=Notification.FLAG_ONGOING_EVENT;
Intent notificationIntent = new Intent( this , MainActivity. class );
PendingIntent pendingIntent = PendingIntent.getActivity( this , 0 , notificationIntent, 0 );
notification.setLatestEventInfo( this , "service" , "防止服務被任務管理器所殺" , pendingIntent);
startForeground(ONGOING_NOTIFICATION, notification);

  后來一次 做自定義Notification的時候,通知欄沒有顯示通知,查看后發現 service 也沒被kill 。所以就進一步去研究了下 最后發現 只用兩行代碼就能保持服務不會被kill,並且不會有通知欄通知代碼如下:

1
2
Notification notification = new Notification();
startForeground( 1 , notification);

實例代碼如下:

  

public class TestService extends Service {
  private static final Class[] mStartForegroundSignature = new Class[] {
    int . class , Notification. class };
  private static final Class[] mStopForegroundSignature = new Class[] { boolean . class };
  private NotificationManager mNM;
  private Method mStartForeground;
  private Method mStopForeground;
  private Object[] mStartForegroundArgs = new Object[ 2 ];
  private Object[] mStopForegroundArgs = new Object[ 1 ];
  @Override
  public IBinder onBind(Intent intent) {
   return null ;
  }
  @Override
  public void onCreate() {
   super .onCreate();
   mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
   try {
    mStartForeground = TestService. class .getMethod( "startForeground" ,
      mStartForegroundSignature);
    mStopForeground = TestService. class .getMethod( "stopForeground" ,
      mStopForegroundSignature);
   } catch (NoSuchMethodException e) {
    mStartForeground = mStopForeground = null ;
   }
   // 我們並不需要為 notification.flags 設置 FLAG_ONGOING_EVENT,因為
   // 前台服務的 notification.flags 總是默認包含了那個標志位
   Notification notification = new Notification();
   // 注意使用 startForeground ,id 為 0 將不會顯示 notification
   startForegroundCompat( 1 , notification);
  }
  @Override
  public void onDestroy() {
   super .onDestroy();
   stopForegroundCompat( 1 );
  }
  // 以兼容性方式開始前台服務
  private void startForegroundCompat( int id, Notification n) {
   if (mStartForeground != null ) {
    mStartForegroundArgs[ 0 ] = id;
    mStartForegroundArgs[ 1 ] = n;
    try {
     mStartForeground.invoke( this , mStartForegroundArgs);
    } catch (IllegalArgumentException e) {
     e.printStackTrace();
    } catch (IllegalAccessException e) {
     e.printStackTrace();
    } catch (InvocationTargetException e) {
     e.printStackTrace();
    }
    return ;
   }
   mNM.notify(id, n);
  }
  // 以兼容性方式停止前台服務
  private void stopForegroundCompat( int id) {
   if (mStopForeground != null ) {
    mStopForegroundArgs[ 0 ] = Boolean.TRUE;
    try {
     mStopForeground.invoke( this , mStopForegroundArgs);
    } catch (IllegalArgumentException e) {
     e.printStackTrace();
    } catch (IllegalAccessException e) {
     e.printStackTrace();
    } catch (InvocationTargetException e) {
     e.printStackTrace();
    }
    return ;
   }
   // 在 setForeground 之前調用 cancel,因為我們有可能在取消前台服務之后
   // 的那一瞬間被kill掉。這個時候 notification 便永遠不會從通知一欄移除
   mNM.cancel(id);
  }
}
  除了加入白名單這種官方解決方法,谷歌提出了使用: ForegroundService 前台服務,讓服務一直以前台任務的方式運行,可以在service 的oncreate來實現前台服務, 通過這個方法必須發送一個通知欄,讓用戶知道服務在運行。關於前台服務將在下一篇隨筆中記錄。
 

通過查詢資料有人提到這個program:: droidwolf/NativeSubprocess · GitHub
  創建 linux 子進程的 so 庫,當初用在 service 免殺上,經測試,在大部分機子上有用。NativeSubprocess 是一個可以讓你在 android 程序中創建 linux 子進程並執行你的 java 代碼的 so 庫。由於市面上典型的內存清理工具值清理 apk 包關聯的進程,而不會處理 linux 原生進程。回頭試試看這樣是否好使。

參考文章:http://www.jb51.net/article/73976.htm

  最后的最后還是要說:請不要做流氓軟件!!讓本來就碎片化的Android手機能運行的更順暢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM