Android - triển khai startForeground cho một dịch vụ?


124

Vì vậy, tôi không chắc chắn nơi / cách thực hiện phương pháp này để làm cho dịch vụ của tôi chạy ở nền trước. Hiện tại tôi bắt đầu dịch vụ của mình bằng cách sau trong một hoạt động khác:

Intent i = new Intent(context, myService.class); 
context.startService(i);

Và sau đó trong myCervice 'onCreate () tôi thử startForeground () ...?

Notification notification = new Notification();
startForeground(1, notification);

Vì vậy, tôi có một chút mất mát và không chắc chắn làm thế nào để thực hiện điều này.


Chà, điều này không hoạt động, ít nhất là theo như tôi có thể nói dịch vụ của tôi vẫn hoạt động như một dịch vụ nền và bị giết.
JDS

Chủ đề được liên kết đến: stackoverflow.com/questions/10962418/
Kiếm

Câu trả lời:


131

Tôi sẽ bắt đầu bằng cách điền hoàn toàn vào Notification. Đây là một dự án mẫu cho thấy việc sử dụng startForeground().


8
Có thể sử dụng startForeground () mà không cần thông báo không? Hoặc sau này chúng ta có thể cập nhật thông báo tương tự?
JRC

2
Có một lý do cụ thể bạn đã sử dụng 1337?
Cody

33
@DoctorOreo: Nó cần phải là duy nhất trong ứng dụng, mặc dù không nhất thiết phải là duy nhất trên thiết bị. Tôi đã chọn 1337 bởi vì, tốt, nó là 1337 . :-)
CommonsWare

Câu hỏi @JRC là một câu hỏi hay. Có thể sử dụng startForeground () mà không cần thông báo không?
Snicolas

2
@Snicolas: Cảm ơn bạn đã chỉ ra một lỗ hổng trong Android. Tôi sẽ làm việc để có được điều này cố định.
CommonsWare

78

Từ hoạt động chính của bạn, bắt đầu dịch vụ với mã sau:

Intent i = new Intent(context, MyService.class); 
context.startService(i);

Sau đó, trong dịch vụ của onCreate()bạn, bạn sẽ tạo thông báo của mình và đặt thông báo là tiền cảnh như vậy:

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 làm thế nào để cập nhật thông báo này từ MainActivity?
Roon13

1
@ Roon13 sử dụng ID, trong trường hợp này là 1337 ... bạn sẽ có thể tạo thông báo mới và gọi startForeground bằng ID
mikebertiean

@ Roon13 kiểm tra câu hỏi này stackoverflow.com/questions/5528288/ từ
mikebertiean

@mikebertiean Làm cách nào tôi có thể gọi startForeground từ MainActivity? Ngoài ra, làm cách nào tôi có thể xóa thông báo khỏi MainActvity khi quá trình kết thúc?
Roon13

@mikebertiean Tôi nhận được rằng tôi phải gọi startForeground một lần nữa trong lớp Service nhưng bằng cách nào? Tôi có phải gọi startService () nữa không?
Roon13

30

Đây là mã của tôi để đặt dịch vụ thành tiền cảnh:

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);

}

Tôi cần xây dựng thông báo bằng PendingIntent, để tôi có thể bắt đầu hoạt động chính của mình từ thông báo.

Để xóa thông báo, chỉ cần gọi stopForeground (true);

Nó được gọi trong onStartCommand (). Vui lòng tham khảo mã của tôi tại: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/lahomaRecorder.java


Nếu bạn xóa thông báo gọi stopForeground (đúng), bạn sẽ hủy dịch vụ
startForground

6
Bạn gọi phương pháp này từ đâu?
Srujan Barai

7
Intent.FLAG_ACTIVITY_NEW_TASKkhông hợp lệ trong bối cảnh PendingIntent.
mixel

30

Giải pháp cho Oreo 8.1

Tôi đã gặp một số vấn đề như RemoteServiceException vì id kênh không hợp lệ với hầu hết các phiên bản Android gần đây. Đây là cách tôi giải quyết nó:

Hoạt động :

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)
    }
}

Dịch vụ nền:

override fun onCreate() {
    super.onCreate()
    startForeground()
}

private fun startForeground() {

    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    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
}

THIẾT BỊ 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;
    }
}

8
Bạn có thể sử dụng ContextCompat.startForegroundService(Context,Intent)trong Hoạt động của mình để thực hiện đúng. ( developer.android.com/reference/android/support/v4/content/ trộm )
Simon Featherstone

3
có thể bạn sẽ muốn sử dụng .setCategory(NotificationCompat.CATEGORY_SERVICE)thay vì Notification.CATEGORY_SERVICEnếu API tối thiểu của bạn <21
Ai đó ở đâu đó vào

6
Vui lòng lưu ý rằng các ứng dụng nhắm mục tiêu Build.VERSION_CODES.P(API cấp 28) trở lên phải yêu cầu quyền Manifest.permission.FOREGROUND_SERVICEđể sử dụng startForeground()- xem developer.android.com/reference/android/app/app
Vadim Kotov

21

Ngoài câu trả lời RAWA , mã an toàn này:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent)
} else {
    startService(intent)
}

Bạn có thể thay đổi thành:

