Làm cách nào để kiểm tra xem dịch vụ nền có đang chạy không?
Tôi muốn một hoạt động Android thay đổi trạng thái của dịch vụ - nó cho phép tôi bật nó nếu nó tắt và tắt nếu nó được bật.
getRunningTasks()
, có lẽ nó sẽ như vậy.
Làm cách nào để kiểm tra xem dịch vụ nền có đang chạy không?
Tôi muốn một hoạt động Android thay đổi trạng thái của dịch vụ - nó cho phép tôi bật nó nếu nó tắt và tắt nếu nó được bật.
getRunningTasks()
, có lẽ nó sẽ như vậy.
Câu trả lời:
Tôi đã có cùng một vấn đề cách đây không lâu. Vì dịch vụ của tôi là cục bộ, nên cuối cùng tôi chỉ sử dụng một trường tĩnh trong lớp dịch vụ để chuyển trạng thái, như được mô tả bởi hackbod ở đây
EDIT (cho hồ sơ):
Đây là giải pháp được đề xuất bởi hackbod:
Nếu mã máy khách và máy chủ của bạn là một phần của cùng một .apk và bạn đang ràng buộc với dịch vụ với một Intent cụ thể (một mã xác định lớp dịch vụ chính xác), thì bạn có thể chỉ cần đặt dịch vụ của mình đặt một biến toàn cục khi nó đang chạy khách hàng của bạn có thể kiểm tra.
Chúng tôi cố tình không có API để kiểm tra xem dịch vụ có đang chạy hay không, vì gần như không gặp sự cố, khi bạn muốn làm điều gì đó giống như bạn kết thúc với điều kiện cuộc đua trong mã của mình.
onDestroy()
không được gọi. Vì vậy, biến tĩnh không thể được cập nhật trong một kịch bản như vậy dẫn đến hành vi không nhất quán.
Tôi sử dụng những điều sau đây từ bên trong một hoạt động:
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Và tôi gọi nó bằng cách sử dụng:
isMyServiceRunning(MyService.class)
Điều này hoạt động đáng tin cậy, bởi vì nó dựa trên thông tin về các dịch vụ đang chạy được cung cấp bởi hệ điều hành Android thông qua ActivityManager # getRastyService .
Tất cả các cách tiếp cận sử dụng các sự kiện onDestroy hoặc onSometing hoặc Binder hoặc biến tĩnh sẽ không hoạt động đáng tin cậy vì là nhà phát triển mà bạn không bao giờ biết, khi Android quyết định giết tiến trình của bạn hoặc gọi lại trong số các cuộc gọi lại được đề cập hay không. Vui lòng lưu ý cột "có thể tiêu diệt" trong bảng sự kiện vòng đời trong tài liệu Android.
getRunningServices
không được dùng nữa. Câu trả lời này cần một bản cập nhật cho phiên bản mới hơn.
Hiểu rồi!
Bạn PHẢI gọi startService()
cho dịch vụ của bạn được đăng ký đúng cách và thông qua BIND_AUTO_CREATE
sẽ không đủ.
Intent bindIntent = new Intent(this,ServiceTask.class);
startService(bindIntent);
bindService(bindIntent,mConnection,0);
Và bây giờ là lớp ServiceTools:
public class ServiceTools {
private static String LOG_TAG = ServiceTools.class.getName();
public static boolean isServiceRunning(String serviceClassName){
final ActivityManager activityManager = (ActivityManager)Application.getContext().getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
return true;
}
}
return false;
}
}
Một bổ sung nhỏ là:
Mục tiêu của tôi là biết được một dịch vụ đang chạy mà không thực sự chạy nó nếu nó không chạy.
Gọi bindService hoặc gọi một ý định có thể bị dịch vụ bắt gặp không phải là một ý tưởng hay vì nó sẽ khởi động dịch vụ nếu nó không chạy.
Vì vậy, như miracle2k đã đề xuất, tốt nhất là có một trường tĩnh trong lớp dịch vụ để biết liệu dịch vụ đã được bắt đầu hay chưa.
Để làm cho nó thậm chí còn sạch hơn, tôi đề nghị chuyển đổi dịch vụ trong một singleton với việc tìm nạp rất rất lười biếng: đó là, không có sự khởi tạo nào ở tất cả các singleton cá thể thông qua các phương thức tĩnh. Phương thức getInstance tĩnh của dịch vụ / singleton của bạn chỉ trả về thể hiện của singleton nếu nó đã được tạo. Nhưng nó không thực sự bắt đầu hoặc kích thích bản thân singleton. Dịch vụ chỉ được bắt đầu thông qua các phương thức bắt đầu dịch vụ bình thường.
Sau đó, sẽ còn sạch hơn khi sửa đổi mẫu thiết kế singleton để đổi tên phương thức getInstance khó hiểu thành một cái gì đó giống như isInstanceCreated() : boolean
phương thức.
Mã sẽ trông như sau:
public class MyService extends Service
{
private static MyService instance = null;
public static boolean isInstanceCreated() {
return instance != null;
}//met
@Override
public void onCreate()
{
instance = this;
....
}//met
@Override
public void onDestroy()
{
instance = null;
...
}//met
}//class
Giải pháp này rất thanh lịch, nhưng nó chỉ phù hợp nếu bạn có quyền truy cập vào lớp dịch vụ và chỉ dành cho các lớp nằm bên cạnh ứng dụng / gói dịch vụ. Nếu các lớp của bạn nằm ngoài ứng dụng / gói dịch vụ thì bạn có thể truy vấn ActivityManager với các giới hạn được gạch chân bởi Pieter-Jan Van Robays.
Bạn có thể sử dụng cái này (Tôi chưa thử cái này, nhưng tôi hy vọng cái này hoạt động):
if(startService(someIntent) != null) {
Toast.makeText(getBaseContext(), "Service is already running", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getBaseContext(), "There is no service running, starting service..", Toast.LENGTH_SHORT).show();
}
Phương thức startService trả về một đối tượng ElementName nếu có một dịch vụ đang chạy. Nếu không, null sẽ được trả lại.
Xem phần mềm trừu tượng công khai startService (dịch vụ ý định) .
Điều này không giống như kiểm tra tôi nghĩ, vì nó bắt đầu dịch vụ, vì vậy bạn có thể thêm stopService(someIntent);
theo mã.
if(startService(someIntent) != null)
sẽ kiểm tra IsserviceRunning
nhưng điều đó cũng sẽ phát dịch vụ mới.
/**
* Check if the service is Running
* @param serviceClass the class of the Service
*
* @return true if the service is running otherwise false
*/
public boolean checkServiceRunning(Class<?> serviceClass){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
if (serviceClass.getName().equals(service.service.getClassName()))
{
return true;
}
}
return false;
}
Một trích xuất từ các tài liệu Android :
Giống như sendBroadcast (Intent) , nhưng nếu có bất kỳ máy thu nào cho Intent, chức năng này sẽ chặn và gửi ngay lập tức chúng trước khi quay trở lại.
Hãy nghĩ về vụ hack này là "ping"Service
. Vì chúng tôi có thể phát đồng bộ, chúng tôi có thể phát và nhận kết quả một cách đồng bộ , trên luồng UI.
Service
@Override
public void onCreate() {
LocalBroadcastManager
.getInstance(this)
.registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
//do not forget to deregister the receiver when the service is destroyed to avoid
//any potential memory leaks
}
private class ServiceEchoReceiver extends BroadcastReceiver {
public void onReceive (Context context, Intent intent) {
LocalBroadcastManager
.getInstance(this)
.sendBroadcastSync(new Intent("pong"));
}
}
Activity
bool serviceRunning = false;
protected void onCreate (Bundle savedInstanceState){
LocalBroadcastManager.getInstance(this).registerReceiver(pong, new IntentFilter("pong"));
LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("ping"));
if(!serviceRunning){
//run the service
}
}
private BroadcastReceiver pong = new BroadcastReceiver(){
public void onReceive (Context context, Intent intent) {
serviceRunning = true;
}
}
Người chiến thắng trong nhiều ứng dụng, tất nhiên, một lĩnh vực boolean tĩnh trên dịch vụ được thiết lập để true
ở Service.onCreate()
và để false
ở Service.onDestroy()
vì nó đơn giản hơn rất nhiều.
Tôi đã sửa đổi một chút một trong các giải pháp được trình bày ở trên, nhưng chuyển lớp thay vì tên chuỗi chung, để đảm bảo so sánh các chuỗi đi ra từ cùng một phương thức class.getName()
public class ServiceTools {
private static String LOG_TAG = ServiceTools.class.getName();
public static boolean isServiceRunning(Context context,Class<?> serviceClass){
final ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
Log.d(Constants.TAG, String.format("Service:%s", runningServiceInfo.service.getClassName()));
if (runningServiceInfo.service.getClassName().equals(serviceClass.getName())){
return true;
}
}
return false;
}
}
và sau đó
Boolean isServiceRunning = ServiceTools.isServiceRunning(
MainActivity.this.getApplicationContext(),
BackgroundIntentService.class);
Class<? extends Service>
Cách thích hợp để kiểm tra xem một dịch vụ có đang chạy hay không chỉ đơn giản là hỏi nó. Triển khai BroadcastReceiver trong dịch vụ của bạn để phản hồi các lệnh ping từ các hoạt động của bạn. Đăng ký BroadcastReceiver khi dịch vụ bắt đầu và hủy đăng ký khi dịch vụ bị hủy. Từ hoạt động của bạn (hoặc bất kỳ thành phần nào), hãy gửi ý định phát sóng cục bộ đến dịch vụ và nếu nó phản hồi, bạn sẽ biết nó đang chạy. Lưu ý sự khác biệt tinh tế giữa ACTION_PING và ACTION_PONG trong mã bên dưới.
public class PingableService extends Service
{
public static final String ACTION_PING = PingableService.class.getName() + ".PING";
public static final String ACTION_PONG = PingableService.class.getName() + ".PONG";
public int onStartCommand (Intent intent, int flags, int startId)
{
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(ACTION_PING));
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy ()
{
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver()
{
@Override
public void onReceive (Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_PING))
{
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
manager.sendBroadcast(new Intent(ACTION_PONG));
}
}
};
}
public class MyActivity extends Activity
{
private boolean isSvcRunning = false;
@Override
protected void onStart()
{
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
manager.registerReceiver(mReceiver, new IntentFilter(PingableService.ACTION_PONG));
// the service will respond to this broadcast only if it's running
manager.sendBroadcast(new Intent(PingableService.ACTION_PING));
super.onStart();
}
@Override
protected void onStop()
{
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onStop();
}
protected BroadcastReceiver mReceiver = new BroadcastReceiver()
{
@Override
public void onReceive (Context context, Intent intent)
{
// here you receive the response from the service
if (intent.getAction().equals(PingableService.ACTION_PONG))
{
isSvcRunning = true;
}
}
};
}
Tôi chỉ muốn thêm một ghi chú vào câu trả lời của @Snicolas. Các bước sau đây có thể được sử dụng để kiểm tra dịch vụ dừng có / không gọi onDestroy()
.
onDestroy()
được gọi: Chuyển đến Cài đặt -> Ứng dụng -> Dịch vụ đang chạy -> Chọn và dừng dịch vụ của bạn.
onDestroy()
không được gọi: Truy cập Cài đặt -> Ứng dụng -> Quản lý ứng dụng -> Chọn và "Buộc dừng" ứng dụng của bạn trong đó dịch vụ của bạn đang chạy. Tuy nhiên, vì ứng dụng của bạn bị dừng ở đây, nên chắc chắn các trường hợp dịch vụ cũng sẽ bị dừng.
Cuối cùng, tôi muốn đề cập rằng cách tiếp cận được đề cập ở đó bằng cách sử dụng một biến tĩnh trong lớp singleton đang làm việc cho tôi.
onDestroy
không phải lúc nào cũng được gọi trong dịch vụ nên điều này là vô ích!
Ví dụ: Chỉ cần chạy lại ứng dụng với một thay đổi từ Eclipse. Ứng dụng được thoát ra một cách mạnh mẽ bằng SIG: 9.
Trước hết, bạn không cố gắng tiếp cận dịch vụ bằng cách sử dụng Trình quản lý hoạt động. (Thảo luận ở đây )
Các dịch vụ có thể tự chạy, bị ràng buộc với một Hoạt động hoặc cả hai. Cách để kiểm tra Hoạt động nếu Dịch vụ của bạn có chạy hay không là bằng cách tạo giao diện (mở rộng Binder) nơi bạn khai báo các phương thức mà cả Hoạt động và Dịch vụ đều hiểu. Bạn có thể làm điều này bằng cách tạo Giao diện của riêng bạn, nơi bạn khai báo ví dụ "isServiceRasty ()". Sau đó, bạn có thể liên kết Hoạt động của mình với Dịch vụ của mình, chạy phương thức isServiceRasty (), Dịch vụ sẽ tự kiểm tra xem nó có chạy hay không và trả về một boolean cho Hoạt động của bạn.
Bạn cũng có thể sử dụng phương pháp này để dừng Dịch vụ của mình hoặc tương tác với nó theo cách khác.
Tôi đã sử dụng hướng dẫn này để tìm hiểu cách thực hiện kịch bản này trong ứng dụng của mình.
Một lần nữa, một giải pháp thay thế khác mà mọi người có thể tìm thấy sạch hơn nếu họ sử dụng các ý định đang chờ xử lý (ví dụ AlarmManager
:
public static boolean isRunning(Class<? extends Service> serviceClass) {
final Intent intent = new Intent(context, serviceClass);
return (PendingIntent.getService(context, CODE, intent, PendingIntent.FLAG_NO_CREATE) != null);
}
Đâu CODE
là một hằng số mà bạn xác định riêng trong lớp để xác định các ý định đang chờ xử lý liên quan đến dịch vụ của bạn.
Dưới đây là một hack thanh lịch bao gồm tất cả Ifs
. Điều này chỉ dành cho các dịch vụ địa phương.
public final class AService extends Service {
private static AService mInstance = null;
public static boolean isServiceCreated() {
try {
// If instance was not cleared but the service was destroyed an Exception will be thrown
return mInstance != null && mInstance.ping();
} catch (NullPointerException e) {
// destroyed/not-started
return false;
}
}
/**
* Simply returns true. If the service is still active, this method will be accessible.
* @return
*/
private boolean ping() {
return true;
}
@Override
public void onCreate() {
mInstance = this;
}
@Override
public void onDestroy() {
mInstance = null;
}
}
Và sau đó:
if(AService.isServiceCreated()){
...
}else{
startService(...);
}
Phiên bản Xamarin C #:
private bool isMyServiceRunning(System.Type cls)
{
ActivityManager manager = (ActivityManager)GetSystemService(Context.ActivityService);
foreach (var service in manager.GetRunningServices(int.MaxValue)) {
if (service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName)) {
return true;
}
}
return false;
}
GetSystemService
.
Đối với trường hợp sử dụng được đưa ra ở đây, chúng ta có thể chỉ cần sử dụng stopService()
giá trị trả về của phương thức. Nó trả về true
nếu có tồn tại dịch vụ được chỉ định và nó bị giết. Khác nó trở lại false
. Vì vậy, bạn có thể khởi động lại dịch vụ nếu kết quả false
khác, đảm bảo rằng dịch vụ hiện tại đã bị dừng. :) Sẽ tốt hơn nếu bạn có cái nhìn này .
Một cách tiếp cận khác sử dụng kotlin. Lấy cảm hứng từ câu trả lời của người dùng khác
fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return manager.getRunningServices(Integer.MAX_VALUE)
.any { it.service.className == serviceClass.name }
}
Như phần mở rộng kotlin
fun Context.isMyServiceRunning(serviceClass: Class<*>): Boolean {
val manager = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return manager.getRunningServices(Integer.MAX_VALUE)
.any { it.service.className == serviceClass.name }
}
Sử dụng
context.isMyServiceRunning(MyService::class.java)
Trong kotlin, bạn có thể thêm biến boolean trong đối tượng đồng hành và kiểm tra giá trị của nó từ bất kỳ lớp nào bạn muốn:
companion object{
var isRuning = false
}
Thay đổi giá trị khi dịch vụ được tạo và hủy
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
Trong Lớp con dịch vụ của bạn Sử dụng Boolean tĩnh để có trạng thái Dịch vụ như được minh họa bên dưới.
Dịch vụ của tôi
class MyService : Service() {
override fun onCreate() {
super.onCreate()
isServiceStarted = true
}
override fun onDestroy() {
super.onDestroy()
isServiceStarted = false
}
companion object {
var isServiceStarted = false
}
}
ChínhActivity.kt
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val serviceStarted = FileObserverService.isServiceStarted
if (!serviceStarted) {
val startFileObserverService = Intent(this, FileObserverService::class.java)
ContextCompat.startForegroundService(this, startFileObserverService)
}
}
}
Đối với kotlin, bạn có thể sử dụng mã dưới đây.
fun isMyServiceRunning(calssObj: Class<SERVICE_CALL_NAME>): Boolean {
val manager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (calssObj.getName().equals(service.service.getClassName())) {
return true
}
}
return false
}
Phản ứng của geekQ nhưng trong lớp Kotlin. Cảm ơn geekQ
fun isMyServiceRunning(serviceClass : Class<*> ) : Boolean{
var manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.name.equals(service.service.className)) {
return true
}
}
return false
}
Cuộc gọi
isMyServiceRunning(NewService::class.java)
ActivityManager.getRunningServices
không được dùng nữa kể từ Android O
Có thể có một số dịch vụ có cùng tên lớp.
Tôi vừa mới tạo hai ứng dụng. Tên gói của ứng dụng đầu tiên là com.example.mock
. Tôi đã tạo một gói con được gọi lorem
trong ứng dụng và một dịch vụ được gọi Mock2Service
. Vì vậy, tên đầy đủ của nó là com.example.mock.lorem.Mock2Service
.
Sau đó, tôi tạo ra ứng dụng thứ hai và một dịch vụ được gọi là Mock2Service
. Tên gói của ứng dụng thứ hai là com.example.mock.lorem
. Tên đầy đủ của dịch vụ làcom.example.mock.lorem.Mock2Service
, quá.
Đây là đầu ra logcat của tôi.
03-27 12:02:19.985: D/TAG(32155): Mock-01: com.example.mock.lorem.Mock2Service
03-27 12:02:33.755: D/TAG(32277): Mock-02: com.example.mock.lorem.Mock2Service
Một ý tưởng tốt hơn là để so sánh ComponentName
trường hợp vì equals()
cácComponentName
so sánh cả hai tên gói và tên lớp. Và không thể có hai ứng dụng có cùng tên gói được cài đặt trên thiết bị.
Phương thức equals () của ComponentName
.
@Override
public boolean equals(Object obj) {
try {
if (obj != null) {
ComponentName other = (ComponentName)obj;
// Note: no null checks, because mPackage and mClass can
// never be null.
return mPackage.equals(other.mPackage)
&& mClass.equals(other.mClass);
}
} catch (ClassCastException e) {
}
return false;
}
Vui lòng sử dụng mã này.
if (isMyServiceRunning(MainActivity.this, xyzService.class)) { // Service class name
// Service running
} else {
// Service Stop
}
public static boolean isMyServiceRunning(Activity activity, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Điều này áp dụng nhiều hơn đối với gỡ lỗi Dịch vụ Ý định vì chúng sinh ra một luồng, nhưng cũng có thể hoạt động cho các dịch vụ thông thường. Tôi tìm thấy chủ đề này nhờ Belling
Trong trường hợp của tôi, tôi đã chơi xung quanh với trình gỡ lỗi và tìm thấy chế độ xem luồng. Nó trông giống như biểu tượng dấu đầu dòng trong MS Word. Dù sao, bạn không cần phải ở chế độ gỡ lỗi để sử dụng nó. Bấm vào quá trình và bấm vào nút đó. Bất kỳ Dịch vụ Ý định nào sẽ hiển thị trong khi họ đang chạy, ít nhất là trên trình giả lập.
Nếu dịch vụ thuộc về một quy trình hoặc APK khác, hãy sử dụng giải pháp dựa trên Trình quản lý hoạt động.
Nếu bạn có quyền truy cập vào nguồn của nó, chỉ cần sử dụng giải pháp dựa trên trường tĩnh. Nhưng thay vì sử dụng boolean, tôi sẽ đề nghị sử dụng đối tượng Date. Trong khi dịch vụ đang chạy, chỉ cần cập nhật giá trị của nó thành 'ngay bây giờ' và khi hoàn thành, đặt thành không. Từ hoạt động bạn có thể kiểm tra xem null của nó hay ngày quá cũ có nghĩa là nó không chạy.
Bạn cũng có thể gửi thông báo quảng bá từ dịch vụ của mình cho biết đang chạy cùng thông tin khác như tiến trình.
Bên trong TheServiceClass xác định:
public static Boolean serviceRunning = false;
Sau đó, trong onStartCommand (...)
public int onStartCommand(Intent intent, int flags, int startId) {
serviceRunning = true;
...
}
@Override
public void onDestroy()
{
serviceRunning = false;
}
Sau đó, gọi if(TheServiceClass.serviceRunning == true)
từ bất kỳ lớp học.
stopService
. Ít nhất là cho các dịch vụ Ý định. onDestroy()
sẽ được gọi ngay lập tức, nhưng onHandleIntent()
vẫn sẽ được chạy
sử dụng đơn giản liên kết với không tạo tự động - xem ps. và cập nhật ...
public abstract class Context {
...
/*
* @return {true} If you have successfully bound to the service,
* {false} is returned if the connection is not made
* so you will not receive the service object.
*/
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
thí dụ :
Intent bindIntent = new Intent(context, Class<Service>);
boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);
tại sao không sử dụng getRastyService ()
List<ActivityManager.RunningServiceInfo> getRunningServices (int maxNum)
Return a list of the services that are currently running.
Lưu ý: phương pháp này chỉ dành cho gỡ lỗi hoặc triển khai giao diện người dùng loại quản lý dịch vụ.
ps. tài liệu android là sai lệch tôi đã mở một vấn đề trên google tracker để loại bỏ mọi nghi ngờ:
https://issuetracker.google.com/issues/68908332
như chúng ta có thể thấy dịch vụ liên kết thực sự gọi một giao dịch thông qua liên kết ActivityManager thông qua các liên kết bộ đệm dịch vụ - tôi không theo dõi dịch vụ nào chịu trách nhiệm liên kết nhưng vì chúng ta có thể thấy kết quả của liên kết là:
int res = ActivityManagerNative.getDefault().bindService(...);
return res != 0;
giao dịch được thực hiện thông qua chất kết dính:
ServiceManager.getService("activity");
kế tiếp:
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
cái này được đặt trong ActivityThread thông qua:
public final void bindApplication(...) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
cái này được gọi trong ActivityManagerService trong phương thức:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
thread.bindApplication(... , getCommonServicesLocked(),...)
sau đó:
private HashMap<String, IBinder> getCommonServicesLocked() {
nhưng không có "hoạt động" chỉ có gói cửa sổ và báo động ..
vì vậy chúng tôi cần quay lại để gọi:
return getIServiceManager().getService(name);
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
điều này thực hiện cuộc gọi thông qua:
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
dẫn đến:
BinderInternal.getContextObject()
và đây là phương thức riêng ....
/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
public static final native IBinder getContextObject();
Bây giờ tôi không có thời gian để đào c vì vậy cho đến khi tôi mổ xẻ cuộc gọi, tôi tạm dừng câu trả lời của mình.
nhưng cách tốt nhất để kiểm tra nếu dịch vụ đang chạy là tạo liên kết (nếu liên kết không được tạo thì không tồn tại dịch vụ) - và truy vấn dịch vụ về trạng thái của nó thông qua liên kết (sử dụng cờ nội bộ được lưu trữ trên trạng thái).
tôi tìm thấy những điều thú vị:
/**
* Provide a binder to an already-bound service. This method is synchronous
* and will not start the target service if it is not present, so it is safe
* to call from {@link #onReceive}.
*
* For peekService() to return a non null {@link android.os.IBinder} interface
* the service must have published it before. In other words some component
* must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
*
* @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
* @param service Identifies the already-bound service you wish to use. See
* {@link android.content.Context#bindService(Intent, ServiceConnection, int)}
* for more information.
*/
public IBinder peekService(Context myContext, Intent service) {
IActivityManager am = ActivityManager.getService();
IBinder binder = null;
try {
service.prepareToLeaveProcess(myContext);
binder = am.peekService(service, service.resolveTypeIfNeeded(
myContext.getContentResolver()), myContext.getOpPackageName());
} catch (RemoteException e) {
}
return binder;
}
Nói ngắn gọn :)
"Cung cấp một chất kết dính cho một dịch vụ đã bị ràng buộc. Phương pháp này là đồng bộ và sẽ không bắt đầu dịch vụ đích nếu nó không có mặt."
dịch vụ IBinder peekService (dịch vụ ý định, String yetType, String CallPackage) ném RemoteException;
*
public static IBinder peekService(IBinder remote, Intent service, String resolvedType)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("android.app.IActivityManager");
service.writeToParcel(data, 0);
data.writeString(resolvedType);
remote.transact(android.os.IBinder.FIRST_CALL_TRANSACTION+84, data, reply, 0);
reply.readException();
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
*
Chuyển đổi kotlin của tôi của các ActivityManager::getRunningServices
câu trả lời dựa. Đặt chức năng này trong một hoạt động-
private fun isMyServiceRunning(serviceClass: Class<out Service>) =
(getSystemService(ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Int.MAX_VALUE)
?.map { it.service.className }
?.contains(serviceClass.name) ?: false
Bạn có thể sử dụng các tùy chọn này từ các tùy chọn Nhà phát triển Android để xem dịch vụ của bạn có còn chạy trong nền không.
1. Open Settings in your Android device.
2. Find Developer Options.
3. Find Running Services option.
4. Find your app icon.
5. You will then see all the service that belongs to your app running in the background.
Bình tĩnh nào mọi người ... :)
Tôi nghĩ rằng giải pháp phù hợp nhất là giữ một cặp khóa-giá trị trong SharedPreferences
khoảng nếu dịch vụ có chạy hay không.
Logic rất thẳng; tại bất kỳ vị trí mong muốn trong lớp dịch vụ của bạn; đặt một giá trị boolean sẽ đóng vai trò là cờ cho bạn về việc dịch vụ có chạy hay không. Sau đó đọc giá trị này bất cứ nơi nào bạn muốn trong ứng dụng của bạn.
Mã mẫu mà tôi đang sử dụng trong ứng dụng của mình ở bên dưới:
Trong lớp Dịch vụ của tôi (Một dịch vụ cho Luồng âm thanh), tôi thực thi mã sau đây khi dịch vụ kết thúc;
private void updatePlayerStatus(boolean isRadioPlaying)
{
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(getString(R.string.str_shared_file_radio_status_key), isRadioPlaying);
editor.commit();
}
Sau đó, trong bất kỳ hoạt động nào của ứng dụng của mình, tôi đang kiểm tra trạng thái của dịch vụ với sự trợ giúp của mã sau;
private boolean isRadioRunning() {
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE);
return sharedPref.getBoolean(getString(R.string.str_shared_file_radio_status_key), false);
}
Không có quyền đặc biệt, không có vòng lặp ... Cách dễ dàng, giải pháp sạch :)
Nếu bạn cần thêm thông tin, xin vui lòng tham khảo liên kết
Hi vọng điêu nay co ich.
onDestroy
không phải lúc nào cũng được gọi khi dịch vụ bị giết. Ví dụ: tôi đã thấy các dịch vụ của mình bị giết trong các tình huống bộ nhớ thấp mà không onDestroy
được gọi.