【Android Developers Training】 80. 管理网络

系统 2179 0

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接: http://developer.android.com/training/basics/network-ops/managing.html


这节课将讲解如何写一个对使用网络资源具有细粒度控制的应用。如果你的应用要执行很多网络操作,你需要提供用户设置,使得用户可以控制你的应用处理数据的行为,比如你的应用同步数据的频率,是仅在有Wi-Fi连接的情况下上传/下载,在漫游时是否处理数据等。当用户可以进行这些设置时,用户就不太会阻止你的应用访问后台数据了,因为他们能精确地控制应用能处理的数据范围。

有关一些通用的关于如何编写最小化影响电池寿命的应用程序,相关的引导可以阅读: Optimizing Battery Life Transferring Data Without Draining the Battery


一). 检查设备的网络连接

一个设备可以有许多种网络连接。这节课关注于使用Wi-Fi连接或者移动网络连接。完整的连接类型列表,可以阅读: ConnectivityManager

一般而言,Wi-Fi速度更快。另外移动数量一般按照流量计费,所以会很昂贵。通常的策略是只在有Wi-Fi连接的情况下获取大数据。

在你执行网络操作之前,检查网络的连接状态时一个好的习惯。这可以防止你的应用错误地使用网络连接方式。如果网络连接无法获取,你的应用应该恰当地做出响应。要检查网络连接状态,通常你会使用下列类:

  • ConnectivityManager  - 响应网络连接状态的查询。它也在网络连接放生改变时,通知应用。
  • NetworkInfo  - 对给定的类型(移动数据或者Wi-Fi)描绘网络接口的状态。

这个代码检测Wi-Fi和移动数据的网络连接。它确定这些网络接口是否可用(网络连接是否可以获取)或者是否已连接(也就是说,网络连接是否存在或者是否可能建立套接字并传输数据):

      
        private
      
      
        static
      
      
        final
      
       String DEBUG_TAG = "NetworkStatusExample"
      
        ;

...      

ConnectivityManager connMgr 
      
      =
      
         (ConnectivityManager) 

        getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo networkInfo 
      
      =
      
         connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 


      
      
        boolean
      
       isWifiConn =
      
         networkInfo.isConnected();

networkInfo 
      
      =
      
         connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);


      
      
        boolean
      
       isMobileConn =
      
         networkInfo.isConnected();

Log.d(DEBUG_TAG, 
      
      "Wifi connected: " +
      
         isWifiConn);

Log.d(DEBUG_TAG, 
      
      "Mobile connected: " + isMobileConn);
    

注意,在做决策时不可以仅根据网络是否可获得来进行。应该在每次进行网络操作之间检查 isConnected() ,因为 isConnected() 能够处理诸如片状移动网络(flaky mobile networks),飞行模式,以及后台限制的数据。

一个更简洁的方法是检查是否有一个可获得的网络连接接口,如下所示。 getActiveNetworkInfo() 方法返回一个 NetworkInfo 实例,它代表它能发现的第一个已连接的网络接口,或者在没有已连接的网络接口时返回 null (意味着无法获取一个网络连接):

      
        public
      
      
        boolean
      
      
         isOnline() {

    ConnectivityManager connMgr 
      
      =
      
         (ConnectivityManager) 

            getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo networkInfo 
      
      =
      
         connMgr.getActiveNetworkInfo();

    
      
      
        return
      
       (networkInfo != 
      
        null
      
       &&
      
         networkInfo.isConnected());

}  
      
    

要查询更详细的连接状态,你可以使用 NetworkInfo.DetailedState ,但一般来说这是不必要的。


二). 管理网络的使用

你可以实现一个用以对应用进行设置的activity,使得用户可以对应用对网络资源的使用进行控制。例如:

  • 你可以只允许用户在连接了Wi-Fi的情况下上传视频;
  • 你可以根据多个标准,如网络可获得否,时间间隔,等等,进行同步(或不进行)。

