Android 5.1.1 trở lên - getRunningAppProcesses () chỉ trả về gói ứng dụng của tôi


100

Có vẻ như Google cuối cùng đã đóng tất cả các cánh cửa để nhận gói ứng dụng nền hiện tại.

Sau bản cập nhật Lollipop, bị lỗi getRunningTasks(int maxNum)và nhờ câu trả lời này , tôi đã sử dụng mã này để lấy gói ứng dụng nền trước kể từ Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Có vẻ như Android 5.1.1 trở lên (6.0 Marshmallow) cũng bị khai tử getRunningAppProcesses(). Bây giờ nó trả về một danh sách các gói ứng dụng của riêng bạn.


UsageStatsManager

Chúng tôi có thể sử dụng UsageStatsManagerAPI mới như được mô tả ở đây nhưng nó không hoạt động cho tất cả các ứng dụng. Một số ứng dụng hệ thống sẽ trả về cùng một gói

com.google.android.googlequicksearchbox

AccessibilityService (Tháng 12 năm 2017: Sắp bị Google cấm sử dụng)

Một số ứng dụng sử dụng AccessibilityService(như đã thấy ở đây ) nhưng nó có một số nhược điểm.


Có cách nào khác để lấy gói ứng dụng đang chạy hiện tại không?


1
Du Speed ​​Booster có vẻ hoạt động. Tôi không chắc liệu nó có sử dụng UsageStatsManager hay không.
thecr0w

3
Lưu ý rằng một số nhà cung cấp lớn đã xóa hoạt động hệ thống cấp quyền truy cập vào API UsageStats khỏi thiết bị của họ. Điều này có nghĩa là các ứng dụng trên các thiết bị đó không bao giờ có thể có được quyền cần thiết.
Kevin Krumwiede

3
Samsung là một trong số đó. Đó là hơn 60% thị trường.
Kevin Krumwiede

3
Dưới đây là một vé từ kiểm tra lỗi android về vấn đề này: code.google.com/p/android-developer-preview/issues/...
Florian Barth

2
Nếu tôi phân tích cú pháp đầu ra của việc chạy pstrong một shell và chính sách là "fg"và nội dung của /proc/[pid]/oom_adj_scorebằng 0thì ứng dụng là ứng dụng nền trước. Thật không may, nó dường như /proc/[pid]/oom_adj_scorekhông còn đọc được trên Android 6.0. gist.github.com/jaredrummler/7d1498485e584c8a120e
Jared Rummler

Câu trả lời:


93

Để có danh sách các tiến trình đang chạy trên Android 1.6 - Android 6.0, bạn có thể sử dụng thư viện này mà tôi đã viết: https://github.com/jaredrummler/AndroidProcesses Thư viện đọc / proc để lấy thông tin về quy trình.

Google đã hạn chế đáng kể quyền truy cập vào / proc trong Android Nougat. Để có được danh sách các tiến trình đang chạy trên Android Nougat, bạn cần sử dụng UsageStatsManager hoặc có quyền truy cập root.

Nhấp vào lịch sử chỉnh sửa cho các giải pháp thay thế trước đó.


4
@androiddeveloper không, tôi chỉ sử dụng libsuperuser vì nó cung cấp một cách dễ dàng để chạy lệnh shell (không phải root hoặc root) và lấy đầu ra. Tôi có thể viết lại nó mà không cần libsuperuser nếu có đủ nhu cầu.
Jared Rummler