ContextCompat.startForegroundService(context, yourIntent);

Nếu bạn sẽ nhìn vào bên trong phương thức này thì bạn có thể thấy rằng phương thức này thực hiện tất cả công việc kiểm tra cho bạn.


9

Nếu bạn muốn biến IntentService thành Dịch vụ tiền cảnh

sau đó bạn nên ghi đè onHandleIntent()như thế này

Override
protected void onHandleIntent(@Nullable Intent intent) {


    startForeground(FOREGROUND_ID,getNotification());     //<-- Makes Foreground

   // Do something

    stopForeground(true);                                // <-- Makes it again a normal Service                         

}

Làm thế nào để thông báo?

đơn giản. Đây là getNotification()phương pháp

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();
}

Sự hiểu biết sâu sắc hơn

Điều gì xảy ra khi một dịch vụ trở thành dịch vụ tiền cảnh

Điều này xảy ra

nhập mô tả hình ảnh ở đây

Dịch vụ tiền cảnh là gì?

Một dịch vụ tiền cảnh,

  • đảm bảo rằng người dùng chủ động nhận thức được điều gì đó đang diễn ra trong nền bằng cách cung cấp thông báo.

  • (quan trọng nhất) không bị giết bởi Hệ thống khi nó thiếu bộ nhớ

Một trường hợp sử dụng dịch vụ tiền cảnh

Thực hiện chức năng tải xuống bài hát trong Ứng dụng Âm nhạc


5

Thêm mã dịch vụ đã cho lớp "OS> = Build.VERSION_CODES.O" trong onCreate ()

@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;
}

Thêm quyền này trong tệp kê khai:

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1

Xử lý ý định về startCommand của dịch vụ bằng cách sử dụng.

 stopForeground(true)

Cuộc gọi này sẽ xóa dịch vụ khỏi trạng thái nền trước , cho phép nó bị hủy nếu cần thêm bộ nhớ. Điều này không ngăn dịch vụ chạy. Đối với điều đó, bạn cần gọi stopSelf () hoặc các phương thức liên quan.

Truyền giá trị đúng hoặc sai được chỉ định nếu bạn muốn xóa thông báo hay không.

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
}

Xử lý tác vụ của bạn khi hủy được gọi bởi stopSelf () .

override fun onDestroy() {
    super.onDestroy()
    //Stop whatever you started
}

Tạo một thông báo để giữ cho dịch vụ chạy ở phía trước.

//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)
}

Đưa ra một nút dừng trên thông báo để dừng dịch vụ khi người dùng cần.

/**
 * 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)
}

1

Lưu ý: Nếu ứng dụng của bạn nhắm mục tiêu API cấp 26 trở lên, hệ thống sẽ áp dụng các hạn chế trong việc sử dụng hoặc tạo các dịch vụ nền trừ khi chính ứng dụng nằm ở phía trước.

Nếu một ứng dụng cần tạo một dịch vụ nền trước, ứng dụng sẽ gọi startForegroundService(). Phương thức đó tạo ra một dịch vụ nền, nhưng phương thức báo hiệu cho hệ thống rằng dịch vụ sẽ tự quảng bá lên tiền cảnh.

Khi dịch vụ đã được tạo, dịch vụ phải gọi dịch vụ của nó startForeground() method within five seconds.


1
Tôi hy vọng bạn đang nói về câu hỏi hiện tại. Mặt khác, không có quy tắc nào như vậy trong cộng đồng Stackoverflow
Farid

@RogerGusmao trong mã môi trường sẵn sàng sản xuất sẽ không luôn luôn lưu dự án của bạn. Bên cạnh đó - có rất nhiều ví dụ tuyệt vời với mã bên dưới và trên câu trả lời của tôi .. Dự án của tôi có vấn đề trong quá trình phát hành chính xác vì tôi không biết về startForegroundServicephương pháp
Andrii Kovalchuk

0

Trong trường hợp của tôi, điều đó hoàn toàn khác vì tôi không có hoạt động để triển khai dịch vụ tại Oreo.

Dưới đây là các bước mà tôi đã sử dụng để giải quyết vấn đề dịch vụ tiền cảnh này -

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;
    }
}

Và sau đó để bắt đầu dịch vụ này, tôi đã kích hoạt dưới cmd -


adb -s "+ serial_id +" shell am startforgroundservice -n com.test.socket.sample / .SocketService


Vì vậy, điều này giúp tôi bắt đầu dịch vụ mà không cần hoạt động trên các thiết bị Oreo :)


0

Giải pháp @mikebertiean gần như đã thực hiện thủ thuật, nhưng tôi gặp vấn đề này với sự thay đổi bổ sung - Tôi sử dụng hệ thống Gingerbread và tôi không muốn thêm một số gói bổ sung chỉ để chạy thông báo. Cuối cùng tôi đã tìm thấy: https://android.googlesource.com/pl platform / frameworks / support.git + / f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationComp

sau đó tôi gặp phải sự cố khác - thông báo chỉ đơn giản là giết chết ứng dụng của tôi khi nó chạy (cách giải quyết vấn đề này: Android: Cách tránh nhấp vào thông báo cuộc gọi onCreate () ), do đó, trong tổng số mã của tôi trong dịch vụ trông như thế này (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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.