Android - 为服务实现 startForeground?
Posted
技术标签:
【中文标题】Android - 为服务实现 startForeground?【英文标题】:Android - implementing startForeground for a service? 【发布时间】:2011-09-17 20:55:53 【问题描述】:所以我不确定在哪里/如何实现此方法以使我的服务在前台运行。目前我在另一个活动中通过以下方式开始我的服务:
Intent i = new Intent(context, myService.class);
context.startService(i);
然后在 myServices 的 onCreate() 中尝试 startForeground()...?
Notification notification = new Notification();
startForeground(1, notification);
所以是的,我有点迷茫,不确定如何实现这一点。
【问题讨论】:
好吧,这不起作用,至少据我所知,我的服务仍然作为后台服务工作并被杀死。 线程链接到:***.com/questions/10962418/… 【参考方案1】:我会先完全填写Notification
。 Here is a sample project 演示startForeground()
的使用。
【讨论】:
是否可以在不通知的情况下使用 startForeground() ?或者我们可以稍后更新相同的通知吗? 你使用1337
有什么特别的原因吗?
@DoctorOreo:它需要在应用程序中是唯一的,但在设备上不一定是唯一的。我选择了 1337,因为它是1337。 :-)
@JRC 问题很好。是否可以在不通知的情况下使用 startForeground()?
@Snicolas:感谢您指出 android 中的一个缺陷。我会努力解决这个问题。【参考方案2】:
在您的主要活动中,使用以下代码启动服务:
Intent i = new Intent(context, MyService.class);
context.startService(i);
然后在您为onCreate()
服务时,您将构建您的通知并将其设置为前台,如下所示:
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.app_icon)
.setContentTitle("My Awesome App")
.setContentText("Doing some work...")
.setContentIntent(pendingIntent).build();
startForeground(1337, notification);
【讨论】:
@mike 如何从 MainActivity 更新此通知? @Roon13 使用 ID,在本例中为 1337 ...您应该能够构建新通知并使用 ID 调用 startForeground @Roon13 看看这个问题***.com/questions/5528288/… @mikebertiean 如何从 MainActivity 调用 startForeground?进程完成后如何清除 MainActvity 的通知? 别忘了:奥利奥8.1解决方案
我遇到了一些问题,例如 RemoteServiceException,因为最新版本的 Android 的频道 ID 无效。我就是这样解决的:
活动:
override fun onCreate(savedInstanceState: Bundle?)
val intent = Intent(this, BackgroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent)
else
startService(intent)
后台服务:
override fun onCreate()
super.onCreate()
startForeground()
private fun startForeground()
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
createNotificationChannel()
else
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
val notificationBuilder = NotificationCompat.Builder(this, channelId )
val notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(PRIORITY_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(101, notification)
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String
val channelId = "my_service"
val channelName = "My Background Service"
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_HIGH)
chan.lightColor = Color.BLUE
chan.importance = NotificationManager.IMPORTANCE_NONE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
JAVA 等价物
public class YourService extends Service
// Constants
private static final int ID_SERVICE = 101;
@Override
public int onStartCommand(Intent intent, int flags, int startId)
super.onStartCommand(intent, flags, startId);
return START_STICKY;
@Override
public void onCreate()
super.onCreate();
// do stuff like register for BroadcastReceiver, etc.
// Create the Foreground Service
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(PRIORITY_MIN)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build();
startForeground(ID_SERVICE, notification);
@RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(NotificationManager notificationManager)
String channelId = "my_service_channelid";
String channelName = "My Foreground Service";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
// omitted the LED color
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager.createNotificationChannel(channel);
return channelId;
【讨论】:
您可以在您的活动中使用ContextCompat.startForegroundService(Context,Intent)
,这将做正确的事情。 (developer.android.com/reference/android/support/v4/content/…)
如果您的最小 API ,您可能希望使用 .setCategory(NotificationCompat.CATEGORY_SERVICE)
而不是 Notification.CATEGORY_SERVICE
请注意,针对Build.VERSION_CODES.P
(API 级别 28)或更高版本的应用必须请求Manifest.permission.FOREGROUND_SERVICE
的权限才能使用startForeground()
- 请参阅developer.android.com/reference/android/app/…【参考方案4】:
这是我将服务设置为前台的代码:
private void runAsForeground()
Intent notificationIntent = new Intent(this, RecorderMainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notification=new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText(getString(R.string.isRecording))
.setContentIntent(pendingIntent).build();
startForeground(NOTIFICATION_ID, notification);
我需要使用 PendingIntent 构建一个通知,以便我可以从该通知开始我的主要活动。
要删除通知,只需调用 stopForeground(true);
它在 onStartCommand() 中被调用。请参考我的代码:https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java
【讨论】:
如果您删除调用 stopForeground(true) 的通知,您将取消 startforeground 服务 你从哪里调用这个方法?Intent.FLAG_ACTIVITY_NEW_TASK
在PendingIntent
的上下文中无效。【参考方案5】:
除了RAWA回答,本和平代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent)
else
startService(intent)
你可以改成:
ContextCompat.startForegroundService(context, yourIntent);
如果你看一下这个方法,你会发现这个方法为你做了所有的检查工作。
【讨论】:
【参考方案6】:如果你想让 IntentService 成为前台服务
那么你应该像这样覆盖onHandleIntent()
Override
protected void onHandleIntent(@Nullable Intent intent)
startForeground(FOREGROUND_ID,getNotification()); //<-- Makes Foreground
// Do something
stopForeground(true); // <-- Makes it again a normal Service
如何通知?
简单。这里是getNotification()
方法
public Notification getNotification()
Intent intent = new Intent(this, SecondActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
NotificationCompat.Builder foregroundNotification = new NotificationCompat.Builder(this);
foregroundNotification.setOngoing(true);
foregroundNotification.setContentTitle("MY Foreground Notification")
.setContentText("This is the first foreground notification Peace")
.setSmallIcon(android.R.drawable.ic_btn_speak_now)
.setContentIntent(pendingIntent);
return foregroundNotification.build();
深入了解
当服务变成前台服务时会发生什么
发生这种情况
什么是前台服务?
前台服务,
确保用户主动意识到后台正在发生的事情 通过提供通知。
(最重要的是)在内存不足时不会被系统杀死
前台服务用例
Implementing song download functionality in a Music App
【讨论】:
【参考方案7】:在 onCreate() 中为“OS >= Build.VERSION_CODES.O”添加给定的代码服务类
@Override
public void onCreate()
super.onCreate();
.................................
.................................
//For creating the Foreground Service
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
// .setPriority(PRIORITY_MIN)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build();
startForeground(110, notification);
@RequiresApi(Build.VERSION_CODES.O)
private String getNotificationChannel(NotificationManager notificationManager)
String channelId = "channelid";
String channelName = getResources().getString(R.string.app_name);
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager.createNotificationChannel(channel);
return channelId;
在清单文件中添加此权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
【讨论】:
【参考方案8】:使用 startCommand 处理服务意图。
stopForeground(true)
此调用将从前台状态中删除服务,如果需要更多内存,则可以将其终止。 这不会停止服务运行。为此,您需要调用 stopSelf() 或相关方法。
传递值 true 或 false 指示是否要删除通知。
val ACTION_STOP_SERVICE = "stop_service"
val NOTIFICATION_ID_SERVICE = 1
...
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int
super.onStartCommand(intent, flags, startId)
if (ACTION_STOP_SERVICE == intent.action)
stopForeground(true)
stopSelf()
else
//Start your task
//Send forground notification that a service will run in background.
sendServiceNotification(this)
return Service.START_NOT_STICKY
stopSelf() 调用销毁时处理您的任务。
override fun onDestroy()
super.onDestroy()
//Stop whatever you started
创建通知以保持服务在前台运行。
//This is from Util class so as not to cloud your service
fun sendServiceNotification(myService: Service)
val notificationTitle = "Service running"
val notificationContent = "<My app> is using <service name> "
val actionButtonText = "Stop"
//Check android version and create channel for Android O and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
//You can do this on your own
//createNotificationChannel(CHANNEL_ID_SERVICE)
//Build notification
val notificationBuilder = NotificationCompat.Builder(applicationContext, CHANNEL_ID_SERVICE)
notificationBuilder.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_location)
.setContentTitle(notificationTitle)
.setContentText(notificationContent)
.setVibrate(null)
//Add stop button on notification
val pStopSelf = createStopButtonIntent(myService)
notificationBuilder.addAction(R.drawable.ic_location, actionButtonText, pStopSelf)
//Build notification
val notificationManagerCompact = NotificationManagerCompat.from(applicationContext)
notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notificationBuilder.build())
val notification = notificationBuilder.build()
//Start notification in foreground to let user know which service is running.
myService.startForeground(NOTIFICATION_ID_SERVICE, notification)
//Send notification
notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notification)
在通知上提供停止按钮以在用户需要时停止服务。
/**
* Function to create stop button intent to stop the service.
*/
private fun createStopButtonIntent(myService: Service): PendingIntent?
val stopSelf = Intent(applicationContext, MyService::class.java)
stopSelf.action = ACTION_STOP_SERVICE
return PendingIntent.getService(myService, 0,
stopSelf, PendingIntent.FLAG_CANCEL_CURRENT)
【讨论】:
【参考方案9】:注意:如果您的应用以 API 级别 26 或更高级别为目标,系统会限制使用或创建后台服务,除非应用本身位于前台。
如果应用需要创建前台服务,应用应该调用startForegroundService()
。该方法创建一个后台服务,但该方法向系统发出信号,该服务将把自己提升到前景。
服务创建后,服务必须调用其startForeground() method within five seconds.
【讨论】:
我希望你在谈论当前的问题。否则,*** 社区没有这样的规则 @RogerGusmao 在生产环境代码中不会总是保存您的项目。此外 - 在我的答案下方和上方有很多很好的示例代码。我的项目在发布期间出现问题正是因为我不知道startForegroundService
方法【参考方案10】:
就我而言,情况完全不同,因为我没有在 Oreo 上启动该服务的活动。
以下是我用来解决此前台服务问题的步骤 -
public class SocketService extends Service
private String TAG = this.getClass().getSimpleName();
@Override
public void onCreate()
Log.d(TAG, "Inside onCreate() API");
if (Build.VERSION.SDK_INT >= 26)
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setSmallIcon(R.drawable.ic_launcher);
mBuilder.setContentTitle("Notification Alert, Click Me!");
mBuilder.setContentText("Hi, This is Android Notification Detail!");
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// notificationID allows you to update the notification later on.
mNotificationManager.notify(100, mBuilder.build());
startForeground(100, mBuilder.mNotification);
Toast.makeText(getApplicationContext(), "inside onCreate()", Toast.LENGTH_LONG).show();
@Override
public int onStartCommand(Intent resultIntent, int resultCode, int startId)
Log.d(TAG, "inside onStartCommand() API");
return startId;
@Override
public void onDestroy()
super.onDestroy();
Log.d(TAG, "inside onDestroy() API");
@Override
public IBinder onBind(Intent intent)
// TODO Auto-generated method stub
return null;
然后启动这个服务,我在 cmd 下触发 -
adb -s " + serial_id + " shell am startforegroundservice -n com.test.socket.sample/.SocketService
所以这有助于我在奥利奥设备上不活动的情况下启动服务:)
【讨论】:
【参考方案11】:@mikebertiean 解决方案几乎成功了,但我遇到了额外的问题——我使用 Gingerbread 系统,我不想添加一些额外的包来运行通知。最后我发现: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java
然后我遇到了其他问题——通知只是在我的应用程序运行时杀死了它(如何解决这个问题:Android: How to avoid that clicking on a Notification calls onCreate()),所以总的来说我的服务代码看起来像这样(C#/Xamarin):
Intent notificationIntent = new Intent(this, typeof(MainActivity));
// make the changes to manifest as well
notificationIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification(Resource.Drawable.Icon, "Starting service");
notification.SetLatestEventInfo(this, "MyApp", "Monitoring...", pendingIntent);
StartForeground(1337, notification);
【讨论】:
以上是关于Android - 为服务实现 startForeground?的主要内容,如果未能解决你的问题,请参考以下文章
Android - 为服务实现 startForeground?
使用 Python/(GAE) 为 Android 应用推送通知的服务器端实现 [关闭]