Android 8.0: java.lang.IllegalStateException: Không được phép bắt đầu dịch vụ Ý định


360

Khi khởi chạy ứng dụng, ứng dụng sẽ khởi động dịch vụ cần thực hiện một số tác vụ mạng. Sau khi nhắm mục tiêu API cấp 26, ứng dụng của tôi không khởi động được dịch vụ trên Android 8.0 trên nền.

Nguyên nhân bởi: java.lang.IllegalStateException: Không được phép bắt đầu dịch vụ Intent {cmp = my.app.tt / com.my.service}: ứng dụng nằm trong nền uid UidRecord {90372b1 u0a136 CEM idle procs: 1 seq , 0)}

theo tôi hiểu nó liên quan đến: Giới hạn thực hiện nền

Phương thức startService () hiện ném IllegalStateException nếu một ứng dụng nhắm mục tiêu Android 8.0 cố gắng sử dụng phương thức đó trong tình huống khi không được phép tạo dịch vụ nền.

" trong một tình huống khi nó không được phép " - ý nghĩa thực sự của nó là gì ?? Và làm thế nào để khắc phục nó. Tôi không muốn đặt dịch vụ của mình là "tiền cảnh"


4
Điều đó có nghĩa là bạn không thể bắt đầu dịch vụ khi ứng dụng của bạn ở chế độ nền
Tim

22
điều này không liên quan gì đến quyền hạn thời gian chạy
Tim

10
Sử dụng startForegroundService()thay vì startService().
ếchatto

2
Bạn có thể thử sử dụng targetSdkVersion 25 nhưng biên dịch với compileSdkVersion 26. Bằng cách này bạn có thể sử dụng các lớp mới từ Android 8 và thư viện suppport mới nhất nhưng ứng dụng của bạn sẽ không bị giới hạn bởi Giới hạn thực thi nền.
Kacper Dziubek

2
@KacperDziubek Điều đó sẽ hoạt động nhưng là một giải pháp tạm thời vì nó sẽ được yêu cầu nhắm mục tiêu SDK26 vào mùa thu năm 2018.
RightHandedMonkey

Câu trả lời:


194

Các tình huống được phép là một danh sách trắng tạm thời trong đó dịch vụ nền hoạt động giống như trước Android O.

Trong một số trường hợp nhất định, một ứng dụng nền được đặt trên danh sách trắng tạm thời trong vài phút. Mặc dù một ứng dụng nằm trong danh sách trắng, nó có thể khởi chạy các dịch vụ mà không giới hạn và các dịch vụ nền của nó được phép chạy. Một ứng dụng được đặt trên danh sách trắng khi nó xử lý một tác vụ hiển thị cho người dùng, chẳng hạn như:

  • Xử lý tin nhắn Firebase Cloud Messaging (FCM) ưu tiên cao.
  • Nhận được phát sóng, chẳng hạn như tin nhắn SMS / MMS.
  • Thực hiện PendingIntent từ một thông báo.
  • Bắt đầu một VpnService trước khi ứng dụng VPN tự quảng bá cho tiền cảnh.

Nguồn: https://developer.android.com/about/versions/oreo/background.html

Vì vậy, nói cách khác, nếu dịch vụ nền của bạn không đáp ứng các yêu cầu trong danh sách trắng, bạn phải sử dụng JobScheduler mới . Về cơ bản, nó giống như một dịch vụ nền, nhưng nó được gọi định kỳ thay vì chạy liên tục trong nền.

Nếu bạn đang sử dụng IntentService, bạn có thể thay đổi thành JobIntentService. Xem câu trả lời của @ kosev bên dưới .


Tôi đang gặp sự cố sau khi tôi muốn bắt đầu dịch vụ ngay sau khi tôi nhận được tin nhắn GCM của "cao thủ". Tôi vẫn sử dụng GCM: "com.google.android.gms: play-services-gcm: 11.4.2", không phải 'com.google.firebase: firebase-Messaging: 11.4.2'. Không chắc nó có vấn đề gì nữa ..
Alex Radzishevsky

"Về cơ bản nó giống như một dịch vụ nền, nhưng nó được gọi định kỳ thay vì chạy liên tục trong nền." - không chắc ý của bạn là gì, vì các dịch vụ Android chưa bao giờ được thực thi liên tục. Họ bắt đầu, chạy, rồi tắt máy.
Melllvar