要写一个支持网络访问和管理网络使用的应用,你的应用需要有相应的权限和intent过滤器。

  • 下面摘录的的配置清单包含下列权限:
  • 你可以声明针对于 ACTION_MANAGE_NETWORK_USAGE (该action自Android 4.0开始引入)的intent过滤器,它用来表明你的应用定义了一个可以对数据使用进行控制的activity。 ACTION_MANAGE_NETWORK_USAGE 展示了对某个特定应用的数据使用管理的设置。当你的应用有允许用户设置网络使用的activity时,你应该为该activity配置此intent过滤器。在样例代码中,该action由 SettingsActivity 捕获并处理,它显示一个配置界面,让用户来选择何时下载:
      
        <?
      
      
        xml version="1.0" encoding="utf-8"
      
      
        ?>
      
      
        <
      
      
        manifest 
      
      
        xmlns:android
      
      
        ="http://schemas.android.com/apk/res/android"
      
      
        

    package
      
      
        ="com.example.android.networkusage"
      
      
        

    ...
      
      
        >
      
      
        <
      
      
        uses-sdk 
      
      
        android:minSdkVersion
      
      
        ="4"
      
      
         

           android:targetSdkVersion
      
      
        ="14"
      
      
        />
      
      
        <
      
      
        uses-permission 
      
      
        android:name
      
      
        ="android.permission.INTERNET"
      
      
        />
      
      
        <
      
      
        uses-permission 
      
      
        android:name
      
      
        ="android.permission.ACCESS_NETWORK_STATE"
      
      
        />
      
      
        <
      
      
        application

        
      
      
        ...
      
      
        >
      
      
        

        ...

        
      
      
        <
      
      
        activity 
      
      
        android:label
      
      
        ="SettingsActivity"
      
      
         android:name
      
      
        =".SettingsActivity"
      
      
        >
      
      
        <
      
      
        intent-filter
      
      
        >
      
      
        <
      
      
        action 
      
      
        android:name
      
      
        ="android.intent.action.MANAGE_NETWORK_USAGE"
      
      
        />
      
      
        <
      
      
        category 
      
      
        android:name
      
      
        ="android.intent.category.DEFAULT"
      
      
        />
      
      
        </
      
      
        intent-filter
      
      
        >
      
      
        </
      
      
        activity
      
      
        >
      
      
        </
      
      
        application
      
      
        >
      
      
        </
      
      
        manifest
      
      
        >
      
    

三). 实现一个配置Activity

如同你所看到的上述清单文件,样例中的activity: SettingsActivity有一个针对于 ACTION_MANAGE_NETWORK_USAGE 的intent过滤器。 SettingsActivity是 PreferenceActivity 的一个子类。它显示一个配置界面(如图1所示)让用户可以进行下列控制:

  • 是否显示每一个XML源条目的摘要,或仅对每个条目显示其链接;
  • 是否在任何情况下下载XML源,或只在连接了Wi-Fi的情况下。

 

图1. 配置activity

下面的代码是 SettingsActivity ,注意它实现了 OnSharedPreferenceChangeListener 。当用户进行了设置的变更时,会激活 onSharedPreferenceChanged() ,在该方法中会把 refreshDisplay 变为 true ,这会导致当用户回到主activity时发生显示刷新:

      
        public
      
      
        class
      
       SettingsActivity 
      
        extends
      
       PreferenceActivity 
      
        implements
      
      
         OnSharedPreferenceChangeListener {

    

    @Override

    
      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

        
      
      
        super
      
      
        .onCreate(savedInstanceState);

        

        
      
      
        //
      
      
         Loads the XML preferences file
      
      
                addPreferencesFromResource(R.xml.preferences);

    }

  

    @Override

    
      
      
        protected
      
      
        void
      
      
         onResume() {

        
      
      
        super
      
      
        .onResume();



        
      
      
        //
      
      
         Registers a listener whenever a key changes            
      
      

        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(
      
        this
      
      
        );

    }

  

    @Override

    
      
      
        protected
      
      
        void
      
      
         onPause() {

        
      
      
        super
      
      
        .onPause();



       
      
      
        //
      
      
         Unregisters the listener set in onResume().

       
      
      
        //
      
      
         It's best practice to unregister listeners when your app isn't using them to cut down on 

       
      
      
        //
      
      
         unnecessary system overhead. You do this in onPause().            
      
      

       getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
      
        this
      
      
        );    

    }

  

    
      
      
        //
      
      
         When the user changes the preferences selection, 

    
      
      
        //
      
      
         onSharedPreferenceChanged() restarts the main activity as a new

    
      
      
        //
      
      
         task. Sets the refreshDisplay flag to "true" to indicate that

    
      
      
        //
      
      
         the main activity should update its display.

    
      
      
        //
      
      
         The main activity queries the PreferenceManager to get the latest settings.
      
      
            

    @Override

    
      
      
        public
      
      
        void
      
      
         onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {    

        
      
      
        //
      
      
         Sets refreshDisplay to true so that when the user returns to the main

        
      
      
        //
      
      
         activity, the display refreshes to reflect the new settings.
      
      

        NetworkActivity.refreshDisplay = 
      
        true
      
      
        ;

    }

}
      
    

