كيفية التحقق مما إذا كانت الخدمة تعمل على نظام Android؟

StackOverflow https://stackoverflow.com/questions/600207

  •  03-07-2019
  •  | 
  •  

سؤال

كيف يمكنني التحقق من تشغيل خدمة الخلفية؟

أريد نشاط Android يقوم بتبديل حالة الخدمة - فهو يتيح لي تشغيلها إذا كانت متوقفة عن التشغيل وإيقاف تشغيلها إذا كانت قيد التشغيل.

هل كانت مفيدة؟

المحلول

كان لي نفس المشكلة منذ وقت ليس ببعيد.نظرًا لأن خدمتي كانت محلية، انتهى بي الأمر ببساطة باستخدام حقل ثابت في فئة الخدمة لتبديل الحالة، كما هو موضح بواسطة hackbod هنا

تحرير (للسجل):

هذا هو الحل الذي اقترحه hackbod:

إذا كان رمز العميل والخادم الخاص بك جزءًا من نفس .APK وكنت مرتبطًا بالخدمة بقصد ملموس يمكن لعميلك التحقق.

ليس لدينا عن عمد واجهة برمجة تطبيقات للتحقق مما إذا كانت الخدمة قيد التشغيل لأنه ، دون أن تفشل تقريبًا ، عندما تريد أن تفعل شيئًا كهذا ينتهي بك الأمر إلى ظروف السباق في الكود الخاص بك.

نصائح أخرى

وأنا استخدم ما يلي من داخل النشاط:

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

وأنا أسميها باستخدام:

isMyServiceRunning(MyService.class)

وهذا يعمل بشكل صحيح، لأنه يقوم على معلومات حول تشغيل الخدمات التي يقدمها نظام التشغيل أندرويد من خلال <لأ href = "http://developer.android.com/reference/android/app/ActivityManager.html#getRunningServices ٪ 28int٪ 29 "> ActivityManager # getRunningServices .

وتسمى جميع المناهج باستخدام onDestroy أو الأحداث onSometing أو المجلدات أو متغيرات ثابتة لن تعمل بشكل صحيح لأنه كما مطور تعرف أبدا، عندما يقرر الروبوت لقتل العملية أو أي من الاسترجاعات المذكورة أم لا. يرجى ملاحظة عمود "killable" في أحداث دورة الحياة الجدول في وثائق الروبوت.

فهمتها!

أنت يجب يتصل startService() ليتم تسجيل خدمتك وتمريرها بشكل صحيح BIND_AUTO_CREATE لن يكفي.

Intent bindIntent = new Intent(this,ServiceTask.class);
startService(bindIntent);
bindService(bindIntent,mConnection,0);

والآن فئة 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;
     }
}

وومكملا صغير هو:

وهدفي هو معرفة الطقس والخدمة قيد التشغيل دون تشغيل actualy إذا لم يكن قيد التشغيل.

ودعوة bindService، أو الدعوة إلى وجود نية التي يمكن القبض عليهم من قبل الخدمة ليست فكرة جيدة ثم لأنها سوف تبدأ الخدمة إذا لم يكن قيد التشغيل.

وهكذا، كما اقترح miracle2k، والأفضل هو أن يكون حقل ثابت في فئة الخدمة لمعرفة ما إذا كان قد تم بدء تشغيل الخدمة أم لا.

لجعله أكثر نظافة، وأقترح لتحويل الخدمة في المفرد مع جلب جدا كسول جدا: هذا هو، لا يوجد مثيل في كل من في المفرد سبيل المثال من خلال أساليب ثابتة. طريقة getInstance ثابت خدمتكم / المفرد يعود فقط مثيل المفرد إذا كان قد تم إنشاؤه. ولكن لا يبدأ actualy أو instanciate والمفرد نفسها. بدء تشغيل الخدمة إلا من خلال أساليب بدء الخدمة العادية.

وسيكون بعد ذلك حتى أنظف لتعديل نمط تصميم المفرد لإعادة تسمية طريقة getInstance مربكة إلى ما يشبه طريقة isInstanceCreated() : boolean.

وسوف كود تبدو:

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

وهذا الحل هو أنيق، وإنما هو ذات الصلة فقط إذا كان لديك الوصول إلى فئة الخدمة وفقط لفئات iside التطبيق / حزمة الخدمة. إذا الفصول الدراسية خارج التطبيق خدمة / حزمة ثم هل يمكن الاستعلام عن ActivityManager مع القيود أكده بيتر يان فان Robays.

ويمكنك استخدام هذا (أنا لم يحاول هذا بعد، ولكن آمل أن يكون هذا يعمل):

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

