Android实现流量统计和网速监控悬浮窗

很多安全卫士类软件都实现了网速监测功能,也算是一个比较实用的功能。Android下,TrafficStats类实现了对流量的统计。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static long getMobileRxBytes()//获取通过Mobile连接收到的字节总数,但不包含WiFi   
  2. static long getMobileRxPackets()//获取Mobile连接收到的数据包总数   
  3. static long getMobileTxBytes()//Mobile发送的总字节数   
  4. static long getMobileTxPackets()//Mobile发送的总数据包数   
  5. static long getTotalRxBytes()//获取总的接受字节数,包含Mobile和WiFi等   
  6. static long getTotalRxPackets()//总的接受数据包数,包含Mobile和WiFi等   
  7. static long getTotalTxBytes()//总的发送字节数,包含Mobile和WiFi等   
  8. static long getTotalTxPackets()//发送的总数据包数,包含Mobile和WiFi等   
  9. static long getUidRxBytes(int uid)//获取某个网络UID的接受字节数   
  10. static long getUidTxBytes(intuid) //获取某个网络UID的发送字节数  

这些就是TrafficStats提供的一些接口。那么,我们首先要创建一个桌面悬浮窗,用于显示网速。然后再Service里面,显示悬浮窗。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import android.app.Service;  
  2. import android.content.Intent;  
  3. import android.graphics.PixelFormat;  
  4. import android.os.Binder;  
  5. import android.os.IBinder;  
  6. import android.util.Log;  
  7. import android.view.Gravity;  
  8. import android.view.LayoutInflater;  
  9. import android.view.MotionEvent;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.view.View.OnTouchListener;  
  13. import android.view.WindowManager;  
  14. import android.view.WindowManager.LayoutParams;  
  15. import android.widget.LinearLayout;  
  16. import android.widget.TextView;  
  17.   
  18. import com.yyh.utils.WidgetUtils;  
  19.   
  20. public class ManagerService extends Service {  
  21.     private static final String TAG = "ManagerService";  
  22.     public LinearLayout mFloatLayout;  
  23.     public WindowManager.LayoutParams wmParams;  
  24.     public WindowManager mWindowManager;  
  25.     public TextView mFloatView;  
  26.     private ServiceBinder binder = new ServiceBinder();  
  27.   
  28.     @Override  
  29.     public void onCreate() {  
  30.         super.onCreate();  
  31.         createFloatView();  
  32.     }  
  33.   
  34.     @Override  
  35.     public int onStartCommand(Intent intent, int flags, int startId) {  
  36.         return START_STICKY_COMPATIBILITY;  
  37.     }  
  38.   
  39.     @Override  
  40.     public IBinder onBind(Intent intent) {  
  41.         return binder;  
  42.     }  
  43.   
  44.     @Override  
  45.     public void onTrimMemory(int level) {  
  46.         Log.i("test"" onTrimMemory...");  
  47.   
  48.     }  
  49.         /** 创建悬浮窗 */  
  50.     private void createFloatView() {  
  51.         wmParams = new WindowManager.LayoutParams();  
  52.         mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);  
  53.         wmParams.type = LayoutParams.TYPE_SYSTEM_ALERT;// 设置window  
  54.                                                         // type为TYPE_SYSTEM_ALERT  
  55.         wmParams.format = PixelFormat.RGBA_8888;// 设置图片格式,效果为背景透明  
  56.         wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;// 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)  
  57.         wmParams.gravity = Gravity.LEFT | Gravity.TOP;// 默认位置:左上角  
  58.         wmParams.width = WidgetUtils.dpToPx(getApplicationContext(), 65);  
  59.         wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  60.         wmParams.x = (WidgetUtils.getScreenWidth(getApplicationContext()) - wmParams.width) / 2;// 设置x、y初始值,相对于gravity  
  61.         wmParams.y = 10;  
  62.         // 获取浮动窗口视图所在布局  
  63.         LayoutInflater inflater = LayoutInflater.from(getApplication());  
  64.         mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);  
  65.         mWindowManager.addView(mFloatLayout, wmParams);// 添加mFloatLayout  
  66.         mFloatView = (TextView) mFloatLayout.findViewById(R.id.speed);  
  67.         mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));  
  68.         // 设置监听浮动窗口的触摸移动  
  69.         mFloatView.setOnTouchListener(new OnTouchListener() {  
  70.             @Override  
  71.             public boolean onTouch(View v, MotionEvent event) {  
  72.                 // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标  
  73.                 wmParams.x = (int) event.getRawX() - mFloatView.getMeasuredWidth() / 2;  
  74.                 Log.i(TAG, "RawX" + event.getRawX());  
  75.                 Log.i(TAG, "X" + event.getX());  
  76.                 wmParams.y = (int) event.getRawY() - mFloatView.getMeasuredHeight() / 2 - 25;// 减25为状态栏的高度  
  77.                 Log.i(TAG, "RawY" + event.getRawY());  
  78.                 Log.i(TAG, "Y" + event.getY());  
  79.                 mWindowManager.updateViewLayout(mFloatLayout, wmParams);// 刷新  
  80.                 return false// 此处必须返回false,否则OnClickListener获取不到监听  
  81.             }  
  82.         });  
  83.         mFloatView.setOnClickListener(new OnClickListener() {  
  84.             @Override  
  85.             public void onClick(View arg0) {  
  86.                             // do something... 跳转到应用  
  87.             }  
  88.         });  
  89.     }  
  90.   
  91.     public void setSpeed(String str) {  
  92.         mFloatView.setText(str.toString());  
  93.     }  
  94.   
  95.     @Override  
  96.     public void onDestroy() {  
  97.         super.onDestroy();  
  98.         if (mFloatLayout != null && mWindowManager != null) {  
  99.             mWindowManager.removeView(mFloatLayout);// 移除悬浮窗口  
  100.         }  
  101.         startService(new Intent(this, ManagerService.class));  
  102.     }  
  103.   
  104.     class ServiceBinder extends Binder {  
  105.         public ManagerService getService() {  
  106.             return ManagerService.this;  
  107.         }  
  108.     }  
  109. }  
