一、1像素一直在前台
private Handler mHandler;
private boolean isScreenOn = true;
private PendingIntent pendingIntent;
private List<ScreenStateListener> screenStateListeners = null;
@Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
//标记屏幕为锁屏状态
isScreenOn = false;
//开启一像素的Activity
startOnePixelActivity(context);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
//标记屏幕为解屏状态
isScreenOn = true;
if(pendingIntent!=null){
pendingIntent.cancel();
}
}
}
//开启一像素的Activity
private void startOnePActivity(final Context context){
if(mHandler==null){
mHandler = new Handler(Looper.myLooper());
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//如果屏幕此时已经打开,则不执行
if(isScreenOn){
return;
}
if(pendingIntent!=null){
pendingIntent.cancel();
}
Intent startOnePixelActivity = new Intent(context, OnePixelActivity.class);
startOnePixelActivity.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//启动一像素包活activity
pendingIntent = PendingIntent.getActivity(context, 0, startOnePixelActivity, 0);
try {
pendingIntent.send();
} catch (Exception e) {
e.printStackTrace();
}
notifyScreenOff();
}
},1000);
}
1、启动App(比如进入MainActivity),按home键让app返回后台
2、锁定屏幕,此时注册好的监听广播会启动OnePixelActivity
3、解锁屏幕,此时广播接受到此广播后会finish 掉OnePixelActivity.
4、因为OnePixelActivity是非singleInstance,所以此时本来已经进入后台的MainActivity的页面会自动打开。给用户造成干扰:我明明已经按下home键了,怎么此页面又自动打开了?而换成OnePixelActivity的启动模式改成singleInstance的话就可以很好的避免此问题。
<activity android:name=".OnePActivity"
android:launchMode="singleInstance"
android:excludeFromRecents="true"/>
5、另外需要注意的是,当屏幕解锁的时候,OnePixelActivity的onResume得到执行,所以在该Activity的onResume方法执行finish效果最好:
protected void onResume() {
super.onResume();
if (DeviceUtils.isScreenOn(this)) {//如果屏幕已经打开
finish();
}
}
二、前台服务
<service
android:name=".service.BluetoothService"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|layoutDirection|fontScale"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync"
android:permission="TODO">
<intent-filter android:priority="1000">
<action android:name="com.ispring2.action.MYSERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
1、这里有些手机不使用前台服务会造成网络请求失败的情况(红米 K50),
2、Android 要求前台服务要绑定一个通知;
3、START_STICKY粘性返回, 第一次死掉会自己唤醒, 但是往后唤醒时间变长, 最后唤醒会失效.
Intent t1 = new Intent(MainActivity.this, BluetoothService.class);
startForegroundService(t1);
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
MyLog.e("onStartCommand");
startNotification();
startConnThread();//onStartCommand
return START_STICKY;
}
public void startNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(InputADBUtils.getInstance().notificationIdInt);
NotificationChannel channel = new NotificationChannel(InputADBUtils.getInstance().notificationId, getApplication().getString(R.string.app_name), NotificationManager.IMPORTANCE_MIN);
notificationManager.createNotificationChannel(channel);
startForeground(InputADBUtils.getInstance().notificationIdInt, getNotification());
}
private Notification getNotification() {
Notification.Builder builder = null;
builder = new Notification.Builder(this).setSmallIcon(R.mipmap.ic_launcher_round).setContentText(getString(R.string.notification_tips));
builder.setChannelId(InputADBUtils.getInstance().notificationId);
return builder.build();
}
三、双进程守护
两个进程的Service,相互守护;当一个进程的Service挂的时候, 另一个进程的Service负责重启挂掉的Service
manifest.xml
<service android:name=".KeepAliveService"/>
//将RemoteService和KeepAliveServcie处于不同的进程
<service android:name=".RemoteService" android:process=":remote"/>
定义一个 AIDL
interface GuardAidl {
void notifyAlive();
}
KeepAliveService.java
/**
* 播放无声音乐,来保持进程包活
*/
//播放无声音乐,来保持进程保活
public class KeepAliveService extends Service {
private boolean isScreenON = true;//控制暂停
private MediaPlayer mediaPlayer;
//锁屏广播监听
private ScreenStateReceiver screenStateReceiver;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("KeepAliveService", "---KeepAliveService 启动---");
//注册锁屏广播
registerScreenStateReceiver();
//开启前台Service
startForeground(this);
//start HideNotifactionService
startHideNotificationService();
//绑定守护进程
bindRemoteService();
return START_STICKY;
}
private void bindRemoteService(){
Intent intent = new Intent(this, RemoteService.class);
bindService(intent,connection,Context.BIND_ABOVE_CLIENT);
}
private void startHideNotificationService() {
try {
// if(Build.VERSION.SDK_INT < 25){
startService(new Intent(this, HideNotificationService.class));
// }
} catch (Exception e) {
}
}
@Override
public void onCreate() {
super.onCreate();
}
//将keepAliveBinder交给RemoteService
public IBinder onBind(Intent intent) {
return keepAliveBinder;
}
private GuardAidl.Stub keepAliveBinder = new GuardAidl.Stub() {
@Override
public void notifyAlive() throws RemoteException {
Log.i(null, "Hello RemoteService!");
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Intent remoteService = new Intent(KeepAliveService.this,
RemoteService.class);
KeepAliveService.this.startService(remoteService);
Intent intent = new Intent(KeepAliveService.this, RemoteService.class);
//将KeepAliveService和RemoteService进行绑定
KeepAliveService.this.bindService(intent, connection,
Context.BIND_ABOVE_CLIENT);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
//与RemoteService绑定成功
GuardAidl remoteBinder = GuardAidl.Stub.asInterface(service);
remoteBinder.notifyAlive();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
Log.e("KeepAliveService", "---KeepAliveService onDestroy---");
if (screenStateReceiver != null) {
unregisterReceiver(screenStateReceiver);
}
}
public static void startForeground(Service service) {
Intent intent = new Intent(service.getApplicationContext(), com.fanjun.keeplive.receiver.NotificationClickReceiver.class);
intent.setAction(com.fanjun.keeplive.receiver.NotificationClickReceiver.CLICK_NOTIFICATION);
Notification notification = NotificationUtils.createNotification(service, "1", "2", R.drawable.ic_launcher_background, intent);
service.startForeground(13691, notification);
}
private void registerScreenStateReceiver() {
screenStateReceiver = new ScreenStateReceiver();
screenStateReceiver.addListener(new ScreenStateReceiver.ScreenStateListener() {
@Override
public void screenOn() {
isScreenON = true;
Log.e("ScreenStateReceiver", "---音乐关闭---");
pause();
}
@Override
public void screenOff() {
isScreenON = false;
Log.e("ScreenStateReceiver", "---音乐开启---");
play();
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(screenStateReceiver, intentFilter);
}
private void initMediaPlayer() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.novioce);
mediaPlayer.setVolume(0f, 0f);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
if (isScreenON) {
return;
}
//循环播放
play();
}
});
}
//防止Service启动的时候屏幕处于解锁状态
if (!DeviceUtils.isScreenOn(this)) {
play();
}
}
private void play() {
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
}
RemoteService.java
public class RemoteService extends Service {
public IBinder onBind(Intent intent) {
return remoteBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
bindService(new Intent(RemoteService.this, LocalService.class),
connection, Context.BIND_ABOVE_CLIENT);
return super.onStartCommand(intent, flags, startId);
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Intent intent = new Intent(RemoteService.this, KeepAliveService.class);
//将KeepAliveService和RemoteService进行绑定
RemoteService.this.bindService(intent, connection,
Context.BIND_ABOVE_CLIENT);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
//与RemoteService绑定成功
GuardAidl remoteBinder = GuardAidl.Stub.asInterface(service);
remoteBinder.notifyAlive();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
private GuardAidl.Stub remoteBinder = new GuardAidl.Stub(){
@Override
public void notifyAlive() throws RemoteException {
Log.i(null,"Hello KeepAliveService!");
}
};
}
四、JobService
系统服务 BindService 的方式把应用内 Manifest中 配置的 JobService 启动起来,并通过进程间通信 Binder 方式调用JobService的onStartJob、onStopJob等方法来进行Job的管理。
即便在执行任务之前应用程序进程被杀,也不会导致任务中断,Jobservic e不会因应用退出而退出; 当然 JobScheduler 可以利用这种机制做很多后台定时任务
public class KeepAliveJobService extends JobService {
private JobScheduler mJobScheduler;
@Override
public boolean onStartJob(JobParameters jobParameters) {
startService(this);
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
startService(this);
return false;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//启动Servcie
startService(this);
//初始化定时任务
scheduleJob(startId);
return START_STICKY;
}
private void startService(Context context) {
//设置为前台服务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent2 = new Intent(getApplicationContext(), com.fanjun.keeplive.receiver.NotificationClickReceiver.class);
intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
Notification notification = NotificationUtils.createNotification(this, "1", "2", R.drawable.ic_launcher_background, intent2);
startForeground(13691, notification);
}
//启动本地服务
Intent keepAliveIntent= new Intent(context, KeepAliveService.class);
//启动守护进程
Intent guardIntent = new Intent(context, RemoteService.class);
startService(keepAliveIntent);
startService(guardIntent);
}
private void scheduleJob(int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(startId++,
new ComponentName(getPackageName(), KeepAliveJobService.class.getName()));
if (Build.VERSION.SDK_INT >= 24) {
builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最小延迟时间
builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最长延时时间
builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
} else {
builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
}
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setRequiresCharging(true); // 当插入充电器,执行该任务
mJobScheduler.schedule(builder.build());
}
}
启动方式:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//启动定时器,在定时器中启动本地服务和守护进程
Intent intent = new Intent(application, JobHandlerService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
application.startForegroundService(intent);
} else {
application.startService(intent);
}
} else {
//启动本地服务
Intent localIntent = new Intent(application, LocalService.class);
//启动守护进程
Intent guardIntent = new Intent(application, RemoteService.class);
application.startService(localIntent);
application.startService(guardIntent);
}