وطريقة startService بإرجاع كائن ComponentName إذا كان هناك خدمة تعمل بالفعل. إن لم يكن، سيتم إرجاع قيمة خالية.

وانظر <م> مجردة العام ComponentName startService (خدمة النوايا) .

وهذه ليست مثل فحص أعتقد، لأنه بدء تشغيل الخدمة، بحيث يمكنك إضافة stopService(someIntent); بموجب قانون.

    public boolean checkServiceRunning(){
         ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) 
                {
                    if ("com.example.yourpackagename.YourServiceName"
                            .equals(service.service.getClassName())) 
                    {
                        return true;
                    }
                }
             return false;
    }

ولقد معدلة بشكل طفيف أحد الحلول الواردة أعلاه، ولكن اجتياز الدرجة بدلا من اسم سلسلة عام، وذلك للتأكد من مقارنة سلاسل تخرج من نفس طريقة 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;
    }
}

ثم

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);

مقتطف من ذكري المظهر المستندات:

يحب إرسال البث (النية), ، ولكن إذا كان هناك أي أجهزة استقبال للقصد ستحظر هذه الوظيفة وإرسالها فورًا قبل العودة.

فكر في هذا الاختراق على أنه "أمر pinging" لـ Service نظرًا لأنه يمكننا البث بشكل متزامن، يمكننا البث والحصول على نتيجة - بشكل متزامن - على مؤشر ترابط واجهة المستخدم.

Service

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
}

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

أريد فقط إضافة ملاحظة إلى إجابةSnicolas.يمكن استخدام الخطوات التالية للتحقق من إيقاف الخدمة مع/بدون الاتصال onDestroy().

  1. onDestroy() مُسَمًّى:انتقل إلى الإعدادات -> التطبيق -> تشغيل الخدمات -> حدد الخدمة وأوقفها.

  2. onDestroy() لم يتم استدعاؤه:انتقل إلى الإعدادات -> التطبيق -> إدارة التطبيقات -> حدد و"فرض الإيقاف" للتطبيق الذي تعمل فيه الخدمة.ومع ذلك، بما أن تطبيقك متوقف هنا، فمن المؤكد أنه سيتم أيضًا إيقاف مثيلات الخدمة.

أخيرًا، أود أن أذكر أن النهج المذكور هناك باستخدام متغير ثابت في فئة المفردة يناسبني.

لا يسمى onDestroy دائما في الخدمة حتى هذا لا طائل منه!

وعلى سبيل المثال: مجرد تشغيل التطبيق مرة أخرى مع تغيير واحد من الكسوف. وخرجت من تطبيق بالقوة باستخدام SIG: 9

والطريقة الصحيحة لمعرفة ما اذا كان على الخدمة قيد التشغيل هو ببساطة يطلب منها. تنفيذ BroadcastReceiver في خدمتكم يستجيب لالأصوات من الأنشطة الخاصة بك. تسجيل BroadcastReceiver عند بدء تشغيل الخدمة، وإلغاء تسجيل عندما يتم تدمير هذه الخدمة. من نشاطك (أو أي عنصر)، وإرسال البث المحلي النية لخدمة وإذا كان يستجيب، وانت تعرف انها تعمل. لاحظ الفرق الدقيق بين ACTION_PING وACTION_PONG في التعليمات البرمجية أدناه.

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

وأولا وقبل كل شيء musn't محاولة للوصول إلى الخدمة باستخدام ActivityManager. (مناقشة هنا )

و، الالتزام الخدمات يمكن تشغيلها من تلقاء نفسها إلى آخر أو كليهما. الطريق للتحقق في آخر إذا الخدمة الخاص بك يعمل أم لا غير بجعل اجهة (الذي يمتد بيندر) حيث تقوم بتعريف الأساليب التي على حد سواء، وآخر والخدمة، وفهم. يمكنك القيام بذلك عن طريق واجهة خاصة بك حيث قمت بتعريف على سبيل المثال "isServiceRunning ()". يمكنك ثم ربط آخر لخدمة الخاص بك، قم بتشغيل isServiceRunning طريقة ()، فإن خدمة تحقق لنفسها إذا كان يعمل أم لا، وإرجاع منطقية إلى آخر الخاص بك.

ويمكنك أيضا استخدام هذه الطريقة لوقف الخدمة الخاص بك أو التفاعل معها بطريقة أخرى.

وأنا استخدم هذا تعليمي لمعرفة كيفية تنفيذ هذا السيناريو في التطبيق الخاص بي.

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

