Android: ฉันจะรับกิจกรรมเบื้องหน้าปัจจุบัน (จากบริการ) ได้อย่างไร


180

มีวิธี Android ดั้งเดิมเพื่อรับการอ้างอิงถึงกิจกรรมที่กำลังทำงานอยู่จากบริการหรือไม่?

ฉันมีบริการที่ทำงานอยู่เบื้องหลังและฉันต้องการอัปเดตกิจกรรมปัจจุบันของฉันเมื่อมีเหตุการณ์เกิดขึ้น (ในบริการ) มีวิธีง่าย ๆ (เช่นเดียวกับที่ฉันแนะนำไว้ข้างต้น)?


บางทีนี่อาจให้ความคิดกับคุณstackoverflow.com/a/28423385/185022
AZ_

คำตอบ:


87

มีวิธี Android ดั้งเดิมเพื่อรับการอ้างอิงถึงกิจกรรมที่กำลังทำงานอยู่จากบริการหรือไม่?

คุณไม่สามารถเป็นเจ้าของ "กิจกรรมที่กำลังทำงานอยู่"

ฉันมีบริการที่ทำงานอยู่เบื้องหลังและฉันต้องการอัปเดตกิจกรรมปัจจุบันของฉันเมื่อมีเหตุการณ์เกิดขึ้น (ในบริการ) มีวิธีง่าย ๆ (เช่นเดียวกับที่ฉันแนะนำไว้ข้างต้น)?

  1. ส่งการออกอากาศIntentไปยังกิจกรรม - นี่คือตัวอย่างโครงการที่แสดงรูปแบบนี้
  2. ให้กิจกรรมจัดหาPendingIntent(เช่นผ่านcreatePendingResult()) ที่บริการเรียกใช้
  3. ให้กิจกรรมลงทะเบียนวัตถุโทรกลับหรือฟังด้วยบริการผ่านbindService()และให้บริการเรียกวิธีการเหตุการณ์บนวัตถุติดต่อกลับ / รับฟังนั้น
  4. ส่งการออกอากาศที่สั่งซื้อIntentไปยังกิจกรรมโดยมีลำดับความสำคัญต่ำBroadcastReceiverเป็นข้อมูลสำรอง (เพื่อเพิ่มNotificationหากกิจกรรมไม่ได้อยู่บนหน้าจอ) - นี่คือโพสต์บล็อกที่มีรูปแบบนี้เพิ่มเติม

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

1
@ George: สิ่งที่คุณต้องการจะไม่ช่วยให้คุณ เมื่อคุณชี้ให้เห็นว่าคุณมี "กิจกรรมมากมาย" ดังนั้นมันเป็นคลาสที่แยกกันทั้งหมด ดังนั้นจึงไม่มีอะไรที่บริการของคุณสามารถทำได้กับพวกเขาโดยไม่ต้องมีการinstanceofตรวจสอบบล็อกขนาดใหญ่หรือปรับโครงสร้างกิจกรรมเพื่อแบ่งปันซุปเปอร์คลาสหรืออินเทอร์เฟซร่วมกัน และถ้าคุณจะปรับโครงสร้างกิจกรรมคุณอาจทำในลักษณะที่เหมาะสมกับกรอบงานและครอบคลุมสถานการณ์เพิ่มเติมเช่นไม่มีกิจกรรมใดที่กำลังทำอยู่ # 4 น่าจะเป็นงานที่น้อยที่สุดและยืดหยุ่นที่สุด
CommonsWare

2
ขอบคุณ แต่ฉันมีทางออกที่ดีกว่า กิจกรรมทั้งหมดของฉันขยายคลาส BaseActivity ที่กำหนดเอง ฉันได้ตั้งค่า ContextRegister ที่ลงทะเบียนกิจกรรมเป็นปัจจุบันเมื่อใดก็ตามที่อยู่ในเบื้องหน้าโดยมีตัวอักษร 3 บรรทัดในคลาส BaseActivity ของฉัน ยังขอบคุณสำหรับการสนับสนุน
George

3
@ George: ระวังการรั่วไหลของหน่วยความจำ สมาชิกข้อมูลคงที่ไม่แน่นอนจะต้องหลีกเลี่ยงใน Java เป็นไปได้
CommonsWare

ฉันใส่วัตถุไว้แน่น แต่ฉันจะเก็บเรื่องนี้ไว้ในใจ ขอบคุณ
George

