Android 8.0: java.lang.IllegalStateException: ไม่อนุญาตให้เริ่มเจตนาบริการ


360

ในการเปิดแอปพลิเคชันแอปจะเริ่มบริการที่ควรทำงานเครือข่ายบางอย่าง หลังจากกำหนดเป้าหมาย API ระดับ 26 แล้วแอปพลิเคชันของฉันไม่สามารถเริ่มให้บริการบน Android 8.0 บนพื้นหลัง

เกิดจาก: java.lang.IllegalStateException: ไม่อนุญาตให้เริ่มบริการ Intent {cmp = my.app.tt / com.my.service}: แอปอยู่ในพื้นหลัง uid UidRecord {90372b1 u0a136 CEM idle procs: 1 seq (0,0 , 0)}

ตามที่ฉันเข้าใจแล้วว่าเกี่ยวข้องกับ: ขีด จำกัด การดำเนินการพื้นหลัง

ตอนนี้เมธอด startService () จะส่ง IllegalStateException หากแอปที่กำหนดเป้าหมาย Android 8.0 พยายามใช้วิธีการนั้นในสถานการณ์ที่ไม่ได้รับอนุญาตให้สร้างบริการพื้นหลัง

" ในสถานการณ์ที่ไม่ได้รับอนุญาต " - ความหมายคืออะไร? และวิธีการแก้ไข ฉันไม่ต้องการตั้งค่าบริการของฉันเป็น "เบื้องหน้า"


4
หมายความว่าคุณไม่สามารถเริ่มบริการได้เมื่อแอปของคุณอยู่ในพื้นหลัง
ทิม

22
สิ่งนี้ไม่มีส่วนเกี่ยวข้องกับสิทธิ์อนุญาตรันไทม์
ทิม

10
ใช้แทนstartForegroundService() startService()
frogatto

2
คุณสามารถลองใช้ targetSdkVersion 25 แต่คอมไพล์ด้วย compileSdkVersion 26 ด้วยวิธีนี้คุณสามารถใช้คลาสใหม่จาก Android 8 และ suppport library ใหม่ล่าสุด แต่แอปของคุณจะไม่ถูก จำกัด โดย Background Execution Limits
Kacper Dziubek

2
@KacperDziubek ที่ควรใช้งานได้ แต่เป็นวิธีแก้ปัญหาชั่วคราวเนื่องจากต้องใช้ SDK26 เป้าหมายในฤดูใบไม้ร่วงปี 2018
RightHandedMonkey

คำตอบ:


194

สถานการณ์ที่อนุญาตเป็นรายการที่อนุญาตชั่วคราวซึ่งบริการพื้นหลังทำงานเหมือนกับก่อนหน้า Android O

ภายใต้สถานการณ์บางอย่างแอปพื้นหลังจะอยู่ในรายการที่อนุญาตชั่วคราวเป็นเวลาหลายนาที ในขณะที่แอพอยู่ในรายการที่อนุญาต แต่สามารถเปิดบริการได้โดยไม่ จำกัด และอนุญาตให้ใช้บริการพื้นหลัง แอพถูกวางไว้ในบัญชีปลอดภัยเมื่อจัดการงานที่ผู้ใช้มองเห็นได้เช่น:

  • การจัดการข้อความ Firebase Cloud Messaging (FCM) ที่มีลำดับความสำคัญสูง
  • การรับสัญญาณออกอากาศเช่นข้อความ SMS / MMS
  • ดำเนินการ PendingIntent จากการแจ้งเตือน
  • การเริ่มต้น VpnService ก่อนที่แอพ VPN จะโปรโมตตัวเองไปยังส่วนหน้า

ที่มา: https://developer.android.com/about/versions/oreo/background.html

ดังนั้นในคำอื่น ๆ ถ้าบริการพื้นหลังของคุณไม่เป็นไปตามข้อกำหนดบัญชีขาวที่คุณต้องใช้JobSchedulerใหม่ JobSchedulerโดยพื้นฐานแล้วจะเหมือนกับบริการพื้นหลัง แต่จะถูกเรียกเป็นระยะแทนที่จะเรียกใช้ในพื้นหลังอย่างต่อเนื่อง

