借鉴了2篇文章:1像素的Activity让应用在息屏后保活 , Android保证service不被杀掉-增强版: 进程保活(根据用户需求慎用)
关于周期网上有好多文章都是提到了“不死的服务”。很多文章提到了做出一个不死的服务。具体提到的方式有:
onStartCommand方法,返回START_STICKY
也就是在service的onstartcommand函数里返回这个值
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
flags = START_STICKY;
return super.onStartCommand(intent, flags, startId);
}
这个时候如果清理的是一个设置了START_STICKY的app,用任务管理器(方法-)还是在设置里面应用管理杀死服务(方法二)都会被杀掉,后来发现在清理软件里吧这个应用加入白名单之后。在使用方法一的时候。acrivity被清理掉了,但是服务不会被清理。,使用方法二的时候,服务会被关闭,然后一段时间又会重新自动出现。即START_STICKY产生的效果。
让你的应用在任务管理器中不显示出来
在配置mainActivity的时候加上这句就可以实现
android:excludeFromRecents="true"
指定应用程序的一些组件运行在不同的进程中
android:process
该进程属性可用于activities、services、content providers和broadcast receivers 和指定的进程中应该执行的特定组件。
在这个例子中,我指定MusicService必须执行在一个单独的“music”的进程:
<manifest ...>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Main" >
<activity
android:name=".MusicActivity"
/>
<service
android:name=".MusicService"
android:process=":music"
/>
</application>
</manifest>
它有什么意义呢?
在这个简短的介绍中,我提到了每一个Android应用程序在运行的时候都有一个不能超出的内存预算值。更精确的说,这限制了它只能在单个基础的进程上运行。换句话说,应用程序的每一个进程都将会有一个专门的内存预算(更不用说其中止时也有更酷的不同的规则)
一个像素保活
原理:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。
适用范围
适用场景: 本方案主要解决第三方应用及系统管理工具在检测到锁屏事件后一段时间(一般为5分钟以内)内会杀死后台进程,已达到省电的目的问题。
适用版本: 适用于所有的 Android 版本。
具体实现
首先在你的不死Service中注册屏幕亮灭的广播
代码:
package com.luuuzi.notkillservice;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.Log;
/**
* 需要一直运行的服务,在后台服务去开启广播
* Created by Administrator on 2017/11/13.
*/
public class BackService extends Service {
private String tag = "tag";
private MyBroadCastReceiver receiver;
//NotificationManager notificationManager;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onCreate() {
Log.i(tag,"BackService:启动广播");
//动态注册广播接收者
receiver=new MyBroadCastReceiver();
IntentFilter filter=new IntentFilter();
//添加屏幕熄灭时的action
filter.addAction(Intent.ACTION_SCREEN_OFF);
//添加屏幕亮起时的action
filter.addAction(Intent.ACTION_SCREEN_ON);
//注册广播,并广播内容
registerReceiver(receiver,filter);
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(tag, "onstart方法");
//销毁广播
//unregisterReceiver(receiver);
return Service.START_STICKY;
}
@Override
public void onDestroy() {
Log.i(tag,"BackService:onDestroy销毁广播");
unregisterReceiver(receiver);
super.onDestroy();
}
}
然后在监听屏幕亮,灭的广播中写入以下代码
package com.luuuzi.notkillservice;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* 接收系统发来的广播
* 监听屏幕熄灭和亮起的广播
* Created by Administrator on 2017/11/13.
*/
public class MyBroadCastReceiver extends BroadcastReceiver {
private String tag = "MyBroadCastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
Intent intent1 = new Intent(context, OnePxActivity.class);
/**
* //activity继承了context重载了startActivity方法,如果使用acitvity中的startActivity,不会有任何限制。
// Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want
而如果直接使用context的startActivity则会报上面的错误,
// 根据错误提示信息, 可以得知, 如果要使用这种方式需要打开新的TASK。
*
*/
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
Log.i(tag, "MyBroadCastReceiver:屏幕熄灭,");
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
//屏幕亮起时,需要关闭一个像素的Activity,
// 同样是通过广播去传递然后关闭
Intent intent2 = new Intent("FinishActivity");
//发送无序广播
context.sendBroadcast(intent2);//发送对应的广播
Log.i(tag, "MyBroadCastReceiver:屏幕亮起");
}
}
}
之后就是一个像素的Activity中编写,在这个Activity中同样需要注册一个广播用于监听接收,屏幕亮起后的广播,然后finish(),一个像素的Activity
package com.luuuzi.notkillservice;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
/**
* 1像素的Activity
* Created by Administrator on 2017/11/13.
*/
public class OnePxActivity extends Activity{
private String tag="OnePxActivity";
//创建一个广播接收者,用于接收广播来关闭Activity
private BroadcastReceiver receiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(tag,"监听屏幕亮起后关闭一个像素的Activity");
//收到广播后关闭当前的Activity
OnePxActivity.this.finish();
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取当前Activity页面的窗体
Window window=getWindow();
//设置属性参数
window.setGravity(Gravity.LEFT |Gravity.TOP);
WindowManager.LayoutParams params=window.getAttributes();
params.x=0;
params.y=0;
params.width=1;
params.height=1;
//给窗体添加属性
window.setAttributes(params);
//动态注册广播,通过该广播来关闭当前Activity(即一个像素的Activity)
registerReceiver(receiver,new IntentFilter("FinishActivity"));
Log.i(tag,"==========OnePxActivity:onCreate");
}
@Override
protected void onDestroy() {
//销毁广播
unregisterReceiver(receiver);
Log.i(tag,"=========OnePxActivity:onDestroy");
super.onDestroy();
}
}
<activity android:name=".OnePxActivity"
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:process=":process"
android:theme="@style/undeadActivityStyle" />
主题配置
<!--1像素的Activity配置的主题-->
<style name="undeadActivityStyle">
<!-- 背景设置为透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<!-- 是否有边框 -->
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<!-- 是否浮动在界面上 -->
<item name="android:windowIsFloating">true</item>
<!-- 是否透明 :透明-->
<item name="android:windowIsTranslucent">true</item>
<!-- 窗体上面是否有遮盖 无遮盖 -->
<item name="android:windowContentOverlay">@null</item>
<!-- 为窗体的Enter和Exit设置动画 -->
<item name="android:windowAnimationStyle">@null</item>
<!-- 是否禁止窗体显示前显示的View -->
<item name="android:windowDisablePreview">true</item>
<item name="android:windowNoDisplay">false</item>
</style>
最后去开启你的一直运行的后台服务
//开启服务
Log.i(tag,"开启服务");
Intent intent = new Intent(MainActivity.this, BackService.class);
startService(intent);
以上就实现了屏幕锁屏后应用不会被杀死掉的逻辑
方法2
让通过notification挂起在通知栏上,实现前台的服务
1.创建长期运行Service
/**
* 需要一直运行的服务,在后台服务去开启广播
* 创建一个通知,让其长期在后台运行
* Created by Administrator on 2017/11/13.
*/
public class BackService extends Service {
private ScreenReceiver receiver;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onCreate() {
//将通知栏挂起
Intent intent = new Intent();
startForeground(10, createNotification(getApplicationContext()));
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
/**
* 创建通知
*
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public Notification createNotification(Context context) {
// 1.通过上下文对象吧获取通知的管理者
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//2.定义通知,链式调用(返回值一直都是自己就可以链式调用) 高版本的写法
Notification notification = new Notification.Builder(context)
.setContentTitle("好实再联盟") //标题
.setContentText("语音播报已开启") //内容
.setSmallIcon(R.mipmap.logo) //图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.logo)).build();
//notification.flags = Notification.FLAG_NO_CLEAR|Notification.FLAG_ONGOING_EVENT;
//设置标记
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
return notification;
}
}
2,创建一个接收系统广播的广播接收者
/**
* 接收系统发来的广播,然后去判断后台进程是否被杀死,
* 如果被杀死了则重启Service
* Created by Administrator on 2017/11/13.
*/
public class MyBroadCastReceiver extends BroadcastReceiver {
private String tag = "MyBroadCastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
//记录是否被杀死,如果被杀死了,咋重新启动
boolean isServiceRunning = false;
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.hsz88.merchant.service.BackService".equals(service.service.getClassName())) {
Log.i(tag, "服务没被杀死");
isServiceRunning = true;
}
}
if (!isServiceRunning) {
Log.i(tag, "服务被杀死了");
Intent i = new Intent(context, BackService.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(i);
}
}
}
3.启动Service和BroadCastReceiver
/**
* 启动Service
*/
//开启服务
Log.i(tag, "开启服务");
Intent intent = new Intent(mContext, BackService.class);
startService(intent);
//启动监听系统时间广播
receiver = new MyBroadCastReceiver();
IntentFilter filter = new IntentFilter();
//监听系统广播,该广播一分钟发一次
filter.addAction(Intent.ACTION_TIME_TICK);
//启动该广播
registerReceiver(receiver, filter);