2
FirebaseInstanceIdService onTokenRefreshphương thức của nó là một thông điệp FCM ưu tiên cao?
Dây Rehn

@phnmnn không, GCMTaskService không thực sự theo FCM do đó chúng không hoạt động.
Abhinav Upadhyay

4
Bạn không nên sử dụng WorkManager (ở đây: developer.android.com/topic/lologists/arch architecture / workmanager) thay vì JobScheduler hoặc những người khác? Ý tôi là: youtu.be/IrKoBFLwTN0
nhà phát triển Android

255

Tôi có giải pháp. Đối với các thiết bị trước 8.0, bạn chỉ cần sử dụng startService(), nhưng đối với các thiết bị sau 7.0, bạn phải sử dụng startForgroundService(). Đây là mẫu cho mã để bắt đầu dịch vụ.

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

Và trong lớp dịch vụ, vui lòng thêm mã bên dưới để thông báo:

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

Trong đó O là phiên bản Android 26.


9
Một dịch vụ nền tảng là một cái gì đó mà người dùng sẽ nhận thức được và cần thông báo. Nó cũng sẽ ANR nếu nó chạy quá lâu. Vì vậy, nó không thực sự là một câu trả lời phù hợp nếu ứng dụng đang chạy ẩn.
SimonH

80
Có một ContextCompat.startForegroundService(...)lib hỗ trợ có thể được sử dụng thay thế.
jayeffkay

37
Đó không phải là một giải pháp.
JacksOnF1re

17
Tôi cũng đồng ý rằng đây không phải là một giải pháp. Đó là một cách giải quyết và nó có ích, nhưng các giới hạn nền trong Oreo đã được giới thiệu vì một lý do. Bỏ qua những giới hạn theo cách này chắc chắn không phải là cách tiếp cận chính xác (mặc dù nó hoạt động). Cách tốt nhất là sử dụng JobScheduler (tham khảo câu trả lời được chấp nhận).
Vratislav Jindra

6
Tôi không nghĩ rằng đó sẽ là một trải nghiệm người dùng tốt nếu bạn phải hiển thị một thông báo tiền cảnh trống. Xem xét thực tế rằng bạn phải. - Android 8.0 giới thiệu phương thức mới startForegroundService () để bắt đầu một dịch vụ mới ở nền trước. Sau khi hệ thống tạo dịch vụ, ứng dụng có năm giây để gọi phương thức startForeground () của dịch vụ để hiển thị thông báo hiển thị cho người dùng của dịch vụ mới. Nếu ứng dụng không gọi startForeground () trong thời gian giới hạn, hệ thống sẽ dừng dịch vụ và tuyên bố ứng dụng là ANR.
gót chân

85

Cách tốt nhất là sử dụng JobIntentService mới cho Oreo hoặc các dịch vụ cũ nếu không có sẵn.

Khai báo trong bảng kê khai của bạn:

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

Và trong dịch vụ của bạn, bạn phải thay thế onHandleIntent bằng 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
    }

}

Sau đó, bạn bắt đầu dịch vụ của mình với:

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


Làm thế nào bạn có thể gọi một phương thức không tĩnh bên trong một phương thức tĩnh? Bạn có thể vui lòng giải thích?
Maddy

@Maddy enqueueWork(...)là một phương thức tĩnh.
hgoebl

2
Nơi bạn sẽ gọi YourService.enqueueWork (bối cảnh, ý định mới ()); ? Từ máy thu phát sóng?
TheLearner

Tôi không tin rằng đây là giải pháp dễ nhất. Xem bình luận của tôi dưới đây về WorkManager. Nó sử dụng JobIntentService khi thích hợp, nhưng nó có tấm nồi hơi ít hơn nhiều.
TALE

36

Nếu dịch vụ đang chạy trong một luồng nền bằng cách mở rộng IntentService, bạn có thể thay thế IntentServicebằngJobIntentService được cung cấp như một phần của Thư viện hỗ trợ Android

Ưu điểm của việc sử dụng JobIntentServicelà, nó hoạt động như mộtIntentService thiết bị trước O và trên O và cao hơn, nó gửi nó như một công việc

JobSchedulercũng có thể được sử dụng cho các công việc định kỳ / theo yêu cầu. Nhưng, đảm bảo xử lý khả năng tương thích ngược vì JobSchedulerAPI chỉ khả dụng từ API 21