四). 响应配置变更

当用户在配置界面改变了设置,它会改变应用的行为。在下面的代码片段中,应用在 onStart() 方法内检查各项设置。如果在设置和设备网络连接之间能够匹配的上(例如,设置是“Wi-Fi”且应用具有一个可使用的Wi-Fi连接),那么应用会下载数据源并刷新页面:

      
        public
      
      
        class
      
       NetworkActivity 
      
        extends
      
      
         Activity {

    
      
      
        public
      
      
        static
      
      
        final
      
       String WIFI = "Wi-Fi"
      
        ;

    
      
      
        public
      
      
        static
      
      
        final
      
       String ANY = "Any"
      
        ;

    
      
      
        private
      
      
        static
      
      
        final
      
       String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"
      
        ;

   

    
      
      
        //
      
      
         Whether there is a Wi-Fi connection.
      
      
        private
      
      
        static
      
      
        boolean
      
       wifiConnected = 
      
        false
      
      
        ; 

    
      
      
        //
      
      
         Whether there is a mobile connection.
      
      
        private
      
      
        static
      
      
        boolean
      
       mobileConnected = 
      
        false
      
      
        ;

    
      
      
        //
      
      
         Whether the display should be refreshed.
      
      
        public
      
      
        static
      
      
        boolean
      
       refreshDisplay = 
      
        true
      
      
        ;

    

    
      
      
        //
      
      
         The user's current network preference setting.
      
      
        public
      
      
        static
      
       String sPref = 
      
        null
      
      
        ;

    

    
      
      
        //
      
      
         The BroadcastReceiver that tracks network connectivity changes.
      
      
        private
      
       NetworkReceiver receiver = 
      
        new
      
      
         NetworkReceiver();

    

    @Override

    
      
      
        public
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

        
      
      
        super
      
      
        .onCreate(savedInstanceState);

        

        
      
      
        //
      
      
         Registers BroadcastReceiver to track network connection changes.
      
      

        IntentFilter filter = 
      
        new
      
      
         IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);

        receiver 
      
      = 
      
        new
      
      
         NetworkReceiver();

        
      
      
        this
      
      
        .registerReceiver(receiver, filter);

    }

    

    @Override 

    
      
      
        public
      
      
        void
      
      
         onDestroy() {

        
      
      
        super
      
      
        .onDestroy();

        
      
      
        //
      
      
         Unregisters BroadcastReceiver when app is destroyed.
      
      
        if
      
       (receiver != 
      
        null
      
      
        ) {

            
      
      
        this
      
      
        .unregisterReceiver(receiver);

        }

    }

    

    
      
      
        //
      
      
         Refreshes the display if the network connection and the

    
      
      
        //
      
      
         pref settings allow it.
      
      
            

    @Override

    
      
      
        public
      
      
        void
      
      
         onStart () {

        
      
      
        super
      
      
        .onStart();  

        

        
      
      
        //
      
      
         Gets the user's network preference settings
      
      

        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(
      
        this
      
      
        );

        

        
      
      
        //
      
      
         Retrieves a string value for the preferences. The second parameter

        
      
      
        //
      
      
         is the default value to use if a preference value is not found.
      
      

        sPref = sharedPrefs.getString("listPref", "Wi-Fi"
      
        );



        updateConnectedFlags(); 

       

        
      
      
        if
      
      
        (refreshDisplay){

            loadPage();    

        }

    }

    

    
      
      
        //
      
      
         Checks the network connection and sets the wifiConnected and mobileConnected

    
      
      
        //
      
      
         variables accordingly. 
      
      
        public
      
      
        void
      
      
         updateConnectedFlags() {

        ConnectivityManager connMgr 
      
      =
      
         (ConnectivityManager) 

                getSystemService(Context.CONNECTIVITY_SERVICE);

        

        NetworkInfo activeInfo 
      
      =
      
         connMgr.getActiveNetworkInfo();

        
      
      
        if
      
       (activeInfo != 
      
        null
      
       &&
      
         activeInfo.isConnected()) {

            wifiConnected 
      
      = activeInfo.getType() ==
      
         ConnectivityManager.TYPE_WIFI;

            mobileConnected 
      
      = activeInfo.getType() ==
      
         ConnectivityManager.TYPE_MOBILE;

        } 
      
      
        else
      
      
         {

            wifiConnected 
      
      = 
      
        false
      
      
        ;

            mobileConnected 
      
      = 
      
        false
      
      
        ;

        }  

    }

      

    
      
      
        //
      
      
         Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
      
      
        public
      
      
        void
      
      
         loadPage() {

        
      
      
        if
      
       (((sPref.equals(ANY)) && (wifiConnected ||
      
         mobileConnected))

                
      
      || ((sPref.equals(WIFI)) &&
      
         (wifiConnected))) {

            
      
      
        //
      
      
         AsyncTask subclass
      
      
        new
      
      
         DownloadXmlTask().execute(URL);

        } 
      
      
        else
      
      
         {

            showErrorPage();

        }

    }

