一、常見(jiàn)?;罘桨?/h4>
1、監(jiān)聽(tīng)廣播:監(jiān)聽(tīng)全局的靜態(tài)廣播,比如時(shí)間更新的廣播、開(kāi)機(jī)廣播、解鎖屏、網(wǎng)絡(luò)狀態(tài)、解鎖加鎖亮屏暗屏(3.1版本),高版本需要應(yīng)用開(kāi)機(jī)后運(yùn)行一次才能監(jiān)聽(tīng)這些系統(tǒng)廣播,目前此方案失效??梢愿鼡Q思路,做APP啟動(dòng)后的保活(監(jiān)聽(tīng)廣播啟動(dòng)?;畹那芭_(tái)服務(wù))
2、定時(shí)器、JobScheduler:假如應(yīng)用被系統(tǒng)殺死,那么定時(shí)器則失效,此方案失效。JobService在5.0,5.1,6.0作用很大,7.0時(shí)候有一定影響(可以在電源管理中給APP授權(quán))
3、雙進(jìn)程(NDK方式Fork子進(jìn)程)、雙Service守護(hù):高版本已失效,5.0起系統(tǒng)回收策略改成進(jìn)程組。雙Service方案也改成了應(yīng)用被殺,任何后臺(tái)Service無(wú)法正常狀態(tài)運(yùn)行
4、提高Service優(yōu)先級(jí):只能一定程度上緩解Service被立馬回收
二、保活
- 1、AIDL方式單進(jìn)程、雙進(jìn)程方式?;頢ervice
- 2、降低oom_adj的值:常駐通知欄(可通過(guò)啟動(dòng)另外一個(gè)服務(wù)關(guān)閉Notification,不對(duì)oom_adj值有影響)、使用”1像素“的Activity覆蓋在getWindow()的view上、循環(huán)播放無(wú)聲音頻(黑科技,7.0下殺不掉)
- 3、監(jiān)聽(tīng)鎖屏廣播:使Activity始終保持前臺(tái)
- 4、使用自定義鎖屏界面:覆蓋了系統(tǒng)鎖屏界面。
- 5、通過(guò)android:process屬性來(lái)為Service創(chuàng)建一個(gè)進(jìn)程
- 6、跳轉(zhuǎn)到系統(tǒng)白名單界面讓用戶(hù)自己添加app進(jìn)入白名單
三、復(fù)活
- 1、JobScheduler:原理類(lèi)似定時(shí)器,5.0,5.1,6.0作用很大,7.0時(shí)候有一定影響(可以在電源管理中給APP授權(quán))
- 2、推送互相喚醒復(fù)活:極光、友盟、以及各大廠商的推送
- 3、同派系A(chǔ)PP廣播互相喚醒:比如今日頭條系、阿里系
方案實(shí)現(xiàn)效果統(tǒng)計(jì)
1、雙進(jìn)程守護(hù)方案(基于onStartCommand() return START_STICKY)
- 1、原生5.0、5.1:原生任務(wù)欄滑動(dòng)清理app,Service會(huì)被殺掉,然后被拉起,接著一直存活
- 2、金立F100(5.1):一鍵清理直接殺掉整個(gè)app,包括雙守護(hù)進(jìn)程。不手動(dòng)清理情況下,經(jīng)測(cè)試能鎖屏存活至少40分鐘
- 3、華為暢享5x(6.0):一鍵清理直接殺掉整個(gè)app,包括雙守護(hù)進(jìn)程。不手動(dòng)清理下,鎖屏只存活10s。結(jié)論:雙進(jìn)程守護(hù)方案失效。
- 4、美圖m8s(7.1.1):一鍵清理直接殺掉整個(gè)app,包括雙守護(hù)進(jìn)程。不清理情況下,鎖屏?xí)斜粴⑦^(guò)程(9分鐘左右被殺),之后重新復(fù)活,之后不斷被干掉然后又重新復(fù)活。結(jié)論:雙守護(hù)進(jìn)程可在后臺(tái)不斷拉起Service。
- 5、原生7.0:任務(wù)欄清除APP后,Service存活。使用此方案后Service照樣存活。
- 6、LG V30+(7.1.2):不加雙進(jìn)程守護(hù)的時(shí)候,一鍵清理無(wú)法殺掉服務(wù)。加了此方案之后也不能殺掉服務(wù),鎖屏存活(測(cè)試觀察大于50分鐘)
- 7、小米8(8.1):一鍵清理直接干掉app并且包括雙守護(hù)進(jìn)程。不清理情況下,不加守護(hù)進(jìn)程方案與加守護(hù)進(jìn)程方案Service會(huì)一直存活,12分鐘左右closed。結(jié)論:此方案沒(méi)有起作用
1、監(jiān)聽(tīng)廣播:監(jiān)聽(tīng)全局的靜態(tài)廣播,比如時(shí)間更新的廣播、開(kāi)機(jī)廣播、解鎖屏、網(wǎng)絡(luò)狀態(tài)、解鎖加鎖亮屏暗屏(3.1版本),高版本需要應(yīng)用開(kāi)機(jī)后運(yùn)行一次才能監(jiān)聽(tīng)這些系統(tǒng)廣播,目前此方案失效??梢愿鼡Q思路,做APP啟動(dòng)后的保活(監(jiān)聽(tīng)廣播啟動(dòng)?;畹那芭_(tái)服務(wù))
2、定時(shí)器、JobScheduler:假如應(yīng)用被系統(tǒng)殺死,那么定時(shí)器則失效,此方案失效。JobService在5.0,5.1,6.0作用很大,7.0時(shí)候有一定影響(可以在電源管理中給APP授權(quán))
3、雙進(jìn)程(NDK方式Fork子進(jìn)程)、雙Service守護(hù):高版本已失效,5.0起系統(tǒng)回收策略改成進(jìn)程組。雙Service方案也改成了應(yīng)用被殺,任何后臺(tái)Service無(wú)法正常狀態(tài)運(yùn)行
4、提高Service優(yōu)先級(jí):只能一定程度上緩解Service被立馬回收
結(jié)論:除了華為此方案無(wú)效以及未更改底層的廠商不起作用外(START_STICKY字段就可以保持Service不被殺)。此方案可以與其他方案混合使用
2、監(jiān)聽(tīng)鎖屏廣播打開(kāi)1像素Activity(基于onStartCommand() return START_STICKY)
- 1、原生5.0、5.1:鎖屏后3s服務(wù)被干掉然后重啟(START_STICKY字段起作用)
- 2、華為暢享5x(6.0):鎖屏只存活4s。結(jié)論:方案失效。
- 3、美圖m8s(7.1.1):同原生5.0
- 4、原生7.0:同美圖m8s。
- 5、LG V30+(7.1.2):鎖屏后情況跟不加情況一致,服務(wù)一致保持運(yùn)行,結(jié)論:此方案不起作用
- 6、小米8(8.1):關(guān)屏過(guò)2s之后app全部被干掉。結(jié)論:此方案沒(méi)有起作用
結(jié)論:此方案無(wú)效果
3、故意在后臺(tái)播放無(wú)聲的音樂(lè)(基于onStartCommand() return START_STICKY)
- 1、原生5.0、5.1:鎖屏后3s服務(wù)被干掉然后重啟(START_STICKY字段起作用)
- 2、華為暢享5x(6.0):一鍵清理后服務(wù)依然存活,需要單獨(dú)清理才可殺掉服務(wù),鎖屏8分鐘后依然存活。結(jié)論:此方案適用
- 3、美圖m8s(7.1.1):同5.0
- 4、原生7.0:任務(wù)管理器中關(guān)閉APP后服務(wù)被干掉,大概過(guò)3s會(huì)重新復(fù)活(同僅START_STICKY字段模式)。結(jié)論:看不出此方案有沒(méi)有其作用
- 5、LG V30+(7.1.2):使用此方案前后效果一致。結(jié)論:此方案不起作用
- 6、小米8(8.1):一鍵清理可以殺掉服務(wù)。鎖屏后?;畛^(guò)20分鐘
結(jié)論:成功對(duì)華為手機(jī)保活。小米8下也成功突破20分鐘
4、使用JobScheduler喚醒Service(基于onStartCommand() return START_STICKY)
- 1、原生5.0、5.1:任務(wù)管理器中干掉APP,服務(wù)會(huì)在周期時(shí)間后重新啟動(dòng)。結(jié)論:此方案起作用
- 2、華為暢享5x(6.0):一鍵清理直接殺掉APP,過(guò)12s左右會(huì)自動(dòng)重啟服務(wù),JobScheduler起作用
- 3、美圖m8s(7.1.1):一鍵清理直接殺掉APP,無(wú)法自動(dòng)重啟
- 4、原生7.0:同美圖m8s(7.1.1)
- 5、小米8(8.1):同美圖m8s(7.1.1)
結(jié)論:只對(duì)5.0,5.1、6.0起作用
5、混合使用的效果,并且在通知欄彈出通知
- 1、原生5.0、5.1:任務(wù)管理器中干掉APP,服務(wù)會(huì)在周期時(shí)間后重新啟動(dòng)。鎖屏超過(guò)11分鐘存活
- 2、華為暢享5x(6.0):一鍵清理后服務(wù)依然存活,需要單獨(dú)清理才可殺掉服務(wù)。結(jié)論:方案適用。
- 3、美圖m8s(7.1.1):一鍵清理APP會(huì)被殺掉。正常情況下鎖屏后服務(wù)依然存活。
- 4、原生7.0:任務(wù)管理器中關(guān)閉APP后服務(wù)被干掉,過(guò)2s會(huì)重新復(fù)活
- 5、小米8(8.1):一鍵清理可以殺掉服務(wù),鎖屏下后臺(tái)?;顣r(shí)間超過(guò)38分鐘
- 6、榮耀10(8.0):一鍵清理殺掉服務(wù),鎖屏下后臺(tái)?;顣r(shí)間超過(guò)23分鐘
結(jié)論:高版本情況下可以使用彈出通知欄、雙進(jìn)程、無(wú)聲音樂(lè)提高后臺(tái)服務(wù)的保活概率
實(shí)現(xiàn)具體過(guò)程
一、雙進(jìn)程實(shí)現(xiàn)方案
使用AIDL綁定方式新建2個(gè)Service優(yōu)先級(jí)(防止服務(wù)同時(shí)被系統(tǒng)殺死)不一樣的守護(hù)進(jìn)程互相拉起對(duì)方,并在每一個(gè)守護(hù)進(jìn)程的```ServiceConnection```的綁定回調(diào)里判斷保活Service是否需要重新拉起和對(duì)守護(hù)線程進(jìn)行重新綁定。
- 1、新建一個(gè)AIDL文件
KeepAliveConnection
interface KeepAliveConnection {
}
- 2、新建一個(gè)服務(wù)類(lèi)
StepService,onBind()方法返回new KeepAliveConnection.Stub()對(duì)象,并在ServiceConnection的綁定回調(diào)中對(duì)守護(hù)進(jìn)程服務(wù)類(lèi)GuardService的啟動(dòng)和綁定。
/**
* 主進(jìn)程 雙進(jìn)程通訊
*
* @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) {
// 斷開(kāi)鏈接
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;
}
}
- 3、對(duì)守護(hù)進(jìn)程
GuardService進(jìn)行和2一樣的處理
/**
* 守護(hù)進(jìn)程 雙進(jìn)程通訊
*
* @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) {
// 斷開(kāi)鏈接
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;
}
}
- 4、在Activity中在啟動(dòng)需要?;畹腄ownloadService服務(wù)后然后啟動(dòng)保活的雙進(jìn)程
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);
//雙守護(hù)線程,優(yōu)先級(jí)不一樣
startAllServices();
}
@Override
public void onContentChanged() {
super.onContentChanged();
mShowTimeTv = findViewById(R.id.tv_show_time);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
/**
* 開(kāi)啟所有守護(hù)Service
*/
private void startAllServices() {
startService(new Intent(this, StepService.class));
startService(new Intent(this, GuardService.class));
}
}
二、監(jiān)聽(tīng)到鎖屏廣播后使用“1”像素Activity提升優(yōu)先級(jí)
- 1、該Activity的View只要設(shè)置為1像素然后設(shè)置在Window對(duì)象上即可。在Activity的onDestroy周期中進(jìn)行?;罘?wù)的存活判斷從而喚醒服務(wù)。"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();
}
}
- 2、對(duì)廣播進(jìn)行監(jiān)聽(tīng),封裝為一個(gè)ScreenReceiverUtil類(lèi),進(jìn)行鎖屏解鎖的廣播動(dòng)態(tài)注冊(cè)監(jiān)聽(tīng)
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;
// 動(dòng)態(tài)啟動(dòng)廣播接收器
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);
}
/**
* 監(jiān)聽(tīng)sreen狀態(tài)對(duì)外回調(diào)接口
*/
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)) { // 開(kāi)屏
mStateReceiverListener.onSreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
mStateReceiverListener.onSreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解鎖
mStateReceiverListener.onUserPresent();
}
}
}
}
- 3、對(duì)1像素Activity進(jìn)行防止內(nèi)存泄露的處理,新建一個(gè)
ScreenManager類(lèi)
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);
}
/**
* 啟動(dòng)SinglePixelActivity
*/
public void startActivity() {
Intent intent = new Intent(mContext, SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
/**
* 結(jié)束SinglePixelActivity
*/
public void finishActivity() {
if (mActivity != null) {
Activity activity = mActivity.get();
if (activity != null) {
activity.finish();
}
}
}
}
- 4、對(duì)1像素的Style進(jìn)行特殊處理,在
style文件中新建一個(gè)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>
- 5、讓
SinglePixelActivity使用singleInstance啟動(dòng)模式,在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" />
- 6、在?;罘?wù)類(lèi)DownloadService中對(duì)監(jiān)聽(tīng)的廣播進(jìn)行注冊(cè)和對(duì)
SinglePixelActivity進(jìn)行控制。
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, "關(guān)閉了1像素Activity");
}
@Override
public void onSreenOff() {
mScreenManager.startActivity();
Logger.d(TAG, "打開(kāi)了1像素Activity");
}
@Override
public void onUserPresent() {
}
};
private OnTimeChangeListener mOnTimeChangeListener;
@Override
public void onCreate() {
super.onCreate();
// 注冊(cè)鎖屏廣播監(jiān)聽(tīng)器
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 = "時(shí)間為:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec;
if (mOnTimeChangeListener != null) {
mOnTimeChangeListener.showTime(time);
}
Logger.d(TAG, time);
}
};
mRunTimer = new Timer();
// 每隔1s更新一下時(shí)間
mRunTimer.schedule(mTask, 1000, 1000);
}
private void stopRunTimer() {
if (mRunTimer != null) {
mRunTimer.cancel();
mRunTimer = null;
}
mTimeSec = 0;
mTimeMin = 0;
mTimeHour = 0;
Logger.d(TAG, "時(shí)間為:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec);
}
public interface OnTimeChangeListener {
void showTime(String time);
}
public class DownloadBinder extends Binder {
public void setOnTimeChangeListener(OnTimeChangeListener onTimeChangeListener) {
mOnTimeChangeListener = onTimeChangeListener;
}
}
}
---
3、在后臺(tái)播放音樂(lè)
- 1、準(zhǔn)備一段無(wú)聲的音頻,新建一個(gè)播放音樂(lè)的Service類(lèi),將播放模式改為無(wú)限循環(huán)播放。在其onDestroy方法中對(duì)自己重新啟動(dòng)。
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,啟動(dòng)服務(wù)");
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, "啟動(dòng)后臺(tái)播放音樂(lè)");
mMediaPlayer.start();
}
}
private void stopPlayMusic() {
if (mMediaPlayer != null) {
Logger.d(TAG, "關(guān)閉后臺(tái)播放音樂(lè)");
mMediaPlayer.stop();
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopPlayMusic();
Logger.d(TAG, TAG + "---->onCreate,停止服務(wù)");
// 重啟自己
Intent intent = new Intent(getApplicationContext(), PlayerMusicService.class);
startService(intent);
}
}
- 2、 在?;畹腄ownloadServie服務(wù)類(lèi)的onCreate方法中對(duì)PlayerMusicService進(jìn)行啟動(dòng)
Intent intent = new Intent(this, PlayerMusicService.class);
startService(intent);
- 3、在Manifest文件中進(jìn)行注冊(cè)
<service
android:name=".service.PlayerMusicService"
android:enabled="true"
android:exported="true"
android:process=":music_service" />
4、使用JobScheduler喚醒Service
- 1、新建一個(gè)繼承自JobService的ScheduleService類(lèi),在其onStartJob回調(diào)中對(duì)DownloadService進(jìn)行存活的判斷來(lái)重啟。
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啟動(dòng)了DownloadService");
}
jobFinished(params, false);
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
2、 在DownloadService服務(wù)類(lèi)中進(jìn)行JobScheduler的注冊(cè)和使用
/**
* 使用JobScheduler進(jìn)行?;? */
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()));
//周期設(shè)置為了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!");
}
}
- 3、在manifest文件中進(jìn)行權(quán)限設(shè)置
<service
android:name=".service.ScheduleService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
關(guān)于推送類(lèi)拉活
根據(jù)華為官方文檔集成HUAWEI Push
- 1、華為暢玩5X(6.0):APP全部進(jìn)程被殺死時(shí)可以被拉起。
- 2、華為nove 3e(8.0):APP全部進(jìn)程被殺死時(shí)無(wú)法被拉起,能收到推送。
- 3、華為榮耀10(8.1):同2
結(jié)論:理論情況下,華為推送應(yīng)該可以拉起華為機(jī)器才對(duì),感覺(jué)是我沒(méi)花錢(qián)的原因
補(bǔ)充:ServiceAliveUtils 類(lèi)如下
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;
}
}