window.setAttributes(params);
//KeepAliveManager中的保活Activity初始化为本Activity
KeepAliveManager.getInstance().setKeepLiveActivity(this);
}
@Override
protected void onStart() {
Log.d(MainActivity.TAG, “OnePieceActivity onStart”);
super.onStart();
}
@Override
protected void onRestart() {
Log.d(MainActivity.TAG, “OnePieceActivity onRestart”);
super.onRestart();
}
@Override
protected void onStop() {
Log.d(MainActivity.TAG, “OnePieceActivity onStop”);
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(MainActivity.TAG, “OnePieceActivity onDestroy”);
super.onDestroy();
}
}
MainActivity :主要用于控制广播的注册与注销。
package com.zyc.onepiece;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
public static String TAG = “MyLog”;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(MainActivity.TAG, “MainActivity onCreate”);
KeepAliveManager.getInstance().registerKeepLiveReceiver(this);
}
@Override
protected void onStart() {
Log.d(MainActivity.TAG, “MainActivity onStart”);
super.onStart();
}
@Override
protected void onRestart() {
Log.d(MainActivity.TAG, “MainActivity onRestart”);
super.onRestart();
}
@Override
protected void onStop() {
Log.d(MainActivity.TAG, “MainActivity onStop”);
super.onStop();
}
@Override
protected void onDestroy() {
KeepAliveManager.getInstance().unregisterKeepLiveReceiver(this);
super.onDestroy();
}
}
运行,发现在笔者的一加6T(Android 10)下应用内熄屏可以拉起1像素保活Activity,但按Home回到主页后,再熄屏虽然可以接到广播,但无法拉起Activity:
设置前台Service
本方案原理是拥有前台Service的进程将拥有更高的优先级,也就更难被回收掉。注意:部分Android版本前台Service会在顶部通知栏露出“马脚”:
可以试着在前台Service创建通知后立马清除它来隐藏自己。利用相同id前台Service会使用同一条通知的特性,创建一个与前台Service有相id的“替罪羊”前台Service,然后结束它,这样通知会被随之清除,但原本的Service可以继续工作。
项目结构如下。
AndroidManifest.xml:
//不加该权限会报错:java.lang.SecurityException: Permission Denial: startForeground from pid=XXX, uid=XXX requires android.permission.FOREGROUND_SERVICE
…
ForegroundService :模拟工作的(前台)线程,如有必要会开启ScapegoatService来清除通知栏通知。由于缺乏更多高版本系统样本,Android 8.0以上部分可能不靠谱,不过推荐还是老老实实按谷歌要求发出通知为妙。
package com.zyc.foregroundservice;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.RequiresApi;
import java.util.Timer;
import java.util.TimerTask;
public class ForegroundService extends Service {
private static final int SERVICE_ID = 1;
private Timer timer;
private TimerTask timerTask;
public static int count;
public ForegroundService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(MainActivity.TAG, “创建前台服务”);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startTask();
//判断版本
if (Build.VERSION.SDK_INT < 18) {//Android4.3以下版本
//直接调用startForeground即可,不会在通知栏创建通知
startForeground(SERVICE_ID, new Notification());
} else if (Build.VERSION.SDK_INT < 24) {//Android4.3 - 7.0之间
Intent scapegoatIntent = new Intent(this, ScapegoatService.class);
startService(scapegoatIntent);
} else {//Android 8.0以上
//经测试,本人的一加6T(Android 10)这样写并不会在通知栏创建通知,其他机型与版本效果仍需考证
startForeground(SERVICE_ID, new Notification());
}
return START_STICKY;
}
/**
- 开启定时任务,count每秒+1
*/
private void startTask() {
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
Log.d(MainActivity.TAG, "服务运行中,count: " + count);
count++;
}
};
timer.schedule(timerTask, 0, 1000);
}
/**
- 结束定时任务
*/
private void stopTask() {