หากคุณใช้ IntentService คุณสามารถเปลี่ยนเป็น JobIntentService ดู @ kosev ของคำตอบด้านล่าง


ฉันได้รับความเสียหายหลังจากที่ฉันต้องการเริ่มให้บริการหลังจากที่ฉันได้รับข้อความ GCM ของ "สูง" prio ฉันยังใช้ GCM: "com.google.android.gms: play-services-gcm: 11.4.2" ไม่ใช่ "com.google.firebase: firebase-messaging: 11.4.2" ไม่แน่ใจว่ามันเป็นเรื่องสำคัญ ..
อเล็กซ์ Radzishevsky

"โดยพื้นฐานแล้วมันเหมือนกับบริการพื้นหลัง แต่มันถูกเรียกเป็นระยะแทนที่จะเรียกใช้ในพื้นหลังอย่างต่อเนื่อง" - ไม่แน่ใจว่าคุณหมายถึงอะไรเพราะบริการ Android ไม่เคยดำเนินการอย่างต่อเนื่อง พวกเขาเริ่มทำงานแล้วปิดตัวลง
Melllvar

2
เป็นFirebaseInstanceIdService และonTokenRefreshวิธีข้อความ FCM มีความสำคัญสูง?
Cord Rehn

@ phnmnn ไม่, GCMTaskService ไม่ได้ติดตาม FCM จริงๆดังนั้นจึงไม่ทำงาน
Abhinav Upadhyay

4
คุณไม่ควรใช้ WorkManager (ที่นี่: developer.android.com/topic/lไลบรารี/architecture/workmanager) แทน JobScheduler หรือคนอื่น ๆ ? ฉันหมายถึงสิ่งนี้: youtu.be/IrKoBFLwTN0
นักพัฒนา Android

255

ฉันได้ทางออกแล้ว สำหรับก่อน 8.0 อุปกรณ์ที่คุณต้องใช้เพียงstartService()แต่สำหรับการโพสต์ 7.0 startForgroundService()อุปกรณ์ที่คุณต้องใช้ นี่คือตัวอย่างสำหรับรหัสเพื่อเริ่มบริการ

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, ServedService.class));
    } else {
        context.startService(new Intent(context, ServedService.class));
    }

และในระดับบริการโปรดเพิ่มรหัสด้านล่างเพื่อรับการแจ้งเตือน:

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}

โดยที่ O คือ Android เวอร์ชั่น 26


9
บริการเบื้องหน้าคือสิ่งที่ผู้ใช้จะต้องทราบและต้องการการแจ้งเตือน มันจะยัง ANR ถ้ามันทำงานนานเกินไป ดังนั้นจึงไม่ใช่คำตอบที่เหมาะสมถ้าแอพทำงานอยู่เบื้องหลัง
SimonH

80
มีการContextCompat.startForegroundService(...)สนับสนุน lib ที่สามารถใช้แทนได้
jayeffkay

37
นั่นไม่ใช่ทางออก
JacksOnF1 ถึง

17
ฉันยอมรับด้วยว่านี่ไม่ใช่วิธีแก้ปัญหา มันเป็นวิธีแก้ปัญหาและช่วยได้ แต่มีการ จำกัด พื้นหลังใน Oreo ด้วยเหตุผล การข้ามขีด จำกัด เหล่านี้ด้วยวิธีนี้ไม่ใช่วิธีที่ถูกต้อง (แม้ว่าจะใช้งานได้) วิธีที่ดีที่สุดคือใช้ JobScheduler (อ้างอิงจากคำตอบที่ยอมรับ)
Vratislav Jindra

6
ฉันไม่คิดว่ามันจะเป็นประสบการณ์การใช้งานที่ดีหากคุณต้องแสดงการแจ้งเตือนเบื้องหน้าที่ว่างเปล่า พิจารณาข้อเท็จจริงที่ว่าคุณต้อง - Android 8.0 แนะนำวิธีการใหม่ startForegroundService () เพื่อเริ่มบริการใหม่ในเบื้องหน้า หลังจากระบบสร้างบริการแล้วแอปมีเวลาห้าวินาทีในการเรียกวิธีการ startForeground () ของบริการเพื่อแสดงการแจ้งเตือนที่ผู้ใช้มองเห็นได้ของบริการใหม่ หากแอพไม่ได้เรียก startForeground () ภายในเวลาที่กำหนดระบบจะหยุดให้บริการและแจ้งให้แอปเป็น ANR
heeleeaz

