จะตรวจสอบว่าบริการทำงานบน Android ได้อย่างไร?


936

ฉันจะตรวจสอบว่าบริการพื้นหลังทำงานอยู่ได้อย่างไร?

ฉันต้องการกิจกรรม Android ที่สลับสถานะของบริการ - ให้ฉันเปิดใช้งานถ้ามันถูกปิดและปิดถ้ามันเปิดอยู่



17
คำตอบที่ถูกต้องอยู่ด้านล่างและไม่ใช่คำตอบ: stackoverflow.com/a/5921190/2369122
toidiu

1
@toidiu หากยังไม่ได้รับการแก้ไขเช่นgetRunningTasks()นั้นอาจเป็นไปได้
Kevin Krumwiede

การใช้ฟังก์ชัน getSystemService () คุณสามารถดึงข้อมูลบริการที่กำลังทำงานอยู่ทั้งหมดได้ วนรอบผ่านมันและตรวจสอบบริการของคุณที่มีอยู่ในรายการที่นี่คุณสามารถดูตัวอย่างขนาดเล็กwiki.workassis.com/android-check-the-service-is-running
Bikesh M

คำตอบ:


292

ฉันมีปัญหาเดียวกันเมื่อไม่นานมานี้ เนื่องจากบริการของฉันอยู่ในพื้นที่ฉันจึงลงเอยด้วยการใช้ฟิลด์คงที่ในคลาสบริการเพื่อสลับสถานะตามที่แฮ็คบ็อดอธิบายไว้ที่นี่

แก้ไข (สำหรับบันทึก):

นี่คือทางออกที่เสนอโดย hackbod:

หากรหัสลูกค้าและเซิร์ฟเวอร์ของคุณเป็นส่วนหนึ่งของ. apk และคุณมีผลผูกพันกับบริการด้วยเจตนาชัดเจน (หนึ่งที่ระบุคลาสบริการที่แน่นอน) จากนั้นคุณสามารถให้บริการของคุณตั้งค่าตัวแปรทั่วโลกเมื่อมันทำงานที่ ลูกค้าของคุณสามารถตรวจสอบ

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


27
@Pacerier โซลูชันที่คุณอ้างอิงต้องการเริ่มบริการและฉันคิดว่าโซลูชันที่มีความยืดหยุ่นดีที่สุดควรอนุญาตให้คุณตรวจสอบว่าบริการกำลังทำงานอยู่หรือไม่โดยไม่เริ่มบริการ
ทอม

17
ถ้าระบบหยุดบริการคุณจะตรวจจับและสลับตัวแปรของคุณอย่างไร
jmng

23
เมื่อแอพถูกฆ่าตายบริการที่เริ่มทำงานก็ถูกฆ่าเช่นกัน แต่onDestroy()จะไม่เรียกใช้บริการ ดังนั้นตัวแปรแบบคงที่ไม่สามารถอัปเดตได้ในสถานการณ์ดังกล่าวซึ่งส่งผลให้เกิดพฤติกรรมที่ไม่สอดคล้องกัน
faizal

5
@faizal ตัวแปรคงที่จะไม่ถูกปรับให้เป็น intialized อีกครั้งดังนั้นการตั้งค่ากลับเป็นค่าเริ่มต้นที่ระบุว่าบริการไม่ทำงานอีกต่อไปหรือไม่
PabloC

12
@faizal บริการท้องถิ่นไม่ใช่กระบวนการแยกต่างหากดังนั้นหากบริการถูกฆ่าแอปจะฆ่าด้วย
Sever

1674

ฉันใช้สิ่งต่อไปนี้จากภายในกิจกรรม:

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 หรือตัวแปรแบบสแตติกจะไม่ทำงานได้อย่างน่าเชื่อถือเพราะในฐานะนักพัฒนาที่คุณไม่เคยรู้มาก่อนเมื่อ Android ตัดสินใจฆ่ากระบวนการของคุณหรือโทรกลับที่กล่าวถึง โปรดทราบว่าคอลัมน์ "killable" ในตารางวงจรชีวิตในเอกสาร Android


85
ขอบคุณสำหรับการแก้ปัญหานี้ ฉันต้องการเพิ่ม: "com.example.MyService" แทนที่จะใช้ MyService.class.getName () แทน
peter.bartos

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

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

