写个程序,‘监视’你的手机!

3,818 阅读2分钟

最近遇到这样的一个需求,获取用户当前正在使用的App。

在Android5.0之前,使用下面的代码即可获得相关信息:

        //5.0及以下
        private fun getTopAppOld(context: Context) {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val topActivity = activityManager.getRunningTasks(1)[0].topActivity.packageName
            (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
        }

但是5.0以后此方法就不行了,需要使用UsageStatsManager应用使用数据统计服务。

使用UsageStatsManager应用使用数据统计服务需要用户手动授权。

startActivityForResult(
    new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);

同时需要在注册文件中注册权限

<uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>

我们可以同下面的方法判断是否取得权限:

//检测用户是否对本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;
    }

取得权限之后,就可以启动一个后的service来监听当前用户使用的App。

import android.app.ActivityManager
import android.app.Service
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.widget.Toast
import java.util.concurrent.TimeUnit

class TopAppInfoService : Service() {
    private var myThread: MyThread? = null

    class MyThread constructor(private val context: Context) : Thread() {
        private var isRun = true

        fun setStop() {
            isRun = false
        }

        override fun run() {
            while (isRun) {
                try {
                    TimeUnit.SECONDS.sleep(2)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        getTopAppNew(context)
                    } else {
                        getTopAppOld(context)
                    }
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }

            }
        }

        //5.0及以下
        private fun getTopAppOld(context: Context) {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val topActivity = activityManager.getRunningTasks(1)[0].topActivity.packageName
            (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
        }

        //5.0及以上
        private fun getTopAppNew(context: Context) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                val m = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
                if (m != null) {
                    val now = System.currentTimeMillis()
                    //获取60秒之内的应用数据
                    val stats = m.queryUsageStats(UsageStatsManager.INTERVAL_BEST, now - 60 * 1000, now)
                    Log.i(TAG, "Running app number in last 60 seconds : " + stats!!.size)

                    var topActivity = ""

                    //取得最近运行的一个app,即当前运行的app
                    if (stats != null && !stats.isEmpty()) {
                        var j = 0
                        for (i in stats.indices) {
                            if (stats[i].lastTimeUsed > stats[j].lastTimeUsed) {
                                j = i
                            }
                        }
                        topActivity = stats[j].packageName//包名
                    }
                    if (!topActivity.isEmpty()) {
                        (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
                    }
                }
            }
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        myThread = MyThread(this)
        myThread!!.start()
        Log.i(TAG, "Service is start.")
    }

    override fun onDestroy() {
        super.onDestroy()
        myThread!!.setStop()
        Log.i(TAG, "Service is stop.")
    }

    fun showToast(txt: String?) {
        if (txt == null) {
            Log.e(TAG, "call method showToast, text is null.")
            return
        }
        val handler = Handler(Looper.getMainLooper())

        handler.post {
            Toast.makeText(this@TopAppInfoService, txt, Toast.LENGTH_LONG)
                    .show()
        }
    }

    companion object {
        val TAG = "TopAppInfoService"

        fun getAppName(context: Context, packageName: String): String {
            val pm = context.packageManager
            var Name: String
            try {
                Name = pm.getApplicationLabel(pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString()
            } catch (e: PackageManager.NameNotFoundException) {
                Name = ""
            }

            return Name
        }
    }
}

上面对Android5.0做了兼容,可以拿到App的包名和应用名。

剩下的就是在适当的时候启动服务了:

 btnStart = (Button) this.findViewById(R.id.btnStart);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TopAppInfoService.class);
                startService(intent);
            }
        });
        btnStop = (Button) this.findViewById(R.id.btnStop);
        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TopAppInfoService.class);
                stopService(intent);
            }
        });

在华为,小米和一加的真机上通过测试,请放心食用。

祝你生活愉快。