لاستخدام الحالات الواردة هنا يجوز لنا ببساطة الاستفادة من قيمة الإرجاع طريقة stopService() ل. وتعود true في حال وجود الخدمة المحددة وقتله. إلا فإنه يعود false. لذلك قد إعادة تشغيل الخدمة إذا كانت النتيجة false آخر وأكد أنه تم إيقاف الخدمة الحالية. :) سيكون من الأفضل إذا كان لديك نظرة على الموقع هذا .

مرة أخرى، هناك بديل آخر قد يجده الأشخاص أكثر نظافة إذا استخدموا النوايا المعلقة (على سبيل المثال مع 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);
}

أين CODE هو ثابت تحدده بشكل خاص في صفك لتحديد النوايا المعلقة المرتبطة بخدمتك.

وفيما يلي الإختراق الأنيق الذي يغطي جميع Ifs. هذا هو عن الخدمات المحلية فقط.

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

وبعد ذلك في وقت لاحق:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }
<اقتباس فقرة>   

ورد geekQ ولكن في الدرجة Kotlin. بفضل 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
}

والدعوة

isMyServiceRunning(NewService::class.java)

ويمكن أن يكون هناك العديد من الخدمات التي لها نفس اسم الفئة.

ولقد خلقت فقط اثنين من تطبيقات. اسم الحزمة من التطبيق الأول هو com.example.mock. أنا خلقت حزمة فرعية تسمى lorem في التطبيق وخدمة تسمى Mock2Service. حتى اسمها مؤهل هو com.example.mock.lorem.Mock2Service.

وبعد ذلك أنا خلقت التطبيق الثاني وخدمة دعا Mock2Service. اسم الحزمة من التطبيق الثاني هو com.example.mock.lorem. اسم مؤهل بشكل كامل من هذه الخدمة هو com.example.mock.lorem.Mock2Service أيضا.

وهنا هو إخراج logcat بلدي.

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

وهناك فكرة أفضل للمقارنة بين الحالات ComponentName لequals() من ComponentName يقارن كل من أسماء حزمة وأسماء فئة. ولا يمكن أن يكون هناك اثنين من التطبيقات مع نفس اسم الحزمة المثبتة على الجهاز.

وهذا يساوي () طريقة 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;
}

ComponentName

وهذا ينطبق أكثر نحو التصحيح خدمة النوايا لأنها تفرخ موضوع، ولكن قد عمل لخدمات منتظمة أيضا. لقد وجدت هذا الموضوع وذلك بفضل نهم

في حالتي، لقد لعبت حوالي مع المصحح وجدت عرض الموضوع. انها نوع من يشبه رمز النقطة في مايكروسوفت وورد. على أي حال، لم يكن لديك لتكون في وضع المصحح لاستخدامها. انقر على هذه العملية، وانقر على هذا الزر. سوف تظهر أي نوايا الخدمات حتى أثناء تشغيلها، على الأقل على المضاهاة.

إذا الخدمة تنتمي إلى عملية أخرى أو APK استخدام الحل على أساس ActivityManager.

إذا كان لديك الوصول إلى مصدرها، ومجرد استخدام الحل استنادا إلى حقل ثابت. ولكن بدلا من ذلك باستخدام منطقية أود أن أقترح استخدام كائن التسجيل. أثناء تشغيل الخدمة، فقط بتحديث قيمته إلى 'الآن'، وعندما ينتهي تعيينها إلى قيمة خالية. من النشاط يمكنك معرفة ما اذا كان باطلا، أو تاريخ قديم جدا الأمر الذي يعني أنه ليست قيد التشغيل.

ويمكنك أيضا إرسال إشعار البث من خدمة يشير إلى أنه تم تشغيل على طول مزيد من المعلومات مثل التقدم.

وداخل TheServiceClass تعريف:

 public static Boolean serviceRunning = false;

وبعد ذلك في onStartCommand (...)

 public int onStartCommand(Intent intent, int flags, int startId) {

    serviceRunning = true;
    ...
}

 @Override
public void onDestroy()
{
    serviceRunning = false;

} 

وثم استدعاء if(TheServiceClass.serviceRunning == true) من أي فئة.

ربط الاستخدام البسيط مع عدم إنشاء تلقائي - انظر ملاحظة.وتحديث...

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

مثال :

    Intent bindIntent = new Intent(context, Class<Service>);
    boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);

لماذا لا تستخدم؟ getRunningServices()

List<ActivityManager.RunningServiceInfo> getRunningServices (int maxNum)
Return a list of the services that are currently running.