1
Xin lỗi vì điều đó. Chỉ là tò mò. :(
nhà phát triển android

1
@JaredRummler Tôi hiện đang trong quá trình triển khai giải pháp của bạn vào ứng dụng của tôi. Có vẻ như đang hoạt động tốt theo như tôi có thể nói
guy.gc 21/09/15

1
@androiddeveloper, Nếu bạn muốn sắp xếp nó dựa trên một thuộc tính khác, hãy Processthực hiện Comparablevà ghi đè compareTophương thức. Sau đó, khi bạn sử dụng Collections.sort(processesList), nó sẽ sử dụng theo thứ tự bạn đã chỉ định.
Srini

2
@BruceWayne Tôi nghĩ Jared đang tham khảo liên kết này: https://source.android.com/devices/tech/security/selinux/index.html
Eduardo Herzer 21/10/15

24
 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        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();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

Sử dụng phương pháp này để nhận nhiệm vụ tiền cảnh. Bạn sẽ cần Quyền hệ thống "android: get_usage_stats"

public static boolean needPermissionForBlocking(Context context){
    try {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
        return  (mode != AppOpsManager.MODE_ALLOWED);
    } catch (PackageManager.NameNotFoundException e) {
        return true;
    }
}

NẾU người dùng bật điều này trong cài đặt -> Bảo mật-> ứng dụng có quyền truy cập sử dụng. Sau đó u sẽ nhận được nhiệm vụ tiền cảnh. Quy trình tương tự Làm sạch thảm bằng Cheetahamobile liên kết google play


Như đã chỉ ra trong OP và các bình luận, có những nhược điểm lớn khi sử dụng UsageStatsManager. Samsung đã loại bỏ các yêu cầu và việc người dùng cấp quyền cho hệ thống khá khó chịu.
Jared Rummler

Tôi đã thử nghiệm trong thiết bị moto e2. Nó đang hoạt động tốt và vâng, chúng tôi cần thực hiện quyền này. tất cả chúng ta đều phải đối mặt với những vấn đề giống nhau. Tất cả chúng ta thực sự cần một cách tiếp cận tốt hơn. Chúc may mắn anh chàng
Tarun Sharma

2
Cách tiếp cận này ít nhất không hoạt động trên LG G3 vì không có mục menu "Cài đặt -> Bảo mật-> Ứng dụng có quyền truy cập sử dụng"
Dmitry

2
Đó không phải là câu trả lời cho câu hỏi của tôi. Hơn nữa, không có thông tin mới được cung cấp trong câu trả lời này.
Lior Iluz

1
Hoạt động tốt nhưng không đúng cách trong marshmallow. nếu thông báo đến thì quá trình chạy hiện tại sẽ là thông báo gói nhận được. thực sự ứng dụng không chạy trong foreground hoặc background :( giải pháp cần thiết..
WonderSoftwares

6

Hãy xem tại https://github.com/ricvalerio/foregroundappchecker , nó có thể là thứ bạn cần. Cung cấp mã mẫu và loại bỏ nỗi đau khi phải triển khai máy dò tiền cảnh phiên bản chéo.

Đây là hai mẫu:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Hoặc thường xuyên kiểm tra:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);

2
Cảm ơn nhưng không có gì mới ở đây. Anh ấy chỉ gói API UsageStatsManager được đề cập trong OP. Người dùng sẽ cần phải cho phép truy cập, như các phương pháp khác từ Android 5.1.1
Lior Iluz

@ Jérémy bạn đã yêu cầu các quyền cần thiết chưa?
rvalerio

Có vẻ như nó cũng bỏ phiếu mọi lúc, phải không?
nhà phát triển android

4

Google chỉ giới hạn chức năng này cho các ứng dụng hệ thống. Như đã được báo cáo trong một phiếu lỗi , bạn sẽ cần quyền REAL_GET_TASKS để truy cập vào đó.

Các ứng dụng hiện phải có ... quyền.REAL_GET_TASKS để có thể nhận thông tin quy trình cho tất cả các ứng dụng. Chỉ thông tin quy trình cho ứng dụng gọi điện sẽ được trả lại nếu ứng dụng không có quyền. Các ứng dụng đặc quyền sẽ tạm thời có thể lấy thông tin quy trình cho tất cả các ứng dụng nếu chúng không có quyền mới, nhưng không dùng quyền ... nữa.GET_TASKS Ngoài ra, chỉ các ứng dụng hệ thống mới có thể có được quyền REAL_GET_TASKS.


Tôi thấy applock vẫn đang hoạt động trên 5.1.1, sau khi ứng dụng được cấp quyền bảo mật -> "ứng dụng có quyền truy cập sử dụng" ... Điều này hơi đáng ngạc nhiên
mahesh ren 22/09/15

"Các quyền có chữ ký cấp độ bảo vệ, đặc quyền hoặc signatureOrSystem chỉ được cấp cho các ứng dụng hệ thống. Nếu một ứng dụng là một ứng dụng không thuộc hệ thống thông thường, ứng dụng đó sẽ không bao giờ có thể sử dụng các quyền này." Android studio nói .. Chúc bạn may mắn khi có Google được chấp nhận là "ứng dụng hệ thống".
Jérémy

2

Chỉ cần đưa ra một tối ưu hóa tiềm năng cho những gì tôi tưởng tượng là một đoạn mã được sao chép nhiều để phát hiện ứng dụng hàng đầu trên Android M.

Điều này

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    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();
        }
    }
}

Có thể đơn giản hóa điều này

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

Tôi thấy mình đang sử dụng mã này trong vòng lặp 2 giây và tự hỏi tại sao tôi lại sử dụng một giải pháp phức tạp là O (n * log (n)) khi một giải pháp đơn giản hơn có sẵn trong Collections.max () là O (n ).


0
public class AccessibilityDetectingService 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(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Sau đó, khởi động dịch vụ và bật trợ năng ứng dụng trong cài đặt thiết bị của bạn-> trợ năng-> Ứng dụng trên dịch vụ đó.


Điều này đã được đề cập trong câu hỏi và bạn vừa sao chép mã từ nguồn gốc của nó trên StackOverflow.
Sam

-2

Hãy thử sử dụng getRunningServices()thay vì getRunningAppProcesses()phương pháp.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);

3
Chào mừng bạn đến với Stack Overflow! Tôi không nghĩ rằng điều này sẽ hoạt động vì ứng dụng nền trước có thể không chạy một dịch vụ.
Sam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.