ฉันจะตรวจสอบว่าบริการพื้นหลังทำงานอยู่ได้อย่างไร?
ฉันต้องการกิจกรรม Android ที่สลับสถานะของบริการ - ให้ฉันเปิดใช้งานถ้ามันถูกปิดและปิดถ้ามันเปิดอยู่
getRunningTasks()
นั้นอาจเป็นไปได้
ฉันจะตรวจสอบว่าบริการพื้นหลังทำงานอยู่ได้อย่างไร?
ฉันต้องการกิจกรรม Android ที่สลับสถานะของบริการ - ให้ฉันเปิดใช้งานถ้ามันถูกปิดและปิดถ้ามันเปิดอยู่
getRunningTasks()
นั้นอาจเป็นไปได้
คำตอบ:
ฉันมีปัญหาเดียวกันเมื่อไม่นานมานี้ เนื่องจากบริการของฉันอยู่ในพื้นที่ฉันจึงลงเอยด้วยการใช้ฟิลด์คงที่ในคลาสบริการเพื่อสลับสถานะตามที่แฮ็คบ็อดอธิบายไว้ที่นี่
แก้ไข (สำหรับบันทึก):
นี่คือทางออกที่เสนอโดย hackbod:
หากรหัสลูกค้าและเซิร์ฟเวอร์ของคุณเป็นส่วนหนึ่งของ. apk และคุณมีผลผูกพันกับบริการด้วยเจตนาชัดเจน (หนึ่งที่ระบุคลาสบริการที่แน่นอน) จากนั้นคุณสามารถให้บริการของคุณตั้งค่าตัวแปรทั่วโลกเมื่อมันทำงานที่ ลูกค้าของคุณสามารถตรวจสอบ
เราจงใจไม่มี API เพื่อตรวจสอบว่าบริการกำลังทำงานอยู่หรือไม่เพราะเกือบจะไม่ล้มเหลวเมื่อคุณต้องการทำบางสิ่งเช่นที่คุณจบลงด้วยสภาพการแข่งขันในรหัสของคุณ
onDestroy()
จะไม่เรียกใช้บริการ ดังนั้นตัวแปรแบบคงที่ไม่สามารถอัปเดตได้ในสถานการณ์ดังกล่าวซึ่งส่งผลให้เกิดพฤติกรรมที่ไม่สอดคล้องกัน
ฉันใช้สิ่งต่อไปนี้จากภายในกิจกรรม:
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
getRunningServices
เลิกใช้แล้ว คำตอบนี้ต้องการการอัพเดตสำหรับเวอร์ชั่นที่ใหม่กว่า
เข้าใจแล้ว!
คุณต้องเรียก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;
}
}
ส่วนประกอบเล็ก ๆ คือ:
เป้าหมายของฉันคือการรู้ว่าบริการกำลังทำงานอยู่โดยไม่ใช้งานจริงหากไม่ได้ทำงานอยู่
การโทร 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 ขีดเส้นใต้ไว้
คุณสามารถใช้สิ่งนี้ (ฉันยังไม่ได้ลอง แต่หวังว่าจะได้ผล):
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);
ภายใต้รหัส
if(startService(someIntent) != null)
นั้นจะตรวจสอบว่าIsserviceRunning
แต่มันจะเล่นบริการใหม่
/**
* 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;
}
สารสกัดจากเอกสาร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()
เพราะมันเป็นเรื่องง่ายมาก
ฉันได้ปรับแก้เล็กน้อยหนึ่งในโซลูชั่นที่นำเสนอข้างต้น แต่ผ่านชั้นเรียนแทนที่จะเป็นชื่อสตริงทั่วไปเพื่อให้แน่ใจว่าได้เปรียบเทียบสตริงที่ออกมาจากวิธีการเดียวกัน 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>
วิธีที่เหมาะสมในการตรวจสอบว่าบริการกำลังทำงานอยู่หรือไม่ ใช้ 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;
}
}
};
}
ฉันแค่ต้องการเพิ่มบันทึกลงในคำตอบโดย @Snicolas ขั้นตอนต่อไปสามารถนำมาใช้ในการตรวจสอบบริการแบบครบวงจรที่มี / onDestroy()
ไม่มีการเรียก
onDestroy()
ชื่อ: ไปที่การตั้งค่า -> แอปพลิเคชัน -> บริการที่ใช้งาน -> เลือกและหยุดบริการของคุณ
onDestroy()
ไม่ได้รับการโทร: ไปที่การตั้งค่า -> แอปพลิเคชัน -> จัดการแอปพลิเคชัน -> เลือกและ "บังคับหยุด" แอปพลิเคชันของคุณที่บริการของคุณทำงานอยู่ อย่างไรก็ตามเมื่อแอปพลิเคชันของคุณหยุดที่นี่ดังนั้นแน่นอนว่าบริการจะหยุดทำงานเช่นกัน
ในที่สุดฉันอยากจะพูดถึงว่าวิธีการดังกล่าวโดยใช้ตัวแปรคงที่ในชั้นเรียนเดี่ยวทำงานสำหรับฉัน
onDestroy
ไม่ได้ถูกเรียกใช้บริการเสมอไปดังนั้นสิ่งนี้จึงไร้ประโยชน์!
ตัวอย่างเช่น: เพียงแค่เรียกใช้แอปอีกครั้งด้วยการเปลี่ยนแปลงเดียวจาก Eclipse แอปพลิเคชันถูกบังคับให้ออกโดยใช้ SIG: 9
ก่อนอื่นคุณไม่ต้องพยายามเข้าถึงบริการโดยใช้ ActivityManager (พูดถึงที่นี่ )
บริการสามารถทำงานด้วยตัวเองถูกผูกไว้กับกิจกรรมหรือทั้งสองอย่าง วิธีการเช็คอินกิจกรรมหากบริการของคุณกำลังทำงานอยู่หรือไม่โดยการทำอินเทอร์เฟซ (ที่ขยาย Binder) ซึ่งคุณประกาศวิธีการที่ทั้งกิจกรรมและบริการเข้าใจ คุณสามารถทำได้โดยสร้างอินเทอร์เฟซของคุณเองที่คุณประกาศเช่น "isServiceRunning ()" จากนั้นคุณสามารถผูกกิจกรรมของคุณกับบริการของคุณเรียกใช้เมธอด isServiceRunning () บริการจะตรวจสอบตัวเองว่ามันกำลังทำงานอยู่หรือไม่และส่งคืนบูลีนไปที่กิจกรรมของคุณ
นอกจากนี้คุณยังสามารถใช้วิธีนี้เพื่อหยุดบริการของคุณหรือโต้ตอบกับมันในอีกทางหนึ่ง
ฉันใช้บทช่วยสอนนี้เพื่อเรียนรู้วิธีใช้สถานการณ์นี้ในแอปพลิเคชันของฉัน
อีกครั้งทางเลือกอื่นที่ผู้คนอาจพบว่าสะอาดกว่าหากพวกเขาใช้ 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
เป็นค่าคงที่ที่คุณกำหนดเอกชนในชั้นเรียนของคุณเพื่อระบุเจตนาที่รอดำเนินการที่เกี่ยวข้องกับการให้บริการของคุณ
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(...);
}
รุ่น 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
สำหรับ
อีกวิธีใช้ 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)
ใน kotlin คุณสามารถเพิ่มตัวแปรบูลีนในวัตถุที่เป็นคู่หูและตรวจสอบค่าของมันจากคลาสที่คุณต้องการ:
companion object{
var isRuning = false
}
เปลี่ยนค่าเมื่อสร้างและทำลายบริการ
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
ใน 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)
}
}
}
สำหรับ 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
}
การตอบสนองของ 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)
ActivityManager.getRunningServices
เลิกใช้แล้วตั้งแต่ Android O
อาจมีบริการหลายอย่างที่มีชื่อคลาสเดียวกัน
ฉันเพิ่งสร้างสองแอพ 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;
}
กรุณาใช้รหัสนี้
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;
}
สิ่งนี้ใช้กับการแก้ไขจุดบกพร่องของ Intent Service ได้มากกว่าเนื่องจากพวกเขาวางไข่เธรด แต่อาจใช้ได้กับบริการปกติเช่นกัน ฉันพบกระทู้นี้ด้วย Binging
ในกรณีของฉันฉันเล่นกับดีบักเกอร์และพบมุมมองเธรด มันดูเหมือนไอคอนสัญลักษณ์แสดงหัวข้อย่อยใน MS Word อย่างไรก็ตามคุณไม่จำเป็นต้องอยู่ในโหมดดีบักเกอร์เพื่อใช้งาน คลิกที่กระบวนการและคลิกที่ปุ่มนั้น บริการแสดงเจตนาใด ๆ จะปรากฏขึ้นในขณะที่กำลังทำงานอย่างน้อยในโปรแกรมจำลอง
หากบริการเป็นของกระบวนการอื่นหรือ 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)
จากชั้นใดก็ได้
stopService
นี้จะไม่ทำงานถ้าคุณโทร อย่างน้อยสำหรับบริการเจตนา onDestroy()
จะถูกเรียกใช้ในทันที แต่onHandleIntent()
จะยังคงทำงานอยู่
ใช้งานง่ายผูกกับไม่สร้างอัตโนมัติ - ดู 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();
ฉันไม่ได้มีเวลาที่จะขุดในคดังนั้นจนกว่าฉันจะแยกสายที่เหลือฉันระงับคำตอบของฉัน
แต่วิธีที่ดีที่สุดสำหรับการตรวจสอบว่าบริการที่กำลังทำงานอยู่คือการสร้างการเชื่อมโยง (ถ้าไม่มีการผูกบริการที่ไม่ได้สร้าง) - และสอบถามบริการเกี่ยวกับสถานะผ่านการผูก (ใช้การตั้งค่าสถานะภายในที่เก็บไว้ในสถานะ
ฉันพบว่าน่าสนใจ:
/**
* 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;
}
* * * *
การแปลง 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
คุณสามารถใช้ตัวเลือกนี้จากตัวเลือกนักพัฒนา 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.
เอาง่ายๆนะ ... :)
ฉันคิดว่าทางออกที่เหมาะสมที่สุดคือการจับคู่ค่าคีย์ใน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);
}
ไม่มีการอนุญาตพิเศษไม่มีการวนซ้ำ ... วิธีง่ายๆวิธีแก้ปัญหาที่สะอาด :)
หากคุณต้องการข้อมูลเพิ่มเติมโปรดอ้างอิงลิงค์
หวังว่านี่จะช่วยได้
onDestroy
ไม่ได้เรียกเสมอเมื่อบริการถูกฆ่าตาย ตัวอย่างเช่นฉันเคยเห็นบริการของฉันถูกฆ่าในสถานการณ์ที่หน่วยความจำเหลือน้อยโดยไม่onDestroy
ถูกเรียก