40
ผู้คนพบว่ามันยอดเยี่ยมที่ต้องผ่านทุกอย่างเพื่อตรวจสอบว่าเซิร์ฟเวอร์กำลังทำงานอยู่หรือไม่?
Rui Marques

80
เริ่มต้นAndroid O , getRunningServicesเลิกใช้แล้ว คำตอบนี้ต้องการการอัพเดตสำหรับเวอร์ชั่นที่ใหม่กว่า
poring91

75

เข้าใจแล้ว!

คุณต้องเรียก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;
     }
}

นี่จะแสดงรายการเพียงแค่บริการของระบบใช่ไหม! ดังนั้นบริการในพื้นที่ของฉันจึงถูกแยกออกจากรายการและฉันจะได้รับเท็จ (
Ewoks

สิ่งนี้ใช้ได้กับบริการภายนอกสำหรับบริการในท้องถิ่นนั้นค่อนข้างชัดเจนหากคุณใช้
Kevin Parker

11
ขอโทษ แต่ฉันต้องบอกว่านั่นเป็นคำตอบที่ไร้สาระสุด ๆ ทำไมมันชัดเจนมาก!
Ewoks

10
ไม่ชัดเจนว่าคุณหมายถึงอะไรที่นี่ ... ใครกำลังพูดถึงการล่มสลายทั้งหมด! ฉันไม่สนใจที่จะหยุดมัน บริการสามารถเริ่มหยุดหรืออาจเป็นบริการที่ตั้งใจและจะหยุดเองเมื่อเสร็จแล้ว ... คำถามคือจะรู้ได้อย่างไรว่ามันยังทำงานอยู่หรือไม่หลังจาก 3 นาทีเป็นต้น
Ewoks

1
ไม่ถูกต้องที่จะให้การแสดงผลที่จะต้องเริ่มบริการที่ถูกผูกไว้ด้วย NO การผูกอัตโนมัติสร้างไม่ตรงตามที่ระบุไว้ มันจะสร้าง (และ "เริ่มต้น") บริการหากบริการยังไม่ได้ทำงาน
Sreedevi J

57

ส่วนประกอบเล็ก ๆ คือ:

เป้าหมายของฉันคือการรู้ว่าบริการกำลังทำงานอยู่โดยไม่ใช้งานจริงหากไม่ได้ทำงานอยู่

การโทร bindService หรือการโทรหาจุดประสงค์ที่บริการสามารถจับได้นั้นไม่ใช่ความคิดที่ดีเพราะจะเริ่มให้บริการหากไม่ได้ทำงานอยู่

ดังนั้นตามที่ miracle2k แนะนำให้ดีที่สุดคือการมีฟิลด์แบบสแตติกในคลาสบริการเพื่อทราบว่าบริการได้เริ่มขึ้นหรือไม่

เพื่อให้มันสะอาดยิ่งขึ้นฉันแนะนำให้เปลี่ยนการบริการในซิงเกิลตันด้วยการดึงข้อมูลที่ขี้เกียจมากนั่นคือไม่มีอินสแตนซ์ของอินสแตนซ์ซิงเกิลตันทั้งหมดด้วยวิธีการคงที่ เมธอด getInstance แบบสแตติกของเซอร์วิส / singleton ของคุณเพียงแค่ส่งคืนอินสแตนซ์ของ 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 ขีดเส้นใต้ไว้


32
นี่เป็นข้อบกพร่อง ไม่รับประกันว่าจะเรียก onDestroy
Pacerier

8
เมื่อระบบมีหน่วยความจำเหลือน้อยบริการของคุณจะถูกฆ่าโดยอัตโนมัติโดยไม่ต้องเรียกใช้ onDestroy ของคุณซึ่งเป็นสาเหตุที่ฉันบอกว่านี่เป็นข้อบกพร่อง
Pacerier

17
@Pacerier แต่ถ้าระบบฆ่ากระบวนการแฟล็กอินสแตนซ์จะยังคงได้รับการรีเซ็ต ฉันเดาว่าเมื่อตัวรับสัญญาณถูกโหลดต่อไป (โพสต์ระบบฆ่าบริการ) สแตติก 'คงสถานะ' จะถูกสร้างใหม่เป็นโมฆะ
ทอม

2
อย่างน้อยก็ดีกว่าการวนซ้ำผ่านบริการทั้งหมดเหล่านั้นใน isMyServiceRunning ซึ่งทำให้เกิดความล่าช้าหากทำสิ่งต่าง ๆ ในการหมุนอุปกรณ์ทุกครั้ง :)
Gunnar Forsgren - Mobimation

