1. 程式人生 > >TrafficStats類實現Android監聽網速

TrafficStats類實現Android監聽網速

一、網路狀態

       Android市場的逐漸降溫及形成穩定,其實說明了Android的發展走向穩重成熟的階段。

       除卻最開始的功能實現,到如今的使用者體驗至上,Android研發面臨的挑戰卻從未冷卻。基於大多數APP均是以APP為資料展示框架,實現客戶與伺服器資料互動,網路扮演了十分重要的角色。

       網路狀態,除了WiFi,資料,網路不可用等,還有網路條件不好等情形。存有標誌網路速度的則能較好地標示當前網路情形。

       以下則實現監聽網速,展示當前網速條件。藉由給客戶更好的使用者體驗。

二、實現監聽網速

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(int
     * uid) //獲取某個網路UID的傳送位元組數
     */
實現整體思路:

        建立浮動視窗,用於展示網速狀態;應用程式後臺執行,浮動視窗依舊顯示,則使用Service展示浮動視窗;在Activity中開啟Service。

網速監聽主體功能實現,TrafficBean.java:

package com.future.netobserverdemo.bean;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 功能:流量Bean屬性資訊
 * 作者:vision
 * 時間:2016/10/19
 */
public class TrafficBean implements Serializable {
    /**
     * 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(int
     * uid) //獲取某個網路UID的傳送位元組數
     */

    /**
     * 不支援狀態【標識變數】
     */
    private static final int UNSUPPORT = -1;
    /**
     * 列印資訊標誌
     */
    private static final String TAG = "TrafficBean";
    /**
     * 當前物件例項
     */
    private static TrafficBean instance;
    /**
     * 當前應用的uid
     */
    static int UUID;
    /**
     * 上一次記錄網路位元組流
     */
    private long preRxBytes = 0;
    /**
     *
     */
    private Timer mTimer = null;
    /**
     * 上下文物件
     */
    private Context context;
    /**
     * 訊息處理器
     */
    private Handler handler;
    /**
     * 更新頻率
     */
    private final int UPDATE_FREQUENCY = 1;
    private int times = 1;


    /**
     * 構造方法
     *
     * @param context
     * @param handler
     * @param uid
     */
    public TrafficBean(Context context, Handler handler, int uid) {
        this.context = context;
        this.handler = handler;
        this.UUID = uid;
    }

    public TrafficBean(Context context, Handler handler) {
        this.context = context;
        this.handler = handler;
    }

    /**
     * 獲取例項物件
     *
     * @param context
     * @param handler
     * @return
     */
    public static TrafficBean getInstance(Context context, Handler handler) {
        if (instance == null) {
            instance = new TrafficBean(context, handler);
        }
        return instance;
    }

    /**
     * 獲取總流量
     *
     * @return
     */
    public long getTrafficInfo() {
        long recTraffic = UNSUPPORT;//下載流量
        long sendTraffic = UNSUPPORT;//上傳流量
        recTraffic = getRecTraffic();
        sendTraffic = getSendTraffic();

        if (recTraffic == UNSUPPORT || sendTraffic == UNSUPPORT) {
            return UNSUPPORT;
        } else {
            return recTraffic + sendTraffic;
        }
    }

    /**
     * 獲取上傳流量
     *
     * @return
     */
    private long getSendTraffic() {
        long sendTraffic = UNSUPPORT;
        sendTraffic = TrafficStats.getUidTxBytes(UUID);
        if (sendTraffic == UNSUPPORT) {
            return UNSUPPORT;
        }
        RandomAccessFile rafSend = null;
        String sndPath = "/proc/uid_stat/" + UUID + "/tcp_snd";
        try {
            rafSend = new RandomAccessFile(sndPath, "r");
            sendTraffic = Long.parseLong(rafSend.readLine());
        } catch (FileNotFoundException e) {
            Log.e(TAG, "FileNotFoundException: " + e.getMessage());
            sendTraffic = UNSUPPORT;
        } catch (IOException e) {
            Log.e(TAG, "IOException: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                if (rafSend != null)
                    rafSend.close();
            } catch (IOException e) {
                Log.w(TAG, "Close RandomAccessFile exception: " + e.getMessage());
            }
        }
        return sendTraffic;
    }

    /**
     * 獲取下載流量
     * 某個應用的網路流量資料儲存在系統的
     * /proc/uid_stat/$UID/tcp_rcv | tcp_snd檔案中
     *
     * @return
     */
    private long getRecTraffic() {
        long recTraffic = UNSUPPORT;
        recTraffic = TrafficStats.getUidRxBytes(UUID);
        if (recTraffic == UNSUPPORT) {
            return UNSUPPORT;
        }
        Log.i(TAG, recTraffic + " ---1");
        //訪問資料檔案
        RandomAccessFile rafRec = null;
        String rcvPath = "/proc/uid_stat/" + UUID + "/tcp_rcv";
        try {
            rafRec = new RandomAccessFile(rcvPath, "r");
            recTraffic = Long.parseLong(rafRec.readLine()); // 讀取流量統計
        } catch (FileNotFoundException e) {
            Log.e(TAG, "FileNotFoundException: " + e.getMessage());
            recTraffic = UNSUPPORT;
        } catch (IOException e) {
            Log.e(TAG, "IOException: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                if (rafRec != null)
                    rafRec.close();
            } catch (IOException e) {
                Log.w(TAG, "Close RandomAccessFile exception: " + e.getMessage());
            }
        }
        Log.i("test", recTraffic + "--2");
        return recTraffic;
    }


    /**
     * 獲取當前下載流量總和
     *
     * @return
     */
    public static long getNetworkRxBytes() {
        return TrafficStats.getTotalRxBytes();
    }

    /**
     * 獲取當前上傳流量總和
     *
     * @return
     */
    public static long getNetworkTxBytes() {
        return TrafficStats.getTotalTxBytes();
    }

    /**
     * 獲取當前網速
     *
     * @return
     */
    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();
                        handler.sendMessage(msg);
                        times = 1;
                    } else {
                        times++;
                    }
                }
            }, 1000, 1000);
        }
    }

    /**
     * 停止網速監聽計算
     */
    public void stopCalculateNetSpeed() {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
    }

    /**
     * 獲取當前應用uid
     *
     * @return
     */
    public int getUid() {
        try {
            PackageManager pm = context.getPackageManager();
            //修改
            ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            return ai.uid;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return -1;
    }
}
Service中建立浮動視窗,展示網速狀態,ManagerService.java:
public class ManagerService extends Service {
    private static final String TAG = "ManagerService";
    /**
     * 浮動窗口布局
     */
    private LinearLayout floatLL;
    public WindowManager.LayoutParams params;
    /**
     * 浮動串口布局引數
     */
    public WindowManager windowManager;
    public TextView floatTV;
    private ServiceBinder binder = new ServiceBinder();


    @Override
    public void onCreate() {
        super.onCreate();
        createFloatView();
    }

    /**
     * 建立浮動窗口布局
     */
    private void createFloatView() {
        params = new WindowManager.LayoutParams();
        windowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 設定window
        // type為TYPE_SYSTEM_ALERT
        params.format = PixelFormat.RGBA_8888;// 設定圖片格式,效果為背景透明
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 設定浮動視窗不可聚焦(實現操作除浮動視窗外的其他可見視窗的操作)
        params.gravity = Gravity.LEFT | Gravity.TOP;// 預設位置:左上角
        params.width = WidgetUtils.dpToPx(getApplicationContext(), 65);
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.x = (WidgetUtils.getScreenWidth(getApplicationContext()) - params.width) / 2;// 設定x、y初始值,相對於gravity
        params.y = 10;
        // 獲取浮動視窗檢視所在佈局
        LayoutInflater inflater = LayoutInflater.from(getApplication());
        floatLL = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
        windowManager.addView(floatLL, params);// 新增mFloatLayout
        floatTV = (TextView) floatLL.findViewById(R.id.move_desc);
        floatTV.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        // 設定監聽浮動視窗的觸控移動
        floatTV.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // getRawX是觸控位置相對於螢幕的座標,getX是相對於按鈕的座標
                params.x = (int) event.getRawX() - floatTV.getMeasuredWidth() / 2;
                Log.i(TAG, "RawX" + event.getRawX());
                Log.i(TAG, "X" + event.getX());
                params.y = (int) event.getRawY() - floatTV.getMeasuredHeight() / 2 - 25;// 減25為狀態列的高度
                Log.i(TAG, "RawY" + event.getRawY());
                Log.i(TAG, "Y" + event.getY());
                windowManager.updateViewLayout(floatLL, params);// 重新整理
                return false; // 此處必須返回false,否則OnClickListener獲取不到監聽
            }
        });
        floatTV.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {
                Toast.makeText(getApplicationContext(), "浮動視窗被點選了!", Toast.LENGTH_SHORT).show();
            }
        });
    }


    public void setSpeed(String str) {
        floatTV.setText(str);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (floatLL != null && windowManager != null) {
            windowManager.removeView(floatLL);// 移除懸浮視窗
        }
        startService(new Intent(this, ManagerService.class));
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY_COMPATIBILITY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }


    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
    }

    public class ServiceBinder extends Binder {
        public ManagerService getService() {
            return ManagerService.this;
        }
    }
}
Activity中繫結service,實現功能:
public class MainActivity extends AppCompatActivity {
    /**
     * Activity管理器
     */
    private ActivityManager activityManager;
    /**
     * 浮動圖示
     */
    private TextView moveDesc;
    /**
     * 訊息處理器
     */
    private Handler handler;
    /**
     * 流量資訊物件
     */
    private TrafficBean trafficBean;
    /**
     * 服務
     */
    private ManagerService service;
    /**
     * 標記資訊
     */
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        moveDesc = (TextView) findViewById(R.id.tv);

        try {
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 1) {
                        moveDesc.setText(msg.obj + "kb/s");
                        if (service != null) {
                            service.setSpeed(msg.obj + "kb/s");
                        }
                    }
                    super.handleMessage(msg);
                }
            };
            trafficBean = new TrafficBean(this, handler, 12580);
            trafficBean.startCalculateNetSpeed();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Log.d(TAG, "總流量 = " + trafficBean.getTrafficInfo());
        Intent intent = new Intent(MainActivity.this, ManagerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }


    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder iBinder) {
            service = ((ManagerService.ServiceBinder) iBinder).getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            service = null;
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        trafficBean.stopCalculateNetSpeed();
        unbindService(conn);
    }
}

展示效果:



三、細節注意

1,使用與建立對應,onCreate()中繫結,onDestory()中解綁;

2,清單檔案配置許可權及服務和廣播接收者。

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

<service android:name="com.future.netobserverdemo.service.ManagerService" />

       <receiver android:name="com.future.netobserverdemo.service.ManagerReceiver">
            <intent-filter android:priority="2147483647"> <!-- 優先順序加最高 -->
                <!-- 系統啟動完成後會呼叫 -->
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <!-- 解鎖完成後會呼叫 -->
                <action android:name="android.intent.action.USER_PRESENT" />
                <!-- 監聽情景切換 -->
                <action android:name="android.media.RINGER_MODE_CHANGED" />
            </intent-filter>
        </receiver>

原始碼

上班族如何安排時間才能每天都早起,讀書,跑步,冥想?

                         1,早起毀一天 -- 任何依靠自制力的事情,最後都會失敗;                          2,心態陷阱 --  貪多,為了讀書而讀書,為了瀏覽而瀏覽,促進心理焦慮                                     ----  通過某種行為,達到某種目的-----  目的                          3,目標--手段--執行意圖  ---  同樣的效果,更節省意志力                                      ----  愛好他,比強壓自己學習更好,好太多                          4,為了讓自己幸福 -- 幫助自己去完成更美好的事情,同時讚美自己                                      ---- 自我增強存在價值

相關推薦

TrafficStats實現Android

一、網路狀態        Android市場的逐漸降溫及形成穩定,其實說明了Android的發展走向穩重成熟的階段。        除卻最開始的功能實現,到如今的使用者體驗至上,Android研發面臨的挑戰卻從未冷卻。基於大多數APP均是以APP為資料展示框架,實現客戶與

Android 自定義Dialog,並在Activity中實現按鈕

實際開發中,經常會用到Dialog,比如退出時候會彈出是否退出,或者還有一些編輯框也會用Dialog實現,效果圖如下: 開發中遇到的問題無非在於如果在Activity中監聽這個Dialog中實現的按鈕,Dialog類如下,在MyDialog這個類中實現了一個LeaveMyDialogLi

Android UI事件處理——實現事件介面的4種方法

前段時間看到一個同學的android課程有這樣一個作業要求:....... 非內部類實現onClickListener監聽介面的方式監聽按鈕單擊事件 ....... 感覺蠻奇怪,一般對於UI事件的處理