85

วิธีที่ดีที่สุดคือใช้JobIntentServiceซึ่งใช้ JobScheduler ใหม่สำหรับ Oreo หรือบริการเก่าถ้าไม่สามารถใช้งานได้

ประกาศในรายการของคุณ:

<service android:name=".YourService"
         android:permission="android.permission.BIND_JOB_SERVICE"/>

และในบริการของคุณคุณต้องเปลี่ยน onHandleIntent ด้วย onHandleWork:

public class YourService extends JobIntentService {

    public static final int JOB_ID = 1;

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, YourService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // your code
    }

}

จากนั้นคุณเริ่มบริการด้วย:

YourService.enqueueWork(context, new Intent());


วิธีที่คุณสามารถเรียกวิธีการแบบไม่คงที่ภายในวิธีการแบบคงที่ได้อย่างไร คุณช่วยอธิบายได้มั้ย
แมดดี้

@Maddy enqueueWork(...)เป็นวิธีการคงที่เช่นกัน
hgoebl

2
คุณจะเรียก YourService.enqueueWork (บริบท, เจตนาใหม่ ()) ที่ไหน; ? จากเครื่องรับสัญญาณออกอากาศ?
TheLearner

ฉันไม่เชื่อว่านี่เป็นทางออกที่ง่ายที่สุด ดูความคิดเห็นของฉันด้านล่างเกี่ยวกับ WorkManager มันใช้ JobIntentService ตามความเหมาะสม แต่มันมีจานหม้อต้มน้อยกว่ามาก
นิทาน

36

หากบริการกำลังทำงานในเธรดพื้นหลังโดยขยายIntentServiceคุณสามารถแทนที่IntentServiceด้วยJobIntentServiceซึ่งมีให้เป็นส่วนหนึ่งของห้องสมุดสนับสนุน Android

ข้อดีของการใช้JobIntentServiceคือมันทำงานเป็นIntentService pre-O และบน O และสูงกว่ามันส่งไปเป็นงาน

JobSchedulerสามารถใช้สำหรับงานตามช่วงเวลาหรือตามคำขอ แต่ให้แน่ใจว่าจัดการกับความเข้ากันได้แบบย้อนหลังเนื่องจากJobSchedulerAPI พร้อมใช้งานจาก API 21 เท่านั้น


1
ปัญหาเกี่ยวกับ JobIntentService คือ Android สามารถกำหนดเวลาการทำงานของคุณได้ตามอำเภอใจและไม่สามารถเริ่มได้โดยปริยายหากไม่มีการแก้ไขใด ๆ ซึ่งแตกต่างจาก IntentService ดูstackoverflow.com/questions/52479262/…
kilokahn

15

ในOreo Android ที่กำหนดไว้ขีด จำกัด ของการบริการที่พื้นหลัง

เพื่อปรับปรุงประสบการณ์ผู้ใช้ Android 8.0 (API ระดับ 26) มีข้อ จำกัด เกี่ยวกับสิ่งที่แอพสามารถทำได้ในขณะทำงานในพื้นหลัง

อย่างไรก็ตามหากคุณต้องการใช้บริการตลอดเวลาคุณสามารถใช้บริการเบื้องหน้าได้

ข้อ จำกัด บริการพื้นหลัง:ในขณะที่แอปพลิเคชั่นไม่มีการใช้งานมีข้อ จำกัด ในการใช้บริการพื้นหลัง สิ่งนี้ไม่ได้ใช้กับบริการเบื้องหน้าซึ่งเห็นได้ชัดมากขึ้นกับผู้ใช้

ดังนั้นคุณสามารถให้บริการเบื้องหน้าได้ คุณจะต้องแสดงการแจ้งเตือนให้ผู้ใช้ทราบเมื่อบริการของคุณกำลังทำงาน ดูคำตอบนี้ (มีคนอื่นอีกมากมาย)

ทางออกถ้า -

คุณไม่ต้องการการแจ้งเตือนสำหรับบริการของคุณ?

