应用后台持续定位:官方说明
单页面单次/连续定位切换:官方说明
获取地址描述数据【地址转坐标】【地址转坐标】:官方说明
获取两条经纬度线段的交点坐标工具类
ScheduledExecutorService 实现定时任务
如果打算使用定时器的话,可以使用ScheduledExecutorService替换Timer,这个更好用!
本地进程
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/6/5 20:34
* Desc : 本地进程
*/
public class LocalService extends Service {
public static final String TAG = "守护进程";
private MediaPlayer mediaPlayer = null;
private MyBuilder myBuilder;
private ScheduledExecutorService threadPool = null;
private AMapLocationClient mClient;
private ScreenReceiverUtil mScreenListener;
private ScreenManager mScreenManager;
private final int MSGWHAT = 19;
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == MSGWHAT) {
ThreadPoolUtils.execute(new Runnable() {
@Override
public void run() {
Http_Tens.postService(null, LocalService.class.getName());
}
});
handler.sendEmptyMessageDelayed(MSGWHAT, 1000 * 10);
}
}
};
public LocalService() {
}
@Override
public void onCreate() {
super.onCreate();
if (myBuilder == null) {
myBuilder = new MyBuilder();
}
mScreenListener = new ScreenReceiverUtil(this);//注册锁屏广播监听器
mScreenManager = ScreenManager.getInstance(this);
mScreenListener.setScreenReceiverListener(mScreenListenerer);
initGD();
}
//这里,只是使用一下连续定位,因为单次定位的话,会导致手机状态栏上面定位图标一直闪,影响体验
private void initGD() {
mClient = new AMapLocationClient(App.getContext());
AMapLocationClientOption mLocationOption = new AMapLocationClientOption();
mLocationOption.setOnceLocation(false);
mLocationOption.setInterval(1000 * 3);//默认连续定位是2s,高德APP设置的是1S
mClient.setLocationOption(mLocationOption);
mClient.setLocationListener(new AMapLocationListener() {
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
Log_Ma.e(TAG, "------1111-----------" + aMapLocation.toString());
if (aMapLocation != null && aMapLocation.getErrorCode() == 0) {
initAmap(aMapLocation);
} else {
WeiLan_GaoDe.getInstance().initOneLocation(App.getContext(), new WeiLan_GaoDe.OneListener() {
@Override
public void succeed(AMapLocation loc) {
initAmap(loc);
}
@Override
public void onError() {
PowerManagerUtil.getInstance().awakening(LocalService.this);
}
});
}
}
});
mClient.startLocation();
}
private void initAmap(AMapLocation loc) {
MMKV.defaultMMKV().putString(Constant.AMapLocation_Time, loc.getTime() + "");
MMKV.defaultMMKV().putString(Constant.AMapLocation_Latitude, loc.getLatitude() + "");
MMKV.defaultMMKV().putString(Constant.AMapLocation_Longitude, loc.getLongitude() + "");
MMKV.defaultMMKV().putString(Constant.AMapLocation_CityCode, loc.getCityCode() + "");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log_Ma.e(TAG, "LocalService:LocalService 启动");
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(this, R.raw.aa);
mediaPlayer.setVolume(0f, 0f);//声音设置为0
mediaPlayer.setLooping(true); //循环播放
mediaPlayer.start();
//启用前台服务,提升优先级
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(12001, NotifyManager.getInstance().getNotification(this, 1));
}
//绑定守护进程
Intent intent3 = new Intent(this, RemoteService.class);
bindService(intent3, connection, Context.BIND_ABOVE_CLIENT);
// initTimer();
handler.removeMessages(MSGWHAT);
handler.sendEmptyMessageDelayed(MSGWHAT, 1000 * 2);
return Service.START_STICKY;
}
private void initTimer() {
destroyTimer();
threadPool = Executors.newSingleThreadScheduledExecutor();
threadPool.scheduleWithFixedDelay(new ScanScheduledExecutor(this), 5, 10, TimeUnit.SECONDS);
}
//静态弱引用方式,防止内存泄露
private static class ScanScheduledExecutor implements Runnable {
private SoftReference<Context> softReference;
ScanScheduledExecutor(Context context) {
softReference = new SoftReference<Context>(context);
}
@Override
public void run() {
try {
Context context = softReference.get();
if (context != null) {
boolean isScreenOn = PowerManagerUtil.getInstance().isScreenOn(context);
Log_Ma.e(TAG, "屏幕是否点亮:" + isScreenOn);
WeiLan_GaoDe.getInstance().initOneLocation(context, new WeiLan_GaoDe.OneListener() {
@Override
public void succeed(AMapLocation loc) {
Http_Tens.postService(loc, LocalService.class.getName());
}
@Override
public void onError() {
if (!isScreenOn) {
PowerManagerUtil.getInstance().awakening(App.getContext());
}
}
});
}
} catch (Exception e) {
Log_Ma.e("ScheduledExecutorService e:" + e.toString());
}
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (!Http_Date_Utils.getInstance().getUserID().equals("")) {
try {
if (myBuilder != null) {
GuardAidl guardAidl = GuardAidl.Stub.asInterface(iBinder);
guardAidl.wakeUp();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
if (!Http_Date_Utils.getInstance().getUserID().equals("")) {
String str = MMKV.defaultMMKV().getString(Constant.killService, "");
if (!TextUtils.isEmpty(str)) {
Intent remoteService = new Intent(LocalService.this, RemoteService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(remoteService);
} else {
startService(remoteService);
}
Intent intent = new Intent(LocalService.this, RemoteService.class);
bindService(intent, this, Context.BIND_ABOVE_CLIENT);
}
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
Log_Ma.e(TAG, "onDestroy() called");
unbindService(connection);
destroyTimer();
mClient.stopLocation();
mClient.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
mScreenListener.stopScreenReceiverListener();
handler.removeCallbacksAndMessages(null);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBuilder;
}
private class MyBuilder extends GuardAidl.Stub {
@Override
public void wakeUp() {
}
}
//先取消上一个任务,防止重复的任务
private void destroyTimer() {
if (threadPool != null) {
Log_Ma.e(TAG, "destroyTimer() called");
threadPool.shutdownNow();
threadPool = null;
}
}
private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
@Override
public void onSreenOn() {
Log_Ma.e(TAG, "开屏 -------------------");
}
@Override
public void onSreenOff() {
Log_Ma.e(TAG, "锁屏 打开了1像素Activity");
mScreenManager.startActivity();
}
@Override
public void onUserPresent() {
Log_Ma.e(TAG, "解锁 关闭了1像素Activity");
mScreenManager.finishActivity();
}
};
}
守护进程:
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/6/5 20:35
* Desc : 守护进程
*/
public class RemoteService extends Service {
public static final String TAG = "守护进程";
private MyBuilder myBuilder;
@Override
public void onCreate() {
super.onCreate();
if (myBuilder == null) {
myBuilder = new MyBuilder();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBuilder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log_Ma.e(TAG, "RemoteService 绑定LocalService");
bindService(new Intent(this, LocalService.class), connection, Context.BIND_ABOVE_CLIENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(12003, NotifyManager.getInstance().getNotification(this, 3));
}
return Service.START_STICKY;
}
private class MyBuilder extends GuardAidl.Stub {
@Override
public void wakeUp() throws RemoteException {
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
if (!Http_Date_Utils.getInstance().getUserID().equals("")) {
String str = MMKV.defaultMMKV().getString(Constant.killService, "");
if (!TextUtils.isEmpty(str)) {
startService(new Intent(RemoteService.this, LocalService.class));
bindService(new Intent(RemoteService.this, LocalService.class), this, Context.BIND_ABOVE_CLIENT);
PowerManagerUtil.getInstance().wakeUpScreen(RemoteService.this);
}
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
Job线程
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/07/12 14:39
* Desc : Job线程
*/
public class JobHandlerService extends JobService {
public static final String TAG = "JobHandlerService";
private JobScheduler jobScheduler = null;
@Override
public boolean onStartJob(JobParameters jobParameters) {
//开启定时任务 定时轮寻 判断应用Service是否被杀死
Log_Ma.e(TAG, "onStartJob() called with: jobParameters = [" + jobParameters + "]");
boolean messageServiceAlive = ServiceUtils.serviceAlive(LocalService.class.getName());
if (!messageServiceAlive) {
startService(this);
}
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
//如果被杀死则重启Service
Log_Ma.e(TAG, "onStopJob() called with: jobParameters = [" + jobParameters + "]");
boolean messageServiceAlive = ServiceUtils.serviceAlive(LocalService.class.getName());
if (!messageServiceAlive) {
startService(this);
}
return false;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log_Ma.e(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
int startId1 = startId;
startService(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(startId1++, new ComponentName(this, JobHandlerService.class));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//7.0 以上的版本 , 设置延迟 5 秒执行
builder.setMinimumLatency(1000 * 30); //最短延迟时间,单位毫秒
builder.setOverrideDeadline(1000 * 60); //最长延迟时间,单位毫秒
} else {
/*设置任务运行的周期(每X毫秒,运行一次)*/
builder.setPeriodic(1000 * 60 * 1);
}
//共有(NETWORK_TYPE_NONE默认)、(NETWORK_TYPE_ANY任何网络状态)、(NETWORK_TYPE_UNMETERED不需要计量的网络状态)、(NETWORK_TYPE_NOT_ROAMING非漫游状态)
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
/*是否需要在充电状态执行 默认是false,只有当设备在充电时这个任务才会被执行。
这个也并非只是插入充电器,而且还要在电池处于健康状态的情况下才会触发,一般来说是手机电量>15%*/
builder.setRequiresCharging(true);//是否需要在充电状态执行 默认是false
/*指定Job在空闲状态才能运行。设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后,执行工作*/
builder.setRequiresDeviceIdle(false);
/*这个方法告诉系统当设备重启之后任务是否还要继续执行。*/
builder.setPersisted(false);
/*
特殊:设置回退/重试的策略,详细的可以参阅Google API。
类似网络原理中的冲突退避,当一个任务的调度失败时需要重试,所采取的策略。
第一个参数时第一次尝试重试的等待间隔,单位为毫秒,
预设的参数有:DEFAULT_INITIAL_BACKOFF_MILLIS 30000 、MAX_BACKOFF_DELAY_MILLIS 18000000。
第二个参数是对应的退避策略,预设的参数有:BACKOFF_POLICY_EXPONENTIAL 二进制退避。
等待间隔呈指数增长 BACKOFF_POLICY_LINEAR。
*/
builder.setBackoffCriteria(1000 * 60 * 1, JobInfo.BACKOFF_POLICY_LINEAR);//设置重试方案
jobScheduler.schedule(builder.build());
}
return Service.START_STICKY;
}
private void startService(Context context) {
String str = MMKV.defaultMMKV().getString(Constant.killService, "");
if (!TextUtils.isEmpty(str)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(12001, NotifyManager.getInstance().getNotification(this, 4));
}
startService(new Intent(context, LocalService.class));
startService(new Intent(context, RemoteService.class));
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log_Ma.e(TAG, "onDestroy() called");
}
@Override
public void onCreate() {
super.onCreate();
Log_Ma.e(TAG, "onCreate() called");
}
}
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/07/12 14:41
* Desc : 通知
*/
public class HideForegroundService extends Service {
private Handler handler = null;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(12005, NotifyManager.getInstance().getNotification(this,5));
if (handler == null) {
handler = new Handler();
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
stopForeground(true);
stopSelf();
}
}, 1500);
return Service.START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
电池工具类 【保证在息屏状体下,CPU可以正常运行】
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/9/22 15:46
* Desc : 电池工具类 【保证在息屏状体下,CPU可以正常运行】
*/
public class PowerManagerUtil {
public static final String TAG = "PowerManagerUtil";
//使用volatile关键字保其可见性
volatile private static PowerManagerUtil instance = null;
private PowerManager.WakeLock mWakelock;
private PowerManagerUtil() {
}
public static PowerManagerUtil getInstance() {
try {
if (instance != null) {//懒汉式
} else {
//创建实例之前可能会有一些准备性的耗时工作
Thread.sleep(300);
synchronized (PowerManagerUtil.class) {
if (instance == null) {//二次检查
instance = new PowerManagerUtil();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* @param context
* @return 判断屏幕是否处于点亮状态 true【亮屏】 false【息屏】
*/
public boolean isScreenOn(final Context context) {
try {
Method isScreenMethod = PowerManager.class.getMethod("isScreenOn", new Class[]{});
PowerManager pm = (PowerManager) context.getSystemService(POWER_SERVICE);
boolean screenState = (Boolean) isScreenMethod.invoke(pm);
return screenState;
} catch (Exception e) {
return false;
}
}
/**
* 唤醒屏幕
*/
@SuppressLint("InvalidWakeLockTag")
public void wakeUpScreen(final Context context) {
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
/*
* SCREEN_DIM_WAKE_LOCK CPU:保持运转 屏幕:保持显示但可以是暗的 键盘灯:关闭
* SCREEN_BRIGHT_WAKE_LOCK CPU:保持运转 屏幕:保持高亮 键盘灯:关闭
* FULL_WAKE_LOCK CPU:保持运转 屏幕:保持高亮 键盘灯:点亮
* PARTIAL_WAKE_LOCK CPU:保持运转 屏幕:可以关闭 键盘灯:可以关闭
*
* ACQUIRE_CAUSES_WAKEUP 强制使屏幕亮起,这种锁主要针对一些必须通知用户的操作【慎用,会导致锁屏情况下,来消息屏幕频繁亮起,体验不好!】
* 不能和 PARTIAL_WAKE_LOCK 一起用
*
* ON_AFTER_RELEASE 在释放锁时回收activity的timer计时器【不能和 PARTIAL_WAKE_LOCK 一起用】
* */
PowerManager.WakeLock lock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
/*
* https://blog.csdn.net/fengyeNom1/article/details/121373158【详解】
* 在通常的wakelock使用时,会报错:java.lang.RuntimeException: WakeLock under-locked。
* 这是因为出现了上述release函数末尾if(mCount<0)的情,用 setReferenceCounted(false) 就可以解决这个问题。
* 这个函数的作用:是不是需要计算锁的数量?设置为false时,在release的时候,不管你acquire()了多少回,可以一次releaseWakeLock掉。
*
* 注意这个方法默认为true,意味着一个WakeLock调用acquire()多次,也必须release()多次才能释放,
* 如果释放次数比acquire()多,则抛出异常: java.lang.RuntimeException: WakeLock under-locked
* */
lock.setReferenceCounted(false);
lock.acquire();
lock.release();
Log_Ma.e(TAG, "wakeUpScreen: 正常唤醒操作");
// FIXME: 注意:WakeLock的设置是 Activiy 级别的,不是针对整个Application应用的。所以application下有多个activity一定需要注意下!
}
} catch (Exception e) {
LiteOrmDBUtil.insert(new Bean_ERROR(0, "PowerManagerUtil类中wakeUpScreen方法手动获取异常:" + e.toString()));
Log_Ma.e(TAG, "wakeUpScreen: " + e.toString());
}
}
/**
* 唤醒CPU
*/
@SuppressLint("InvalidWakeLockTag")
public void awakening(Context context) {
try {
if (mWakelock == null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) context.getSystemService(POWER_SERVICE);
mWakelock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
mWakelock.setReferenceCounted(false);
mWakelock.acquire();//点亮屏幕
}
}
Log_Ma.e(TAG, "awakening: 获取CPU唤醒锁-------获取----获取---");
} catch (Exception e) {
Log_Ma.e(TAG, "awakening: 获取CPU唤醒锁" + e.toString());
}
}
public void release() {
try {
if (mWakelock != null) {
mWakelock.release();
mWakelock = null;
}
Log_Ma.e(TAG, "awakening: 释放CPU唤醒锁---------- 释放---- 释放 释放--");
} catch (Exception e) {
Log_Ma.e(TAG, "awakening: 释放CPU唤醒锁" + e.toString());
}
}
}
【总开关设置,开启服务VS关闭服务】
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/6/5 17:42
* Desc : 保活设置
*/
public class KeepLive {
public static final String TAG = "KeepLive";
public static void startWork(Context context) {
MMKV.defaultMMKV().putString(Constant.killService, "mzz");//一定要注意这个标签的使用,就是本地持久化一个数值,后边要判断的
//-----------------------------------切换双进程保活------------------------------------------
MMKV.defaultMMKV().putString(Constant.Service_TAG, "1");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//启动定时器,在定时器中启动本地服务和守护进程
Intent jobSer = new Intent(context, JobHandlerService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(jobSer);
} else {
context.startService(jobSer);
}
} else {
//启动本地服务
Intent localIntent = new Intent(context, LocalService.class);
//启动守护进程
Intent guardIntent = new Intent(context, RemoteService.class);
context.startService(localIntent);
context.startService(guardIntent);
}
}
public static void stop(Context context) {
MMKV.defaultMMKV().removeValueForKey(Constant.killService);
Intent localIntent = new Intent(context, LocalService.class);
Intent guardIntent = new Intent(context, RemoteService.class);
Intent hideIntent = new Intent(context, HideForegroundService.class);
Intent jobIntent = new Intent(context, JobHandlerService.class);
context.stopService(jobIntent);
context.stopService(hideIntent);
context.stopService(localIntent);
context.stopService(guardIntent);
}
}
“1”像素Activity
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/5/23 9:48
* Desc : “1”像素Activity
*/
public class KeepLiveActivity extends AppCompatActivity {
public static final String TAG = "KeepLiveActivity";
@Override
protected void onCreate(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);
startSer();
Log_Ma.e(TAG, "嘿嘿~,用到我,起来了");
}
@Override
protected void onDestroy() {
startSer();
super.onDestroy();
Log_Ma.e(TAG, "嘿嘿~,不用我,关闭了");
}
private void startSer() {
if (!ServiceUtils.serviceAlive(LocalService_NoKip.class.getName())) {
Intent intentAlive = new Intent(this, LocalService_NoKip.class);
startService(intentAlive);
}
}
}
别忘了在manifest里面配置
<activity
android:name=".kepplive.KeepLiveActivity"
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:theme="@style/SingleActivityStyle" />
用到的主题theme
<style name="SingleActivityStyle">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="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:keepScreenOn">true</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowNoDisplay">false</item>
</style>
对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/9/22 9:41
* Desc : 对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类
*/
public class ScreenManager {
public static final String TAG="ScreenManager";
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;
}
/**
* 获得 KeepLiveActivity 的引用
*/
public void setSingleActivity(Activity activity) {
mActivity = new WeakReference<>(activity);
}
/**
* 启动 KeepLiveActivity
*/
public void startActivity() {
Intent intent = new Intent(mContext, KeepLiveActivity.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();
}
}
}
}
锁屏解锁的广播动态注册监听
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/9/22 9:40
* Desc : 锁屏解锁的广播动态注册监听
*/
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();
}
}
}
}
一像素的知识完事了,然后去 AIDL
AIDL
// Declare any non-default types here with import statements
interface GuardAidl {
//相互唤醒服务
void wakeUp();
}
manifest文件配置
<service android:name=".kepplive.LocalService" />
<service
android:name=".kepplive.RemoteService"
android:process=":remote">
</service>
<service
android:name=".kepplive.JobHandlerService"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".kepplive.HideForegroundService" />
开始使用
在需要开启的地方调用:KeepLive.startWork(context);
结束定位调用:KeepLive.stop(context);
最后我这里有一个手机电池优化的方法,就是app在锁屏状态下,不因为电池优化策略而被杀死
//优化电池
public void setPower(Context context) {
boolean isIgnoring = false;
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
if (powerManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
isIgnoring = powerManager.isIgnoringBatteryOptimizations(context.getPackageName());//是否进行了电池优化处理
}
}
if (!isIgnoring) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
} catch (Exception e) {
}
}
}
}
最后我又实现了一个逻辑,就是在15S杀死得情况下,不调用进程保活,要是超出15S的话,才调佣保活机制,这样的话,体验更好点,不至于说关不掉,呵呵哈哈哈或
在Application中的onCreate()里面调佣getAPPTime();
private void getAPPTime() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
Log_Ma.e(TAG, "onActivityCreated: " + getAcName(activity));
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
Log_Ma.e(TAG, "onActivityStopped: " + getAcName(activity));
count--;
if (count == 0) {
mTime = System.currentTimeMillis();
Log_Ma.e(TAG, ">>>>>>>>>>>>>>>>>>>切到后台");
}
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
Log_Ma.e(TAG, "onActivityStarted: " + getAcName(activity));
if (count == 0) {
Log_Ma.e(TAG, ">>>>>>>>>>>>>>>>>>>切到前台");
}
count++;
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
// Log_Ma.e(TAG, "onActivityResumed: " + getName(activity));
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
// Log_Ma.e(TAG, "onActivityPaused: " + getName(activity));
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
// Log_Ma.e(TAG, "onActivitySaveInstanceState: " + getName(activity));
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
Log_Ma.e(TAG, "onActivityDestroyed: " + getAcName(activity));
if (count == 0) {
String name = activity.getClass().getSimpleName();
if (TextUtils.equals(name, Activity_Main_2.class.getSimpleName())) {
if (System.currentTimeMillis() - mTime < 1000 * 15) {
KeepLive.stop(activity);
}
}
}
}
private String getAcName(Activity activity) {
return activity.getPackageName() + " / " + activity.getClass().getSimpleName();
}
});
}