มีวิธี Android ดั้งเดิมเพื่อรับการอ้างอิงถึงกิจกรรมที่กำลังทำงานอยู่จากบริการหรือไม่?
ฉันมีบริการที่ทำงานอยู่เบื้องหลังและฉันต้องการอัปเดตกิจกรรมปัจจุบันของฉันเมื่อมีเหตุการณ์เกิดขึ้น (ในบริการ) มีวิธีง่าย ๆ (เช่นเดียวกับที่ฉันแนะนำไว้ข้างต้น)?
มีวิธี Android ดั้งเดิมเพื่อรับการอ้างอิงถึงกิจกรรมที่กำลังทำงานอยู่จากบริการหรือไม่?
ฉันมีบริการที่ทำงานอยู่เบื้องหลังและฉันต้องการอัปเดตกิจกรรมปัจจุบันของฉันเมื่อมีเหตุการณ์เกิดขึ้น (ในบริการ) มีวิธีง่าย ๆ (เช่นเดียวกับที่ฉันแนะนำไว้ข้างต้น)?
คำตอบ:
มีวิธี Android ดั้งเดิมเพื่อรับการอ้างอิงถึงกิจกรรมที่กำลังทำงานอยู่จากบริการหรือไม่?
คุณไม่สามารถเป็นเจ้าของ "กิจกรรมที่กำลังทำงานอยู่"
ฉันมีบริการที่ทำงานอยู่เบื้องหลังและฉันต้องการอัปเดตกิจกรรมปัจจุบันของฉันเมื่อมีเหตุการณ์เกิดขึ้น (ในบริการ) มีวิธีง่าย ๆ (เช่นเดียวกับที่ฉันแนะนำไว้ข้างต้น)?
Intent
ไปยังกิจกรรม - นี่คือตัวอย่างโครงการที่แสดงรูปแบบนี้PendingIntent
(เช่นผ่านcreatePendingResult()
) ที่บริการเรียกใช้bindService()
และให้บริการเรียกวิธีการเหตุการณ์บนวัตถุติดต่อกลับ / รับฟังนั้นIntent
ไปยังกิจกรรมโดยมีลำดับความสำคัญต่ำBroadcastReceiver
เป็นข้อมูลสำรอง (เพื่อเพิ่มNotification
หากกิจกรรมไม่ได้อยู่บนหน้าจอ) - นี่คือโพสต์บล็อกที่มีรูปแบบนี้เพิ่มเติมinstanceof
ตรวจสอบบล็อกขนาดใหญ่หรือปรับโครงสร้างกิจกรรมเพื่อแบ่งปันซุปเปอร์คลาสหรืออินเทอร์เฟซร่วมกัน และถ้าคุณจะปรับโครงสร้างกิจกรรมคุณอาจทำในลักษณะที่เหมาะสมกับกรอบงานและครอบคลุมสถานการณ์เพิ่มเติมเช่นไม่มีกิจกรรมใดที่กำลังทำอยู่ # 4 น่าจะเป็นงานที่น้อยที่สุดและยืดหยุ่นที่สุด
อัปเดต : สิ่งนี้ใช้ไม่ได้กับกิจกรรมของแอปอื่น ๆใน 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"/>
As 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.
Google ขู่ว่าจะลบแอปออกจาก Play Store หากพวกเขาใช้บริการการเข้าถึงเพื่อจุดประสงค์ที่ไม่สามารถเข้าถึงได้ แต่นี้เป็นข่าวที่ถูกพิจารณาใหม่
AccessibilityService
AccessibilityService
คุณสามารถตรวจสอบหน้าต่างที่ใช้งานอยู่ในปัจจุบันโดยใช้onAccessibilityEvent
โทรกลับตรวจสอบประเภทเหตุการณ์เพื่อกำหนดว่าเมื่อใดที่หน้าต่างปัจจุบันเปลี่ยนไปTYPE_WINDOW_STATE_CHANGED
PackageManager.getActivityInfo()
ตรวจสอบว่าหน้าต่างเป็นกิจกรรมโดยการเรียกGET_TASKS
อนุญาต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() {}
}
ผสานสิ่งนี้เข้ากับรายการของคุณ:
<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
settingsActivity
ของyour.app.ServiceSettingsActivity
ดังนั้นคุณควรเปลี่ยนเป็นกิจกรรมการตั้งค่าของคุณสำหรับบริการการเข้าถึงของคุณ ฉันคิดว่ากิจกรรมการตั้งค่าเป็นทางเลือกอยู่แล้วดังนั้นฉันจึงลบส่วนนั้นออกจากคำตอบเพื่อให้ง่ายขึ้น
onAccessibilityEvent
ไม่ได้รับเหตุการณ์ใด ๆ แต่ถ้าฉันปิดใช้งานและเปิดใช้งานอีกครั้งบริการการเข้าถึงบริการจะเปิดใช้งานอีกครั้งและ onAccessibilityEvent
เริ่มทำงาน
สามารถทำได้โดย:
ใช้คลาสแอปพลิเคชันของคุณเองลงทะเบียน 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;
}
}
ในสายบริการของคุณgetApplication()
และส่งไปยังชื่อคลาสแอปของคุณ (แอปในกรณีนี้) กว่าที่คุณจะโทรได้app.getActiveActivity()
- นั่นจะให้กิจกรรมที่มองเห็นได้ในปัจจุบัน (หรือเป็นโมฆะเมื่อไม่มีกิจกรรมปรากฏให้เห็น) คุณสามารถได้รับชื่อของกิจกรรมโดยการโทรactiveActivity.getClass().getSimpleName()
onActivityResumed()
, onActivityPaused()
และเพื่อgetActiveActivity()
ที่จะดูว่ามัน Callet ถ้าเคยดังนั้น
activity
และactiveActivity
เหมือนกันก่อนที่จะกำหนดactiveActivity
ด้วยnull
เพื่อหลีกเลี่ยงข้อผิดพลาดอันเนื่องมาจากคำสั่งเรียกของวิธีการวงจรชีวิตของกิจกรรมต่าง ๆ
ฉันไม่สามารถหาวิธีแก้ปัญหาที่ทีมของเราจะมีความสุขด้วยดังนั้นเราจึงรีดของเราเอง เราใช้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()
ถูกละเว้นจากโค้ดด้านบนเป็นเพียงตัวแปรแบบคงที่ที่ตั้งค่าจากตัวสร้างแอปพลิเคชัน)
activity
และcurrentActivity
เหมือนกันก่อนที่จะกำหนดcurrentActivity
ด้วยnull
เพื่อหลีกเลี่ยงข้อผิดพลาดเนื่องจากการเรียกคำสั่งของวิธีการวงจรชีวิตของกิจกรรมต่างๆ
ActivityManager
ถ้าคุณต้องการที่จะรู้ว่าแอพลิเคชันActivityManager
ที่มีกิจกรรมปัจจุบันคุณสามารถทำได้โดยใช้ เทคนิคที่คุณสามารถใช้ได้ขึ้นอยู่กับเวอร์ชั่นของ Android:
ActivityManager.getRunningTasks
( ตัวอย่าง )ActivityManager.getRunningAppProcesses
( ตัวอย่าง )ActivityManager.RunningAppProcessInfo.processState
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" />
ฉันใช้สิ่งนี้ในการทดสอบ มัน 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");
}
ใช้รหัสนี้สำหรับ 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();
}
}
นี่คือคำตอบของฉันที่ใช้งานได้ดี ...
คุณควรจะได้รับกิจกรรมปัจจุบันด้วยวิธีนี้ ... หากคุณจัดโครงสร้างแอปของคุณด้วยกิจกรรมบางอย่างที่มีหลายส่วนและคุณต้องการติดตามกิจกรรมปัจจุบันของคุณคุณต้องใช้เวลามาก senario ของฉันคือฉันมีหนึ่งกิจกรรมที่มีหลายแฟรกเมนต์ ดังนั้นฉันสามารถติดตามกิจกรรมปัจจุบันผ่าน Application Object ซึ่งสามารถเก็บสถานะปัจจุบันทั้งหมดของตัวแปร Global
นี่คือวิธี เมื่อคุณเริ่มกิจกรรมของคุณคุณเก็บกิจกรรมนั้นโดย Application.setCurrentActivity (getIntent ()); แอปพลิเคชั่นนี้จะเก็บไว้ ในคลาสบริการของคุณคุณสามารถทำได้เช่น Intent currentIntent = Application.getCurrentActivity (); . getApplication () startActivity (currentIntent);
ฉันไม่ทราบว่าเป็นคำตอบที่โง่ แต่แก้ไขปัญหานี้ด้วยการจัดเก็บค่าสถานะในการตั้งค่าที่ใช้ร่วมกันทุกครั้งที่ฉันป้อน onCreate () ของกิจกรรมใด ๆ จากนั้นฉันใช้ค่าจากการตั้งค่าแบบ Shered เพื่อค้นหากิจกรรมเบื้องหน้า
เพิ่งค้นพบเกี่ยวกับเรื่องนี้ ด้วย apis เป็น:
targetSdkVersion 26
ActivityManager.getCurrentActivity (บริบท)
หวังว่านี่คือการใช้งานใด ๆ
getCurrentActivity()
ฟังก์ชั่นในActivityManager
ชั้นเรียน ( developer.android.com/reference/android/app/ActivityManager ) ตลกที่ฟังก์ชั่นนี้มีอยู่: ActivityManager.isUserAMonkey()
.