คุณสามารถทำงานเป็นงวดได้ 1. เริ่มบริการของคุณ 2. บริการจะทำงานและ 3. หยุดตัวเอง แอพของคุณจะไม่ถือว่าแบตเตอรี่หมด

คุณสามารถใช้งานระยะกับผู้จัดการปลุก , กำหนดการงาน , Evernote-งานหรือผู้จัดการที่ทำงาน

  • ผู้จัดการงานเป็นทางออกที่ดีที่สุดสำหรับงานตามกำหนดเวลา ซึ่งถูกนำมาใช้กับAndroid Architecture Componentองค์ประกอบสถาปัตยกรรม
  • ซึ่งแตกต่างจาก Job-Scheduler (เฉพาะ> 21 API) มันจะใช้ได้กับทุกรุ่น
  • นอกจากนี้ก็จะเริ่มทำงานหลังจากที่โหมดสแตนด์บาย Doze-
  • สร้างAndroid Boot Receiverสำหรับกำหนดเวลาบริการหลังการบู๊ตอุปกรณ์

ฉันได้ทดสอบการใช้บริการตลอดไปกับ Work-Manager


WorkManager ดูเหมือนจะเป็นวิธีที่ดีที่สุดถ้าสมมติว่างานไม่จำเป็นต้องดำเนินการทันที สามารถใช้กับ API 14 ได้แบบย้อนหลังโดยใช้ JobScheduler บนอุปกรณ์ที่มี API 23+ และการรวมกันของ BroadcastReceiver + AlarmManager บนอุปกรณ์ที่มี API 14-22
James Allen

สิ่งสำคัญเกี่ยวกับ WorkManager คือWorkManager มีไว้สำหรับงานที่ไม่สามารถเรียกใช้ได้นั่นคือไม่จำเป็นต้องเรียกใช้ทันที
#: 30867

13

ใช่นั่นเป็นเพราะคุณไม่สามารถเริ่มบริการในพื้นหลังได้อีกต่อไปใน API 26 ดังนั้นคุณสามารถเริ่มต้น ForegroundService เหนือ API 26

คุณจะต้องใช้

ContextCompat.startForegroundService(...)

และโพสต์การแจ้งเตือนขณะประมวลผลการรั่วไหล


1
OP โดยเฉพาะกล่าวว่าเขาไม่ต้องการเป็นเบื้องหน้า ควรใส่ความคิดเห็นหรือเป็นส่วนหนึ่งของคำตอบที่สมบูรณ์ยิ่งขึ้น
Ricardo A.

7

ตามที่ @ kosev กล่าวในคำตอบของเขาคุณสามารถใช้ JobIntentService แต่ฉันใช้โซลูชันทางเลือก - ฉันจับ IllegalStateException และเริ่มบริการเป็นเบื้องหน้า ตัวอย่างเช่นฟังก์ชั่นนี้เริ่มบริการของฉัน:

@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
    val context = App.context
    val intent = Intent(context, serviceType)
    intent.action = intentAction
    intentExtraSetup(intent)
    intent.putExtra(NEED_FOREGROUND_KEY, false)

    try {
        context.startService(intent)
    }
    catch (ex: IllegalStateException) {
        intent.putExtra(NEED_FOREGROUND_KEY, true)
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent)
        }
        else {
            context.startService(intent)
        }
    }
}

และเมื่อฉันประมวลผลเจตนาฉันทำสิ่งนั้น:

override fun onHandleIntent(intent: Intent?) {
    val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
    if(needToMoveToForeground) {
        val notification = notificationService.createSyncServiceNotification()
        startForeground(notification.second, notification.first)

        isInForeground = true
    }

    intent?.let {
        getTask(it)?.process()
    }
}

ฉันชอบทางออกลองของคุณ สำหรับฉันมันเป็นวิธีการแก้ปัญหาเพราะบางครั้งcontext.startServiceทำงานในพื้นหลัง - บางครั้งไม่ได้ - นี่เป็นวิธีที่ดีที่สุดเท่านั้นมิฉะนั้นคุณต้องติดตั้งโค้ดเพิ่มเติมในคลาสหลักของคุณextending Applicationและimplementing ActivityLifecycleCallbacksติดตามว่าแอปอยู่ในเบื้องหน้าหรือพื้นหลังแล้ว ตาม
Pierre