...

    

}
      
    

五). 检测连接变更

最后一部分内容是关于 NetworkReceiver BroadcastReceiver 的子类。当设备的网络连接发生了变化, NetworkReceiver 会拦截 CONNECTIVITY_ACTION ,确定当前的连接状态时什么,并相应地将 wifiConnected mobileConnected 设置为true/false。产生的结果是,下一次用户回到应用时,应用会仅下载最后一个源并在 NetworkActivity.refreshDisplay 为true的情况下刷新页面。

配置一个会被不必要地调用的 BroadcastReceiver会消耗系统资源。样例中,在 onCreate() 方法中注册了 BroadcastReceiver   NetworkReceiver,并在 onDestroy() 中销毁它。一种更轻量级的解决方法是在清单文件中声明一个   <receiver> 。当你这样做了之后,它会在任何时间唤起你的应用,甚至是你已经好几周没有运行该应用了。如果在主 activity 中注册或注销 NetworkReceiver, 用户在离开应用后就不会再被唤起了。如果你在清单文件中声明了一个 <receiver>,并且你明确的知道你在什么地方会需要它,那么你可以使用 setComponentEnabledSetting() 在恰当地时机启用或禁用它。

下面是 NetworkReceiver:

      
        public
      
      
        class
      
       NetworkReceiver 
      
        extends
      
      
         BroadcastReceiver {   

      

@Override


      
      
        public
      
      
        void
      
      
         onReceive(Context context, Intent intent) {

    ConnectivityManager conn 
      
      =
      
          (ConnectivityManager)

        context.getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo networkInfo 
      
      =
      
         conn.getActiveNetworkInfo();

       

    
      
      
        //
      
      
         Checks the user prefs and the network connection. Based on the result, decides whether

    
      
      
        //
      
      
         to refresh the display or keep the current display.

    
      
      
        //
      
      
         If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
      
      
        if
      
       (WIFI.equals(sPref) && networkInfo != 
      
        null
      
       && networkInfo.getType() ==
      
         ConnectivityManager.TYPE_WIFI) {

        
      
      
        //
      
      
         If device has its Wi-Fi connection, sets refreshDisplay

        
      
      
        //
      
      
         to true. This causes the display to be refreshed when the user

        
      
      
        //
      
      
         returns to the app.
      
      

        refreshDisplay = 
      
        true
      
      
        ;

        Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();



    
      
      
        //
      
      
         If the setting is ANY network and there is a network connection

    
      
      
        //
      
      
         (which by process of elimination would be mobile), sets refreshDisplay to true.
      
      

    } 
      
        else
      
      
        if
      
       (ANY.equals(sPref) && networkInfo != 
      
        null
      
      
        ) {

        refreshDisplay 
      
      = 
      
        true
      
      
        ;

                 

    
      
      
        //
      
      
         Otherwise, the app can't download content--either because there is no network

    
      
      
        //
      
      
         connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there 

    
      
      
        //
      
      
         is no Wi-Fi connection.

    
      
      
        //
      
      
         Sets refreshDisplay to false.
      
      

    } 
      
        else
      
      
         {

        refreshDisplay 
      
      = 
      
        false
      
      
        ;

        Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();

    }

}
      
    

【Android Developers Training】 80. 管理网络使用


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论