1
ตัวแปรอินสแตนซ์ของคุณไม่ควรประกาศครั้งสุดท้ายมิฉะนั้นจะไม่สามารถตั้งค่าหรือไม่เป็นโมฆะโดยวิธีการ onCreate () หรือ onDestroy ()
k2col

27

คุณสามารถใช้สิ่งนี้ (ฉันยังไม่ได้ลอง แต่หวังว่าจะได้ผล):

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 หากมีเซอร์วิสที่รันอยู่แล้ว หากไม่ได้รับค่า null จะถูกส่งคืน

ดูนามธรรมสาธารณะ componentName startService (บริการเจตนา)

นี่ไม่ใช่การตรวจสอบที่ฉันคิดว่าเพราะมันเริ่มให้บริการเพื่อให้คุณสามารถเพิ่มstopService(someIntent);ภายใต้รหัส


11
ไม่ตรงกับที่เอกสารพูด ตามลิงค์ของคุณ: "ส่งคืนถ้าบริการกำลังเริ่มทำงานหรือกำลังทำงานอยู่ ComponentName ของบริการจริงที่เริ่มต้นจะถูกส่งคืน แต่ถ้าบริการไม่มีอยู่จะถูกส่งคืนเป็นค่าว่าง"
Gabriel

เป็นคนที่คิดดี แต่ไม่เหมาะกับสถานการณ์ปัจจุบัน
Code_Life

5
มันเป็นวิธีที่ไม่เหมาะสมเพราะเมื่อทริกเกอร์ IDE if(startService(someIntent) != null)นั้นจะตรวจสอบว่าIsserviceRunningแต่มันจะเล่นบริการใหม่
Chintan Khetiya

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

6
นี่จะเริ่มบริการใช่ไหม เพียงแค่ต้องการที่จะตรวจสอบสถานะของบริการแทนที่จะเริ่มต้นมัน ...
Raptor

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

21

สารสกัดจากเอกสารAndroid :

เช่นเดียวกับsendBroadcast (Intent)แต่ถ้ามีตัวรับสัญญาณใด ๆ สำหรับ Intent ฟังก์ชั่นนี้จะบล็อกและส่งทันทีก่อนส่งคืน

คิดว่าสับนี้เป็น Service"กระตุกที่" เนื่องจากเราสามารถออกอากาศแบบซิงโครนัสเราสามารถออกอากาศและรับผลลัพธ์แบบซิงโครนัสบนเธรด 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;   
        }
    }

ผู้ชนะในการใช้งานมากเป็นของหลักสูตรที่สนามบูลคงที่ในการบริการที่มีการตั้งค่าtrueในService.onCreate()และfalseในService.onDestroy()เพราะมันเป็นเรื่องง่ายมาก


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

13

ฉันได้ปรับแก้เล็กน้อยหนึ่งในโซลูชั่นที่นำเสนอข้างต้น แต่ผ่านชั้นเรียนแทนที่จะเป็นชื่อสตริงทั่วไปเพื่อให้แน่ใจว่าได้เปรียบเทียบสตริงที่ออกมาจากวิธีการเดียวกัน 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);

เพื่อเพิ่มเติมด้านที่เข้มงวดคุณสามารถเปลี่ยนพารามิเตอร์ของคลาสเป็นClass<? extends Service>
silentsudo

11

วิธีที่เหมาะสมในการตรวจสอบว่าบริการกำลังทำงานอยู่หรือไม่ ใช้ BroadcastReceiver ในบริการของคุณที่ตอบสนองต่อการส่ง Ping จากกิจกรรมของคุณ ลงทะเบียน 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;
            }
        }
    };
}

1
ฉันชอบวิธีนี้มาก เป็นรหัสหนักเล็กน้อยที่ฉลาด แต่จะใช้ได้เสมอ ฉันไม่เห็นว่าเจตนาการออกอากาศถูกเลิกใช้เร็ว ๆ นี้ :)
34475

8

