Gur*_*thi 5 android android-8.0-oreo
Android Oreo对运行后台服务施加了很多限制.现在服务在Oreo中的表现与以前一样.
但是,如果我必须在后台运行很长一段时间的服务,该怎么办呢?
我正在开发一个应用程序,当用户摇动手机时启动手电筒.为此,我必须将Sensor侦听器代码放在服务中.
如何防止android系统不杀死服务.
PS:我不想通过通知启动前台服务.
Com*_*are 11
如何防止android系统不杀死服务.
总结评论:使用前台服务,在专用频道上发布通知,并将频道设置为IMPORTANCE_DEFAULT.建议用户可以将该通道静音(例如,长按Notification通知阴影中的).使用专用频道意味着您仍然可以在其他频道上发送通知.您的通知也应该有用:
如果用户想要将其关闭一段时间,请执行"停止"操作以停止服务
点击通知本身会导致您配置应用行为的活动
我不想通过通知启动前台服务.
然后很可能你不能写你的应用程序.
我不能排除Android 8.x中可能被利用来获得无限期服务的一些错误的可能性.事实上,我认为很可能会有一些东西漂浮在那里.但是,这显然违背了谷歌的意图,这意味着:
利用这种技术,没有谷歌认为有效的理由,可能会让你的应用程序被Play商店禁止,如果这是你计划分发的方式
这个漏洞可能会在Android的未来版本中修复,与谷歌的军备竞赛往往是一个失败的主张
有足够的"空中手势"应用程序浮动(即,根据摇动做事情),理想情况下,谷歌会为它添加一些专用的低功耗API.例如,他们可以添加功能以JobScheduler允许您注册摇动事件并JobService在这种情况下调用,就像它们允许您注册a中的更改一样ContentProvider.我不知道他们是否会提供这样的API,但如果你愿意,你可以为它提交功能请求.
在 Oreo 或更高版本上使服务不可阻挡,而无需显示通知是可能的(是的,我们可以)。
让我来解释一下如何使服务只能由用户而不是系统停止(或者更好地说停止它们的唯一方法是卸载您的应用程序)。
请注意,即使我在我看来使服务不可阻挡也不是一个好技术,并且出于不同的原因(例如电池消耗、清晰的用户体验等),我对此持反对意见。
首先,您需要在清单文件中声明服务。
单独的名称“:serviceNonStoppable”使服务在单独的进程中运行,而不是在主应用程序进程中运行。更适合需要单独运行的后台进程。为了让我们自己的服务进程对其他进程或应用程序不可见,您需要设置exported=false 参数。描述“@string/service_description”会告诉用户你的服务做什么以及为什么用户不应该停止他们(你在strings.xml中创建这个描述)。
<service
android:process=":serviceNonStoppable"
android:name="your.package.name.serviceOn"
android:exported="false"
android:description="@string/service_description" />
Run Code Online (Sandbox Code Playgroud)
其次,我们去创建一个支持类,其中包含可用于不同点的静态方法。
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
class Utils {
// This is a support class witch have static methods to use everywhere
final static int NOTIFICATION_INT_CHANNEL_ID = 110211; // my daughter birthday but you can change that with your number
final static String NOTIFICATION_STRING_CHANNEL_ID = "put.a.random.id.here"; //if you write "the.pen.is.on.the.table" is the same
final static int TEST_THIS = 111; // or you can put here something else
final static String BROADCAST_MSG_ID = "BROADCAST_MSG_ID"; // or you can put here something else
final static String APP_MESSAGE = "your.package.name.action.APP_MESSAGE"; // or you can put here pippo.pluto.and.papperino
static void returnUpMyService(final Context context) {
try {
//to avoid crashes when this method is called by service (from itself) make sure the service is not alredy running (maybe is in cache)
if (killServiceIfRun(context)) {
startServiceOn(context);
}
} finally {
System.out.println(" I'm trying to start service ");
}
}
private static boolean killServiceIfRun(final Context context) {
boolean isRunning = isMyServiceRunning(context);
if (!isRunning) { return true; }
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// maybe killing process is not terminated by system in this fase
//I force to kill them by my one
if (manager != null) {
manager.killBackgroundProcesses(getServicename(context));
return true;
}
return true;
} catch (Exception e) {
System.out.println("killServiceIfRun error: " + e.toString());
}
return false;
}
private static boolean isServiceInCache(final Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null && manager.getRunningAppProcesses() != null) {
if (manager.getRunningAppProcesses().size() > 0) {
for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
if (process.processName != null) {
if (process.processName.equalsIgnoreCase(getServicename(context))) {
// Here we know that the service is running but sleep brrrrrrrr
if (process.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
return true;
}
}
}
}
}
}
return false;
}
static void StartMyService(Context context) {
// If the sevice is running doesn't need to restart
if (isMyServiceRunning(context) && !isServiceInCache(context)) {
return;
}
// If service is running but is in chache is the same like killed, so we need to kill them
if (isServiceInCache(context)) {
// this method at first kill and after that start the service
returnUpMyService(context);
} else {
//Otherwise we start own service
startServiceOn(context);
}
}
private static void startServiceOn(final Context context) {
// After we had been sure about that service doesn't exist
// we make a schedule to restart them
new ScheduledThreadPoolExecutor(1).schedule(() -> {
//Create an instance of serviceOn
serviceOn service = new serviceOn();
//prepare the launch intent
Intent launchIntent = new Intent(context, service.getClass());
// Now we start in background our service
context.startForegroundService(launchIntent);
// I put 50 ms to allow the system to take more time to execute GC on my killed service before
}, 50, TimeUnit.MILLISECONDS);
}
private static boolean isMyServiceRunning(final Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null && manager.getRunningAppProcesses() != null) {
if (manager.getRunningAppProcesses().size() > 0) {
for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
if (process != null && process.processName != null && process.processName.equalsIgnoreCase(getServicename(context))) {
return true;
}
}
}
}
return false;
}
static void SendMsgToService(Context context, int id, Map<String, Object> params) {
try {
Intent mServiceIntent = new Intent(APP_MESSAGE);
if (params != null) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
//System.out.println(entry.getKey() + "/" + entry.getValue());
if (entry.getValue() instanceof String) {
mServiceIntent.putExtra(entry.getKey(), (String) entry.getValue());
} else if (entry.getValue() instanceof Integer) {
mServiceIntent.putExtra(entry.getKey(), (Integer) entry.getValue());
} else if (entry.getValue() instanceof Float) {
mServiceIntent.putExtra(entry.getKey(), (Float) entry.getValue());
} else if (entry.getValue() instanceof Double) {
mServiceIntent.putExtra(entry.getKey(), (Double) entry.getValue());
} else if (entry.getValue() instanceof byte[]) {
mServiceIntent.putExtra(entry.getKey(), (byte[]) entry.getValue());
}
}
}
mServiceIntent.putExtra(BROADCAST_MSG_ID, id);
context.sendBroadcast(mServiceIntent);
} catch (RuntimeException e) {
System.out.println(e.toString());
}
}
private static String getServicename(final Context context) {
// the name declared in manifest you remember?
return context.getPackageName() + ":serviceNonStoppable";
}
}
Run Code Online (Sandbox Code Playgroud)
这是扩展 IntentService 的服务类。
import android.app.IntentService;
import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class serviceOn extends IntentService {
// Needed to keep up notifying without show the icon
private ScheduledExecutorService notifyer = null;
// don't remove this. cause error becouse we declare this service in manifest
public serviceOn() {
super("put.a.constant.name.here");
}
// We need this class to capture messages from main activity
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, Intent intent) {
if (intent != null) {
if (intent.getAction() != null) {
if (intent.getAction().equals(Utils.APP_MESSAGE)) {
int msgID = intent.getIntExtra(Utils.BROADCAST_MSG_ID, -1);
switch (msgID) {
case Utils.TEST_THIS:
String message = intent.getStringExtra("message");
if (!TextUtils.isEmpty(message)) {
System.out.println(message);
}
//Do your task here
//Do your task here
//Do your task here
//Do your task here
break;
}
}
}
}
}
};
@Override
protected void onHandleIntent(@Nullable Intent intent) { }
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
try {
// First of all we need to register our receiver
List<String> actions = Arrays.asList(
Utils.APP_MESSAGE, // this is the string which identify our mesages
Intent.ACTION_SCREEN_ON, // this event is raised on sreen ON by system
Intent.ACTION_SCREEN_OFF, // this event is raised on screen OFF by system
Intent.ACTION_TIME_TICK);// this event is raised every minute by system (helpful for periodic tasks)
for (String curIntFilter : actions) {
IntentFilter filter = new IntentFilter(curIntFilter);
registerReceiver(broadcastReceiver, filter);
}
} catch (RuntimeException e) {
e.printStackTrace();
}
final Notification notificationDefault = new NotificationCompat.Builder(getApplicationContext(), Utils.NOTIFICATION_STRING_CHANNEL_ID)
.setOngoing(true) //Ongoing notifications do not have an 'X' close button, and are not affected by the "Clear all" button
.setCategory(Notification.CATEGORY_SERVICE) // indicate this service is running in background
.setSmallIcon(R.drawable.ic_radio) // put here a drawable from your drawables library
.setContentTitle("My Service") // Put here a title for the notification view on the top
// A smaller explanation witch system show to user this service is running
// in background (if existing other services from other apps in background)
.setContentText("My Service is unstoppable and need to run in background ")
.build();
// This is an efficient workaround to lie the system if we don't wont to show notification icon on top of the phone but a little aggressive
notifyer = Executors.newSingleThreadScheduledExecutor();
notifyer.scheduleAtFixedRate(() -> {
try {
// Here start the notification witch system need to permit this service to run and take this on.
// And we repeat that task every 15 seconds
startForeground(Utils.NOTIFICATION_INT_CHANNEL_ID, notificationDefault);
//immediately after the system know about our service and permit this to run
//at this point we remove that notification (note that is never shown before)
stopForeground(true);
//better not invoke Exception classes on error, make all a little heavy
} finally {
// Log here to tell you your code is called
System.out.println(" Service is running");
}
// So, the first call is after 1000 millisec, and successively is called every 15 seconds for infinite
}, 1000, 15000, TimeUnit.MILLISECONDS);
}
@Override
public void onDestroy() {
// unregister the receiver
unregisterReceiver(broadcastReceiver);
// stop the notifyer
if (notifyer != null) {
notifyer.shutdownNow();
notifyer = null;
System.out.println(" notifyer.shutdownNow() ");
}
final Context context = getBaseContext();
try {
new Thread() {
@Override
public void run() {
// The magic but dirty part
// When the system detect inactivity by our service decides to put them in cache or kill it
// Yes system you can kill me but I came up stronger than before
Utils.returnUpMyService(context);
}
}.start();
} finally {
System.out.println("You stop me LOL ");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里的用法。
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import java.util.HashMap;
class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Sstart the first time
Utils.StartMyService(this);
// Test after 3 seconds
new Handler().postDelayed(() -> {
Utils.SendMsgToService(X_App.getContext(), Utils.TEST_THIS, new HashMap<String, Object>() {{
put("message", "Hello from main activity");
}});
}, 3000);
}
}
Run Code Online (Sandbox Code Playgroud)