说起android 保活真是一把鼻涕一把泪,由于国内厂商对系统的大刀阔斧各种变态的省电机制,真不知道当初自己为什么会写android T.T …………………
虽然谷歌爸爸在每个Android版本都在手机电量方面努力进行优化,不过还是没有一个解决后台服务泛滥电量消耗过快的很好的办法(PS:骚一句就是之所以android手机越来越卡就是因为很多APP在后台耍流氓,导致你根本杀不掉进程,然后手机的运行内存越来越小,最后就是卡卡卡),于是Android 6.0系统中,引入了DOZE模式既在设备未接通电源且屏幕关闭了一段时间以后,系统会对CPU,网络,Alarm等活动进行限制,从而延长电池的使用寿命,当然系统不会一直处于DOZE模式下,会间歇性的退出DOZE模式来执行同步,Alarm任务。
目前我是用的方案是一个像素+前台服务+jobScheduler 在权限给足的情况 能在小米和华为6.0、8.0的系统上苟延残喘。
一个像素的方案网上也非常多不做过多的解释,就是在屏幕关闭的时候打开一个1px的透明的activity据说是QQ的保活方案,屏幕开启的时候再去finsh掉这个activty即可,大致代码如下:
/**
* Created by zkx on 2018/4/24.
* 一个像素的保活界面
*/
public class LiveActivity extends BaseActivity {
public static final String TAG="LiveActivity";
private BroadcastReceiver endReceiver=null;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
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);
//结束该页面的广播
endReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
registerReceiver(endReceiver, new IntentFilter("finish"));
//检查屏幕状态
checkScreen();
}
@Override
protected void onResume() {
super.onResume();
checkScreen();
}
/**
* 检查屏幕状态 isScreenOn为true 屏幕“亮”结束该Activity
*/
private void checkScreen() {
PowerManager pm = (PowerManager) LiveActivity.this.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
if (isScreenOn) {
finish();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (endReceiver!=null){
unregisterReceiver(endReceiver);
}
}
}
当然还需要在设置1像素Activity的样式
广播的代码:
/**
* Created by zkx on 2018/4/24.
*/
public class LiveBroadcastReceiver extends BroadcastReceiver {
public static final String TAG="LiveActivity";
private KeepLiveManager mKeepLiveManager;
private HomeActivity mHomeActivity;
public LiveBroadcastReceiver(HomeActivity homeActivity){
this.mHomeActivity=homeActivity;
mKeepLiveManager=mHomeActivity.getKeepLiveManger();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()){
case Intent.ACTION_SCREEN_OFF://屏幕被关闭
Intent it=new Intent(context, LiveActivity.class);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(it);
break;
case Intent.ACTION_SCREEN_ON://屏幕被打开
context.sendBroadcast(new Intent("finish"));
/**
* 以下的代码会导致屏幕解锁后会出现返回主界面的情况
*/
// Intent main = new Intent(Intent.ACTION_MAIN);
//// main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// //注释掉这段避免在再次打开APP界面时出现的返回主界面的问题
// //main.addCategory(Intent.CATEGORY_HOME);
// context.startActivity(main);
break;
}
}
}
这个太简单了就是直接启动一个服务与MainActivty绑定即可,这里就不再多说啦
重点介绍下JobScheduler
Android从5.0开始,增加支持一种特殊的机制,即任务调度JobScheduler,该工具集成了常见的几种运行条件,开发者只需添加少数几行代码,即可完成原来要多种组件配合的工作。比如在无线网络状态下执行自动下载更新包或者进行数据同步等等任务都可以通过JobScheduler来进行设置。
This is an API for scheduling various types of jobs against the framework that will be executed in your application’s own process.
See JobInfo for more description of the types of jobs that can be run and how to construct them. You will construct these JobInfo objects and pass them to the JobScheduler with schedule(JobInfo). When the criteria declared are met, the system will execute this job on your application’s JobService. You identify which JobService is meant to execute the logic for your job when you create the JobInfo with JobInfo.Builder(int, android.content.ComponentName).
The framework will be intelligent about when you receive your callbacks, and attempt to batch and defer them as much as possible. Typically if you don’t specify a deadline on your job, it can be run at any moment depending on the current state of the JobScheduler’s internal queue, however it might be deferred as long as until the next time the device is connected to a power source.
You do not instantiate this class directly; instead, retrieve it through Context.getSystemService(Context.JOB_SCHEDULER_SERVICE).
biubiubiu 我看不太懂,大致的意识应该将JobInfo传递给JobScheduler来执行任务,然后我们可以通过Context.getSystemService(Context.JOB_SCHEDULER_SERVICE). 来获取jobScheduler.
再看jobinfo
看起来好像很厉害的样子,就是你要干嘛数据封装到这个对象中然后传递过去就好啦。哎呀这种解释自己去翻文档就好了直接上代码了!
大体代码如下咯:
/**
* Created by zkx on 2018/4/24.
* 用于开启程序的保活系列
*/
public class KeepLiveManager {
public static final String MESSENGER_INTENT_KEY
= BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY";
//消息
public static final int MSG_KEEP_LIVE_START = 0;
public static final int MSG_KEEP_LIVE_STOP = 1;
public static final int MSG_RELEASE_WAKE_LOCK = 2;
// public static final int MSG_COLOR_STOP = 3;
// public static final String MESSENGER_INTENT_KEY
// = BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY";
// public static final String WORK_DURATION_KEY =
// BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY";
private int mJobId = 0;
// Handler for incoming messages from the service.
// 用于来自服务的传入消息的处理程序。
private IncomingMessageHandler mHandler;
private ComponentName mServiceComponent;
private Context mContext;
public KeepLiveManager(Context context){
mHandler =new IncomingMessageHandler(this);
mServiceComponent =new ComponentName(context,JobSchedulerService.class);
this.mContext=context;
}
private PowerManager.WakeLock wakeLock = null;
/**
* 获取电源锁,保持该服务在屏幕熄灭时仍然获取CPU时,保持运行
*/
private void acquireWakeLock() {
if (null == wakeLock) {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, getClass()
.getCanonicalName());
if (null != wakeLock) {
wakeLock.acquire();
}
}
}
// 释放设备电源锁
private void releaseWakeLock() {
if (null != wakeLock && wakeLock.isHeld()) {
wakeLock.release();
wakeLock = null;
}
}
/**
* 提供调用停止工作服务
*/
public void stopService(){
mContext.stopService(new Intent(mContext, JobSchedulerService.class));
}
/**
* 提供调用开始工作服务
*/
public void startSetvice(){
Intent startServiceIntent = new Intent(mContext, JobSchedulerService.class);
Messenger messengerIncoming = new Messenger(mHandler);
startServiceIntent.putExtra(MESSENGER_INTENT_KEY,messengerIncoming);
mContext.startService(startServiceIntent);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void scheduleJob() {
//开始配置JobInfo
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
builder.setRequiresCharging(false); //是否在充电时执行
builder.setRequiresDeviceIdle(false); //是否在空闲时执行
builder.setMinimumLatency(2000);//延迟执行
builder.setOverrideDeadline(3000);
builder.setPersisted(false);//是否在设备重启的时候执行任务
JobScheduler tm = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(builder.build());
//tm.schedule(builder.build())会返回一个int类型的数据
//如果schedule方法失败了,它会返回一个小于0的错误码。否则它会返回我们在JobInfo.Builder中定义的标识id。
}
/**
* Executed when user clicks on CANCEL ALL.
*
* 当用户点击取消所有时执行
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void cancelAllJobs() {
JobScheduler tm = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.cancelAll();
// Toast.makeText(m, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show();
}
private static class IncomingMessageHandler extends Handler {
private WeakReference mKeepLiveManager;
private static final long RELEASE_WAKE_LOCK_DELY = 1000;
public IncomingMessageHandler(KeepLiveManager keepLiveManager){
mKeepLiveManager=new WeakReference<>(keepLiveManager);
}
@Override
public void handleMessage(Message msg) {
KeepLiveManager manager = mKeepLiveManager.get();
if (manager == null) {
return;
}
super.handleMessage(msg);
if (manager==null){
return;
}
switch (msg.what) {
case MSG_KEEP_LIVE_START:
// ApplicationUtil.speak("任务执行了" + manager.mJobId + "次");
manager.acquireWakeLock();
// manager.finishJob();
// manager.sechedulerJob();
sendEmptyMessageDelayed(MSG_RELEASE_WAKE_LOCK, RELEASE_WAKE_LOCK_DELY);
break;
case MSG_KEEP_LIVE_STOP:
// manager.finishJob();
break;
case MSG_RELEASE_WAKE_LOCK:
manager.releaseWakeLock();
break;
}
}
}
}
这3个一起上可以存活率还是可以,但是目前没有哪个APP可以做到百分百保活,当然除了白名单用户比如QQ,微信这种爸爸特别有钱的
唉~ 但是在华为7.0的系统上虽然保活成功了但是任务执行还是延迟的非常厉害的,既然已经耍流氓了,那就一耍到底,我在我的前台服务中开启了soundPool然后间隔4S播放一个空音效,因为是一个任务型的APP在执行任务时候必须存活,所以耗电量还是勉强可以!!!!
哈哈哈哈!耍流氓成功!