一、監聽廣播:監聽全局的靜態廣播,好比時間更新的廣播、開機廣播、解鎖屏、網絡狀態、解鎖加鎖亮屏暗屏(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被立馬回收網絡
一、雙進程守護方案(基於onStartCommand() return START_STICKY)app
結論:除了華爲此方案無效以及未更改底層的廠商不起做用外(START_STICKY字段就能夠保持Service不被殺)。此方案能夠與其餘方案混合使用ide
結論:此方案無效果oop
三、故意在後臺播放無聲的音樂(基於onStartCommand() return START_STICKY)測試
結論:成功對華爲手機保活。小米8下也成功突破20分鐘ui
四、使用JobScheduler喚醒Service(基於onStartCommand() return START_STICKY)this
結論:只對5.0,5.一、6.0起做用
五、混合使用的效果,而且在通知欄彈出通知
結論:高版本狀況下可使用彈出通知欄、雙進程、無聲音樂提升後臺服務的保活機率
使用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
結論:理論狀況下,華爲推送應該能夠拉起華爲機器纔對,感受是我沒花錢的緣由
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
連接:https://www.jianshu.com/p/b53...
AndroidUtils:Android開發不得不收藏的Utils
Google開發者大會:你不得不知的Tensorflow小技巧
在這裏得到的不只僅是技術!