然后构造一个网速监测的工具类,用于获取流量变化信息。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import java.io.FileNotFoundException;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4. import java.math.BigDecimal;  
  5. import java.util.Timer;  
  6. import java.util.TimerTask;  
  7. import android.content.Context;  
  8. import android.content.pm.ApplicationInfo;  
  9. import android.content.pm.PackageManager;  
  10. import android.content.pm.PackageManager.NameNotFoundException;  
  11. import android.net.TrafficStats;  
  12. import android.os.Handler;  
  13. import android.os.Message;  
  14. import android.util.Log;  
  15.   
  16. /** 
  17.  * 应用的流量信息 
  18.  * @author yuyuhang 
  19.  */  
  20. public class TrafficInfo {  
  21.   
  22.     private static final int UNSUPPORTED = -1;  
  23.     private static final String LOG_TAG = "test";  
  24.   
  25.     private static TrafficInfo instance;  
  26.       
  27.     static int uid;  
  28.     private long preRxBytes = 0;  
  29.     private Timer mTimer = null;  
  30.     private Context mContext;  
  31.     private Handler mHandler;  
  32.       
  33.     /** 更新频率(每几秒更新一次,至少1秒) */  
  34.     private final int UPDATE_FREQUENCY = 1;  
  35.     private int times = 1;  
  36.   
  37.     public TrafficInfo(Context mContext, Handler mHandler, int uid) {  
  38.         this.mContext = mContext;  
  39.         this.mHandler = mHandler;  
  40.         this.uid = uid;  
  41.     }  
  42.   
  43.     public TrafficInfo(Context mContext, Handler mHandler) {  
  44.         this.mContext = mContext;  
  45.         this.mHandler = mHandler;  
  46.     }  
  47.       
  48.     public static TrafficInfo getInstant(Context mContext, Handler mHandler) {  
  49.         if (instance == null) {  
  50.             instance = new TrafficInfo(mContext, mHandler);  
  51.         }  
  52.         return instance;  
  53.     }  
  54.   
  55.     /** 
  56.      * 获取总流量 
  57.      */  
  58.     public long getTrafficInfo() {  
  59.         long rcvTraffic = UNSUPPORTED; // 下载流量  
  60.         long sndTraffic = UNSUPPORTED; // 上传流量  
  61.         rcvTraffic = getRcvTraffic();  
  62.         sndTraffic = getSndTraffic();  
  63.         if (rcvTraffic == UNSUPPORTED || sndTraffic == UNSUPPORTED)  
  64.             return UNSUPPORTED;  
  65.         else  
  66.             return rcvTraffic + sndTraffic;  
  67.     }  
  68.   
  69.     /** 
  70.      * 获取下载流量 某个应用的网络流量数据保存在系统的/proc/uid_stat/$UID/tcp_rcv | tcp_snd文件中 
  71.      */  
  72.     public long getRcvTraffic() {  
  73.         long rcvTraffic = UNSUPPORTED; // 下载流量  
  74.         rcvTraffic = TrafficStats.getUidRxBytes(uid);  
  75.         if (rcvTraffic == UNSUPPORTED) { // 不支持的查询  
  76.             return UNSUPPORTED;  
  77.         }  
  78.         Log.i("test", rcvTraffic + "--1");  
  79.         RandomAccessFile rafRcv = null, rafSnd = null// 用于访问数据记录文件  
  80.         String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv";  
  81.         try {  
  82.             rafRcv = new RandomAccessFile(rcvPath, "r");  
  83.             rcvTraffic = Long.parseLong(rafRcv.readLine()); // 读取流量统计  
  84.         } catch (FileNotFoundException e) {  
  85.             Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());  
  86.             rcvTraffic = UNSUPPORTED;  
  87.         } catch (IOException e) {  
  88.             Log.e(LOG_TAG, "IOException: " + e.getMessage());  
  89.             e.printStackTrace();  
  90.         } finally {  
  91.             try {  
  92.                 if (rafRcv != null)  
  93.                     rafRcv.close();  
  94.                 if (rafSnd != null)  
  95.                     rafSnd.close();  
  96.             } catch (IOException e) {  
  97.                 Log.w(LOG_TAG, "Close RandomAccessFile exception: " + e.getMessage());  
  98.             }  
  99.         }  
  100.         Log.i("test", rcvTraffic + "--2");  
  101.         return rcvTraffic;  
  102.     }  
  103.   
  104.     /** 
  105.      * 获取上传流量 
  106.      */  
  107.     public long getSndTraffic() {  
  108.         long sndTraffic = UNSUPPORTED; // 上传流量  
  109.         sndTraffic = TrafficStats.getUidTxBytes(uid);  
  110.         if (sndTraffic == UNSUPPORTED) { // 不支持的查询  
  111.             return UNSUPPORTED;  
  112.         }  
  113.         RandomAccessFile rafRcv = null, rafSnd = null// 用于访问数据记录文件  
  114.         String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";  
  115.         try {  
  116.             rafSnd = new RandomAccessFile(sndPath, "r");  
  117.             sndTraffic = Long.parseLong(rafSnd.readLine());  
  118.         } catch (FileNotFoundException e) {  
  119.             Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());  
  120.             sndTraffic = UNSUPPORTED;  
  121.         } catch (IOException e) {  
  122.             Log.e(LOG_TAG, "IOException: " + e.getMessage());  
  123.             e.printStackTrace();  
  124.         } finally {  
  125.             try {  
  126.                 if (rafRcv != null)  
  127.                     rafRcv.close();  
  128.                 if (rafSnd != null)  
  129.                     rafSnd.close();  
  130.             } catch (IOException e) {  
  131.                 Log.w(LOG_TAG, "Close RandomAccessFile exception: " + e.getMessage());  
  132.             }  
  133.         }  
  134.         return sndTraffic;  
  135.     }  
  136.   
  137.     /** 
  138.      * 获取当前下载流量总和 
  139.      */  
  140.     public static long getNetworkRxBytes() {  
  141.         return TrafficStats.getTotalRxBytes();  
  142.     }  
  143.   
  144.     /** 
  145.      * 获取当前上传流量总和 
  146.      */  
  147.     public static long getNetworkTxBytes() {  
  148.         return TrafficStats.getTotalTxBytes();  
  149.     }  
  150.   
  151.     /** 
  152.      * 获取当前网速,小数点保留一位 
  153.      */  
  154.     public double getNetSpeed() {  
  155.         long curRxBytes = getNetworkRxBytes();  
  156.         if (preRxBytes == 0)  
  157.             preRxBytes = curRxBytes;  
  158.         long bytes = curRxBytes - preRxBytes;  
  159.         preRxBytes = curRxBytes;  
  160.         //int kb = (int) Math.floor(bytes / 1024 + 0.5);  
  161.         double kb = (double)bytes / (double)1024;  
  162.         BigDecimal bd = new BigDecimal(kb);  
  163.         return bd.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();  
  164.     }  
  165.   
  166.     /** 
  167.      * 开启流量监控 
  168.      */  
  169.     public void startCalculateNetSpeed() {  
  170.         preRxBytes = getNetworkRxBytes();  
  171.         if (mTimer != null) {  
  172.             mTimer.cancel();  
  173.             mTimer = null;  
  174.         }  
  175.         if (mTimer == null) {  
  176.             mTimer = new Timer();  
  177.             mTimer.schedule(new TimerTask() {  
  178.                 @Override  
  179.                 public void run() {  
  180.                     if(times == UPDATE_FREQUENCY){  
  181.                         Message msg = new Message();  
  182.                         msg.what = 1;  
  183.                         //msg.arg1 = getNetSpeed();  
  184.                         msg.obj = getNetSpeed();  
  185.                         mHandler.sendMessage(msg);  
  186.                         times = 1;  
  187.                     } else {  
  188.                         times++;  
  189.                     }  
  190.                 }  
  191.             }, 10001000); // 每秒更新一次  
  192.         }  
  193.     }  
  194.   
  195.     public void stopCalculateNetSpeed() {  
  196.         if (mTimer != null) {  
  197.             mTimer.cancel();  
  198.             mTimer = null;  
  199.         }  
  200.     }  
  201.   
  202.     /** 
  203.      * 获取当前应用uid 
  204.      */  
  205.     public int getUid() {  
  206.         try {  
  207.             PackageManager pm = mContext.getPackageManager();  
  208.             ApplicationInfo ai = pm.getApplicationInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);  
  209.             return ai.uid;  
  210.         } catch (NameNotFoundException e) {  
  211.             e.printStackTrace();  
  212.         }  
  213.         return -1;  
  214.     }  
  215. }  