139

อัปเดต : สิ่งนี้ใช้ไม่ได้กับกิจกรรมของแอปอื่น ๆใน Android 5.0


นี่เป็นวิธีที่ดีในการทำโดยใช้เครื่องมือจัดการกิจกรรม คุณจะได้รับ taskTasks จากผู้จัดการกิจกรรม มันจะกลับงานที่ใช้งานอยู่ในปัจจุบันก่อนเสมอ จากนั้นคุณจะได้รับกิจกรรมยอดนิยม

ตัวอย่างที่นี่

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

เมื่อคุณได้รับวัตถุ ComponentName โดยขอ topActivity จากรายการของคุณ

นี่คือตัวอย่าง

    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    Log.d("topActivity", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
    ComponentName componentInfo = taskInfo.get(0).topActivity;
    componentInfo.getPackageName();

คุณจะต้องได้รับอนุญาตจากรายการของคุณ:

<uses-permission android:name="android.permission.GET_TASKS"/>

3
คำตอบของคุณถูกต้อง 100 %hanxx 4 มันดีกว่าที่คุณโพสต์คำตอบเดียวกันที่นี่
Shahzad Imam

1
@ArtOfWarfare ยังไม่ได้รับการยืนยัน แต่ใช่จำนวนของชิ้นส่วนที่คุณไม่เกี่ยวข้องเนื่องจากพวกเขาโฮสต์โดยกิจกรรมเดียวเสมอ
เนลสันรามิเรซ

2
ถ้าฉันต้องการกิจกรรมปัจจุบันบ่อยขึ้นฉันต้องสำรวจบ่อยมากใช่มั้ย มีวิธีรับเหตุการณ์โทรกลับเมื่อใดก็ตามที่กิจกรรมเบื้องหน้าเปลี่ยนไปหรือไม่
nizam.sp

43
เพื่อให้ทุกคนตระหนักถึงเอกสารจะระบุสิ่งนี้เกี่ยวกับวิธีการ getRunningTasks () -------- หมายเหตุ: วิธีนี้มีไว้สำหรับการดีบักและนำเสนอส่วนต่อประสานผู้ใช้การจัดการงานเท่านั้น สิ่งนี้ไม่ควรใช้สำหรับตรรกะหลักในแอปพลิเคชันเช่นการตัดสินใจระหว่างพฤติกรรมที่แตกต่างกันตามข้อมูลที่พบที่นี่ ไม่รองรับการใช้งานดังกล่าวและอาจมีการแตกในอนาคต ตัวอย่างเช่นหากแอปพลิเคชันหลายตัวสามารถทำงานอย่างแข็งขันในเวลาเดียวกันสมมติฐานที่ทำเกี่ยวกับความหมายของข้อมูลที่นี่เพื่อวัตถุประสงค์ในการควบคุมการไหลจะไม่ถูกต้อง ------------
Ryan

30
semifake บน api 21As of LOLLIPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive.
sherpya

116

คำเตือน: การละเมิดของ Google Play

Google ขู่ว่าจะลบแอปออกจาก Play Store หากพวกเขาใช้บริการการเข้าถึงเพื่อจุดประสงค์ที่ไม่สามารถเข้าถึงได้ แต่นี้เป็นข่าวที่ถูกพิจารณาใหม่


ใช้ AccessibilityService

ประโยชน์ที่ได้รับ

  • ผ่านการทดสอบและทำงานใน Android 2.2 (API 8) ผ่าน Android 7.1 (API 25)
  • ไม่ต้องการการสำรวจ
  • ไม่ต้องการการGET_TASKSอนุญาต

ข้อเสีย

  • ผู้ใช้แต่ละคนจะต้องเปิดใช้งานบริการในการตั้งค่าการเข้าถึงของ Android
  • สิ่งนี้ไม่น่าเชื่อถือ 100% บางครั้งเหตุการณ์เกิดขึ้นตามลำดับ
  • บริการทำงานอยู่เสมอ
  • เมื่อผู้ใช้พยายามเปิดใช้งานผู้ใช้จะAccessibilityServiceไม่สามารถกดปุ่ม OK หากแอปวางซ้อนทับบนหน้าจอ แอพบางตัวที่ทำเช่นนี้คือ Velis Auto Brightness และ Lux สิ่งนี้อาจสร้างความสับสนเนื่องจากผู้ใช้อาจไม่รู้ว่าทำไมพวกเขาจึงไม่สามารถกดปุ่มหรือวิธีแก้ไขได้
  • ผู้AccessibilityServiceไม่รู้กิจกรรมปัจจุบันจนกว่าจะมีการเปลี่ยนแปลงกิจกรรมครั้งแรก

ตัวอย่าง

บริการ

public class WindowChangeDetectingService extends AccessibilityService {

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();

        //Configure these here for compatibility with API 13 and below.
        AccessibilityServiceInfo config = new AccessibilityServiceInfo();
        config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
        config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

        if (Build.VERSION.SDK_INT >= 16)
            //Just in case this helps
            config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

        setServiceInfo(config);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.getPackageName() != null && event.getClassName() != null) {
                ComponentName componentName = new ComponentName(
                    event.getPackageName().toString(),
                    event.getClassName().toString()
                );

                ActivityInfo activityInfo = tryGetActivity(componentName);
                boolean isActivity = activityInfo != null;
                if (isActivity)
                    Log.i("CurrentActivity", componentName.flattenToShortString());
            }
        }
    }

    private ActivityInfo tryGetActivity(ComponentName componentName) {
        try {
            return getPackageManager().getActivityInfo(componentName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    @Override
    public void onInterrupt() {}
}

AndroidManifest.xml

ผสานสิ่งนี้เข้ากับรายการของคุณ:

<application>
    <service
        android:label="@string/accessibility_service_name"
        android:name=".WindowChangeDetectingService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibilityservice"/>
    </service>
</application>

ข้อมูลการบริการ

ใส่สิ่งนี้ในres/xml/accessibilityservice.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
 start in Android 4.1.1 -->
<accessibility-service
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagIncludeNotImportantViews"
    android:description="@string/accessibility_service_description"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:ignore="UnusedAttribute"/>

การเปิดใช้งานบริการ

ผู้ใช้แอปแต่ละรายจะต้องเปิดใช้งานอย่างชัดเจนAccessibilityServiceเพื่อให้สามารถใช้งานได้ ดูStackOverflow คำตอบสำหรับวิธีการทำสิ่งนี้

โปรดทราบว่าผู้ใช้จะไม่สามารถกดปุ่มตกลงเมื่อพยายามเปิดใช้งานบริการการเข้าถึงหากแอพวางซ้อนทับบนหน้าจอเช่น Velis Auto Brightness หรือ Lux


1
@lovemint สิ่งที่ฉันหมายถึงคือ: ฉันระบุตัวอย่างsettingsActivityของyour.app.ServiceSettingsActivityดังนั้นคุณควรเปลี่ยนเป็นกิจกรรมการตั้งค่าของคุณสำหรับบริการการเข้าถึงของคุณ ฉันคิดว่ากิจกรรมการตั้งค่าเป็นทางเลือกอยู่แล้วดังนั้นฉันจึงลบส่วนนั้นออกจากคำตอบเพื่อให้ง่ายขึ้น
Sam

1
ขอบคุณมากคำถามสุดท้ายเท่านั้น หากฉันต้องการจัดการจาก API 8 ถึงปัจจุบันฉันควรทำงานในรหัสแทนการใช้ XML หรือไม่
paolo2988

1
@lovemint นั่นถูกต้องแล้ว ฉันเพิ่งอัปเดตโค้ดตัวอย่างเพื่อทำสิ่งนี้
Sam

1
@ Sam ฉันสามารถยืนยันได้ว่าบริการนี้ทำงานได้อย่างสมบูรณ์ พฤติกรรมที่แปลกประหลาดอย่างหนึ่งฉันใช้ความตั้งใจในกิจกรรมหลักเพื่อเปิดใช้งานบริการการเข้าถึง หลังจากเปิดใช้งานครั้งแรกบริการนี้จะเปิดใช้งาน แต่onAccessibilityEventไม่ได้รับเหตุการณ์ใด ๆ แต่ถ้าฉันปิดใช้งานและเปิดใช้งานอีกครั้งบริการการเข้าถึงบริการจะเปิดใช้งานอีกครั้งและ onAccessibilityEventเริ่มทำงาน
paolo2988

2
ขอบคุณสำหรับรหัสของคุณ ฉันสร้างแอพนี้สำหรับคุณ :)
ห้องนิรภัย

20

สามารถทำได้โดย:

  1. ใช้คลาสแอปพลิเคชันของคุณเองลงทะเบียน ActivityLifecycleCallbacks - วิธีนี้คุณจะเห็นว่าเกิดอะไรขึ้นกับแอพของเรา ทุกครั้งที่กลับมาโทรกลับจะกำหนดกิจกรรมที่มองเห็นได้ในปัจจุบันบนหน้าจอและเมื่อหยุดชั่วคราวจะเป็นการลบการบ้านออก มันใช้วิธีการregisterActivityLifecycleCallbacks()ที่เพิ่มเข้ามาใน API 14

    public class App extends Application {
    
    private Activity activeActivity;
    
    @Override
    public void onCreate() {
        super.onCreate();
        setupActivityListener();
    }
    
    private void setupActivityListener() {
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
                activeActivity = activity;
            }
            @Override
            public void onActivityPaused(Activity activity) {
                activeActivity = null;
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {
            }
        });
    }
    
    public Activity getActiveActivity(){
        return activeActivity;
    }
    
    }
  2. ในสายบริการของคุณgetApplication()และส่งไปยังชื่อคลาสแอปของคุณ (แอปในกรณีนี้) กว่าที่คุณจะโทรได้app.getActiveActivity()- นั่นจะให้กิจกรรมที่มองเห็นได้ในปัจจุบัน (หรือเป็นโมฆะเมื่อไม่มีกิจกรรมปรากฏให้เห็น) คุณสามารถได้รับชื่อของกิจกรรมโดยการโทรactiveActivity.getClass().getSimpleName()