ข้อยกเว้นนี้สามารถถูกจับได้หรือไม่
thecr0w

5

จากบันทึกประจำรุ่น firebaseพวกเขาระบุว่าการรองรับ Android O ได้รับการเปิดตัวครั้งแรกใน 10.2.1 (แม้ว่าฉันจะแนะนำให้ใช้เวอร์ชันล่าสุด)

โปรดเพิ่มการอ้างอิงการส่งข้อความ firebase ใหม่สำหรับ Android O

compile 'com.google.firebase:firebase-messaging:11.6.2'

อัปเกรดบริการ Google Play และที่เก็บ Google หากจำเป็น


สิ่งนี้ไม่ตอบคำถามหรือคำถามไม่มีส่วนเกี่ยวข้องกับ firebase มันควรจะใส่เป็นความคิดเห็น
Ricardo A.

5

หากความตั้งใจใด ๆ ที่เคยทำงานได้ดีเมื่อแอปอยู่ในพื้นหลังก็จะไม่เป็นเช่นนั้นอีกต่อไปจาก Android 8 และสูงกว่า หมายถึงเจตนาที่ต้องดำเนินการบางอย่างเมื่อแอปอยู่ในพื้นหลังเท่านั้น

ขั้นตอนด้านล่างจะต้องมีการปฏิบัติตาม:

  1. ดังกล่าวข้างต้นเจตนาควรจะใช้แทนJobIntentService IntentService
  2. คลาสที่ขยายJobIntentServiceควรใช้onHandleWork(@NonNull Intent intent)เมธอดและควรมีเมธอดด้านล่างซึ่งจะเรียกonHandleWorkเมธอด:

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, xyz.class, 123, work);
    }
  3. โทรenqueueWork(Context, intent)จากชั้นเรียนที่มีการกำหนดความตั้งใจของคุณ

    รหัสตัวอย่าง:

    Public class A {
    ...
    ...
        Intent intent = new Intent(Context, B.class);
        //startService(intent); 
        B.enqueueWork(Context, intent);
    }

ชั้นด้านล่างก่อนหน้านี้ขยายระดับบริการ

Public Class B extends JobIntentService{
...

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, B.class, JobId, work);
    }

    protected void onHandleWork(@NonNull Intent intent) {
        ...
        ...
    }
}
  1. com.android.support:support-compatเป็นสิ่งจำเป็นสำหรับJobIntentService- 26.1.0 Vใช้ฉัน

  2. สิ่งสำคัญที่สุดคือการทำให้แน่ใจว่าเวอร์ชันไลบรารี่ของ Firebase เปิดอย่างน้อย10.2.1ฉันมีปัญหากับ10.2.0- ถ้าคุณมี!

  3. รายการของคุณควรได้รับอนุญาตด้านล่างสำหรับระดับบริการ:

    service android:name=".B"
    android:exported="false"
    android:permission="android.permission.BIND_JOB_SERVICE"

หวังว่านี่จะช่วยได้


4

ฉันเห็นการตอบสนองมากมายที่แนะนำให้ใช้ ForegroundService ในการใช้ ForegroundService จะต้องมีการแจ้งเตือนที่เกี่ยวข้อง ผู้ใช้จะเห็นการแจ้งเตือนนี้ ขึ้นอยู่กับสถานการณ์พวกเขาอาจรำคาญกับแอพของคุณและถอนการติดตั้ง

ทางออกที่ง่ายที่สุดคือการใช้สถาปัตยกรรมชิ้นส่วนใหม่ที่เรียกว่า WorkManager คุณสามารถตรวจสอบเอกสารที่นี่: https://developer.android.com/topic/l ไลบรารี/architecture/workmanager/

คุณเพียงแค่กำหนดคลาสคนงานของคุณที่ขยายคนงาน

public class CompressWorker extends Worker {

