**一、監聽廣播:**監聽全局的靜態廣播,好比時間更新的廣播、開機廣播、解鎖屏、網絡狀態、解鎖加鎖亮屏暗屏(3.1版本),高版本須要應用開機後運行一次才能監聽這些系統廣播,目前此方案失效。能夠更換思路,作APP啓動後的保活(監聽廣播啓動保活的前臺服務)android
**二、定時器、JobScheduler:**假如應用被系統殺死,那麼定時器則失效,此方案失效。JobService在5.0,5.1,6.0做用很大,7.0時候有必定影響(能夠在電源管理中給APP受權)c++
**三、雙進程(NDK方式Fork子進程)、雙Service守護:**高版本已失效,5.0起系統回收策略改爲進程組。雙Service方案也改爲了應用被殺,任何後臺Service沒法正常狀態運行面試
**四、提升Service優先級:**只能必定程度上緩解Service被立馬回收bash
一、AIDL方式單進程、雙進程方式保活Service網絡
二、下降oom_adj的值:常駐通知欄(可經過啓動另一個服務關閉Notification,不對oom_adj值有影響)、使用」1像素「的Activity覆蓋在getWindow()的view上、循環播放無聲音頻(黑科技,7.0下殺不掉)app
三、監聽鎖屏廣播:使Activity始終保持前臺ide
四、使用自定義鎖屏界面:覆蓋了系統鎖屏界面。oop
五、經過android:process屬性來爲Service建立一個進程測試
六、跳轉到系統白名單界面讓用戶本身添加app進入白名單ui
一、JobScheduler:原理相似定時器,5.0,5.1,6.0做用很大,7.0時候有必定影響(能夠在電源管理中給APP受權)
二、推送互相喚醒復活:極光、友盟、以及各大廠商的推送
三、同派系APP廣播互相喚醒:好比今日頭條系、阿里系
一、雙進程守護方案(基於onStartCommand() return START_STICKY)
一、原生5.0、5.1:原生任務欄滑動清理app,Service會被殺掉,而後被拉起,接着一直存活
二、金立F100(5.1):一鍵清理直接殺掉整個app,包括雙守護進程。不手動清理狀況下,經測試能鎖屏存活至少40分鐘
三、華爲暢享5x(6.0):一鍵清理直接殺掉整個app,包括雙守護進程。不手動清理下,鎖屏只存活10s。結論:雙進程守護方案失效。
四、美圖m8s(7.1.1):一鍵清理直接殺掉整個app,包括雙守護進程。不清理狀況下,鎖屏會有被殺過程(9分鐘左右被殺),以後從新復活,以後不斷被幹掉而後又從新復活。結論:雙守護進程可在後臺不斷拉起Service。
五、原生7.0:任務欄清除APP後,Service存活。使用此方案後Service照樣存活。
六、LG V30+(7.1.2):不加雙進程守護的時候,一鍵清理沒法殺掉服務。加了此方案以後也不能殺掉服務,鎖屏存活(測試觀察大於50分鐘)
七、小米8(8.1):一鍵清理直接幹掉app而且包括雙守護進程。不清理狀況下,不加守護進程方案與加守護進程方案Service會一直存活,12分鐘左右closed。結論:此方案沒有起做用
**結論:**除了華爲此方案無效以及未更改底層的廠商不起做用外(START_STICKY字段就能夠保持Service不被殺)。此方案能夠與其餘方案混合使用
一、原生5.0、5.1:鎖屏後3s服務被幹掉而後重啓(START_STICKY字段起做用)
二、華爲暢享5x(6.0):鎖屏只存活4s。結論:方案失效。
三、美圖m8s(7.1.1):同原生5.0
四、原生7.0:同美圖m8s。
五、LG V30+(7.1.2):鎖屏後狀況跟不加狀況一致,服務一致保持運行,結論:此方案不起做用
六、小米8(8.1):關屏過2s以後app所有被幹掉。結論:此方案沒有起做用
**結論:**此方案無效果
三、故意在後臺播放無聲的音樂(基於onStartCommand() return START_STICKY)
一、原生5.0、5.1:鎖屏後3s服務被幹掉而後重啓(START_STICKY字段起做用)
二、華爲暢享5x(6.0):一鍵清理後服務依然存活,須要單獨清理纔可殺掉服務,鎖屏8分鐘後依然存活。結論:此方案適用
三、美圖m8s(7.1.1):同5.0
四、原生7.0:任務管理器中關閉APP後服務被幹掉,大概過3s會從新復活(同僅START_STICKY字段模式)。結論:看不出此方案有沒有其做用
五、LG V30+(7.1.2):使用此方案先後效果一致。結論:此方案不起做用
六、小米8(8.1):一鍵清理能夠殺掉服務。鎖屏後保活超過20分鐘
**結論:**成功對華爲手機保活。小米8下也成功突破20分鐘
四、使用JobScheduler喚醒Service(基於onStartCommand() return START_STICKY)
一、原生5.0、5.1:任務管理器中幹掉APP,服務會在週期時間後從新啓動。結論:此方案起做用
二、華爲暢享5x(6.0):一鍵清理直接殺掉APP,過12s左右會自動重啓服務,JobScheduler起做用
三、美圖m8s(7.1.1):一鍵清理直接殺掉APP,沒法自動重啓
四、原生7.0:同美圖m8s(7.1.1)
五、小米8(8.1):同美圖m8s(7.1.1)
**結論:**只對5.0,5.一、6.0起做用
五、混合使用的效果,而且在通知欄彈出通知
一、原生5.0、5.1:任務管理器中幹掉APP,服務會在週期時間後從新啓動。鎖屏超過11分鐘存活
二、華爲暢享5x(6.0):一鍵清理後服務依然存活,須要單獨清理纔可殺掉服務。結論:方案適用。
三、美圖m8s(7.1.1):一鍵清理APP會被殺掉。正常狀況下鎖屏後服務依然存活。
四、原生7.0:任務管理器中關閉APP後服務被幹掉,過2s會從新復活
五、小米8(8.1):一鍵清理能夠殺掉服務,鎖屏下後臺保活時間超過38分鐘
六、榮耀10(8.0):一鍵清理殺掉服務,鎖屏下後臺保活時間超過23分鐘
**結論:**高版本狀況下可使用彈出通知欄、雙進程、無聲音樂提升後臺服務的保活機率
使用AIDL綁定方式新建2個Service優先級(防止服務同時被系統殺死)不同的守護進程互相拉起對方,並在每個守護進程的ServiceConnection
的綁定回調裏判斷保活Service是否須要從新拉起和對守護線程進行從新綁定。
一、新建一個AIDL文件
KeepAliveConnection
interface KeepAliveConnection {
}
複製代碼
二、新建一個服務類StepService,onBind()方法返回new KeepAliveConnection.Stub()對象,並在ServiceConnection的綁定回調中對守護進程服務類GuardService的啓動和綁定。
/**
* 主進程 雙進程通信
*
* @author LiGuangMin
* @time Created by 2018/8/17 11:26
*/
public class StepService extends Service {
private final static String TAG = StepService.class.getSimpleName();
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Logger.d(TAG, "StepService:創建連接");
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if (!isServiceRunning) {
Intent i = new Intent(StepService.this, DownloadService.class);
startService(i);
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
// 斷開連接
startService(new Intent(StepService.this, GuardService.class));
// 從新綁定
bindService(new Intent(StepService.this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new KeepAliveConnection.Stub() {
};
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1, new Notification());
// 綁定創建連接
bindService(new Intent(this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT);
return START_STICKY;
}
}
複製代碼
三、對守護進程GuardService進行和2同樣的處理
/**
* 守護進程 雙進程通信
*
* @author LiGuangMin
* @time Created by 2018/8/17 11:27
*/
public class GuardService extends Service {
private final static String TAG = GuardService.class.getSimpleName();
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Logger.d(TAG, "GuardService:創建連接");
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if (!isServiceRunning) {
Intent i = new Intent(GuardService.this, DownloadService.class);
startService(i);
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
// 斷開連接
startService(new Intent(GuardService.this, StepService.class));
// 從新綁定
bindService(new Intent(GuardService.this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new KeepAliveConnection.Stub() {
};
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1, new Notification());
// 綁定創建連接
bindService(new Intent(this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT);
return START_STICKY;
}
}
複製代碼
四、在Activity中在啓動須要保活的DownloadService服務後而後啓動保活的雙進程
public class MainActivity extends AppCompatActivity {
private TextView mShowTimeTv;
private DownloadService.DownloadBinder mDownloadBinder;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mDownloadBinder = (DownloadService.DownloadBinder) service;
mDownloadBinder.setOnTimeChangeListener(new DownloadService.OnTimeChangeListener() {
@Override
public void showTime(final String time) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mShowTimeTv.setText(time);
}
});
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, DownloadService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
//雙守護線程,優先級不同
startAllServices();
}
@Override
public void onContentChanged() {
super.onContentChanged();
mShowTimeTv = findViewById(R.id.tv_show_time);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
/**
* 開啓全部守護Service
*/
private void startAllServices() {
startService(new Intent(this, StepService.class));
startService(new Intent(this, GuardService.class));
}
}
複製代碼
一、該Activity的View只要設置爲1像素而後設置在Window對象上便可。在Activity的onDestroy週期中進行保活服務的存活判斷從而喚醒服務。"1像素"Activity以下
public class SinglePixelActivity extends AppCompatActivity {
private static final String TAG = SinglePixelActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window mWindow = getWindow();
mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams attrParams = mWindow.getAttributes();
attrParams.x = 0;
attrParams.y = 0;
attrParams.height = 1;
attrParams.width = 1;
mWindow.setAttributes(attrParams);
ScreenManager.getInstance(this).setSingleActivity(this);
}
@Override
protected void onDestroy() {
if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) {
Intent intentAlive = new Intent(this, DownloadService.class);
startService(intentAlive);
}
super.onDestroy();
}
}
複製代碼
二、對廣播進行監聽,封裝爲一個ScreenReceiverUtil類,進行鎖屏解鎖的廣播動態註冊監聽
public class ScreenReceiverUtil {
private Context mContext;
private SreenBroadcastReceiver mScreenReceiver;
private SreenStateListener mStateReceiverListener;
public ScreenReceiverUtil(Context mContext) {
this.mContext = mContext;
}
public void setScreenReceiverListener(SreenStateListener mStateReceiverListener) {
this.mStateReceiverListener = mStateReceiverListener;
// 動態啓動廣播接收器
this.mScreenReceiver = new SreenBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiver(mScreenReceiver, filter);
}
public void stopScreenReceiverListener() {
mContext.unregisterReceiver(mScreenReceiver);
}
/**
* 監聽sreen狀態對外回調接口
*/
public interface SreenStateListener {
void onSreenOn();
void onSreenOff();
void onUserPresent();
}
public class SreenBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (mStateReceiverListener == null) {
return;
}
if (Intent.ACTION_SCREEN_ON.equals(action)) { // 開屏
mStateReceiverListener.onSreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
mStateReceiverListener.onSreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解鎖
mStateReceiverListener.onUserPresent();
}
}
}
}
複製代碼
三、對1像素Activity進行防止內存泄露的處理,新建一個ScreenManager類
public class ScreenManager {
private static final String TAG = ScreenManager.class.getSimpleName();
private static ScreenManager sInstance;
private Context mContext;
private WeakReference<Activity> mActivity;
private ScreenManager(Context mContext) {
this.mContext = mContext;
}
public static ScreenManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new ScreenManager(context);
}
return sInstance;
}
/** 得到SinglePixelActivity的引用
* @param activity
*/
public void setSingleActivity(Activity activity) {
mActivity = new WeakReference<>(activity);
}
/**
* 啓動SinglePixelActivity
*/
public void startActivity() {
Intent intent = new Intent(mContext, SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
/**
* 結束SinglePixelActivity
*/
public void finishActivity() {
if (mActivity != null) {
Activity activity = mActivity.get();
if (activity != null) {
activity.finish();
}
}
}
}
複製代碼
四、對1像素的Style進行特殊處理,在style文件中新建一個SingleActivityStyle
<style name="SingleActivityStyle" parent="android:Theme.Holo.Light.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowNoDisplay">false</item>
複製代碼
五、讓SinglePixelActivity使用singleInstance啓動模式,在manifest文件中
<activity
android:name=".activity.SinglePixelActivity"
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:theme="@style/SingleActivityStyle" />
複製代碼
六、在保活服務類DownloadService中對監聽的廣播進行註冊和對SinglePixelActivity進行控制。
public class DownloadService extends Service {
public static final int NOTICE_ID = 100;
private static final String TAG = DownloadService.class.getSimpleName();
private DownloadBinder mDownloadBinder;
private NotificationCompat.Builder mBuilderProgress;
private NotificationManager mNotificationManager;
private ScreenReceiverUtil mScreenListener;
private ScreenManager mScreenManager;
private Timer mRunTimer;
private int mTimeSec;
private int mTimeMin;
private int mTimeHour;
private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
@Override
public void onSreenOn() {
mScreenManager.finishActivity();
Logger.d(TAG, "關閉了1像素Activity");
}
@Override
public void onSreenOff() {
mScreenManager.startActivity();
Logger.d(TAG, "打開了1像素Activity");
}
@Override
public void onUserPresent() {
}
};
private OnTimeChangeListener mOnTimeChangeListener;
@Override
public void onCreate() {
super.onCreate();
// 註冊鎖屏廣播監聽器
mScreenListener = new ScreenReceiverUtil(this);
mScreenManager = ScreenManager.getInstance(this);
mScreenListener.setScreenReceiverListener(mScreenListenerer);
mDownloadBinder = new DownloadBinder();
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Logger.d(TAG, "onStartCommand");
startRunTimer();
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mDownloadBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Logger.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (mManager == null) {
return;
}
mManager.cancel(NOTICE_ID);
stopRunTimer();
// mScreenListener.stopScreenReceiverListener();
}
private void startRunTimer() {
TimerTask mTask = new TimerTask() {
@Override
public void run() {
mTimeSec++;
if (mTimeSec == 60) {
mTimeSec = 0;
mTimeMin++;
}
if (mTimeMin == 60) {
mTimeMin = 0;
mTimeHour++;
}
if (mTimeHour == 24) {
mTimeSec = 0;
mTimeMin = 0;
mTimeHour = 0;
}
String time = "時間爲:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec;
if (mOnTimeChangeListener != null) {
mOnTimeChangeListener.showTime(time);
}
Logger.d(TAG, time);
}
};
mRunTimer = new Timer();
// 每隔1s更新一下時間
mRunTimer.schedule(mTask, 1000, 1000);
}
private void stopRunTimer() {
if (mRunTimer != null) {
mRunTimer.cancel();
mRunTimer = null;
}
mTimeSec = 0;
mTimeMin = 0;
mTimeHour = 0;
Logger.d(TAG, "時間爲:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec);
}
public interface OnTimeChangeListener {
void showTime(String time);
}
public class DownloadBinder extends Binder {
public void setOnTimeChangeListener(OnTimeChangeListener onTimeChangeListener) {
mOnTimeChangeListener = onTimeChangeListener;
}
}
}
複製代碼
一、準備一段無聲的音頻,新建一個播放音樂的Service類,將播放模式改成無限循環播放。在其onDestroy方法中對本身從新啓動。
public class PlayerMusicService extends Service {
private final static String TAG = PlayerMusicService.class.getSimpleName();
private MediaPlayer mMediaPlayer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Logger.d(TAG, TAG + "---->onCreate,啓動服務");
mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
mMediaPlayer.setLooping(true);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
startPlayMusic();
}
}).start();
return START_STICKY;
}
private void startPlayMusic() {
if (mMediaPlayer != null) {
Logger.d(TAG, "啓動後臺播放音樂");
mMediaPlayer.start();
}
}
private void stopPlayMusic() {
if (mMediaPlayer != null) {
Logger.d(TAG, "關閉後臺播放音樂");
mMediaPlayer.stop();
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopPlayMusic();
Logger.d(TAG, TAG + "---->onCreate,中止服務");
// 重啓本身
Intent intent = new Intent(getApplicationContext(), PlayerMusicService.class);
startService(intent);
}
}
複製代碼
二、 在保活的DownloadServie服務類的onCreate方法中對PlayerMusicService進行啓動
Intent intent = new Intent(this, PlayerMusicService.class);
startService(intent);
複製代碼
三、在Manifest文件中進行註冊
<service
android:name=".service.PlayerMusicService"
android:enabled="true"
android:exported="true"
android:process=":music_service" />
複製代碼
一、新建一個繼承自JobService的ScheduleService類,在其onStartJob回調中對DownloadService進行存活的判斷來重啓。
public class ScheduleService extends JobService {
private static final String TAG = ScheduleService.class.getSimpleName();
@Override
public boolean onStartJob(JobParameters params) {
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if (!isServiceRunning) {
Intent i = new Intent(this, DownloadService.class);
startService(i);
Logger.d(TAG, "ScheduleService啓動了DownloadService");
}
jobFinished(params, false);
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
複製代碼
二、 在DownloadService服務類中進行JobScheduler的註冊和使用
/**
* 使用JobScheduler進行保活
*/
private void useJobServiceForKeepAlive() {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (jobScheduler == null) {
return;
}
jobScheduler.cancelAll();
JobInfo.Builder builder =
new JobInfo.Builder(1024, new ComponentName(getPackageName(), ScheduleService.class.getName()));
//週期設置爲了2s
builder.setPeriodic(1000 * 2);
builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
int schedule = jobScheduler.schedule(builder.build());
if (schedule <= 0) {
Logger.w(TAG, "schedule error!");
}
}
複製代碼
三、在manifest文件中進行權限設置
<service
android:name=".service.ScheduleService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
複製代碼
根據華爲官方文檔集成HUAWEI Push
一、華爲暢玩5X(6.0):APP所有進程被殺死時能夠被拉起。
二、華爲nove 3e(8.0):APP所有進程被殺死時沒法被拉起,能收到推送。
三、華爲榮耀10(8.1):同2
**結論:**理論狀況下,華爲推送應該能夠拉起華爲機器纔對,感受是我沒花錢的緣由
public class ServiceAliveUtils {
public static boolean isServiceAlice() {
boolean isServiceRunning = false;
ActivityManager manager =
(ActivityManager) MyApplication.getMyApplication().getSystemService(Context.ACTIVITY_SERVICE);
if (manager == null) {
return true;
}
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("demo.lgm.com.keepalivedemo.service.DownloadService".equals(service.service.getClassName())) {
isServiceRunning = true;
}
}
return isServiceRunning;
}
}
複製代碼
做者:minminaya 連接:www.jianshu.com/p/b5371df6d…
AndroidUtils:Android開發不得不收藏的Utils
Google開發者大會:你不得不知的Tensorflow小技巧
在這裏得到的不只僅是技術!