1
ฉันได้รับ exeull ของ Nullpointer ที่ activeActivity.getClass (). getSimpleName (). คุณช่วยได้ไหม
เริ่มต้น

เช่นเดียวกับที่คุณเห็นจาก ActivityLifecycleCallbacks - ในกรณีที่ไม่สามารถมองเห็นกิจกรรม application.getActiveActivity () จะส่งกลับค่า null นั่นหมายความว่าจะไม่เห็นกิจกรรมใด ๆ คุณต้องตรวจสอบเรื่องนี้ในบริการของคุณหรือที่อื่นคุณใช้สิ่งนี้
Vit Veres

ตกลง .. แต่ในกรณีที่กิจกรรมดำเนินการต่อแล้วไม่ควรคืนค่าว่างหรือไม่ กิจกรรมของฉันได้เปิดตัวและในประวัติย่อฉันได้รับ
เริ่มต้น

ยากที่จะคาดเดาพยายามที่จะนำจุดพักที่onActivityResumed(), onActivityPaused()และเพื่อgetActiveActivity()ที่จะดูว่ามัน Callet ถ้าเคยดังนั้น
Vit Veres

@ ผู้เริ่มต้นจะต้องตรวจสอบว่า activityและactiveActivityเหมือนกันก่อนที่จะกำหนดactiveActivityด้วยnullเพื่อหลีกเลี่ยงข้อผิดพลาดอันเนื่องมาจากคำสั่งเรียกของวิธีการวงจรชีวิตของกิจกรรมต่าง ๆ
Rahul Tiwari

