Android - ใช้ startForeground สำหรับบริการหรือไม่


124

ดังนั้นฉันไม่แน่ใจว่าจะใช้วิธีนี้ที่ไหน / อย่างไรเพื่อให้บริการของฉันทำงานในส่วนหน้า ขณะนี้ฉันเริ่มให้บริการตามกิจกรรมต่อไปนี้ในกิจกรรมอื่น:

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

จากนั้นใน myServices 'onCreate () ฉันลอง startForeground () ... ?

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

ใช่ฉันหลงทางเล็กน้อยและไม่แน่ใจว่าจะใช้สิ่งนี้อย่างไร


วิธีนี้ใช้ไม่ได้ผลอย่างน้อยที่สุดเท่าที่ฉันสามารถบอกได้ว่าบริการของฉันยังคงทำงานเป็นบริการเบื้องหลังและถูกฆ่าตาย
JDS

เธรดเชื่อมโยงกับ: stackoverflow.com/questions/10962418/…
Snicolas

คำตอบ:


131

ฉันจะเริ่มต้นด้วยการกรอกข้อมูลในไฟล์Notification. นี่คือโครงการตัวอย่างที่สาธิตการใช้งานstartForeground().


8
เป็นไปได้ไหมที่จะใช้ startForeground () โดยไม่มีการแจ้งเตือน หรือเราสามารถอัปเดตการแจ้งเตือนเดียวกันในภายหลังได้หรือไม่?
JRC

2
มีเหตุผลเฉพาะที่คุณใช้1337หรือไม่?
Cody

33
@DoctorOreo: ต้องไม่ซ้ำกันภายในแอพแม้ว่าจะไม่จำเป็นต้องไม่ซ้ำกันบนอุปกรณ์ก็ตาม ผมเลือก 1337 เพราะดีมันเป็น1337 :-)
CommonsWare

คำถาม @JRC เป็นคำถามที่ดี เป็นไปได้ไหมที่จะใช้ startForeground () โดยไม่มีการแจ้งเตือน
Snicolas

2
@Snicolas: ขอบคุณที่ชี้ให้เห็นข้อบกพร่องใน Android ฉันจะดำเนินการแก้ไขปัญหานี้
CommonsWare

78

จากกิจกรรมหลักของคุณเริ่มบริการด้วยรหัสต่อไปนี้:

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 ได้อย่างไร?
รูน 13

1
@ Roon13 โดยใช้ ID ในกรณีนี้ 1337 ... คุณควรจะสามารถสร้างการแจ้งเตือนใหม่และโทรไปที่ startForeground ด้วย ID
mikebertiean

@ Roon13 ลองดูคำถามนี้stackoverflow.com/questions/5528288/…
mikebertiean

@mikebertiean ฉันจะเรียก startForeground จาก MainActivity ได้อย่างไร? ฉันจะล้างการแจ้งเตือนจาก MainActvity ได้อย่างไรเมื่อกระบวนการเสร็จสิ้น
รูน 13

@mikebertiean ฉันเข้าใจแล้วว่าฉันต้องโทรไปที่ startForeground อีกครั้งในชั้น Service แต่อย่างไร ฉันต้องโทรไปที่ startService () อีกครั้งหรือไม่?
รูน 13

30

นี่คือรหัสของฉันในการตั้งค่าบริการเป็นเบื้องหน้า:

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 (จริง)

ถูกเรียกใน onStartCommand () โปรดอ้างอิงรหัสของฉันที่: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


หากคุณลบการแจ้งเตือนที่เรียก stopForeground (จริง) คุณกำลังยกเลิกบริการ
startforeground

6
คุณเรียกวิธีนี้มาจากไหน?
Srujan Barai

7
Intent.FLAG_ACTIVITY_NEW_TASKPendingIntentไม่ถูกต้องในบริบทของ
mixel

30

โซลูชันสำหรับ Oreo 8.1

ฉันพบปัญหาบางอย่างเช่นRemoteServiceExceptionเนื่องจากรหัสช่องไม่ถูกต้องกับ Android เวอร์ชันล่าสุด นี่คือวิธีที่ฉันแก้ไข:

กิจกรรม :

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

BackgroundService:

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
}

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
คุณสามารถใช้ContextCompat.startForegroundService(Context,Intent)ในกิจกรรมของคุณซึ่งจะทำในสิ่งที่ถูกต้อง ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone

3
คุณอาจต้องการใช้.setCategory(NotificationCompat.CATEGORY_SERVICE)แทนNotification.CATEGORY_SERVICEหาก min API ของคุณเป็น <21
ใครสักคนอยู่ที่ไหนสักแห่ง

6
โปรดทราบว่าแอปที่กำหนดเป้าหมายBuild.VERSION_CODES.P(API ระดับ 28) ขึ้นไปต้องขออนุญาตจึงManifest.permission.FOREGROUND_SERVICEจะใช้งานได้startForeground()- โปรดดูที่developer.android.com/reference/android/app/…
Vadim Kotov