ฉันแค่ต้องการเพิ่มบันทึกลงในคำตอบโดย @Snicolas ขั้นตอนต่อไปสามารถนำมาใช้ในการตรวจสอบบริการแบบครบวงจรที่มี / onDestroy()ไม่มีการเรียก

  1. onDestroy() ชื่อ: ไปที่การตั้งค่า -> แอปพลิเคชัน -> บริการที่ใช้งาน -> เลือกและหยุดบริการของคุณ

  2. onDestroy()ไม่ได้รับการโทร: ไปที่การตั้งค่า -> แอปพลิเคชัน -> จัดการแอปพลิเคชัน -> เลือกและ "บังคับหยุด" แอปพลิเคชันของคุณที่บริการของคุณทำงานอยู่ อย่างไรก็ตามเมื่อแอปพลิเคชันของคุณหยุดที่นี่ดังนั้นแน่นอนว่าบริการจะหยุดทำงานเช่นกัน

ในที่สุดฉันอยากจะพูดถึงว่าวิธีการดังกล่าวโดยใช้ตัวแปรคงที่ในชั้นเรียนเดี่ยวทำงานสำหรับฉัน


7

onDestroy ไม่ได้ถูกเรียกใช้บริการเสมอไปดังนั้นสิ่งนี้จึงไร้ประโยชน์!

ตัวอย่างเช่น: เพียงแค่เรียกใช้แอปอีกครั้งด้วยการเปลี่ยนแปลงเดียวจาก Eclipse แอปพลิเคชันถูกบังคับให้ออกโดยใช้ SIG: 9


6

ก่อนอื่นคุณไม่ต้องพยายามเข้าถึงบริการโดยใช้ ActivityManager (พูดถึงที่นี่ )

บริการสามารถทำงานด้วยตัวเองถูกผูกไว้กับกิจกรรมหรือทั้งสองอย่าง วิธีการเช็คอินกิจกรรมหากบริการของคุณกำลังทำงานอยู่หรือไม่โดยการทำอินเทอร์เฟซ (ที่ขยาย Binder) ซึ่งคุณประกาศวิธีการที่ทั้งกิจกรรมและบริการเข้าใจ คุณสามารถทำได้โดยสร้างอินเทอร์เฟซของคุณเองที่คุณประกาศเช่น "isServiceRunning ()" จากนั้นคุณสามารถผูกกิจกรรมของคุณกับบริการของคุณเรียกใช้เมธอด isServiceRunning () บริการจะตรวจสอบตัวเองว่ามันกำลังทำงานอยู่หรือไม่และส่งคืนบูลีนไปที่กิจกรรมของคุณ

นอกจากนี้คุณยังสามารถใช้วิธีนี้เพื่อหยุดบริการของคุณหรือโต้ตอบกับมันในอีกทางหนึ่ง

ฉันใช้บทช่วยสอนนี้เพื่อเรียนรู้วิธีใช้สถานการณ์นี้ในแอปพลิเคชันของฉัน


3
การสนทนาดังกล่าวเกิดขึ้นใน '12 / 26/07 ' ไม่ว่าจะเป็นเดือนกรกฎาคมของปีนี้ (เช่นในอนาคต) หรือก่อนหน้านี้ Android จะเป็นสาธารณะ ทั้งสองวิธีที่ทำให้ฉันไม่เชื่อใจ
Tom

การอภิปรายที่มาจากวันที่ 26 ธันวาคม 2007 พวกเขาจะคุยรุ่นก่อนวางจำหน่ายผมคิดว่า ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ) ซึ่งได้รับการปล่อยตัวในวันที่ 14 ธันวาคม 2007
ingh.am

6

อีกครั้งทางเลือกอื่นที่ผู้คนอาจพบว่าสะอาดกว่าหากพวกเขาใช้ intents ที่ค้างอยู่ (ตัวอย่างเช่น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เป็นค่าคงที่ที่คุณกำหนดเอกชนในชั้นเรียนของคุณเพื่อระบุเจตนาที่รอดำเนินการที่เกี่ยวข้องกับการให้บริการของคุณ


1
รวมหรืออัปเดตคำตอบก่อนหน้าของคุณ โปรดงดเว้นการโพสต์มากกว่าหนึ่งคำตอบต่อโพสต์
ChuongPham

สามารถขยายคำตอบนี้ได้เช่นจะเชื่อมโยงค่าสำหรับ CODE กับบริการได้อย่างไร
Dave Nottage

จะรับบริบทได้ที่ไหน
โหระพา

6

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

ปัญหาเดียวของปัญหานี้คือถ้าบริการเป็นบริการ Sticky และจะรีสตาร์ทเอง การเรียกใช้ isServiceCreated () จะคืนค่าเท็จหลังจากบริการเริ่มต้นอีกครั้งเนื่องจาก mInstance จะเป็นโมฆะ
Mira_Cole

1
จะไม่ถูกสร้างขึ้นเมื่อมีการรีสตาร์ทเองหรือไม่
TheRealChx101

6

รุ่น 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;
}