16

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

public interface ContextProvider {
    Context getActivityContext();
}

public class MyApplication extends Application implements ContextProvider {
    private Activity currentActivity;

    @Override
    public Context getActivityContext() {
         return currentActivity;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityStarted(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityResumed(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityPaused(Activity activity) {
                MyApplication.this.currentActivity = null;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                // don't clear current activity because activity may get stopped after
                // the new activity is resumed
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                // don't clear current activity because activity may get destroyed after
                // the new activity is resumed
            }
        });
    }
}

แล้วกำหนดค่าภาชนะ DI ของคุณเพื่อการกลับมาของอินสแตนซ์MyApplicationสำหรับContextProviderเช่น

public class ApplicationModule extends AbstractModule {    
    @Provides
    ContextProvider provideMainActivity() {
        return MyApplication.getCurrent();
    }
}

(โปรดทราบว่าการดำเนินการgetCurrent()ถูกละเว้นจากโค้ดด้านบนเป็นเพียงตัวแปรแบบคงที่ที่ตั้งค่าจากตัวสร้างแอปพลิเคชัน)


4
เราจะต้องตรวจสอบว่า activityและcurrentActivityเหมือนกันก่อนที่จะกำหนดcurrentActivityด้วยnullเพื่อหลีกเลี่ยงข้อผิดพลาดเนื่องจากการเรียกคำสั่งของวิธีการวงจรชีวิตของกิจกรรมต่างๆ
Rahul Tiwari

