Android 取得应用程序的启动次数和运行时间等信息

使用情景:最近有个需求是统计后台应用运行时间,如果一个应用在后台运行超过一定时间就Kill掉进程,达到省电的目的。此时就可以使用PkgUsageStats这个类来实现啦!

通过com.android.internal.os.PkgUsageStats这个类可以得到一个应用程序的启动次数,运行时间等信息,功能强大,但是google并没有将这个类作为API接口提供给开发者,如果在android源码下开发,可以通过以下代码来使用这个类:
import com.android.internal.app.IUsageStats;
import com.android.internal.os.PkgUsageStats;


//比较两个应用程序的启动次数和运行时间
public final int compare(ApplicationInfo a, ApplicationInfo b) {
ComponentName aName = a.intent.getComponent();
ComponentName bName = b.intent.getComponent();
int result = 0;
// get usagestats service
IUsageStats mUsageStatsService = IUsageStats.Stub
.asInterface(ServiceManager.getService("usagestats"));
try {
// get PkgUsageStats
PkgUsageStats aStats = mUsageStatsService.getPkgUsageStats(aName);
PkgUsageStats bStats = mUsageStatsService.getPkgUsageStats(bName);
if (aStats != null && bStats != null) {
if ((aStats.launchCount > bStats.launchCount)
|| ((aStats.launchCount == bStats.launchCount) && (aStats.usageTime > bStats.usageTime)))
result = -1;
else if ((aStats.launchCount < bStats.launchCount)
|| ((aStats.launchCount == bStats.launchCount) && (aStats.usageTime < bStats.usageTime)))
result = 1;
else {
result = 0;
}
} else if (aStats != null && bStats == null) {
result = -1;
} else if (aStats == null && bStats != null) {
result = 1;
}
} catch (RemoteException e) {
Log.i("TAG", "get package usage stats fail");
}
return result;
}




那么如果想在sdk中使用这个 类要如果作呢--可以使用反射 的方法,代码如下:
public final int compare(ApplicationInfo a, ApplicationInfo b) {
ComponentName aName = a.intent.getComponent();
ComponentName bName = b.intent.getComponent();
int aLaunchCount, bLaunchCount;
long aUseTime, bUseTime;
int result = 0;
try {
// 获得ServiceManager类
Class<?> ServiceManager = Class
.forName("android.os.ServiceManager");
// 获得ServiceManager的getService方法
Method getService = ServiceManager.getMethod("getService",
java.lang.String.class);
// 调用getService获取RemoteService
Object oRemoteService = getService.invoke(null, "usagestats");
// 获得IUsageStats.Stub类
Class<?> cStub = Class
.forName("com.android.internal.app.IUsageStats$Stub");
// 获得asInterface方法
Method asInterface = cStub.getMethod("asInterface",
android.os.IBinder.class);
// 调用asInterface方法获取IUsageStats对象
Object oIUsageStats = asInterface.invoke(null, oRemoteService);
// 获得getPkgUsageStats(ComponentName)方法
Method getPkgUsageStats = oIUsageStats.getClass().getMethod(
"getPkgUsageStats", ComponentName.class);
// 调用getPkgUsageStats 获取PkgUsageStats对象
Object aStats = getPkgUsageStats.invoke(oIUsageStats, aName);
Object bStats = getPkgUsageStats.invoke(oIUsageStats, bName);
// 获得PkgUsageStats类
Class<?> PkgUsageStats = Class
.forName("com.android.internal.os.PkgUsageStats");
aLaunchCount = PkgUsageStats.getDeclaredField("launchCount")
.getInt(aStats);
bLaunchCount = PkgUsageStats.getDeclaredField("launchCount")
.getInt(bStats);
aUseTime = PkgUsageStats.getDeclaredField("usageTime").getLong(
aStats);
bUseTime = PkgUsageStats.getDeclaredField("usageTime").getLong(
bStats);
if ((aLaunchCount > bLaunchCount)
|| ((aLaunchCount == bLaunchCount) && (aUseTime > bUseTime)))
result = 1;
else if ((aLaunchCount < bLaunchCount)
|| ((aLaunchCount == bLaunchCount) && (aUseTime < bUseTime)))
result = -1;
else {
result = 0;
}
} catch (Exception e) {
Log.e("###", e.toString(), e);
}
return result;
}




