安卓5.0后获取所有运行的进程信息

前言

因为项目中有sticker键盘功能,而在安卓应用中想要发送键盘中的sticker只能通过分享的方式(iOS可以将图片放在粘贴板上),而直接调用系统的分享面板比较鸡肋,用户选择一个sticker后还要选择分享的途径。参考其他键盘产品,可以在分享的时候进行筛选,比如,在微信应用中打开了键盘点击sticker的时候只弹出 发送到朋友圈发送给朋友和添加到收藏,在短信中打开就直接转换为彩信。这样可用性就提高了很多。

实现方法就是在用intent调用系统分享的时候加上intent.setPackage(packName);,packName为当前打开键盘的应用包名,那么如何获取到当前手机内运行在最上面的进程包名就成了问题关键。

Android自从Lollipo以泄露用户信息的理由彻底禁止了getRunningTasks方法之后,世道就变得艰难起来,StackOverFlow上曾经大张旗鼓的讨论此问题,很多人使用ActivityManager.RunningAppProcessInfo方法来获取顶层app,这个方法似乎在某个版本中是有效的。但是幸福总是短暂的,到了Android6.0版本,即Marshmallow(api level 23)时,这些方法统统的废了,除了自己app中的信息外,只能获取启动器的信息。道理是很简单的,Marshmallow以权限严格著称,因此对于这种可能泄露其他应用信息的方法一概禁止了。

解决方法 UsageStatsManager

UsageStatsManager是用来统计app使用情况的类,直到Lollipop(api level 21)才加入Android。此类的具体用法可以参考:
UsageStatsManager

  • 使用

    • 1:修改AndroidManifest.xml,添加权限

      <!--注意这里:添加权限-->
      <uses-permission
          android:name="android.permission.PACKAGE_USAGE_STATS"
          tools:ignore="ProtectedPermissions"/>
      
  • 2:检测并引导用户开启权限

//检测用户是否对本app开启了“Apps with usage access”权限
    private boolean hasPermission() {
        AppOpsManager appOps = (AppOpsManager)
                getSystemService(Context.APP_OPS_SERVICE);
        int mode = 0;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                    android.os.Process.myUid(), getPackageName());
        }
        return mode == AppOpsManager.MODE_ALLOWED;
    }

引导用户去设置中开启权限

private static final int MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS = 1101;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS) {
            if (!hasPermission()) {
                //若用户未开启权限,则引导用户开启“Apps with usage access”权限
                startActivityForResult(
                        new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),
                        MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);
            }
        }
    }
  • 3:使用UsageStatsManager来获取当前运行的app
  private String printForegroundTask(Context context) {
       String packagename = "";
       // if the sdk >= 21. It can only use getRunningAppProcesses to get task top package name
       UsageStatsManager usm = (UsageStatsManager) 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()) {
               packagename = mySortedMap.get(
                       mySortedMap.lastKey()).getPackageName();
           }
       } else {// if sdk <= 20, can use getRunningTasks
           ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
           //4.获取正在开启应用的任务栈
           List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(1);
           ActivityManager.RunningTaskInfo runningTaskInfo = runningTasks.get(0);
           //5.获取栈顶的activity,然后在获取此activity所在应用的包名
           packagename = runningTaskInfo.topActivity.getPackageName();
       }
       return packagename;
   }

至此我们就能在安卓5.0之后拿到正在运行的进程名了

But 别的键盘类应用并没有申请过这个权限,而且一个对于开发者来说都不熟悉的系统级权限,用户就更不懂了,那么肯定是实现思路出了问题,仔细研读Android中的IME的API发现了一个方法

  @Override
   public void onStartInput(EditorInfo attribute, boolean restarting) {
       super.onStartInput(attribute, restarting);
       packName = attribute.packageName;
       Log.e(TAG, "onStartInput: "+packName );
   }

在onStartInput方法中就能直接拿到当前进程名。至此键盘分享的问题就解决了。

更多Android中的IME生命周期问题参见https://blog.csdn.net/wyhuiwyhui/article/details/8074691

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容