14

ใช้ ActivityManager

ถ้าคุณต้องการที่จะรู้ว่าแอพลิเคชันActivityManagerที่มีกิจกรรมปัจจุบันคุณสามารถทำได้โดยใช้ เทคนิคที่คุณสามารถใช้ได้ขึ้นอยู่กับเวอร์ชั่นของ Android:

ประโยชน์ที่ได้รับ

  • ควรทำงานได้กับ Android ทุกรุ่นในปัจจุบัน

ข้อเสีย

  • ไม่ทำงานใน Android 5.1+ (เพราะจะส่งคืนเฉพาะแอปของคุณเอง)
  • เอกสารสำหรับ API เหล่านี้กล่าวว่าพวกเขามีไว้สำหรับการดีบักและการจัดการส่วนติดต่อผู้ใช้เท่านั้น
  • หากคุณต้องการอัปเดตตามเวลาจริงคุณต้องใช้การลงคะแนนเลือกตั้ง
  • อาศัย API ที่ซ่อนอยู่: ActivityManager.RunningAppProcessInfo.processState
  • การใช้งานนี้ไม่ได้รับกิจกรรมตัวสลับแอป

ตัวอย่าง (ตามรหัสของ KNaito )

public class CurrentApplicationPackageRetriever {

    private final Context context;

    public CurrentApplicationPackageRetriever(Context context) {
        this.context = context;
    }

    public String get() {
        if (Build.VERSION.SDK_INT < 21)
            return getPreLollipop();
        else
            return getLollipop();
    }

    private String getPreLollipop() {
        @SuppressWarnings("deprecation")
        List<ActivityManager.RunningTaskInfo> tasks =
            activityManager().getRunningTasks(1);
        ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
        ComponentName currentActivity = currentTask.topActivity;
        return currentActivity.getPackageName();
    }