如何看自己的android的详细使用信息:
我们使用Andoroid手机时想看看自己的手机的使用情况,那么我们又如何去操作呢?也是必需得像程序这样要自己写一个程序才能查看吧,如果用户不是编程的,那得怎么办,呵,其实我们查看自己的手机使用详情是没有那么复杂的,今天androidkaifa.com就会大家说一下如何查询自己的手机的使用情况,下面是具体的查看方法,
其实查看方法非常简单,直接进入Android的工程模式即可,操作步骤如下:(笔者的android手机系统是4.2)
1、首先进入Android手机操作系统的拨号界面,直接输入“*#*#4636#*#*”(不加引号)即可以快速进入Android操作系统的工程测试模式。
2、在“测试”模式菜单中有手机信息“Phone information”、电池信息“Battery information”、WI-FI信息“WI-FI information”、使用状态“Usage statistics”四个选项。
3、我们点击选择第二项“Battery information”进入电池信息,然后就可以看到手机电池的详细信息了,其中包括电量等级、电池状态、温度、电池材质、电压等等信息。 
4: 我们相应点击其实的选择就可以看到其实的相应的使用详细信息
==================================================================================================================
import com.android.internal.app.IUsageStats;
import com.android.settings.R;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.os.PkgUsageStats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;


/**
 * Activity to display package usage statistics.
 */
public class UsageStats extends Activity implements OnItemSelectedListener {
    private static final String TAG="UsageStatsActivity";
    private static final boolean localLOGV = true;
    private Spinner mTypeSpinner;
    private ListView mListView;
    private IUsageStats mUsageStatsService;
    private LayoutInflater mInflater;
    private UsageStatsAdapter mAdapter;
    private PackageManager mPm;
    
    public static class AppNameComparator implements Comparator<PkgUsageStats> {
        Map<String, CharSequence> mAppLabelList;
        AppNameComparator(Map<String, CharSequence> appList) {
            mAppLabelList = appList;
        }
        public final int compare(PkgUsageStats a, PkgUsageStats b) {
            String alabel = mAppLabelList.get(a.packageName).toString();
            String blabel = mAppLabelList.get(b.packageName).toString();
            return alabel.compareTo(blabel);
        }
    }
    
    public static class LaunchCountComparator implements Comparator<PkgUsageStats> {
        public final int compare(PkgUsageStats a, PkgUsageStats b) {
            // return by descending order
            return b.launchCount - a.launchCount;
        }
    }
    
    public static class UsageTimeComparator implements Comparator<PkgUsageStats> {
        public final int compare(PkgUsageStats a, PkgUsageStats b) {
            long ret = a.usageTime-b.usageTime;
            if (ret == 0) {
                return 0;
            }
            if (ret < 0) {
                return 1;
            }
            return -1;
        }
    }
    
     // View Holder used when displaying views
    static class AppViewHolder {
        TextView pkgName;
        TextView launchCount;
        TextView usageTime;
    }
    
    class UsageStatsAdapter extends BaseAdapter {
         // Constants defining order for display order
        private static final int _DISPLAY_ORDER_USAGE_TIME = 0;
        private static final int _DISPLAY_ORDER_LAUNCH_COUNT = 1;
        private static final int _DISPLAY_ORDER_APP_NAME = 2;
        
        private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;
        private List<PkgUsageStats> mUsageStats;
        private LaunchCountComparator mLaunchCountComparator;
        private UsageTimeComparator mUsageTimeComparator;
        private AppNameComparator mAppLabelComparator;
        private HashMap<String, CharSequence> mAppLabelMap;
        
