進程保活方案


1、開啟一個像素的Activity

  據說這個是手Q的進程保活方案,基本思想,系統一般是不會殺死前台進程的。所以要使得進程常駐,我們只需要在鎖屏的時候在本進程開啟一個Activity,為了欺騙用戶,讓這個Activity的大小是1像素,並且透明無切換動畫,在開屏幕的時候,把這個Activity關閉掉,所以這個就需要監聽系統鎖屏廣播,我試過了,的確好使,如下。

①OnePixelActivity

public class OnePixelActivity extends Activity {  
  
    private static final String TAG = "MyLog";  
  
    public static OnePixelActivity instance = null;  
  
    @Override  
    protected void onCreate(@Nullable Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_one_pixel);  
        Window window = getWindow();  
        // 放在左上角  
        window.setGravity(Gravity.START | Gravity.TOP);  
        WindowManager.LayoutParams layoutParams = window.getAttributes();  
        // 寬高為1px  
        layoutParams.width = 1;  
        layoutParams.height = 1;  
        // 起始坐標  
        layoutParams.x = 0;  
        layoutParams.y = 0;  
        window.setAttributes(layoutParams);  
        instance = this;  
        Log.d(TAG, "activity onCreate");  
    }  
  
    @Override  
    protected void onDestroy() {  
        instance = null;  
        Log.d(TAG, "activity onDestroy");  
        super.onDestroy();  
    }  
}  

②編寫廣播接收器監聽鎖屏和解鎖action:

public class ScreenBroadcastReceiver extends BroadcastReceiver {  
  
    private static final String TAG = "MyLog";  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String action = intent.getAction();  
        switch (action) {  
            case Intent.ACTION_SCREEN_ON: { //  
                Log.d(TAG, "screen_on");  
                // 關閉一像素Activity  
                if (OnePixelActivity.instance != null) {  
                    OnePixelActivity.instance.finish();  
                }  
                break;  
            }  
            case Intent.ACTION_SCREEN_OFF: {  
                Log.d(TAG, "screen_off");  
                // 開啟一像素Activity  
                Intent activityIntent = new Intent(context, OnePixelActivity.class);  
                activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                context.startActivity(activityIntent);  
                break;  
            }  
            default:  
                break;  
        }  
    }  
}  
值得注意的是Intent.ACTION_SCREEN_ON與Intent.ACTION_SCREEN_OFF只有通過Context.registerReceiver方法注冊的廣播接收器才能監聽到,官方解釋如下:
屏幕關閉信號
 
SCREEN_ON屏幕亮起同上,在此就不給出展示了。
③下面給出Service的例子,我們在啟動服務時使用registerReceiver注冊監聽器,然后在注銷服務時注銷監聽器:
public class WorkService extends Service {  
  
    private static final String TAG = "MyLog";  
  
    private ScreenBroadcastReceiver receiver;  
  
    @Nullable  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "service onCreate");  
        receiver = new ScreenBroadcastReceiver();  
        IntentFilter intentFilter = new IntentFilter();  
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);  
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);  
        registerReceiver(receiver, intentFilter);  
    }  
  
    @Override  
    public void onDestroy() {  
        Log.d(TAG, "service onDestroy");  
        unregisterReceiver(receiver);  
        super.onDestroy();  
    }  
}  

④主Activity啟動服務后關閉自身,模擬沒有Activity的情況:

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        Intent intent = new Intent(this, WorkService.class);  
        startService(intent);  
        finish();  
    }  
}  
通過adb shell可以看到,在鎖屏前應用所處的進程oom_adj值是較高的,鎖屏后由於啟動了Activity,oom_adj值降低了,進程的等級得到了相應的提高,變得更難以被回收了,這樣可以一定程度上緩解我們的應用被第三方應用或系統管理工具在鎖屏后為省電而被殺死的情況:
鎖屏前后log
鎖屏前后oom_adj

 

2.利用Notification提升權限

  這種方法也適用於Service在后台提供服務的場景。由於沒有Activity的緣故,我們Service所在進程的oom_adj值通常是較高的,進程等級較低,容易被系統回收內存時清理掉。這時我們可以通過startForeground方法,把我們的服務提升為前台服務,提高進程的等級。但提升為前台服務必須綁定一個相應的Notification,這是我們不願意看到的。

原理:Android 的前台service機制。但該機制的缺陷是通知欄保留了圖標。

對於 API level < 18 :調用startForeground(ID, new Notification()),發送空的Notification ,圖標則不會顯示。

對於 API level >= 18:在需要提優先級的service A啟動一個InnerService,兩個服務同時startForeground,且綁定同樣的 ID。Stop 掉InnerService ,這樣通知欄圖標即被移除。

這方案實際利用了Android前台service的漏洞。微信在評估了國內不少app已經使用后,才進行了部署。其實目標是讓大家站同一起跑線上,哪天google 把漏洞堵了,效果也是一樣的。

public class KeepLiveService extends Service {

    public static final int NOTIFICATION_ID=0x11;

    public KeepLiveService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
         //API 18以下,直接發送Notification並將其置為前台
        if (Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN_MR2) {
            startForeground(NOTIFICATION_ID, new Notification());
        } else {
            //API 18以上,發送Notification並將其置為前台后,啟動InnerService
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            startService(new Intent(this, InnerService.class));
        }
    }

    public  static class  InnerService extends Service{
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            //發送與KeepLiveService中ID相同的Notification,然后將其取消並取消自己的前台顯示
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopForeground(true);
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(NOTIFICATION_ID);
                    stopSelf();
                }
            },100);

        }
    }
}

 

 3.利用JobScheduler機制拉活

JobService和JobScheduler是Android5.0(API 21)引入的新API,我們可以通過該機制來拉活我們的Service所在進程。
 
JobService
首先我們通過繼承JobService類來實現自己的Service,記得重寫onStartJob和onStopJob方法。然后我們在onCreate方法里面通過JobScheduler來調度我們的Service,值得注意的是需要把參數設置為Persisted:
public class MyJobService extends JobService {  
  
    private static final String TAG = "MyLog";  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate");  
        JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(this, MyJobService.class));  
        // 設置執行延遲  
        builder.setOverrideDeadline(0);  
        // 設置持續運行  
        builder.setPersisted(true);  
        JobScheduler jobScheduler = (JobScheduler) this.getSystemService(Context.JOB_SCHEDULER_SERVICE);  
        jobScheduler.schedule(builder.build());  
    }  
  
    @Override  
    public boolean onStartJob(JobParameters params) {  
        Log.d(TAG, "onStartJob");  
        return false;  
    }  
  
    @Override  
    public boolean onStopJob(JobParameters params) {  
        return false;  
    }  
}  
使用JobService和把Service設置為Persisted都需要我們在Manifest中配置相應的參數:
manifest
 
然后運行服務即可發現,在Service所在進程被殺掉后,我們的Service會自動重啟:
首次運行:
JobService_first
 
使用kill指令殺掉后:
JobService_after_kill
 
該方法依然有它的缺陷:
首先,JobService只適用於Android5.0以上的系統;其次,當進程被force-stop指令殺死后,JobService依舊無法拉活進程。
 


免責聲明!

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



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