    public CompressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Worker.Result doWork() {

        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress();

        // Indicate success or failure with your return value:
        return Result.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

จากนั้นคุณกำหนดเวลาเมื่อคุณต้องการเรียกใช้

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);

ง่าย! มีหลายวิธีที่คุณสามารถกำหนดค่าพนักงาน รองรับงานที่เกิดซ้ำและคุณสามารถทำสิ่งที่ซับซ้อนเช่นการผูกมัดหากคุณต้องการ หวังว่านี่จะช่วยได้


3
WorkManager ปัจจุบันยังคงเป็นอัลฟา
pzulw

3
5 มีนาคม 2019 - WorkManager 1.0.0 เวอร์ชั่นเสถียร
phnmnn

ควรใช้ WorkManager แทนการใช้ interservice หรือ JobIntentService
sivaBE35

1
WorkManager is intended for tasks that are deferrable—that is, not required to run immediately... มันอาจจะง่ายที่สุดอย่างไรก็ตามแอพของฉันต้องการบริการพื้นหลังที่ดำเนินการตามคำขอของผู้ใช้ทันที!
คนอยู่ที่ไหนสักแห่งที่

หากคุณต้องการให้งานเสร็จสมบูรณ์ในทันทีคุณควรใช้บริการเบื้องหน้า ผู้ใช้จะเห็นการแจ้งเตือนและรู้ว่าคุณกำลังทำงานอยู่ ตรวจสอบเอกสารหากคุณต้องการความช่วยเหลือในการตัดสินใจว่าจะใช้อะไร พวกเขามีแนวทางที่ดีสำหรับการประมวลผลพื้นหลัง developer.android.com/guide/background
นิทาน

4

โซลูชันอื่นโดยใช้ JobScheduler สามารถเริ่มให้บริการในพื้นหลังในช่วงเวลาปกติ

สร้างคลาสชื่อUtil.java ก่อน

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;

public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(Context context) {
    ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
    JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
    builder.setMinimumLatency(1*1000);    // wait at least
    builder.setOverrideDeadline(3*1000);  //delay time
    builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);  // require unmetered network
    builder.setRequiresCharging(false);  // we don't care if the device is charging or not
    builder.setRequiresDeviceIdle(true); // device should be idle
    System.out.println("(scheduler Job");

    JobScheduler jobScheduler = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        jobScheduler = context.getSystemService(JobScheduler.class);
    }
    jobScheduler.schedule(builder.build());
   }
  }

จากนั้นสร้างคลาส JobService ชื่อเป็นTestJobService.java

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;

  /**
   * JobService to be scheduled by the JobScheduler.
   * start another service
   */ 
public class TestJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
    Util.schedulerJob(getApplicationContext()); // reschedule the job
    Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onStopJob(JobParameters params) {
    return true;
  }
 }

หลังจากคลาส BroadCast Receiver นั้นชื่อว่าServiceReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

 public class ServiceReceiver extends BroadcastReceiver {
 @Override
public void onReceive(Context context, Intent intent) {
    Util.schedulerJob(context);
 }
}

อัปเดตไฟล์มานิเฟสต์ด้วยรหัสระดับบริการและผู้รับ

<receiver android:name=".ServiceReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service
        android:name=".TestJobService"
        android:label="Word service"
        android:permission="android.permission.BIND_JOB_SERVICE" >

    </service>

ปล่อย main_intent launcher ไปยังไฟล์ mainActivity.java ซึ่งสร้างขึ้นตามค่าเริ่มต้นและการเปลี่ยนแปลงในไฟล์MainActivity.javaคือ

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Util.schedulerJob(getApplicationContext());
  }
 }

WOOAAH !! บริการพื้นหลังเริ่มต้นโดยไม่มีบริการ Foreground


2

หากคุณใช้รหัสใน 8.0 แอปพลิเคชันจะหยุดทำงาน ดังนั้นเริ่มบริการในเบื้องหน้า หากต่ำกว่า 8.0 ให้ใช้สิ่งนี้:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

หากเหนือหรือ 8.0 ให้ใช้สิ่งนี้:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );

ขอแนะนำให้ใช้บริการ Foreground สำหรับกรณีที่ผู้ใช้ต้องระวังว่าบริการกำลังทำงานอยู่ ตัวอย่างทั่วไปสำหรับการเล่นเพลงในพื้นหลัง มีกรณีอื่นที่เหมาะสม แต่คุณไม่ควรเพียงแปลงบริการทั้งหมดของคุณเป็นบริการ Foreground พิจารณาการแปลงบริการของคุณเพื่อใช้ WorkManager จากองค์ประกอบทางสถาปัตยกรรมของ Google เมื่อคุณต้องทำงานในพื้นหลังและรับประกันว่าจะทำงาน
TALE