1
Vấn đề với JobIntentService là Android có thể lên lịch cho công việc của bạn một cách khá tùy tiện và nó không thể được bắt đầu một cách hoàn toàn mà không có sự mày mò nào đó, không giống như IntentService. Xem stackoverflow.com/questions/52479262/
Mạnh

15

Trong Oreo Android xác định giới hạn cho các dịch vụ nền .

Để cải thiện trải nghiệm người dùng, Android 8.0 (API cấp 26) áp đặt các giới hạn đối với những gì ứng dụng có thể làm trong khi chạy trong nền.

Tuy nhiên, nếu bạn luôn cần chạy dịch vụ, thì bạn có thể sử dụng dịch vụ nền trước.

Giới hạn dịch vụ nền: Trong khi một ứng dụng không hoạt động, có những giới hạn đối với việc sử dụng dịch vụ nền. Điều này không áp dụng cho các dịch vụ tiền cảnh, dễ nhận thấy hơn đối với người dùng.

Vì vậy, bạn có thể thực hiện một dịch vụ tiền cảnh . Bạn sẽ cần hiển thị thông báo cho người dùng khi dịch vụ của bạn đang chạy. Xem câu trả lời này (Có nhiều người khác)

Một giải pháp nếu -

bạn không muốn một thông báo cho dịch vụ của bạn?

Bạn có thể thực hiện nhiệm vụ định kỳ, 1. nó bắt đầu dịch vụ của bạn, 2. dịch vụ sẽ thực hiện công việc của nó và 3. tự dừng lại. Bằng cách này, ứng dụng của bạn sẽ không được coi là hết pin.

Bạn có thể sử dụng tác vụ định kỳ với Trình quản lý báo thức , Trình lập lịch công việc , Evernote-Jobs hoặc Trình quản lý công việc .

  • Quản lý công việc là giải pháp tốt nhất cho các nhiệm vụ định kỳ. Được giới thiệu với Thành phần Kiến trúc Android .
  • Không giống như Trình lập lịch trình công việc (chỉ> 21 API), nó sẽ hoạt động cho tất cả các phiên bản.
  • Ngoài ra, nó bắt đầu hoạt động sau chế độ Doze-Standby .
  • Tạo bộ thu khởi động Android để lập lịch dịch vụ sau khi khởi động thiết bị.

Tôi đã thử nghiệm dịch vụ chạy mãi mãi với Work-Manager.


WorkManager có vẻ là cách tốt nhất để đi, giả sử công việc không phải thực hiện ngay lập tức. Nó tương thích ngược với API 14, sử dụng JobScheduler trên các thiết bị có API 23+ và kết hợp BroadcastReceiver + AlarmManager trên các thiết bị có API 14-22
James Allen

điều quan trọng nhất trong WorkManager là WorkManager dành cho các nhiệm vụ có thể bảo vệ được, đó là, không bắt buộc phải chạy ngay lập tức
touhid udoy

13

Vâng, đó là bởi vì bạn không thể bắt đầu các dịch vụ trong nền nữa trên API 26. Vì vậy, bạn có thể bắt đầu Dịch vụ tiền cảnh trên API 26.

Bạn sẽ phải sử dụng

ContextCompat.startForegroundService(...)

và gửi thông báo trong khi xử lý rò rỉ.


1
OP đặc biệt nói rằng anh ấy không muốn làm tiền cảnh. Điều này nên được đặt dưới dạng một nhận xét hoặc là một phần của câu trả lời đầy đủ hơn.
Ricardo A.

7

Như @kosev đã nói trong câu trả lời của mình, bạn có thể sử dụng JobIntentService. Nhưng tôi sử dụng một giải pháp thay thế - tôi bắt IllegalStateException và bắt đầu dịch vụ như tiền cảnh. Ví dụ: chức năng này bắt đầu dịch vụ của tôi:

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

và khi tôi xử lý ý định, tôi sẽ làm như vậy:

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

Tôi thích giải pháp thử bắt của bạn. Đối với tôi nó là một giải pháp bởi vì đôi khi context.startServicelàm việc ở chế độ nền - đôi khi không - ngoại hình này như là cách duy nhất tốt nhất nếu không bạn phải thực hiện nhiều mã trong lớp học chính của bạn extending Applicationimplementing ActivityLifecycleCallbacksvà theo dõi cho dù ứng dụng là trong foreground hoặc background và bắt đầu ý định của bạn phù hợp.
Pierre

Ngoại lệ này có thể bị bắt không?
thecr0w

5

