Android保活众所周知,完全保活除非加入白名单,否则只能保住一段时间。下面一一介绍Android保活方案。由于时间有限,保活方案文章的编写,采用的是逐步完善的方式。在保证内容可用的条件下逐步晚上文字,不足之处还请见谅。完善所有代码后,会写成uni-app插件。
方案一、前台服务+Notification
代码
a、首先在AndroidManifest.xml添加以下权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
b、前台服务代码
public class KeepServices extends Service {
private NotificationManager notificationManager;
private String notificationId = "serviceid";
private String notificationName = "servicename";
@Override
public void onCreate() {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//创建NotificationChannel
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
startForeground(1,getNotification());
}
private Notification getNotification() {
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.mipmap.amin_pic)
.setContentTitle("title")
.setContentText("text");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(notificationId);
}
Notification notification = builder.build();
return notification;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(2000);
Log.d("Services","====保活服务===数据支持====");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
c、在AndroidManifest.xml中注册服务
<service
android:name="com.xiaoya.keepalive.KeepServices"// 找到自己的服务路径
android:enabled="true"
android:exported="true" />
d、启动服务
Intent intent=new Intent(this, KeepServices.class);
startService(intent);
方案二、加入白名单
这里借鉴其他博主的国内厂商白名单跳转工具
public class SettingUtils {
public static void enterWhiteListSetting(Context context) {
try {
context.startActivity(getSettingIntent());
} catch (Exception e) {
context.startActivity(new Intent(Settings.ACTION_SETTINGS));
}
}
private static Intent getSettingIntent() {
ComponentName componentName = null;
String brand = android.os.Build.BRAND;
switch (brand.toLowerCase()) {
case "samsung":
componentName = new ComponentName("com.samsung.android.sm",
"com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity");
break;
case "huawei":
componentName = new ComponentName("com.huawei.systemmanager",
"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
break;
case "xiaomi":
componentName = new ComponentName("com.miui.securitycenter",
"com.miui.permcenter.autostart.AutoStartManagementActivity");
break;
case "vivo":
componentName = new ComponentName("com.iqoo.secure",
"com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity");
break;
case "oppo":
componentName = new ComponentName("com.coloros.oppoguardelf",
"com.coloros.powermanager.fuelgaue.PowerUsageModelActivity");
break;
case "360":
componentName = new
ComponentName("com.yulong.android.coolsafe",
"com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity");
break;
case "meizu":
componentName = new ComponentName("com.meizu.safe",
"com.meizu.safe.permission.SmartBGActivity");
break;
case "oneplus":
componentName = new ComponentName("com.oneplus.security",
"com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
break;
default:
break;
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (componentName != null) {
intent.setComponent(componentName);
} else {
intent.setAction(Settings.ACTION_SETTINGS);
}
return intent;
}
}
方案三、双进程守护 + JobScheduler 保活
参考()
1、JobService 代码
public class KeepAliveJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Log.i("KeepAliveJobService", "JobService onStartJob 开启");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
// 如果当前设备大于 7.0 , 延迟 5 秒 , 再次执行一次
startJob(this);
}
// 判定本地前台进程是否正在运行
boolean isLocalServiceRunning =
ServiceUtils.isServiceRunning(this, LocalForegroundService.class.getName());
if (!isLocalServiceRunning){
startService(new Intent(this, LocalForegroundService.class));
}
// 判定远程前台进程是否正在运行
boolean isRemoteServiceRunning =
ServiceUtils.isServiceRunning(this, RemoteForegroundService.class.getName());
if (!isRemoteServiceRunning){
startService(new Intent(this, RemoteForegroundService.class));
}
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.i("KeepAliveJobService", "JobService onStopJob 关闭");
return false;
}
public static void startJob(Context context){
// 创建 JobScheduler
JobScheduler jobScheduler =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
// 第一个参数指定任务 ID
// 第二个参数指定任务在哪个组件中执行
// setPersisted 方法需要 android.permission.RECEIVE_BOOT_COMPLETED 权限
// setPersisted 方法作用是设备重启后 , 依然执行 JobScheduler 定时任务
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10,
new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName()))
.setPersisted(true);
// 7.0 以下的版本, 可以每隔 5000 毫秒执行一次任务
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){
jobInfoBuilder.setPeriodic(5_000);
}else{
// 7.0 以上的版本 , 设置延迟 5 秒执行
// 该时间不能小于 JobInfo.getMinLatencyMillis 方法获取的最小值
jobInfoBuilder.setMinimumLatency(5_000);
}
// 开启定时任务
jobScheduler.schedule(jobInfoBuilder.build());
}
}
2、判定服务运行工具类
public class ServiceUtils {
/**
* 判定 Service 是否在运行
* @param context
* @return
*/
public static boolean isServiceRunning(Context context, String serviceName){
if(TextUtils.isEmpty(serviceName)) return false;
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 最多获取 200 个正在运行的 Service
List<ActivityManager.RunningServiceInfo> infos =
activityManager.getRunningServices(200);
// 遍历当前运行的 Service 信息, 如果找到相同名称的服务 , 说明某进程正在运行
for (ActivityManager.RunningServiceInfo info: infos){
if (TextUtils.equals(info.service.getClassName(), serviceName)){
return true;
}
}
return false;
}
}
3、清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kim.hsl.two_progress_alive">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Two_Progress_Alive">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 本地提权前台服务 Service -->
<service
android:name=".LocalForegroundService"
android:enabled="true"
android:exported="true"></service>
<!-- 本地服务 , API 18 ~ 25 以上的设备, 关闭通知到专用服务 -->
<service
android:name=".LocalForegroundService$CancelNotificationService"
android:enabled="true"
android:exported="true"></service>
<!-- 远程提权前台服务 Service -->
<service
android:name=".RemoteForegroundService"
android:enabled="true"
android:exported="true"
android:process=":remote"></service>
<!-- 远程服务 , API 18 ~ 25 以上的设备, 关闭通知到专用服务 -->
<service
android:name=".RemoteForegroundService$CancelNotificationService"
android:enabled="true"
android:exported="true"
android:process=":remote"></service>
<!-- JobScheduler 拉活 -->
<service
android:name=".KeepAliveJobService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"></service>
</application>
</manifest>
4、MainActivity 代码
package kim.hsl.two_progress_alive;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过前台 Service 提升应用权限
// 启动普通 Service , 但是在该 Service 的 onCreate 方法中执行了 startForeground
// 变成了前台 Service 服务
startService(new Intent(this, LocalForegroundService.class));
startService(new Intent(this, RemoteForegroundService.class));
// JobScheduler 拉活
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
KeepAliveJobService.startJob(this);
}
}
}
方案四、使用系统服务的return START_STICKY
START_STICKY:
“粘性”。如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
START_NOT_STICKY:
“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
START_REDELIVER_INTENT:
重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
START_STICKY_COMPATIBILITY:
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
只要 targetSdkVersion 不小于5,就默认是 START_STICKY。 但是某些ROM 系统不会拉活。并且经过测试,Service 第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内 Service 被杀死4-5次,则系统不再拉起。
原文链接:
public int onStartCommand(Intent intent, int flags, int startId) {
......
START_STICKY;
}
优点:利用services自身的api,可靠。
缺点:很多手机厂商已经做限制,导致很多类似华为、小米手机无效。只会重启5次。被系统强制停止也无法重启。
方案五、一像素
参考(https://www.mianshigee.com/project/wangchunfei-KeepAlive)
1、Activity
public class HooliganActivity extends Activity {
private static HooliganActivity instance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
Window window = getWindow();
window.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(params);
}
/**
* 开启保活页面
*/
public static void startHooligan() {
Intent intent = new Intent(DWApplication.getAppContext(), HooliganActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
DWApplication.getAppContext().startActivity(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
instance = null;
}
/**
* 关闭保活页面
*/
public static void killHooligan() {
if(instance != null) {
instance.finish();
}
}
}
2.注册清单文件:
<activity android:name=".activity.HooliganActivity"
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:theme="@style/HooliganActivityStyle"/>
<style name="HooliganActivityStyle">
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoDisplay">false</item>
<item name="android:windowDisablePreview">true</item>
</style>
3、监听锁屏和解锁通知,不能静态注册广播,只能动态注册:
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BootCompleteReceiver(),filter);
4、分别在解锁和锁屏时唤醒我的HooliganActivity:
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
HooliganActivity. startHooligan();
} else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)){
HooliganActivity. killHooligan();
}
}
}
5、在最近使用的列表中隐藏应用
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_HOME);
getAppContext().startActivity(intent);
android:excludeFromRecents="true"
至此,整个的保活就结束了 这样你在后台每次锁屏,实际上都会吊起一个一像素的页面,假装app在前台,拥有最高进程优先级。
方案六、无声音乐
import android.media.MediaPlayer;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import android.util.Base64;
import java.io.FileInputStream;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MySer extends Service {
MediaPlayer mediaplayer=null;
//转base64的音频文件
String base64 = "AAAAGGZ0eXBtcDQyAAAAAG1wNDFpc29tAAAAKHV1aWRcpwj7Mo5CBahhZQ7KCpWWAAAADDEwLjAuMTgzNjMuMAAAAG5tZGF0AAAAAAAAABAnDEMgBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBDSX5AAAAAAAAB9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9AAAC/m1vb3YAAABsbXZoZAAAAADeilCc3opQnAAAu4AAAAIRAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAHBdHJhawAAAFx0a2hkAAAAAd6KUJzeilCcAAAAAgAAAAAAAAIRAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABXW1kaWEAAAAgbWRoZAAAAADeilCc3opQnAAAu4AAAAIRVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAQhtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAMxzdGJsAAAAZHN0c2QAAAAAAAAAAQAAAFRtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAAu4AAAAAAADBlc2RzAAAAAAOAgIAfAAAABICAgBRAFQAGAAACM2gAAjNoBYCAgAIRkAYBAgAAABhzdHRzAAAAAAAAAAEAAAABAAACEQAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAYc3RzegAAAAAAAAAAAAAAAQAAAF4AAAAUc3RjbwAAAAAAAAABAAAAUAAAAMl1ZHRhAAAAkG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXIAAAAAAAAAAAAAAAAAAAAAY2lsc3QAAAAeqW5hbQAAABZkYXRhAAAAAQAAAADlvZXpn7MAAAAcqWRheQAAABRkYXRhAAAAAQAAAAAyMDIyAAAAIWFBUlQAAAAZZGF0YQAAAAEAAAAA5b2V6Z+z5py6AAAAMVh0cmEAAAApAAAAD1dNL0VuY29kaW5nVGltZQAAAAEAAAAOABUA2rD/dVfYAQ==";
@Override
public void onCreate() {
super.onCreate();
if(mediaplayer==null){
new Thread(new Runnable(){
@Override
public void run() {
mediaplayer=new MediaPlayer();
try {
byte[] mp3SoundByteArray = Base64.decode(base64, Base64.DEFAULT);// 将字符串转换为byte数组
File tempMp3 = File.createTempFile("s", ".mp3");
tempMp3.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempMp3);
fos.write(mp3SoundByteArray);
fos.close();
FileInputStream fis = new FileInputStream(tempMp3);
mediaplayer.setDataSource(fis.getFD());
mediaplayer.setLooping(true);
mediaplayer.prepareAsync ();//异步准备播放 这部必须设置不然无法播放
mediaplayer.start();//开始播放
} catch (IllegalStateException e) {
System.out.print("出错了="+e);
} catch (SecurityException e) {} catch (IOException e) {
System.out.print("出错了="+e);
} catch (IllegalArgumentException e) {}
}
}).start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
//停止
mediaplayer.stop();
mediaplayer=null;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
方案七、推送互相唤醒复活
......
方案八、app相互拉活
.......