Android實現流量統計和網速監控懸浮窗
很多安全衛士類軟體都實現了網速監測功能,也算是一個比較實用的功能。Android下,TrafficStats類實現了對流量的統計。
static long getMobileRxBytes()//獲取通過Mobile連線收到的位元組總數,但不包含WiFi
static long getMobileRxPackets()//獲取Mobile連線收到的資料包總數
static long getMobileTxBytes()//Mobile傳送的總位元組數
static long getMobileTxPackets()//Mobile傳送的總資料包數
static long getTotalRxBytes()//獲取總的接受位元組數,包含Mobile和WiFi等
static long getTotalRxPackets()//總的接受資料包數,包含Mobile和WiFi等
static long getTotalTxBytes()//總的傳送位元組數,包含Mobile和WiFi等
static long getTotalTxPackets()//傳送的總資料包數,包含Mobile和WiFi等
static long getUidRxBytes(int uid)//獲取某個網路UID的接受位元組數
static long getUidTxBytes(intuid) //獲取某個網路UID的傳送位元組數
這些就是TrafficStats提供的一些介面。那麼,我們首先要建立一個桌面懸浮窗,用於顯示網速。然後再Service裡面,顯示懸浮窗。
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.yyh.utils.WidgetUtils;
public class ManagerService extends Service {
private static final String TAG = "ManagerService";
public LinearLayout mFloatLayout;
public WindowManager.LayoutParams wmParams;
public WindowManager mWindowManager;
public TextView mFloatView;
private ServiceBinder binder = new ServiceBinder();
@Override
public void onCreate() {
super.onCreate();
createFloatView();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY_COMPATIBILITY;
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onTrimMemory(int level) {
Log.i("test", " onTrimMemory...");
}
/** 建立懸浮窗 */
private void createFloatView() {
wmParams = new WindowManager.LayoutParams();
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
wmParams.type = LayoutParams.TYPE_SYSTEM_ALERT;// 設定window
// type為TYPE_SYSTEM_ALERT
wmParams.format = PixelFormat.RGBA_8888;// 設定圖片格式,效果為背景透明
wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;// 設定浮動視窗不可聚焦(實現操作除浮動視窗外的其他可見視窗的操作)
wmParams.gravity = Gravity.LEFT | Gravity.TOP;// 預設位置:左上角
wmParams.width = WidgetUtils.dpToPx(getApplicationContext(), 65);
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.x = (WidgetUtils.getScreenWidth(getApplicationContext()) - wmParams.width) / 2;// 設定x、y初始值,相對於gravity
wmParams.y = 10;
// 獲取浮動視窗檢視所在佈局
LayoutInflater inflater = LayoutInflater.from(getApplication());
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
mWindowManager.addView(mFloatLayout, wmParams);// 新增mFloatLayout
mFloatView = (TextView) mFloatLayout.findViewById(R.id.speed);
mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
// 設定監聽浮動視窗的觸控移動
mFloatView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// getRawX是觸控位置相對於螢幕的座標,getX是相對於按鈕的座標
wmParams.x = (int) event.getRawX() - mFloatView.getMeasuredWidth() / 2;
Log.i(TAG, "RawX" + event.getRawX());
Log.i(TAG, "X" + event.getX());
wmParams.y = (int) event.getRawY() - mFloatView.getMeasuredHeight() / 2 - 25;// 減25為狀態列的高度
Log.i(TAG, "RawY" + event.getRawY());
Log.i(TAG, "Y" + event.getY());
mWindowManager.updateViewLayout(mFloatLayout, wmParams);// 重新整理
return false; // 此處必須返回false,否則OnClickListener獲取不到監聽
}
});
mFloatView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// do something... 跳轉到應用
}
});
}
public void setSpeed(String str) {
mFloatView.setText(str.toString());
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFloatLayout != null && mWindowManager != null) {
mWindowManager.removeView(mFloatLayout);// 移除懸浮視窗
}
startService(new Intent(this, ManagerService.class));
}
class ServiceBinder extends Binder {
public ManagerService getService() {
return ManagerService.this;
}
}
}
然後構造一個網速監測的工具類,用於獲取流量變化資訊。
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.TrafficStats;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* 應用的流量資訊
* @author yuyuhang
*/
public class TrafficInfo {
private static final int UNSUPPORTED = -1;
private static final String LOG_TAG = "test";
private static TrafficInfo instance;
static int uid;
private long preRxBytes = 0;
private Timer mTimer = null;
private Context mContext;
private Handler mHandler;
/** 更新頻率(每幾秒更新一次,至少1秒) */
private final int UPDATE_FREQUENCY = 1;
private int times = 1;
public TrafficInfo(Context mContext, Handler mHandler, int uid) {
this.mContext = mContext;
this.mHandler = mHandler;
this.uid = uid;
}
public TrafficInfo(Context mContext, Handler mHandler) {
this.mContext = mContext;
this.mHandler = mHandler;
}
public static TrafficInfo getInstant(Context mContext, Handler mHandler) {
if (instance == null) {
instance = new TrafficInfo(mContext, mHandler);
}
return instance;
}
/**
* 獲取總流量
*/
public long getTrafficInfo() {
long rcvTraffic = UNSUPPORTED; // 下載流量
long sndTraffic = UNSUPPORTED; // 上傳流量
rcvTraffic = getRcvTraffic();
sndTraffic = getSndTraffic();
if (rcvTraffic == UNSUPPORTED || sndTraffic == UNSUPPORTED)
return UNSUPPORTED;
else
return rcvTraffic + sndTraffic;
}
/**
* 獲取下載流量 某個應用的網路流量資料儲存在系統的/proc/uid_stat/$UID/tcp_rcv | tcp_snd檔案中
*/
public long getRcvTraffic() {
long rcvTraffic = UNSUPPORTED; // 下載流量
rcvTraffic = TrafficStats.getUidRxBytes(uid);
if (rcvTraffic == UNSUPPORTED) { // 不支援的查詢
return UNSUPPORTED;
}
Log.i("test", rcvTraffic + "--1");
RandomAccessFile rafRcv = null, rafSnd = null; // 用於訪問資料記錄檔案
String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv";
try {
rafRcv = new RandomAccessFile(rcvPath, "r");
rcvTraffic = Long.parseLong(rafRcv.readLine()); // 讀取流量統計
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());
rcvTraffic = UNSUPPORTED;
} catch (IOException e) {
Log.e(LOG_TAG, "IOException: " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (rafRcv != null)
rafRcv.close();
if (rafSnd != null)
rafSnd.close();
} catch (IOException e) {
Log.w(LOG_TAG, "Close RandomAccessFile exception: " + e.getMessage());
}
}
Log.i("test", rcvTraffic + "--2");
return rcvTraffic;
}
/**
* 獲取上傳流量
*/
public long getSndTraffic() {
long sndTraffic = UNSUPPORTED; // 上傳流量
sndTraffic = TrafficStats.getUidTxBytes(uid);
if (sndTraffic == UNSUPPORTED) { // 不支援的查詢
return UNSUPPORTED;
}
RandomAccessFile rafRcv = null, rafSnd = null; // 用於訪問資料記錄檔案
String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
try {
rafSnd = new RandomAccessFile(sndPath, "r");
sndTraffic = Long.parseLong(rafSnd.readLine());
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());
sndTraffic = UNSUPPORTED;
} catch (IOException e) {
Log.e(LOG_TAG, "IOException: " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (rafRcv != null)
rafRcv.close();
if (rafSnd != null)
rafSnd.close();
} catch (IOException e) {
Log.w(LOG_TAG, "Close RandomAccessFile exception: " + e.getMessage());
}
}
return sndTraffic;
}
/**
* 獲取當前下載流量總和
*/
public static long getNetworkRxBytes() {
return TrafficStats.getTotalRxBytes();
}
/**
* 獲取當前上傳流量總和
*/
public static long getNetworkTxBytes() {
return TrafficStats.getTotalTxBytes();
}
/**
* 獲取當前網速,小數點保留一位
*/
public double getNetSpeed() {
long curRxBytes = getNetworkRxBytes();
if (preRxBytes == 0)
preRxBytes = curRxBytes;
long bytes = curRxBytes - preRxBytes;
preRxBytes = curRxBytes;
//int kb = (int) Math.floor(bytes / 1024 + 0.5);
double kb = (double)bytes / (double)1024;
BigDecimal bd = new BigDecimal(kb);
return bd.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 開啟流量監控
*/
public void startCalculateNetSpeed() {
preRxBytes = getNetworkRxBytes();
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
if (mTimer == null) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
if(times == UPDATE_FREQUENCY){
Message msg = new Message();
msg.what = 1;
//msg.arg1 = getNetSpeed();
msg.obj = getNetSpeed();
mHandler.sendMessage(msg);
times = 1;
} else {
times++;
}
}
}, 1000, 1000); // 每秒更新一次
}
}
public void stopCalculateNetSpeed() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
/**
* 獲取當前應用uid
*/
public int getUid() {
try {
PackageManager pm = mContext.getPackageManager();
ApplicationInfo ai = pm.getApplicationInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
return ai.uid;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return -1;
}
}
然後再Activity裡面,就需要去啟動Service,以顯示懸浮窗,然後TrafficInfo通過Handler,把當前網速發給Activity,Activity呼叫Service的方法了來更新懸浮窗的View。
import java.util.List;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.widget.TextView;
import com.yyh.utils.TrafficInfo;
public class MainActivity extends ActionBarActivity {
private ActivityManager mActivityManager;
private TextView mTextView;
Handler mHandler;
TrafficInfo speed;
ManagerService service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
mTextView = (TextView) findViewById(R.id.tv);
try {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
mTextView.setText(msg.obj + "kb/s");
if(service != null)
service.setSpeed(msg.obj+"kb/s"); // 設定網速
}
super.handleMessage(msg);
}
};
speed = new TrafficInfo(this,mHandler,TrafficInfo.getUid());
speed.startCalculateNetSpeed(); // 開啟網速監測
} catch (Exception e) {
e.printStackTrace();
}
Log.i("test","總流量="+speed.getTrafficInfo());
Intent intent = new Intent(MainActivity.this, ManagerService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((ManagerService.ServiceBinder) binder).getService();
}
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
speed.stopCalculateNetSpeed();
unbindService(conn);
}
}
這樣,就能夠顯示懸浮窗。如果還需要後臺執行,那Service就需要常駐不被殺死,這部分請參考我另一篇博文:Android 通過JNI實現守護程式,保證Service服務不被殺死
附上Demo原始碼:Android 流量與網速監測(懸浮窗) 原始碼
![](https://i.iter01.com/images/09077b2a5bd5c7cc380b046d41dfaee6a65730341f43b6a118cd6ec5002100e9.png)
相關文章
- 懸浮窗的一種實現 | Android懸浮窗Window應用Android
- Android 懸浮窗Android
- 【轉載】使用WindowManage實現Android懸浮窗Android
- Android懸浮窗的學習Android
- Android 懸浮窗 System Alert WindowAndroid
- Android懸浮框的實現Android
- 懸浮窗開發設計實踐
- 下沉式通知的一種實現 | Android懸浮窗Window應用Android
- Android仿微信文章懸浮窗效果Android
- Android 攝像頭預覽懸浮窗Android
- Android通過WindowManager實現懸浮框Android
- Android 輔助許可權與懸浮窗Android
- 網路卡流量監控指令碼,python實現指令碼Python
- [轉]Android輕鬆實現RecyclerView懸浮條AndroidView
- Android RecyclerView實現頭部懸浮吸頂效果AndroidView
- Android懸浮窗怎麼簡單實現?這樣用 kotlin編寫輕鬆搞定!AndroidKotlin
- Android中的懸浮框Android
- 非侵入式無許可權應用內懸浮窗的實現
- QPM 之懸浮窗設定資訊
- QPM 之懸浮窗助力效能優化優化
- 在Linux中,如何實時監控網路流量?Linux
- iftop--實時網路介面流量監控工具
- 滑鼠懸浮div實現旋轉效果
- Android.Hook框架xposed篇(Http流量監控)AndroidHook框架HTTP
- Android:會呼吸的懸浮氣泡Android
- 網速監控軟體 Traffic Monitor
- 直播平臺製作,Android 懸浮窗延時5秒返回APP問題AndroidAPP
- 滑鼠懸浮圖片實現翻轉效果
- 滑鼠懸浮圖片實現縮放效果
- 滑鼠懸浮實現環形旋轉效果
- 如何獲取Vivo系統的懸浮窗許可權狀態
- 小米 TYPE_TOAST 懸浮窗無效的原因AST
- 電商直播主圖設計素材,輕鬆搞定直播懸浮窗設計!
- 北京智和信通網路流量監控分析平臺
- TiDB監控實現--存活監控TiDB
- Android懸浮框的適配問題Android
- 利用css變數實現按鈕懸浮效果CSS變數
- 記vue下懸浮貼合頂部實現Vue
- iOS 流量監控分析iOS