คุณต้อง'Context` GetSystemServiceสำหรับ
ทดสอบ

5

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


5

อีกวิธีใช้ kotlin เป็นแรงบันดาลใจในการตอบผู้ใช้รายอื่น

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

ในฐานะที่เป็นนามสกุล 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 }
}

การใช้

context.isMyServiceRunning(MyService::class.java)

4

ใน kotlin คุณสามารถเพิ่มตัวแปรบูลีนในวัตถุที่เป็นคู่หูและตรวจสอบค่าของมันจากคลาสที่คุณต้องการ:

companion object{
     var isRuning = false

}

เปลี่ยนค่าเมื่อสร้างและทำลายบริการ

 override fun onCreate() {
        super.onCreate()
        isRuning = true
    }

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }

3

ใน Service Sub-Class ของคุณใช้ Static Boolean เพื่อรับสถานะของบริการดังที่แสดงด้านล่าง

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

3

สำหรับ kotlin คุณสามารถใช้รหัสด้านล่าง

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
}

นี่เป็นคำตอบที่ดีสำหรับการเขียนแบบทดสอบเนื่องจากคุณสามารถใช้ได้โดยไม่ต้องเปลี่ยนรหัสการทำงานของคุณ
Robert Liberatore

2

การตอบสนองของ 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)

6
ActivityManager.getRunningServicesเลิกใช้แล้วตั้งแต่ Android O
Daniel Shatz

1

อาจมีบริการหลายอย่างที่มีชื่อคลาสเดียวกัน

ฉันเพิ่งสร้างสองแอพ com.example.mockชื่อแพคเกจของแอปแรกคือ ฉันสร้างแพ็กเกจย่อยที่เรียกว่าloremใน app 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


1

กรุณาใช้รหัสนี้

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

0

สิ่งนี้ใช้กับการแก้ไขจุดบกพร่องของ Intent Service ได้มากกว่าเนื่องจากพวกเขาวางไข่เธรด แต่อาจใช้ได้กับบริการปกติเช่นกัน ฉันพบกระทู้นี้ด้วย Binging

ในกรณีของฉันฉันเล่นกับดีบักเกอร์และพบมุมมองเธรด มันดูเหมือนไอคอนสัญลักษณ์แสดงหัวข้อย่อยใน MS Word อย่างไรก็ตามคุณไม่จำเป็นต้องอยู่ในโหมดดีบักเกอร์เพื่อใช้งาน คลิกที่กระบวนการและคลิกที่ปุ่มนั้น บริการแสดงเจตนาใด ๆ จะปรากฏขึ้นในขณะที่กำลังทำงานอย่างน้อยในโปรแกรมจำลอง


0

หากบริการเป็นของกระบวนการอื่นหรือ APK ใช้วิธีแก้ปัญหาตาม ActivityManager

หากคุณมีสิทธิ์เข้าถึงแหล่งที่มาเพียงใช้โซลูชันที่ยึดตามฟิลด์คงที่ แต่แทนที่จะใช้บูลีนฉันขอแนะนำให้ใช้วัตถุวันที่ ในขณะที่บริการกำลังทำงานอยู่เพียงแค่อัปเดตค่าเป็น 'ตอนนี้' และเมื่อบริการเสร็จสิ้นให้ตั้งเป็นศูนย์ จากกิจกรรมที่คุณสามารถตรวจสอบว่าเป็นโมฆะหรือวันที่เก่าเกินไปซึ่งจะหมายความว่ามันไม่ทำงาน

นอกจากนี้คุณยังสามารถส่งการแจ้งเตือนการออกอากาศจากบริการของคุณโดยระบุว่ากำลังทำงานตามข้อมูลเพิ่มเติมเช่นความคืบหน้า


0

ภายใน 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)จากชั้นใดก็ได้


4
สิ่งนี้ไม่ทำงานหากบริการของคุณถูกฆ่าโดย Android
ไฮเซนเบิร์ก

@ ไฮเซนเบิร์กฉันเพิ่งพบว่าตัวเอง คุณรู้ไหมว่าทำไมไม่
ทิม