然后再Activity里面,就需要去启动Service,以显示悬浮窗,然后TrafficInfo通过Handler,把当前网速发给Activity,Activity调用Service的方法了来更新悬浮窗的View。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import java.util.List;  
  2. import android.app.ActivityManager;  
  3. import android.app.ActivityManager.RunningAppProcessInfo;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11. import android.os.Message;  
  12. import android.support.v7.app.ActionBarActivity;  
  13. import android.util.Log;  
  14. import android.widget.TextView;  
  15. import com.yyh.utils.TrafficInfo;  
  16.   
  17. public class MainActivity extends ActionBarActivity {  
  18.   
  19.     private ActivityManager mActivityManager;  
  20.     private TextView mTextView;  
  21.     Handler mHandler;  
  22.     TrafficInfo speed;  
  23.     ManagerService service;  
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.activity_main);  
  28.         mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);  
  29.         mTextView = (TextView) findViewById(R.id.tv);  
  30.         try {  
  31.             mHandler = new Handler() {  
  32.                 @Override  
  33.                 public void handleMessage(Message msg) {  
  34.                     if (msg.what == 1) {  
  35.                         mTextView.setText(msg.obj + "kb/s");  
  36.                         if(service != null)  
  37.                             service.setSpeed(msg.obj+"kb/s"); // 设置网速  
  38.                     }  
  39.                     super.handleMessage(msg);  
  40.                 }  
  41.   
  42.             };  
  43.             speed = new TrafficInfo(this,mHandler,TrafficInfo.getUid());  
  44.             speed.startCalculateNetSpeed(); // 开启网速监测  
  45.         } catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.         Log.i("test","总流量="+speed.getTrafficInfo());  
  49.         Intent intent = new Intent(MainActivity.this, ManagerService.class);  
  50.         bindService(intent, conn, Context.BIND_AUTO_CREATE);          
  51.     }  
  52.       
  53.     private ServiceConnection conn = new ServiceConnection() {  
  54.             public void onServiceConnected(ComponentName name, IBinder binder) {  
  55.             service = ((ManagerService.ServiceBinder) binder).getService();  
  56.             }  
  57.             public void onServiceDisconnected(ComponentName name) {  
  58.             service = null;  
  59.             }  
  60.         };  
  61.       
  62.     @Override  
  63.     protected void onDestroy() {  
  64.         super.onDestroy();  
  65.         speed.stopCalculateNetSpeed();  
  66.         unbindService(conn);   
  67.     }  
  68. }  

这样,就能够显示悬浮窗。如果还需要后台运行,那Service就需要常驻不被杀死,这部分请参考我另一篇博文: Android 通过JNI实现守护进程,保证Service服务不被杀死

附上Demo源码:Android 流量与网速监测(悬浮窗) 源码


原文地址:http://blog.csdn.net/yyh352091626/article/details/50599621

扫描二维码关注公众号,回复: 816262 查看本文章

猜你喜欢

转载自blog.csdn.net/f2006116/article/details/50914323