我想创建一个服务并使它在前台运行。
大多数示例代码都带有通知。但我不想显示任何通知。那可能吗?
能给我一些例子吗?还有其他选择吗?
我的应用程序服务正在执行mediaplayer。如何使系统不会杀死我的服务,但应用程序会杀死它本身(例如通过按钮暂停或停止音乐)。
我想创建一个服务并使它在前台运行。
大多数示例代码都带有通知。但我不想显示任何通知。那可能吗?
能给我一些例子吗?还有其他选择吗?
我的应用程序服务正在执行mediaplayer。如何使系统不会杀死我的服务,但应用程序会杀死它本身(例如通过按钮暂停或停止音乐)。
Answers:
作为Android平台的安全功能,在任何情况下都不能在没有通知的情况下获得前台服务。这是因为与前台服务相比,前台服务会消耗大量资源,并且受制于不同的调度约束(即不会很快消失),并且用户需要知道可能在消耗什么电量。所以,不要这样做。
但是,它是可能有一个“假”的通知,也就是说,你可以做一个透明的通知图标(IIRC)。这对您的用户来说是非常不诚实的,并且您没有理由这样做,除了杀死他们的电池从而制造恶意软件。
更新:这已在Android 7.1上“修复”。 https://code.google.com/p/android/issues/detail?id=213309
从4.3版开始,从根本上讲,startForeground()
如果不显示通知就无法启动服务。
但是,您可以使用官方API隐藏图标...不需要透明图标:(NotificationCompat
用于支持旧版本)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);
我对通知本身仍然需要存在的事实感到安心,但对于仍然想隐藏它的人,我可能也找到了解决方法:
startForeground()
使用通知和所有内容启动虚假服务。startForeground()
(相同的通知ID)启动要运行的真实服务stopSelf()
,也可以在onDestroy调用中stopForeground(true)
)。瞧!根本没有任何通知,您的第二项服务保持运行。
从Android 7.1开始,该功能不再起作用,并且可能违反了Google Play的开发者政策。
而是让用户阻止服务通知。
public class ForegroundService extends Service {
static ForegroundService instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
}
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
public class ForegroundEnablingService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ForegroundService.instance == null)
throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");
//Set both services to foreground using the same notification id, resulting in just one notification
startForeground(ForegroundService.instance);
startForeground(this);
//Cancel this service's notification, resulting in zero notifications
stopForeground(true);
//Stop this service so we don't waste RAM.
//Must only be called *after* doing the work or the notification won't be hidden.
stopSelf();
return START_NOT_STICKY;
}
private static final int NOTIFICATION_ID = 10;
private static void startForeground(Service service) {
Notification notification = new Notification.Builder(service).getNotification();
service.startForeground(NOTIFICATION_ID, notification);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />
经过测试并致力于:
从Android 7.1起不再可用。
ForegroundService
您可以使用它(@Kristopher Micinski建议):
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );
更新:
请注意,Android KitKat +版本不再允许这样做。并且请记住,这或多或少违反了Android的设计原则,如@Kristopher Micinski所述,使用户可以看到后台操作
警告:尽管此答案似乎有效,但实际上它默默地阻止您的服务成为前台服务。
原始答案:
只需将您的通知ID设置为零即可:
// field for notification ID
private static final int NOTIF_ID = 0;
...
startForeground(NOTIF_ID, mBuilder.build());
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.cancel(NOTIF_ID);
...
您可以得到的好处是Service
,除非内存压力很大,否则a可以在不被Android系统破坏的情况下以高优先级运行。
编辑
要使其与Pre-Honeycomb和Android 4.4及更高版本一起使用,请确保使用NotificationCompat.Builder
Support Library v7(而不是)提供的支持Notification.Builder
。
Notification.Builder
,支持库v4NotificationCompat.Builder
和支持库v7 NotificationCompat.Builder
。通知没有出现在通知抽屉或状态栏中,但是当我使用getRunningServices()
和检查服务时,该服务未在前台模式下运行dumpsys
。
adb shell dumpsys activity services
来验证。
您可以通过使用自定义布局(在layout_height =“ 0dp”下)在Android 9+上隐藏通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);
custom_notif.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp">
</LinearLayout>
已在Android 9 Pixel 1上进行了测试。此解决方案在Android 8或更低版本上不起作用
更新:此功能不再适用于Android 4.3及更高版本
有一种解决方法。尝试创建没有设置图标的通知,通知将不会显示。不知道它是如何工作的,但是它是有效的:)
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Title")
.setTicker("Title")
.setContentText("App running")
//.setSmallIcon(R.drawable.picture)
.build();
startForeground(101, notification);
(YourServiceName) is running
当您未指定图标时,Android会显示通知而不是您自己的通知。根据CommonsWare的说法,自Android 4.3开始就是这种情况:commonsware.com/blog/2013/07/30/…–
更新:此功能不再适用于Android 4.3及更高版本
我将icon参数设置为Notification的构造函数,并将其设置为零,然后将结果通知传递给startForeground()。日志中没有错误,也没有通知显示。不过,我不知道该服务是否已成功推出-是否可以检查?
编辑:经过dumpsys的检查,并且确实该服务在我的2.3系统上处于前台。尚未检查其他操作系统版本。
Notification()
构造函数。
此处的大多数答案都不起作用,破坏前台服务或违反Google Play政策。
可靠且安全地隐藏通知的唯一方法是让用户阻止该通知。
唯一的方法是阻止来自应用程序的所有通知:
将用户发送到应用程序的详细信息屏幕:
Uri uri = Uri.fromParts("package", getPackageName(), null);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
startActivity(intent);
让用户阻止应用程序的通知
请注意,这也会阻止您的应用程序的敬酒。
在Android O上阻止通知是不值得的,因为OS只会将其替换为“在后台运行”或“使用电池”通知。
使用通知通道来阻止服务通知,而不会影响您的其他通知。
将用户发送到通知频道的设置
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
startActivity(intent);
让用户屏蔽频道的通知
版本4.3(18)及更高版本无法隐藏服务通知,但您可以禁用该图标,版本4.3(18)及以下版本可以隐藏通知
Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);
我发现在Android 8.0上,不使用通知通道仍然可以实现。
public class BootCompletedIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(context, BluetoothService.class);
context.startForegroundService(notificationIntent);
} else {
//...
}
}
}
}
并在BluetoothService.class中:
@Override
public void onCreate(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(this, BluetoothService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle("Title")
.setContentText("App is running")
.setSmallIcon(R.drawable.notif)
.setContentIntent(pendingIntent)
.setTicker("Title")
.setPriority(Notification.PRIORITY_DEFAULT)
.build();
startForeground(15, notification);
}
}
系统不会显示永久性通知,但是您会看到Android“ x应用正在后台运行”通知。
apps are running in the background
通知甚至比针对特定应用的通知更糟糕,因此我不确定这种方法是否值得。
更新:此功能不再适用于Android 7.1及更高版本
这是使您的应用的oom_adj变为1的方法(在ANDROID 6.0 SDK仿真器中测试)。添加临时服务,在您的主要服务呼叫中startForgroundService(NOTIFICATION_ID, notificion)
。然后startForgroundService(NOTIFICATION_ID, notificion)
,在临时服务调用stopForgroundService(true)中一段时间后,以相同的通知ID再次启动临时服务调用以消除持续更新。
您也可以将应用程序声明为持久性的。
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/Theme"
*android:persistent="true"* >
</application>
从本质上讲,这会将您的应用设置为更高的内存优先级,从而降低了该应用被杀死的可能性。
几个月前,我开发了一个简单的媒体播放器。所以我相信如果您正在执行以下操作:
Intent i = new Intent(this, someServiceclass.class);
startService(i);
然后,系统将无法终止您的服务。