    private String getLollipop() {
        final int PROCESS_STATE_TOP = 2;

        try {
            Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");

            List<ActivityManager.RunningAppProcessInfo> processes =
                activityManager().getRunningAppProcesses();
            for (ActivityManager.RunningAppProcessInfo process : processes) {
                if (
                    // Filters out most non-activity processes
                    process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                    &&
                    // Filters out processes that are just being
                    // _used_ by the process with the activity
                    process.importanceReasonCode == 0
                ) {
                    int state = processStateField.getInt(process);

                    if (state == PROCESS_STATE_TOP) {
                        String[] processNameParts = process.processName.split(":");
                        String packageName = processNameParts[0];

                        /*
                         If multiple candidate processes can get here,
                         it's most likely that apps are being switched.
                         The first one provided by the OS seems to be
                         the one being switched to, so we stop here.
                         */
                        return packageName;
                    }
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return null;
    }

    private ActivityManager activityManager() {
        return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

}

ประจักษ์

เพิ่มGET_TASKSสิทธิ์ในการAndroidManifest.xml:

<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />

10
มันไม่ทำงานบน Android M อีกต่อไป getRunningAppProcesses () ตอนนี้ส่งคืนเฉพาะแพ็คเกจแอปพลิเคชันของคุณ
Lior Iluz

1
ใช้งานไม่ได้กับ API ระดับ 22 (Android 5.1) เช่นกัน ทดสอบกับ Build LPB23
sumitb.mdi

9

ฉันใช้สิ่งนี้ในการทดสอบ มัน API> 19 และเฉพาะสำหรับกิจกรรมของแอปของคุณแม้ว่า

@TargetApi(Build.VERSION_CODES.KITKAT)
public static Activity getRunningActivity() {
    try {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Object activityThread = activityThreadClass.getMethod("currentActivityThread")
                .invoke(null);
        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
        activitiesField.setAccessible(true);
        ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
        for (Object activityRecord : activities.values()) {
            Class activityRecordClass = activityRecord.getClass();
            Field pausedField = activityRecordClass.getDeclaredField("paused");
            pausedField.setAccessible(true);
            if (!pausedField.getBoolean(activityRecord)) {
                Field activityField = activityRecordClass.getDeclaredField("activity");
                activityField.setAccessible(true);
                return (Activity) activityField.get(activityRecord);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    throw new RuntimeException("Didn't find the running activity");
}

แทนที่ ArrayMap ด้วย Map และมันจะทำงานบน 4.3 เช่นกัน ไม่ได้ทดสอบเวอร์ชัน Android ก่อนหน้านี้
Oliver Jonas

2

ใช้รหัสนี้สำหรับ API 21 หรือสูงกว่า วิธีนี้ใช้งานได้และให้ผลลัพธ์ที่ดีกว่าเมื่อเปรียบเทียบกับคำตอบอื่น ๆ ซึ่งจะตรวจจับกระบวนการพื้นหน้า

if (Build.VERSION.SDK_INT >= 21) {
    String currentApp = null;
    UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> applist = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time);
    if (applist != null && applist.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : applist) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);

        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }

คุณช่วยอธิบายรายละเอียดเกี่ยวกับผลลัพธ์ที่ได้จากวิธีนี้ดีกว่าคำตอบอื่น ๆ ได้ไหม?
Sam

ใช้งานได้กับเมนูการแจ้งเตือนเช่นกัน ฉันกำลังประสบปัญหาซึ่งถ้าฉันได้รับสายหรือข้อความ UsageStatsManager เริ่มให้ชื่อแพ็คเกจของ systemUi แทนแอพของฉัน
kukroid

@kukroid โพสต์คำถามแยกต่างหากและรวมถึงซอร์สโค้ดข้อมูลอุปกรณ์และแอพส่งข้อความและโทรศัพท์ที่คุณใช้
Sam

0

นี่คือคำตอบของฉันที่ใช้งานได้ดี ...

คุณควรจะได้รับกิจกรรมปัจจุบันด้วยวิธีนี้ ... หากคุณจัดโครงสร้างแอปของคุณด้วยกิจกรรมบางอย่างที่มีหลายส่วนและคุณต้องการติดตามกิจกรรมปัจจุบันของคุณคุณต้องใช้เวลามาก senario ของฉันคือฉันมีหนึ่งกิจกรรมที่มีหลายแฟรกเมนต์ ดังนั้นฉันสามารถติดตามกิจกรรมปัจจุบันผ่าน Application Object ซึ่งสามารถเก็บสถานะปัจจุบันทั้งหมดของตัวแปร Global

นี่คือวิธี เมื่อคุณเริ่มกิจกรรมของคุณคุณเก็บกิจกรรมนั้นโดย Application.setCurrentActivity (getIntent ()); แอปพลิเคชั่นนี้จะเก็บไว้ ในคลาสบริการของคุณคุณสามารถทำได้เช่น Intent currentIntent = Application.getCurrentActivity (); . getApplication () startActivity (currentIntent);


2
นี่ถือว่าบริการทำงานในกระบวนการเดียวกันกับกิจกรรมใช่ไหม
helleye

0

ฉันไม่ทราบว่าเป็นคำตอบที่โง่ แต่แก้ไขปัญหานี้ด้วยการจัดเก็บค่าสถานะในการตั้งค่าที่ใช้ร่วมกันทุกครั้งที่ฉันป้อน onCreate () ของกิจกรรมใด ๆ จากนั้นฉันใช้ค่าจากการตั้งค่าแบบ Shered เพื่อค้นหากิจกรรมเบื้องหน้า


1
เรากำลังมองหาวิธีการดั้งเดิมที่จะทำ
IgniteCoders

-3

เพิ่งค้นพบเกี่ยวกับเรื่องนี้ ด้วย apis เป็น:

  • minSdkVersion 19
  • targetSdkVersion 26

    ActivityManager.getCurrentActivity (บริบท)

หวังว่านี่คือการใช้งานใด ๆ


1
หากว่าฉันมีความหวัง บางทีเมื่อมันได้รับคำตอบมันก็มีอยู่ แต่ ณ ตอนนี้ไม่มีgetCurrentActivity()ฟังก์ชั่นในActivityManagerชั้นเรียน ( developer.android.com/reference/android/app/ActivityManager ) ตลกที่ฟังก์ชั่นนี้มีอยู่: ActivityManager.isUserAMonkey().
ByteSlinger

1
สิ่งนี้จะตรวจพบว่าคุณใช้ตัวแทนทดสอบลิงเพื่อทดสอบกิจกรรมของคุณหรือไม่ แม้ว่าชื่อตลกมันอาจจะมีประโยชน์สำหรับการทดสอบ (และทำให้คนอื่นหัวเราะ, obvs.) developer.android.com/studio/test/monkeyrunner
Ken Corey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.