@Heisenberg เมื่อแอปของฉันถูกฆ่าโดยระบบปฏิบัติการบริการจะเริ่มต้นใหม่และตั้งค่าบูลสแตติกเป็นจริง แต่เมื่อได้รับมันจะรายงานว่าเป็นเท็จ
Tim

stopServiceนี้จะไม่ทำงานถ้าคุณโทร อย่างน้อยสำหรับบริการเจตนา onDestroy()จะถูกเรียกใช้ในทันที แต่onHandleIntent()จะยังคงทำงานอยู่
serggl

1
@Heisenberg จะไม่ฆ่าบริการเนื่องจากหน่วยความจำต่ำยังหมายถึงการฆ่ากระบวนการหรือไม่
นักพัฒนา android

0

ใช้งานง่ายผูกกับไม่สร้างอัตโนมัติ - ดู 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.

หมายเหตุ: วิธีการนี้มีไว้สำหรับการดีบักหรือใช้ส่วนติดต่อผู้ใช้ประเภทการจัดการบริการ


PS เอกสารประกอบหุ่นยนต์ทำให้เข้าใจผิดฉันได้เปิดปัญหาในการติดตามของ Google เพื่อขจัดข้อสงสัยใด ๆ :

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

ในขณะที่เราเห็นบริการ bind จริง ๆ แล้วเรียกใช้ธุรกรรมผ่าน ActivityManager binder ผ่าน Service cache binders - i dint track บริการใดที่มีผลผูกพัน แต่ในขณะที่เราเห็นผลการผูกคือ:

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

การทำธุรกรรมผ่าน binder:

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

ในระยะสั้น :)

"มอบเครื่องผูกให้กับบริการที่ถูกผูกไว้แล้ววิธีนี้จะซิงโครนัสและจะไม่เริ่มบริการเป้าหมายหากไม่มีอยู่"

IBinder peek บริการสาธารณะ (บริการที่ตั้งใจ, String ที่ได้รับการแก้ไขประเภท, การเรียก String แพคเกจ) พ่น 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;
}

* * * *


bindResult (ค่าส่งคืนของวิธี bindService) ไม่ได้มาเป็นเท็จหากบริการไม่ได้ทำงานอยู่
Shangeeth Sivan

0

การแปลง 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

-2

คุณสามารถใช้ตัวเลือกนี้จากตัวเลือกนักพัฒนา Android เพื่อดูว่าบริการของคุณยังทำงานอยู่หรือไม่

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.

-5

เอาง่ายๆนะ ... :)

ฉันคิดว่าทางออกที่เหมาะสมที่สุดคือการจับคู่ค่าคีย์ในSharedPreferencesกรณีที่บริการกำลังทำงานอยู่หรือไม่

ตรรกะนั้นตรงมาก ในตำแหน่งที่ต้องการในระดับบริการของคุณ ใส่ค่าบูลีนซึ่งจะทำหน้าที่เป็นธงสำหรับคุณเกี่ยวกับว่าบริการกำลังทำงานอยู่หรือไม่ จากนั้นอ่านค่านี้ในทุกที่ที่คุณต้องการในใบสมัคร

โค้ดตัวอย่างที่ฉันใช้ในแอพของฉันอยู่ด้านล่าง:

ในคลาส Service ของฉัน (บริการสำหรับ Audio Stream) ฉันเรียกใช้รหัสต่อไปนี้เมื่อบริการหมด

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

ไม่มีการอนุญาตพิเศษไม่มีการวนซ้ำ ... วิธีง่ายๆวิธีแก้ปัญหาที่สะอาด :)

หากคุณต้องการข้อมูลเพิ่มเติมโปรดอ้างอิงลิงค์

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


19
เฉพาะที่ไม่มีใครจะอัปเดตค่าสำหรับคุณเมื่อพวกเขาฆ่าบริการ
Gunnar Forsgren - Mobimation

เมื่อฆ่าบริการ onDestroy () จะเปิดขึ้นและนั่นเป็นไปได้ที่จะอัปเดตสถานะของมัน
Jongz Puangput

5
@JongzPuangput onDestroyไม่ได้เรียกเสมอเมื่อบริการถูกฆ่าตาย ตัวอย่างเช่นฉันเคยเห็นบริการของฉันถูกฆ่าในสถานการณ์ที่หน่วยความจำเหลือน้อยโดยไม่onDestroyถูกเรียก
Sam

@ แซมแล้วอะไรจะถูกเรียก?
Ruchir Baronia

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