ملحوظة:هذه الطريقة مخصصة فقط لتصحيح الأخطاء أو تنفيذ واجهات المستخدم من نوع إدارة الخدمة.


ملاحظة.وثائق Android مضللة، لقد قمت بفتح مشكلة على Google Tracker لإزالة أي شكوك:

https://issuetracker.google.com/issues/68908332

كما يمكننا أن نرى خدمة الربط تستدعي بالفعل معاملة عبر مجلد ActivityManager من خلال مجلدات ذاكرة التخزين المؤقت للخدمة - أتتبع الخدمة المسؤولة عن الربط ولكن كما يمكننا أن نرى نتيجة الربط هي:

int res = ActivityManagerNative.getDefault().bindService(...);
return res != 0;

تتم المعاملة من خلال الموثق:

ServiceManager.getService("activity");

التالي:

  public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);

يتم تعيين هذا في ActivityThread عبر:

 public final void bindApplication(...) {

        if (services != null) {
            // Setup the service cache in the ServiceManager
            ServiceManager.initServiceCache(services);
        }

يتم استدعاء هذا في ActivityManagerService بالطريقة:

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
    ...
    thread.bindApplication(... , getCommonServicesLocked(),...)

ثم:

 private HashMap<String, IBinder> getCommonServicesLocked() {

ولكن لا يوجد "نشاط" سوى حزمة النوافذ والتنبيه ..

لذلك نحن بحاجة إلى العودة للاتصال:

 return getIServiceManager().getService(name);

    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());

هذا يجعل المكالمة من خلال:

    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);

التي تؤدي إلى :

BinderInternal.getContextObject()

وهذا هو الأسلوب الأصلي....

  /**
     * 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();

ليس لدي الوقت الآن للتعمق في الأمر، لذا حتى أقوم بتشريح المكالمة المتبقية وأعلق إجابتي.

ولكن أفضل طريقة للتحقق مما إذا كانت الخدمة قيد التشغيل هو إنشاء ربط (إذا لم يتم إنشاء الربط، فإن الخدمة غير موجودة) - والاستعلام عن الخدمة حول حالتها من خلال الربط (باستخدام العلامة الداخلية المخزنة في حالتها).

تحديث 23.06.2018

لقد وجدت تلك مثيرة للاهتمام:

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

باختصار :)

"توفير رابط لخدمة مرتبطة بالفعل.هذه الطريقة متزامنة ولن تبدأ الخدمة المستهدفة إذا لم تكن موجودة."

إيبندر peekservice العامة (خدمة نية ، سلسلة ResolvedType ، سلسلة Callpackage) يلقي 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;
}

*

في خدمتكم الفرعية من الدرجة الاولى استخدم منطقية ثابت للحصول على حالة الخدمة كما هو موضح أدناه.

وMyService.kt

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()
        isServiceStarted = true
    }
    override fun onDestroy() {
        super.onDestroy()
        isServiceStarted = false
    }
    companion object {
        var isServiceStarted = false
    }
}

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

وبلدي kotlin تحويل الإجابات ActivityManager::getRunningServices القائمة. وضع هذه الوظيفة في نشاط -

private fun isMyServiceRunning(serviceClass: Class<out Service>) =
    (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
        .getRunningServices(Int.MAX_VALUE)
        ?.map { it.service.className }
        ?.contains(serviceClass.name) ?: false

يرجى استخدام هذا الرمز.

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

خذوا راحتكم يا شباب...:)

أعتقد أن الحل الأنسب هو الاحتفاظ بزوج من القيمة الرئيسية SharedPreferences حول ما إذا كانت الخدمة قيد التشغيل أم لا.

المنطق مستقيم جداً؛في أي منصب مرغوب فيه في فئة الخدمة الخاصة بك؛ضع قيمة منطقية ستكون بمثابة علامة لك حول ما إذا كانت الخدمة قيد التشغيل أم لا.ثم اقرأ هذه القيمة أينما تريد في طلبك.

يوجد أدناه نموذج للتعليمات البرمجية التي أستخدمها في تطبيقي:

في فئة الخدمة الخاصة بي (خدمة البث الصوتي)، أقوم بتنفيذ التعليمة البرمجية التالية عند تشغيل الخدمة؛

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

وبعد ذلك، في أي نشاط لتطبيقي، أقوم بالتحقق من حالة الخدمة بمساعدة الكود التالي؛

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

لا أذونات خاصة، لا الحلقات...طريقة سهلة وحل نظيف :)

إذا كنت بحاجة إلى معلومات إضافية، يرجى الرجوع إلى وصلة

أتمنى أن يساعدك هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top