Từ ghi chú phát hành căn cứ , họ nói rằng hỗ trợ cho Android O được phát hành lần đầu tiên vào 10.2.1 (mặc dù tôi khuyên bạn nên sử dụng phiên bản mới nhất).

vui lòng thêm phụ thuộc tin nhắn firebase mới cho Android O

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

nâng cấp dịch vụ google play và kho google nếu cần.


Điều này không trả lời câu hỏi, cũng như câu hỏi không liên quan gì đến căn cứ hỏa lực. Nó nên được đặt như là một bình luận.
Ricardo A.

5

Nếu bất kỳ ý định nào trước đây hoạt động tốt khi ứng dụng ở chế độ nền, thì nó sẽ không còn là vấn đề nữa từ Android 8 trở lên. Chỉ đề cập đến ý định phải thực hiện một số xử lý khi ứng dụng ở chế độ nền.

Các bước dưới đây phải được tuân theo:

  1. Trên mục đích đề cập nên được sử dụng JobIntentServicethay vì IntentService.
  2. Lớp mở rộng JobIntentServicesẽ thực hiện onHandleWork(@NonNull Intent intent)phương thức - và nên có bên dưới phương thức, sẽ gọi onHandleWorkphương thức:

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, xyz.class, 123, work);
    }
  3. Gọi enqueueWork(Context, intent)từ lớp nơi ý định của bạn được xác định.

    Mã mẫu:

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

Lớp dưới đây trước đây đã mở rộng lớp Dịch vụ

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-compatlà cần thiết cho JobIntentService- tôi sử dụng 26.1.0 V.

  2. Quan trọng nhất là đảm bảo phiên bản thư viện Firebase ít nhất là 10.2.1, tôi đã gặp vấn đề với 10.2.0- nếu bạn có bất kỳ!

  3. Tệp kê khai của bạn phải có quyền dưới đây cho lớp Dịch vụ:

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

Hi vọng điêu nay co ich.


4

Tôi thấy rất nhiều phản hồi khuyên bạn chỉ nên sử dụng Dịch vụ tiền cảnh. Để sử dụng Dịch vụ tiền cảnh, phải có một thông báo liên quan đến nó. Người dùng sẽ thấy thông báo này. Tùy thuộc vào tình huống, họ có thể trở nên khó chịu với ứng dụng của bạn và gỡ cài đặt nó.

Giải pháp đơn giản nhất là sử dụng Thành phần Kiến trúc mới có tên WorkManager. Bạn có thể kiểm tra các tài liệu ở đây: https://developer.android.com/topic/lologists/arch architecture / workmanager /

Bạn chỉ cần định nghĩa lớp worker của bạn mở rộng Worker.

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

Sau đó, bạn lên lịch khi bạn muốn chạy nó.

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

Dễ dàng! Có rất nhiều cách bạn có thể cấu hình công nhân. Nó hỗ trợ các công việc định kỳ và thậm chí bạn có thể làm những việc phức tạp như xích nếu bạn cần. Hi vọng điêu nay co ich.


3
Hiện tại WorkManager vẫn là alpha.
pzulw

3
Ngày 05 tháng 3 năm 2019 - WorkManager 1.0.0 phát hành ổn định.
phnmnn

nên sử dụng WorkManager thay vì sử dụng dịch vụ giao nhau hoặc JobIntentService
sivaBE35

1
WorkManager is intended for tasks that are deferrable—that is, not required to run immediately... Tuy nhiên, có thể dễ nhất, ứng dụng của tôi cần một dịch vụ nền thực hiện các yêu cầu của người dùng ngay lập tức!
Ai đó ở đâu đó

Nếu bạn yêu cầu nhiệm vụ phải hoàn thành ngay lập tức, thì bạn nên sử dụng dịch vụ nền trước. Người dùng sẽ thấy một thông báo và biết rằng bạn đang làm việc. Kiểm tra các tài liệu nếu bạn cần trợ giúp để quyết định sử dụng cái gì. Họ có một hướng dẫn khá tốt để xử lý nền. developer.android.com/guide/background
TALE

4

Giải pháp thay thế bằng cách sử dụng JobScheduler, nó có thể bắt đầu dịch vụ ở chế độ nền trong khoảng thời gian thường xuyên.

Đầu tiên tạo lớp có tên là 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());
   }
  }

Sau đó, tạo lớp JobService có tên là 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;
  }
 }

Sau đó, lớp Người nhận BroadCast có tên 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);
 }
}