startForegroundService java.lang.SecurityException: Permission Denial: startForeground from pid=13708, uid=10087 requires android.permission.FOREGROUND_SERVICEต้องได้รับอนุญาตมิฉะนั้น แก้ไขได้ที่stackoverflow.com/a/52382711/550471
คนอยู่ที่ไหนสักแห่งที่

1

หากคุณมีการแจ้งเตือนแบบพุชจากฐานข้อมูลแบบบูรณาการแล้ว

เพิ่ม / ปรับปรุงใหม่ Firebase ข้อความอ้างอิงสำหรับหุ่นยนต์ O (Android 8.0) เนื่องจากพื้นหลังขีด จำกัด การดำเนินการ

compile 'com.google.firebase:firebase-messaging:11.4.0'

อัปเกรดบริการ Google Play และที่เก็บ Google หากจำเป็น

ปรับปรุง:

 compile 'com.google.firebase:firebase-messaging:11.4.2'

0

ใช้startForegroundService()แทนstartService() และอย่าลืมที่จะสร้างstartForeground(1,new Notification());ในบริการของคุณภายใน 5 วินาทีของการเริ่มบริการ


2
ดูเหมือนว่า Notificaction ใหม่ () ไม่ทำงานจาก Android 8.1; คุณควรสร้างช่องสำหรับการแจ้งเตือน: stackoverflow.com/a/47533338/1048087
Prizoff

0

เนื่องจากคะแนนโหวตแย้งกับคำตอบนี้ (+4 / 4 จากการแก้ไขนี้) โปรดดูคำตอบอื่น ๆ ก่อนและใช้สิ่งนี้เป็นรีสอร์ทสุดท้ายเท่านั้น ฉันใช้สิ่งนี้เพียงครั้งเดียวสำหรับแอปเครือข่ายที่ทำงานในฐานะรูทและฉันเห็นด้วยกับความเห็นทั่วไปว่าวิธีนี้ไม่ควรใช้ภายใต้สถานการณ์ปกติ

คำตอบเดิมด้านล่าง:

คำตอบอื่น ๆ นั้นถูกต้องทั้งหมด แต่ฉันอยากจะชี้ให้เห็นว่าอีกวิธีหนึ่งในการหลีกเลี่ยงปัญหานี้คือขอให้ผู้ใช้ปิดการใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับแอพของคุณ (ซึ่งโดยปกติจะไม่ใช่ความคิดที่ดี ดูคำตอบนี้สำหรับวิธีขอยกเลิกการปรับแต่งแบตเตอรี่โดยไม่ทำให้แอปของคุณถูกแบนใน Google Play

คุณควรตรวจสอบด้วยว่าการปรับแบตเตอรี่ให้เหมาะสมถูกปิดอยู่ในเครื่องรับของคุณหรือไม่เพื่อป้องกันไม่ให้เกิดปัญหา:

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
        ?.isIgnoringBatteryOptimizations(packageName) != false) {
    startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash

1
การขอให้ผู้ใช้ของคุณให้คุณใช้บัตรฟรีโดยใช้แบตเตอรีให้ได้มากที่สุดไม่ใช่ทางออกที่ดี พิจารณาการแปลงรหัสของคุณเป็นโซลูชันที่เป็นมิตรกับแบตเตอรี่มากขึ้น ผู้ใช้ของคุณจะขอบคุณ
TALE

5
@TALE บริการแบ็คกราวด์ทุกอย่างสามารถใช้งานแบตเตอรี่ได้ง่ายJobSchedulerและอื่น ๆ แอพบางตัวจำเป็นต้องทำงานในระดับที่ต่ำกว่าแอปพลิเคชั่นการซิงค์ทั่วไป นี่เป็นทางเลือกอื่นเมื่อไม่ได้ผล
Mygod

-17

ห้ามใช้ใน onStartCommand:

return START_NOT_STICKY

เพียงเปลี่ยนเป็น:

return START_STICKY

และมันจะทำงาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.