شرح - com.android.service تنزيل




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

استجابة 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)

كيف أتحقق من تشغيل خدمة الخلفية (على Android)؟

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


Xamarin C # verison.

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

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

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

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

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


أولاً ، لا تحاول الوصول إلى الخدمة باستخدام ActivityManager. (ناقش here )

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

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

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


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

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)

يعمل هذا بشكل موثوق به ، لأنه يعتمد على المعلومات الخاصة بتشغيل الخدمات التي يوفرها نظام تشغيل Android من خلال ActivityManager#getRunningServices .

لن تعمل جميع الطرق التي تستخدم onDestroy أو onSometing events أو Binders أو static variables بشكل صحيح لأنك مطور لا تعرفه مطلقًا ، عندما يقرر Android قتل العملية أو أي من عمليات الاستدعاء المذكورة تسمى أم لا. الرجاء ملاحظة العمود "القابل للقتل" في جدول أحداث دورة حياة في وثائق Android.


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

خذها بسهولة يا شباب ... :)

أعتقد أن الحل الأنسب هو الاحتفاظ بزوج من القيمة الرئيسية في 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);
}

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

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

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


داخل 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) من أي فئة.


لا يتم onDestroy دائمًا في الخدمة ، لذا لا فائدة من ذلك!

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


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

مكمل صغير هو:

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

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

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

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

سيكون من الأنظف حتى تعديل نمط تصميم المفرد لإعادة تسمية أسلوب 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

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


هذا مقتطف من مستندات Android

يتم تسليم البث بالبث (المرسلة مع Context.sendOrderedBroadcast ) إلى جهاز استقبال واحد في كل مرة. ونظراً لأن كل مستقبِل يُنفذ بدوره ، يمكنه أن ينشر نتيجة إلى المستقبل التالي ، أو يمكنه أن يجهض البث تماماً حتى لا يتم تمريره إلى مستقبِلات أخرى.

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

Service

BroadcastReceiver .

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

private class ServiceEchoReceiver{
    public void onReceive (Context context, Intent intent) {
      LocalBroadcastManager
         .getInstance(this)
         .sendBroadcastSync(new Intent("echo"));
    }
}

Activity

    bool serviceRunning = false;

    protected void onCreate (Bundle savedInstanceState){
        LocalBroadcastManager.getInstance(this).registerReceiver(echo);
        LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo"));
        if(!serviceRunning){
           //try and run the service
        }
    }

    private BroadcastReceiver echo = new BroadcastReceiver(){
        public void onReceive (Context context, Intent intent) {
          serviceRunning = true;   
        }
    }

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

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


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

لقد قمت للتو بإنشاء تطبيقين. اسم حزمة التطبيق الأول هو com.example.mock . أنا خلقت حزمة Mock2Service تسمى Mock2Service في التطبيق وخدمة تسمى 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


ربط استخدام بسيط مع عدم إنشاء السيارات - انظر PS.

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.

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

فرع فلسطين. وثائق الروبوت مضللة لقد فتحت قضية على تعقب google للقضاء على أي شكوك:

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

ليس لدي الوقت الآن لحفر في ج حتى حتى تشريح بقية الاتصال أعلق جوابي.

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


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




android-service