Cập nhật tệp Manifest với mã lớp của dịch vụ và người nhận

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

Còn lại trình khởi chạy main_intent sang tệp mainActivity.java được tạo theo mặc định và các thay đổi trong tệp 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 !! Dịch vụ nền bắt đầu mà không có dịch vụ Tiền cảnh


2

Nếu bạn đang chạy mã trên 8.0 thì ứng dụng sẽ bị sập. Vì vậy, bắt đầu dịch vụ ở phía trước. Nếu dưới 8.0 sử dụng điều này:

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

Nếu trên hoặc 8.0 thì sử dụng cái này:

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

Bạn chỉ nên sử dụng các dịch vụ Tiền cảnh cho các trường hợp người dùng cần lưu ý rằng một dịch vụ đang chạy. Ví dụ điển hình là để chơi nhạc trong nền. Có những trường hợp khác có ý nghĩa, nhưng bạn không nên chuyển đổi tất cả các dịch vụ của mình sang dịch vụ Tiền cảnh. Cân nhắc chuyển đổi các dịch vụ của bạn để sử dụng WorkManager từ các thành phần Kiến trúc của Google khi bạn chỉ cần thực hiện một số công việc trong nền và được đảm bảo rằng nó sẽ chạy.
CÂU

startForegroundService yêu cầu sự cho phép, nếu không java.lang.SecurityException: Permission Denial: startForeground from pid=13708, uid=10087 requires android.permission.FOREGROUND_SERVICE. Khắc phục tại stackoverflow.com/a/52382711/550471
Ai đó ở đâu đó vào

1

nếu bạn đã tích hợp thông báo đẩy tin nhắn firebase rồi,

Thêm phụ thuộc nhắn tin firebase mới / cập nhật cho Android O (Android 8.0), do Giới hạn thực thi nền .

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

nâng cấp dịch vụ google play và kho google nếu cần.

Cập nhật:

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

0

Sử dụng startForegroundService()thay vì startService() và đừng quên tạo startForeground(1,new Notification());trong dịch vụ của bạn trong vòng 5 giây kể từ khi bắt đầu dịch vụ.


2
Có vẻ như Thông báo mới () không hoạt động từ Android 8.1; Bạn nên tạo kênh để thông báo: stackoverflow.com/a/47533338/1048087
Prizoff 29/07/18

0

Do số phiếu gây tranh cãi về câu trả lời này (+ 4 / -4 khi chỉnh sửa này), HÃY NHÌN VÀO CÁC TRẢ LỜI KHÁC ĐẦU TIÊN VÀ SỬ DỤNG NÀY CHỈ LÀ MỘT CUỘC ĐỜI CUỐI CÙNG . Tôi chỉ sử dụng điều này một lần cho một ứng dụng mạng chạy bằng root và tôi đồng ý với ý kiến ​​chung rằng giải pháp này không nên được sử dụng trong các trường hợp thông thường.

Câu trả lời gốc dưới đây:

Các câu trả lời khác đều đúng, nhưng tôi muốn chỉ ra rằng một cách khác để giải quyết vấn đề này là yêu cầu người dùng tắt tối ưu hóa pin cho ứng dụng của bạn (đây thường không phải là ý tưởng hay trừ khi ứng dụng của bạn có liên quan đến hệ thống). Xem câu trả lời này để biết cách yêu cầu từ chối tối ưu hóa pin mà không khiến ứng dụng của bạn bị cấm trong Google Play.

Bạn cũng nên kiểm tra xem tối ưu hóa pin có bị tắt trong máy thu của mình không để tránh sự cố thông qua:

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
Yêu cầu người dùng của bạn cho phép bạn vượt qua miễn phí bằng cách sử dụng nhiều pin nhất có thể không phải là một giải pháp tốt. Xem xét chuyển đổi mã của bạn thành một giải pháp thân thiện với pin hơn. Người dùng của bạn sẽ cảm ơn bạn.
CÂU

5
@TALE Không phải mọi dịch vụ nền đều có thể được sử dụng JobSchedulervà sử dụng pin . Một số ứng dụng cần hoạt động ở mức thấp hơn các ứng dụng đồng bộ hóa thông thường. Đây là một giải pháp thay thế khi nó không hoạt động.
Mygod

-17

không sử dụng trong onStartCommand:

return START_NOT_STICKY

chỉ cần thay đổi nó thành:

return START_STICKY

và nó sẽ hoạt động

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.