21

นอกเหนือจากคำตอบRAWAแล้วความสงบของรหัสนี้:

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

คุณสามารถเปลี่ยนเป็น:

ContextCompat.startForegroundService(context, yourIntent);

หากคุณจะดูในวิธีนี้คุณจะเห็นว่าวิธีการตรวจสอบทั้งหมดได้ผลสำหรับคุณ


9

หากคุณต้องการทำให้ IntentService เป็นบริการ Foreground

จากนั้นคุณควรแทนที่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();
}

ความเข้าใจที่ลึกซึ้งยิ่งขึ้น

จะเกิดอะไรขึ้นเมื่อบริการกลายเป็นบริการเบื้องหน้า

สิ่งนี้เกิดขึ้น

ใส่คำอธิบายภาพที่นี่

บริการเบื้องหน้าคืออะไร?

บริการเบื้องหน้า

  • ตรวจสอบให้แน่ใจว่าผู้ใช้ตระหนักถึงสิ่งที่เกิดขึ้นในพื้นหลังโดยแจ้งการแจ้งเตือน

  • (ที่สำคัญที่สุด) จะไม่ถูกฆ่าโดยระบบเมื่อหน่วยความจำเหลือน้อย

กรณีการใช้งานของบริการเบื้องหน้า

การใช้ฟังก์ชันดาวน์โหลดเพลงในแอพเพลง


5

เพิ่มรหัสที่กำหนดคลาสบริการสำหรับ "OS> = Build.VERSION_CODES.O"ใน 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;
}

เพิ่มสิทธิ์นี้ในไฟล์รายการ:

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

1

จัดการกับเจตนาในการเริ่มต้นคำสั่งของบริการโดยใช้

 stopForeground(true)

การโทรนี้จะลบบริการออกจากสถานะเบื้องหน้าทำให้สามารถฆ่าได้หากต้องการหน่วยความจำเพิ่มขึ้น สิ่งนี้ไม่ได้ทำให้บริการหยุดทำงาน เพื่อที่คุณจะต้องเรียกใช้stopSelf ()หรือวิธีการที่เกี่ยวข้อง

การส่งผ่านค่าเป็นจริงหรือเท็จระบุว่าคุณต้องการลบการแจ้งเตือนหรือไม่

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

1

หมายเหตุ: หากแอปของคุณกำหนดเป้าหมาย API ระดับ 26 ขึ้นไประบบจะกำหนดข้อ จำกัด ในการใช้หรือสร้างบริการเบื้องหลังเว้นแต่แอปนั้นจะอยู่เบื้องหน้า

หากแอปต้องการที่จะสร้างบริการเบื้องหน้า, app startForegroundService()ควรจะเรียก วิธีการนั้นจะสร้างบริการพื้นหลัง แต่เมธอดจะส่งสัญญาณไปยังระบบว่าบริการจะโปรโมตตัวเองไปยังส่วนหน้า

เมื่อสร้างบริการแล้วบริการจะต้องเรียกใช้บริการ startForeground() method within five seconds.


1
ฉันหวังว่าคุณกำลังพูดถึงคำถามปัจจุบัน มิฉะนั้นจะไม่มีกฎดังกล่าวในชุมชน Stackoverflow
Farid

@RogerGusmao ในโค้ดสภาพแวดล้อมที่พร้อมใช้งานจริงจะไม่บันทึกโครงการของคุณเสมอไป นอกจากนี้ - มีตัวอย่างที่ดีมากมายพร้อมโค้ดด้านล่างและเหนือคำตอบของฉัน .. โครงการของฉันมีปัญหาในระหว่างการเผยแพร่เพราะฉันไม่รู้เกี่ยวกับstartForegroundServiceวิธีการ
Andrii Kovalchuk

0

ในกรณีของฉันมันแตกต่างไปจากเดิมอย่างสิ้นเชิงเนื่องจากฉันไม่มีกิจกรรมในการเปิดตัวบริการใน 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 +" เชลล์ am startforegroundservice -n com.test.socket.sample /.SocketService


สิ่งนี้ช่วยให้ฉันเริ่มให้บริการโดยไม่ต้องทำกิจกรรมบนอุปกรณ์ Oreo :)


0

โซลูชัน @mikebertiean เกือบจะเป็นเคล็ดลับ แต่ฉันมีปัญหานี้ด้วยการบิดเพิ่มเติม - ฉันใช้ระบบ Gingerbread และฉันไม่ต้องการเพิ่มแพ็คเกจพิเศษเพื่อเรียกใช้การแจ้งเตือน ในที่สุดฉันก็พบ: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

จากนั้นฉันพบปัญหาเพิ่มเติม - การแจ้งเตือนเพียงแค่ฆ่าแอปของฉันเมื่อมันทำงาน (วิธีแก้ปัญหานี้: Android: วิธีหลีกเลี่ยงการคลิกที่การแจ้งเตือนเรียก 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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.