Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Android Network Optimization #58

Open
yunshuipiao opened this issue Jun 1, 2019 · 0 comments
Open

Android Network Optimization #58

yunshuipiao opened this issue Jun 1, 2019 · 0 comments
Labels

Comments

@yunshuipiao
Copy link
Owner

Android Network Optimization

[TOC]

网络问题

流量耗费

**过多以及没有经过处理的网络请求,会消耗用户的网络流量。**Android用户一般都会安装手机管理类App,可以方便清楚查看到每个App耗费的流量,高流量消耗会导致经常处于非Wifi场景下的用户卸载。

电量消耗

用户体验差

  • 应用Apk更新,Apk下载的快慢肯定会影响到应用更新流程的转换率;
  • 类如热修复Patch包、Hybrid资源包等的下载,肯定是越早下载到本地越好;

网络监控

  • AS 自带的 NetWork Mnoitor ,可以看出时间段内的网络请求数量及访问速度
  • Charles、Fiddler等抓包工具:更加强大
  • Stetho

网络优化

网络优化主要从三个方面进行:1. 速度;2. 成功率;3. 流量。

Gzip 压缩

HTTP协议上的Gzip编码是一种用来改进WEB应用程序性能的技术,用来减少传输数据量大小,减少传输数据量大小有两个明显的好处:

  • 可以减少流量消耗
  • 可以减少传输事件

IP 直连和 HttpDns

DNS解析的失败率占联网失败中很大一种,而且首次域名解析一般需要几百毫秒。针对此,我们可以不用域名,才用IP直连省去 DNS 解析过程,节省这部分时间。

另外熟悉阿里云的小伙伴肯定知道HttpDns:HttpDNS基于Http协议的域名解析,替代了基于DNS协议向运营商Local DNS发起解析请求的传统方式,可以避免Local DNS造成的域名劫持和跨网访问问题,解决域名解析异常带来的困扰。

图片处理

图片下载

  • 使用WebP格式;同样的照片,采用WebP格式可大幅节省流量,相对于JPG格式的图片,流量能节省将近 25% 到 35 %;相对于 PNG 格式的图片,流量可以节省将近80%。最重要的是使用WebP之后图片质量也没有改变。
  • 使用缩略图;App中需要加载的图片按需加载,列表中的图片根据需要的尺寸加载合适的缩略图即可,只有用户查看大图的时候才去加载原图。不仅节省流量,同时也能节省内存!之前使用某公司的图片存储服务在原图链接之后拼接宽高参数,根据参数的不同返回相应的图片。

图片上传

图片(文件)的上传失败率比较高,不仅仅因为大文件,同时带宽、时延、稳定性等因素在此场景下的影响也更加明显;

  • 避免整文件传输,采用分片传输;
  • 根据网络类型以及传输过程中的变化动态的修改分片大小;
  • 每个分片失败重传的机会。

备注:图片上传是一项看似简单、共性很多但实际上复杂、需要细分的工作。移动互联网的场景和有线的场景是有很多区别的,例如移动网络的质量/带宽经常会发生“跳变”,但有线网络却是“渐变”。

请求打包

**合并网络请求,减少请求次数。**对于一些接口类如统计,无需实时上报,将统计信息保存在本地,然后根据策略统一上传。这样头信息仅需上传一次,减少了流量也节省了资源。

网络缓存

对服务端返回数据进行缓存,设定有效时间,有效时间之内不走网络请求,减少流量消耗。对网络的缓存可以参见HttpResponseCache。

网络状态

根据网络状态对网络请求进行区别对待,2G与Wifi状态下网络质量肯定是不一样的,那对应的网络策略也应该是不一样的。例如:在Wifi场景下可以进行数据的预取、一些统计的集中上传等;而在2G场景下此类操作以及网络请求的次数策略都应该调低。网络状态可以由TelephonyManager.getNetworkType()方法获取到。

备注:还可以使用Facebook的开源库network-connection-class来做网络状态的判断。

其它

  • 对于网络优化,实际上和内存优化一样,是一项投入巨大的事情。提升网络的成功率尤为困难。因此建议优先进行流量优化,减少干扰项;
  • 弱网不仅仅指代网络不好,移动互联网的网络带宽很容易出现“跳变”,下一秒的传送速度可能降到前一秒的几十分之一;而且即便是信号满格也传不出一个字节;
  • 对于真正的弱网,可以使用抓包工具进行模拟,也有聪明的小伙伴使用wifi精灵进行限速;
  • Facebook的开源项目augmented-traffic-control可以模拟不同的网络环境,针对带宽、时延抖动、丢包率、错包率、包重排序率等方面,堪称弱网调试神器;