android 上下左右鍵的功能實現

  鍵盤中的上下左右與返回鍵。可以直接監聽,不需要在xml或者其他地方註冊。 主要方法是: public boolean onKeyDown(int kCode,KeyEvent kEvent) { switch(kCode) { case KeyEvent.KEYCODE

Android實現流量統計和監控懸浮窗

很多安全衛士類軟體都實現了網速監測功能,也算是一個比較實用的功能。Android下,TrafficStats類實現了對流量的統計。 static long getMobileRxBytes()//獲取通過Mobile連線收到的位元組總數,但不包含WiFi static

android實現事件的2種常用方式

1.使用內部匿名類: private Button button; @Override protected void onCreate(Bundle savedInstanceState) { sup

Javascript----input事件實現動態textarea內容變化

span oninput idt ava ntb ima property onchange pre 1、代碼 <!DOCTYPE html> <html> <head> <title>textarea輸入文字監聽

實現滾輪,導航欄置頂的效果。

nav idt ack doc color win ID cti col 為了項目的制作,需要一個導航欄置頂的效果,遇到了點難度,但還是克服了,通過比較導航和滾動條的高度實現置頂效果 <div id="daohang" style="width:100%;heig

Android APP進入後臺或切換到前臺方案對比

api 5.0 等等 推薦一個 情況 lis 需要 推出 soc 在我們開發的過程中,經常會遇到需要我們判斷app進入後臺,或者切換到前臺的情況。比如我們想判斷app切換到前臺時,顯示一個解鎖界面,要求用戶輸入解鎖密碼才能繼續進行操作;我們想判斷app切換到後臺,記錄一下l

Android 屏幕喚醒和關閉的廣播

n) 應用 dma 今天 new and androidm receive err 今天希望應用程序的服務運行時,可以監聽到屏幕的喚醒。繼續百度學習法,連同監聽閉幕關閉也一同學習了。 此種情況需要動態註冊系統廣播。在AndroidManifest.xml中靜態註冊

Android 屏幕鎖屏&用戶解鎖

clas 開始 ets adc spa contex screen context scree 在做視頻播放器的時候,遇到一個問題,在用戶播放視頻然後鎖屏之後,視頻播放器仍然在繼續播放,遇到類似手機系統狀態改變的問題的時候,首先想到了廣播,下面做個總結: public

JS實現鍵盤(包括組合鍵)

nbsp html gist 收集 and ros register col next 依然使用案例驅動~案例是學習的最好實踐! <html> <head> <meta

Android 文件夾

external 分享 tex xtend 目錄 nac 觸發 改變 @override 在一次Android和pc端的通訊過程中,我們放棄了adb forward來實現socket通訊。而是使用adb push文件,我監聽文件夾... 都學習一下很有必要 本篇簡單Andr

Android藍芽耳機的按鍵事件 藍芽無法響應KeyEvent不到

需求:藍芽耳機的按鍵事件,暫停/播放 音訊/視訊 ,無法響應藍芽KeyEvent的事件 此問題是由於中的藍芽KeyEvent監聽不到導致的,可以通過以下方法監聽藍芽按鍵事件    private MediaSession mSession;  &

Python和Redis實現訂單,語音播報

1.MP3格式的音訊檔案 如 audio.mp3 2.建立虛禮環境 建立虛擬環境 安裝:pip install virtualenv 建立:virtualenv venv 啟用:venv\Scripts\activate 3.安裝所需擴充套件 pip install playso

android 元件構建完成

//有時候要獲取一個元件的位置總是返回0,原因是元件還沒繪製完成,該方法就是等待元件構建完成回撥 mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

android虛擬按鍵的顯示與隱藏

虛擬按鍵在華為手機中大量存在,而虛擬按鍵的存在無疑增加了螢幕適配的難度,往往許多時候我們的介面需要根據虛擬按鍵的顯示與否(虛擬按鍵的顯示隱藏是使用者可以動態調整的)來調整介面的ui顯示,但android系統本身(似乎)並沒有提供此類介面(PS:反正我沒找到類似介面),經過研究得到一個可用的方式。 a

Android 手機按下Home鍵

系統本身有這個廣播,只需要監聽這個廣播就可以了! private BroadcastReceiver mHomeKeyEventReceiver = new BroadcastReceiver() { String SYSTEM_REASON = "reaso

Android獲取當前質量——分四個等級

在開發中,有時候常常需要根據使用者當前的網速來做一些操作,比如圖片的載入,當網速非常好的時候,比如連線的是wifi,我們就會下載高解析度的圖片,反之,當用戶使用的是2g網時,我們則給他下載低解析度的小圖,從而節省使用者流量。 而Facebook其實已經給我們提供了這麼一個庫,詳見netw

AndroidHome鍵、鎖屏、解屏(落雨敏詳細講解)附原始碼

     前言:最近做視訊播放,發現當在播放時點選電源鍵進行鎖屏,視訊繼續播放問題,於是通過廣播進行監聽。廣播監聽可以用於很多地方,在此落雨敏詳細講解Home鍵、鎖屏、解屏的事件處理。先講解思路,然後在一步一步實現,下面會貼出所有程式碼。   &nbs