        UsageStatsAdapter() {
            mUsageStats = new ArrayList<PkgUsageStats>();
            mAppLabelMap = new HashMap<String, CharSequence>();
            PkgUsageStats[] stats;
            try {
                stats = mUsageStatsService.getAllPkgUsageStats();
            } catch (RemoteException e) {
                Log.e(TAG, "Failed initializing usage stats service");
                return;
            }
           if (stats == null) {
               return;
           }
           for (PkgUsageStats ps : stats) {
               mUsageStats.add(ps);
               // load application labels for each application
               CharSequence label;
               try {
                   ApplicationInfo appInfo = mPm.getApplicationInfo(ps.packageName, 0);
                   label = appInfo.loadLabel(mPm);
                } catch (NameNotFoundException e) {
                    label = ps.packageName;
                }
                mAppLabelMap.put(ps.packageName, label);
           }
           // Sort list
           mLaunchCountComparator = new LaunchCountComparator();
           mUsageTimeComparator = new UsageTimeComparator();
           mAppLabelComparator = new AppNameComparator(mAppLabelMap);
           sortList();
        }
        public int getCount() {
            return mUsageStats.size();
        }


        public Object getItem(int position) {
            return mUsageStats.get(position);
        }


        public long getItemId(int position) {
            return position;
        }


        public View getView(int position, View convertView, ViewGroup parent) {
            // A ViewHolder keeps references to children views to avoid unneccessary calls
            // to findViewById() on each row.
            AppViewHolder holder;


            // When convertView is not null, we can reuse it directly, there is no need
            // to reinflate it. We only inflate a new View when the convertView supplied
            // by ListView is null.
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.usage_stats_item, null);


                // Creates a ViewHolder and store references to the two children views
                // we want to bind data to.
                holder = new AppViewHolder();
                holder.pkgName = (TextView) convertView.findViewById(R.id.package_name);
                holder.launchCount = (TextView) convertView.findViewById(R.id.launch_count);
                holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time);
                convertView.setTag(holder);
            } else {
                // Get the ViewHolder back to get fast access to the TextView
                // and the ImageView.
                holder = (AppViewHolder) convertView.getTag();
            }


            // Bind the data efficiently with the holder
            PkgUsageStats pkgStats = mUsageStats.get(position);
            if (pkgStats != null) {
                CharSequence label = mAppLabelMap.get(pkgStats.packageName);
                holder.pkgName.setText(label);
                holder.launchCount.setText(String.valueOf(pkgStats.launchCount));
                holder.usageTime.setText(String.valueOf(pkgStats.usageTime)+" ms");
            } else {
                Log.w(TAG, "No usage stats info for package:" + position);
            }
            return convertView;
        }
        
        void sortList(int sortOrder) {
            if (mDisplayOrder == sortOrder) {
                // do nothing
                return;
            }
            mDisplayOrder= sortOrder;
            sortList();
        }
        private void sortList() {
            if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
                if (localLOGV) Log.i(TAG, "Sorting by usage time");
                Collections.sort(mUsageStats, mUsageTimeComparator);
            } else if (mDisplayOrder == _DISPLAY_ORDER_LAUNCH_COUNT) {
                if (localLOGV) Log.i(TAG, "Sorting launch count");
                Collections.sort(mUsageStats, mLaunchCountComparator);
            } else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
                if (localLOGV) Log.i(TAG, "Sorting by application name");
                Collections.sort(mUsageStats, mAppLabelComparator);
            }
            notifyDataSetChanged();
        }
    }


    /** Called when the activity is first created. */
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        mUsageStatsService = IUsageStats.Stub.asInterface(ServiceManager.getService("usagestats"));
        if (mUsageStatsService == null) {
            Log.e(TAG, "Failed to retrieve usagestats service");
            return;
        }
        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mPm = getPackageManager();
        
        setContentView(R.layout.usage_stats);
        mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
        mTypeSpinner.setOnItemSelectedListener(this);
        
        mListView = (ListView) findViewById(R.id.pkg_list);
        // Initialize the inflater
        
        mAdapter = new UsageStatsAdapter();
        mListView.setAdapter(mAdapter);
    }


    public void onItemSelected(AdapterView<?> parent, View view, int position,
            long id) {
        mAdapter.sortList(position);
    }


    public void onNothingSelected(AdapterView<?> parent) {
        // do nothing
    }
}