网络状态的判断的工具类

public class NetworkUtil {
    //没有网络连接
    public static final int NETWORN_NONE = 0;
    //wifi连接
    public static final int NETWORN_WIFI = 1;
    //手机网络数据连接类型
    public static final int NETWORN_2G = 2;
    public static final int NETWORN_3G = 3;
    public static final int NETWORN_4G = 4;
    public static final int NETWORN_MOBILE = 5;
 
    /**
     * 获取当前网络连接类型
     *
     * @param context
     * @return
     */
    public static int getNetworkType(Context context) {
        //获取系统的网络服务
        ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 
        //如果当前没有网络
        if (null == connManager)
            return NETWORN_NONE;
 
        //获取当前网络类型,如果为空,返回无网络
        NetworkInfo activeNetInfo = connManager.getActiveNetworkInfo();
        if (activeNetInfo == null || !activeNetInfo.isAvailable()) {
            return NETWORN_NONE;
        }
 
        // 判断是不是连接的是不是wifi
        NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        if (null != wifiInfo) {
            NetworkInfo.State state = wifiInfo.getState();
            if (null != state)
                if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
                    return NETWORN_WIFI;
                }
        }
 
        // 如果不是wifi,则判断当前连接的是运营商的哪种网络2g、3g、4g等
        NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
        if (null != networkInfo) {
            NetworkInfo.State state = networkInfo.getState();
            String strSubTypeName = networkInfo.getSubtypeName();
            if (null != state)
                if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
                    switch (activeNetInfo.getSubtype()) {
                        //如果是2g类型
                        case TelephonyManager.NETWORK_TYPE_GPRS: // 联通2g
                        case TelephonyManager.NETWORK_TYPE_CDMA: // 电信2g
                        case TelephonyManager.NETWORK_TYPE_EDGE: // 移动2g
                        case TelephonyManager.NETWORK_TYPE_1xRTT:
                        case TelephonyManager.NETWORK_TYPE_IDEN:
                            return NETWORN_2G;
                        //如果是3g类型
                        case TelephonyManager.NETWORK_TYPE_EVDO_A: // 电信3g
                        case TelephonyManager.NETWORK_TYPE_UMTS:
                        case TelephonyManager.NETWORK_TYPE_EVDO_0:
                        case TelephonyManager.NETWORK_TYPE_HSDPA:
                        case TelephonyManager.NETWORK_TYPE_HSUPA:
                        case TelephonyManager.NETWORK_TYPE_HSPA:
                        case TelephonyManager.NETWORK_TYPE_EVDO_B:
                        case TelephonyManager.NETWORK_TYPE_EHRPD:
                        case TelephonyManager.NETWORK_TYPE_HSPAP:
                            return NETWORN_3G;
                        //如果是4g类型
                        case TelephonyManager.NETWORK_TYPE_LTE:
                            return NETWORN_4G;
                        default:
                            //中国移动 联通 电信 三种3G制式
                            if (strSubTypeName.equalsIgnoreCase("TD-SCDMA") || strSubTypeName.equalsIgnoreCase("WCDMA") || strSubTypeName.equalsIgnoreCase("CDMA2000")) {
                                return NETWORN_3G;
                            } else {
                                return NETWORN_MOBILE;
                            }
                    }
                }
        }
        return NETWORN_NONE;
    }
 
    //判断网络是否连接(是否打开移动网络或者是连接wifi)
    public static boolean isNetworkAvailable(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable();
            }
        }
        return false;
    }
 
    //判断外网是否可用(常用于wifi已经连接但是无法访问外网的情况),耗时操作,不应该放在主线程
    public static boolean isNetworkOnline() {
        Runtime runtime = Runtime.getRuntime();
        try {
            String ip = "www.baidu.com";// 除非百度挂了,否则用这个应该没问题~
            Process ipProcess = runtime.getRuntime().exec("ping -c 3 -w 100 " + ip);//ping3次
            int exitValue = ipProcess.waitFor();
            return (exitValue == 0);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant