TrafficStats类实现Android监听网速

  • Post author:
  • Post category:其他




一、网络状态


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,为了让自己幸福 — 帮助自己去完成更美好的事情,同时赞美自己



—- 自我增强存在价值



版权声明:本文为u013205623原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。