================================================================================

来源网络:

android本身有PkgUsageStats等相关类来统计应用使用情况,但这些类在SDK不公开,只能通过反射或者在源码环境下才能访问到。所以,针对这一特点,如果需要获取应用使用信息,可以采取反射或者源码下开发这两种方式。


1、在源码环境下(源码环境下可以访问一些标记为hide的方法),代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void getPkgUsageStats()  
  2.     {  
  3.         IUsageStats statsService = (IUsageStats) IUsageStats.Stub.  
  4.                 asInterface(ServiceManager.getService("usagestats"));  
  5.           
  6.         PkgUsageStats[] pkgStats = null;  
  7.         try {  
  8.             pkgStats = statsService.getAllPkgUsageStats();  
  9.         } catch (RemoteException e) {  
  10.             // TODO Auto-generated catch block  
  11.             e.printStackTrace();  
  12.         }  
  13.           
  14.         if(pkgStats != null)  
  15.         {  
  16.             StringBuffer sb = new StringBuffer();  
  17.             sb.append("nerver used : ");  
  18.             for(PkgUsageStats usageStats : pkgStats)  
  19.             {  
  20.                 String packageName = usageStats.packageName;  
  21.                 int launchCount = usageStats.launchCount;  
  22.                 long usageTime = usageStats.usageTime;  
  23.                 if(launchCount > 0)  
  24.                     Log.v("getPkgUsageStats",packageName + "  count: " + launchCount + "  time:  "  
  25.                         + usageTime);  
  26.                 else{  
  27.                     sb.append(packageName+" ");  
  28.                 }  
  29.             }  
  30.   
  31.             Log.v("getPkgUsageStats",sb.toString());  
  32.         }  
  33.     }  






2、通过反射来调用,代码如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Use reflect to get Package Usage Statistics data.<br> 
  3.  */  
  4. public static void getPkgUsageStats() {  
  5.     LogUtils.d(TAG, "[getPkgUsageStats]");  
  6.     try {  
  7.         Class<?> cServiceManager = Class  
  8.                 .forName("android.os.ServiceManager");  
  9.         Method mGetService = cServiceManager.getMethod("getService",  
  10.                 java.lang.String.class);  
  11.         Object oRemoteService = mGetService.invoke(null"usagestats");  
  12.   
  13.         // IUsageStats oIUsageStats =  
  14.         // IUsageStats.Stub.asInterface(oRemoteService)  
  15.         Class<?> cStub = Class  
  16.                 .forName("com.android.internal.app.IUsageStats$Stub");  
  17.         Method mUsageStatsService = cStub.getMethod("asInterface",  
  18.                 android.os.IBinder.class);  
  19.         Object oIUsageStats = mUsageStatsService.invoke(null,  
  20.                 oRemoteService);  
  21.   
  22.         // PkgUsageStats[] oPkgUsageStatsArray =  
  23.         // mUsageStatsService.getAllPkgUsageStats();  
  24.         Class<?> cIUsageStatus = Class  
  25.                 .forName("com.android.internal.app.IUsageStats");  
  26.         Method mGetAllPkgUsageStats = cIUsageStatus.getMethod(  
  27.                 "getAllPkgUsageStats", (Class[]) null);  
  28.         Object[] oPkgUsageStatsArray = (Object[]) mGetAllPkgUsageStats  
  29.                 .invoke(oIUsageStats, (Object[]) null);  
  30.         LogUtils.d(TAG, "[getPkgUsageStats] oPkgUsageStatsArray = "+oPkgUsageStatsArray);  
  31.   
  32.         Class<?> cPkgUsageStats = Class  
  33.                 .forName("com.android.internal.os.PkgUsageStats");  
  34.   
  35.         StringBuffer sb = new StringBuffer();  
  36.         sb.append("nerver used : ");  
  37.         for (Object pkgUsageStats : oPkgUsageStatsArray) {  
  38.             // get pkgUsageStats.packageName, pkgUsageStats.launchCount,  
  39.             // pkgUsageStats.usageTime  
  40.             String packageName = (String) cPkgUsageStats.getDeclaredField(  
  41.                     "packageName").get(pkgUsageStats);  
  42.             int launchCount = cPkgUsageStats  
  43.                     .getDeclaredField("launchCount").getInt(pkgUsageStats);  
  44.             long usageTime = cPkgUsageStats.getDeclaredField("usageTime")  
  45.                     .getLong(pkgUsageStats);  
  46.             if (launchCount > 0)  
  47.                 LogUtils.d(TAG, "[getPkgUsageStats] "+ packageName + "  count: "  
  48.                         + launchCount + "  time:  " + usageTime);  
  49.             else {  
  50.                 sb.append(packageName + "; ");  
  51.             }  
  52.         }  
  53.         LogUtils.d(TAG, "[getPkgUsageStats] " + sb.toString());  
  54.     } catch (IllegalArgumentException e) {  
  55.         e.printStackTrace();  
  56.     } catch (IllegalAccessException e) {  
  57.         e.printStackTrace();  
  58.     } catch (InvocationTargetException e) {  
  59.         e.printStackTrace();  
  60.     } catch (NoSuchFieldException e) {  
  61.         e.printStackTrace();  
  62.     } catch (ClassNotFoundException e) {  
  63.         e.printStackTrace();  
  64.     } catch (NoSuchMethodException e) {  
  65.         e.printStackTrace();  
  66.     }  
  67. }  




