一直以来本人都在做Android Multi-Media Framework下的Lib支持库的开发和修改,终于最近告一段落,但根据项目要求,需要写一个和网络相关的service,用java来实现。其实,在Framework及其之上的应用层用java开发,本人并不陌生,此前也做过一段时间,包括定制View,实现界面特效以及多媒体播放器和音乐编辑器,都做过。唯一遗憾的是,自进入嵌入式领域以来,从来没有做过网络相关的程序设计,此次,欣然答应,就是想借机来填补下个人职业生涯中的一项空白,呵呵,于私于公都是有利的。    开始进入正题。    

        网络状态检测的目的是检测当前设备是否已经连接到网络,属于何种类型,是否可用等信息,这些是进行正常网络通信的前提。这里需要说明,这里提供的sample程序,如无说明,默认都是在emulator上运行的,OS是2.3版本的。

        首先,介绍网络状态检测的两个核心class

        1)、ConnectivityManager($SOURCE/frameworks/base/core/java/android/net/ConnectivityManager.java):给出网络连接状态,并在网络连接改变时(如由Wi-Fi连接变为Bluetooth连接)通知应用程序,主要职责有以下几个:

               (1)、监视网络连接(Wi-Fi,GPRS,UMTS,BT等等);

               (2)、在网络连接发生变化时,向应用发送broadcast  Intent;

               (3)、在网络连接失败时,尝试进行“失败转接”到其它可用网络

               (4)、提供API,允许应用程序查询可用网络的粗粒度和细粒度状态

      2)、NetworkInfo($SOURCE/frameworks/base/core/java/android/net/NetworkInfo.java):描述给定类型的网路接口的状态,截止到2.3.4(3.0以上的源码尚未开放),网络连接仅支持Mobile和Wi-Fi。顺便说一下,该class是个网络状态信息存储体,实现了Parcelable class中的部分接口,其余的接口都是进行网络状态的设置和查询。值得注意的是:NetworkInfo源码中提到两个概念:coarse-grained state(粗粒度状态)和fine-grained state(细粒度状态),这和1)的第四点职责相对应。通过分析源码,coarse-grained state包括:CONNECTING(正在连接), CONNECTED(已连接), SUSPENDED(挂起), DISCONNECTING(正在断开连接),DISCONNECTED(连接断开),UNKNOWN(未知状态)。对于fine-grained state这里也不做太多说明,根据源码注释来看,应用程序基本上用的都是coarse-grained state,极少使用fine-grained state。当然,分析源码我们可以看出,实际的网络连接是按fine-grained state进行状态迁移的,只是Android已经进行了fine-grained state到coarse-grained state的映射,是通过一个状态映射表来完成的,举例来说:如将fine-grained state的(IDLE+SCANNING+CONNECTING+AUTHENTICATING+OBTAINING_IPADDR)都映射为coarse-grained state的DISCONNECTED。

        其次,我的sample程序:

(1)、main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  
		android:id="@+id/netinfo"
	    android:layout_width="fill_parent" 
	    android:layout_height="wrap_content" 
	    android:text="network information"
	    />
</LinearLayout>



(2)、Activity所在.java文件NetworkExplorer.java

package com.android.sample.NetworkExplorer;

import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.widget.TextView;

public class NetworkExplorer extends Activity {
	ConnectivityManager cgr;
	NetworkInfo netinfo;
	TextView netinfo_tv;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        netinfo_tv = (TextView)findViewById(R.id.netinfo);
        cgr = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);
    }

	@Override
	protected void onStart() {
		super.onStart();
		
		netinfo = cgr.getActiveNetworkInfo();
		netinfo_tv.setText(netinfo.toString());
	}
}

        最后,我的捉虫之旅

        启动emulator,并执行上面的代码,结果屏幕显示一个大大的Sorry,看着就来气。看看logcat给出下面一大段Error信息:

ERROR/AndroidRuntime(1985): FATAL EXCEPTION: main
ERROR/AndroidRuntime(1985): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.sample.
                            NetworkExplorer/com.android.sample.NetworkExplorer.NetworkExplorer}: java.lang.SecurityException: 
                            ConnectivityService: Neither user 10042 nor current process has 
                            android.permission.ACCESS_NETWORK_STATE.
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1622)
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)
ERROR/AndroidRuntime(1985):     at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(1985):     at android.os.Looper.loop(Looper.java:123)
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread.main(ActivityThread.java:3647)
ERROR/AndroidRuntime(1985):     at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(1985):     at java.lang.reflect.Method.invoke(Method.java:507)
ERROR/AndroidRuntime(1985):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
ERROR/AndroidRuntime(1985):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
ERROR/AndroidRuntime(1985):     at dalvik.system.NativeStart.main(Native Method)
ERROR/AndroidRuntime(1985): Caused by: java.lang.SecurityException: ConnectivityService: 
                            Neither user 10042 nor current process has android.permission.ACCESS_NETWORK_STATE.
ERROR/AndroidRuntime(1985):     at android.os.Parcel.readException(Parcel.java:1322)
ERROR/AndroidRuntime(1985):     at android.os.Parcel.readException(Parcel.java:1276)
ERROR/AndroidRuntime(1985):     at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.
                                java:345)
ERROR/AndroidRuntime(1985):     at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:242)
ERROR/AndroidRuntime(1985):     at com.android.sample.NetworkExplorer.NetworkExplorer.onStart(NetworkExplorer.java:29)
ERROR/AndroidRuntime(1985):     at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129)
ERROR/AndroidRuntime(1985):     at android.app.Activity.performStart(Activity.java:3791)
ERROR/AndroidRuntime(1985):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1595)
ERROR/AndroidRuntime(1985):     ... 11 more

从下而上来分析上面这段错误信息,可以看出NetworkExplorer activity的onStart()函数执行NetworkExplorer.java的29行代码时引发的crash,对比源码看出,29行正好是ConnectivityManager的getActiveNetworkInfo( ),与错误信息的下一步提示完全一致。按照同样的思路往下推,最后到上面发现应用程序缺少网络访问权限:android.permission.ACCESS_NETWORK_STATE。于是在AndroidManifest.xml中为该sample程序添加网络访问权限,如下:

......(略)
    <application android:icon="@drawable/icon" 
       ......(略)
    </application>
    
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

再次执行程序,成功,输出如下信息:

android 判断弱网情况 android检测网络状态_android 判断弱网情况


分析emulator的网络连接如下:

1)、网络连接类型(type):主类型是mobile,子类型是UTMS(University Mobile Telecommunications System,3G);

2)、连接状态(state):CONNECTED/CONNECTED(已连接);

3)、采用该连接的原因(reason):simLoaded(已加载sim卡),目前本人尚未留意android上网络连接的选择原则,如:同时有Mobile和Wi-Fi,则选择何种连接方式;

4)、附加信息(extra):internet(可以使用IP network);

5)、是否漫游(roaming):false,不解释

6)、是否失败转接(failover):false,见前面ConnectivityManager职责说明中的第三点

7)、是否可用(isAvailable):true,不解释