这是获取信息的两种实现方式,另外,要想让程序能够正常运行并成功获取到数据,我们还需要做如下的配置:
1、在AndroidManifest.xml中增加android:sharedUserId="android.uid.system"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xxx"
    android:versionCode="1"
    android:versionName="1.0"
    android:sharedUserId="android.uid.system" >
还有权限的声明
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />


2、对apk进行系统签名,在源码中取platform.pk8、platform.x509.pem、signapk.jar文件并通过如下命令实现apk的签名
java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk

unsigned.apk为签名之前的apk,signed.apk为通过命令签名成功的apk


补充:

UsageStats信息通过UsageStatsService保存在路径data/system/usagestats目录下,在系统启动后,UsageStatsService服务开启,在该Service的构造函数中调用readStatsFromFile()方法从本地获取UsageStats信息,并保存到mStats成员变量中。(见源码UsageStatsService.java

我们通过getAllPkgUsageStats()方法来获取信息,但是该方法所返回的信息并非从文件中读取的全部数据,而是开机后启动过的apk集合,代码如下:

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public PkgUsageStats[] getAllPkgUsageStats() {  
  2.         mContext.enforceCallingOrSelfPermission(  
  3.                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);  
  4.         synchronized (mStatsLock) {  
  5.             int size = mLastResumeTimes.size();  
  6.             if (size <= 0) {  
  7.                 return null;  
  8.             }  
  9.             PkgUsageStats retArr[] = new PkgUsageStats[size];  
  10.             int i = 0;  
  11.             for (Map.Entry<String, Map<String, Long>> entry : mLastResumeTimes.entrySet()) {  
  12.                 String pkg = entry.getKey();  
  13.                 long usageTime = 0;  
  14.                 int launchCount = 0;  
  15.    
  16.                 PkgUsageStatsExtended pus = mStats.get(pkg);  
  17.                 if (pus != null) {  
  18.                     usageTime = pus.mUsageTime;  
  19.                     launchCount = pus.mLaunchCount;  
  20.                 }  
  21.                 retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, entry.getValue());  
  22.                 i++;  
  23.             }  
  24.             return retArr;  
  25.         }  
  26.     }  

 

所以根据以上方法仅能获取开机后被启动过的apk信息集合,那如何获取所有apk的信息集合呢?该Service提供有另一个方法:

public PkgUsageStats getPkgUsageStats(ComponentName componentName) 

我们可以先获取当前系统所有安装包包名,再根据包名逐个通过此方法去获取对应包名的启动次数和运行时间。



posted @ 2015-01-17 20:11  行走的思想  阅读(136)  评论(0编辑  收藏  举报  来源