Android网络篇


http://blog.csdn.net/u011014707/article/details/46584347


        针对互联网设计的操作系统,网络编程、多媒体编程。基础框架构成了Android平台应用开发的三块柱石。本章围绕网络编程协议、网络编程接口、Web服务、XML解析、SIP、NFC、RIL等方面的知识。

        另外,在Android 4.0中,开始支持流量的监控,对企业应用也增强了支持,通过VpnService允许应用构建自己的VPN。

1.无线接入技术概述

        无线接入技术通常都隐藏在TCP/IP协议和平台框架层下。由硬件平台厂商开发完成,在一些封闭的移动系统中,甚至不向应用开发者提供可阅读的源代码。

        3GPP标准的状态信息浏览地址:http://www.3gpp.org/specs/specs.htm.。

        3GPP文档的下载地址:ftp://ftp.3gpp.rog/Spece/archive.

        GSM&&GPRS文档的下载地址:ftp://ftp.3gpp.org/Specs/archive.

        (1)GSM/EDGE

                 GPRS是GSM规范中实现的内容,目的是提供115kbps的分组数据业务,在实际环境中,GPRS的速率是14.4~43.2kbps,以分组交换来补充电路交换在数据业务上的不足。

                 EDGE是GPRS的进一步演进,主要是在GSM系统中采用了一种新的调制方法,即最先进的多时隙操作和8PSK调制技术,提供更为丰富的数据业务。

        (2)TD-SCDMA

                 TD-SCDMA标准主要由中国提出,由于商用方面的严重延迟,更多的是扮演过渡角色,目前,TD-SCDMA在商业上已开始向TD-LTE演进。

        (3)WCDMA

                 WCDMA主要由欧洲和日本提出。

        (4)CDMA2000

                 CDMA2000标准主要由美国提出。

        (5)LTE

                 LTE包括FDD-LTE和TDD-LTE两种类型。

        (6)WiFi

                 作为最主要的局域网技术之一。

        (7)WiMAX

                 作为Intel大力研发的广域网技术。

        (8)BT

                 作为最常用的个域网技术。在Android中,采用的蓝牙协议栈是Bluez,作为一个开源的蓝牙协议栈,Bluez提供了BT所有的常用功能。

                 启动BT的方法如下:

                         if(!mAdapter.isEnabled()){

                                 Intent intent =new Intent(BluetoothAdapter.ACTION_ERQUEST_ENABLE);

                                 startActivity(intent);

                        }

                 设置BT可发现状态的方法如下:

                         Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

                         intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 500);

                         startActivity(intent);

2.基础协议封装

        Android对网络编程提供了3种接口,即Java接口、Apache接口、Android接口,这些接口都离不开基础的网络协议。Android提供了对HTTP、SSL、Cookie、DHCP等协议的封装,并支持套接字编程,同时对URI也提供了支持,除此之外,针对移动终端的特别需要,Android还提供了链接管理器和WiFi管理器来增强对网络的支持。

        (1)HTTP协议

                在Android中,HTTP协议的实现主要体现在android.net.http和org.apache.http等包中。在android.net.http中,主要通过AndroidHttpClient来实现HTTP协议,AndroidHttpClient实际上是Apach的DefaultHttpClient的子类。AndroidHttpClient能够处理Cookie,但是在默认情况下无法维护Cookie。设置Cookie的方法如下:

                        context.setAttribute(ClientContext.COOKIE_STORE, cookiestore);

                不能直接创建AndroidHttpClient,但是通过其newInstance()方法可以获得AndroidHttpClient实例。AndroidHttpClient通常和HttpHost、HttpUriRequest、HttpContext、ResponseHandler一起发起HTTP请求及处理服务器响应。

                org.apache.http包在网络通信中已经使用的非常广泛。

        (2)SSL协议

                SSL和TSL均是由曾经的互联网巨头Netscape设计的,都是针对Web的网络安全协议。这两个协议的当前版本分别为SSL 3.0和TSL 1.0。常见的HTTPS连接就采用了SSL技术。SSL协议的实现与数字签名技术密切相关。

                在android.net.http包中,提供了SslCertificate和SslError来描述X509数字证书信息。在WebView中,通过getCerificate方法可以查看当前页面是否有用SSL证书,另外通过SslCertificate的saveState方法可以将证书信息保存在Bundle中,这有助于实现跨域的访问。

                在javax.net,ssl包中,通过HttpsURLConnection可以管理HTTP连接,通过SSKServer-Socket可以创建SSL套接字。下面是创建SSL套接字的一个示例:

                        SSLSocketFactory factory=HttpsURLConnetion,getDefaultSSLSocketFactory();

                        try{

                                SSLContext context=SSLContext.getInstance("TLS");

                                TrustManager[] tm=new TrustManager[]{new FullX509TrustManager()};

                                context.init(null, tm, new SecureRandom());

                                factory=context.getSocketFactory();

                        }

                        Socket mSocket=factory.createSocket();

                 org.apache.http.conn.ssl包中还提供了SSLSocketFactory等类来执行SSL套接字的创建等。

        (3)Cookie实现

                 Cookie是用于识别用户信息、进行Session跟踪而存储在用户本地终端的数据(通常经过加密)。顺便提一下,Cookie也是由Netscape发明的。
                 由于Cookie拥有自己的生命周期,可以存储用户信息,因此可能暴露用户隐私,使用Cookie具有一定风险。

                 在Android中,Cookie的管理主要位于WebView、java.net.、org.apache.http.cookie等包中。获得Cookie的方法如下:

                         DefaultHttpClient httoclient=new DefaultHttpClient();

                         HttpGet httpget = new HttpGet(http://www.163.com);

                         HttpResponse response=httpclient.execute(httpget);

                         HttpEntity entity=response.getEntity();

                         List<Cookie> cookie=httpclient.getCookieStore().getCookies();

                通过Cookie的getDomain、getPath、getValue、getName、getPorts、getComment、getCommentURL等方法可以获取Cookie的信息,另外,对于基于模拟器的开发,可以通过网络分析工具直接查看HTTP数据包的情况来分析Cookie。

                在WebView中,CookieManager可以用来设置、清除和获取Cookie。清空Cookie的方法如下:

                        CookieSyncManager。createInstance(getApplicationContext());

                        CookieManager.getInstance().removeAllCookie();

                在Android的默认浏览器中,Cookie的信息保存在data\data\com.android.browser\databases\目录下的webview.db中。

        (4)连接性管理

                在Android中,ConnectivityManager提供对网络如WIFI、BT、WIMAX、GPRS、UMTS的连接性管理。获得ConnectivityManager服务的方法如下:

                        ConnectivityManager cnManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

                通过ConectivityManager.getActivityNetworkInfo()可以获得接入方式,判断网络类型和当前状态等,示例如下:

                        NetworkInfo info=cnManager.getActiveNetworkInfo();

                        boolean isMobile=(info!=null && info.getType()==ConnectivityManager.TYPE_MOBILE);     //是否是蜂窝网络

                        boolean isConected=info.isConnented();        //是否已连接

                        boolean isRoaming=isMobile && TelephonyManager.getDefualt().isNetworkRoaming();      //是否漫游

                连接性管理要求应用的权限为android.permission.ACCESS_NETWORK_STATE。当网络发生变化时,系统会广播Action为android.net.conn.CONNECTIVITY_CHANGE的Intent消息。下面是处理连接变化的示例:

                        mNetworkStateIntentReceiver = new BroadcastReceiver(){

                                public void onReceive(Context context, Intent intent){

                                        if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_AVTION)){

                                                NetworkInfo info=intent.getParcelableExtra(ConectivityManager.EXTRA_NETWORK_INFO);

                                                String typeName=info.getTypeName();

                                                String subtypeName=info.getSubtypeName();

                                                sendNetworkType(typeName.toLowerCase(), (subtypeName!=null ?subtypeName.toLowerCase() : ""));

                                                onNetworkToggle(info.isAvailable());

                                       }

                                 }

                          };

        (5)WiFi管理器

                作为最常用的无线局域网络,WiFi几乎成为当前移动终端的标配。获取WifiManager服务的方法如下:

                        WifiManager wifiManager=(WifiManager)getSystemService(WIFI_SERVICE);

                通过WifiManager可以获得设备可用网络的列表、获得当前激活网络的信息、执行热点烧苗、测定信号强度等。启动热点扫描的方法startScan()。

                WiFI的信息,如BSSID、RSSI、SSID、网络ID、MAC地址等,由WifiInfo描述。通过WifiManager可以获得WifiInfo对象,方法如下:

                        WifiInfo wifiinfo=wifiManager.gerConnectionInfo();

                支持WiFi点对点的通信(不经过网络和热点),获得WifiP2pManager服务的方法如下:

                        WifiP2pManager wifiP2pManager=(WifiP2pManager)getSystemService(WIFI_P2P_SERVICE);

                为了进行点对点通信,需要经过如下几个步骤:通过initialize()初始化P2P连接。        通过discoverPeers()发现附近的设备。        通过connect启动P2P连接。

3.Java网络编程接口

        Java网络通信有两种实现方式,一种是基于套接字的方式,另一种是基于HTTP的方式。本节只要介绍基于HTTP的实现,另外对底层NIO技术做简单的介绍。

        (1)HTTP网络通道

                在基于HTTP的实现中,最重要的一个类为HttpURLConnection,通过HttpURLConnection可以发送和接收数据。另外,通过HttpURLConnection还可以设置请求参数,如内容类型、回话Cookie等。下面是通过HttpURLConnection实现网络通信的简单示例:

                        HttpURLConnection urlConn=new URL("http://www.android.com").openConnection();        //打开HTTP连接

                        urlConn.setDoOutput(true);        //设置请求参数时必须将其设置为true

                        urlConn.setRequestMethod("POST");

                        urlConn.setUseCache(false);

                        urlConn.setRequestProperty("Content-type". "application/x-www-form-urlencoded");

                        DataOutputStream dos=new DataOutputStream(urlConn.getOutputStream());

                        dos.writeBytes("name="+URLEncoder.encode("chenmouren","gb2312");

                        dos.flush();

                        dos.close();

                        BufferReader reader=new BufferedReader(new InputStreamReader(urlConn, getInputStream()));

                        reader.readLine();

                        reader.close();

                        urlConn.disconnect();        //释放所有HTTP连接资源

                在将setDoOutput方法设置为true时,必须将HttpURLConnection设置为POST请求,而HttpURLConnection默认采用的时GET请求。所谓GET的请求是指请求参数附在URL后面直接传输,所谓POST请求表示请求参数位于HTTP的头部,POST请求的隐私性显然好些。

                对于HTTPS协议,可通过HttpsURLConnection建立HTTPS连接。系统会先尝试TSL和SSL2,当链接建立失败时,再尝试SSL 3。

                对HTTP请求返回错误相应时,会在HttpURLConnection的getInputStream方法中抛出IOException,通过getErrorStream可以获得出错相应。另外通过getHeaderFields方法可以读取HTTP头部。

                部分网络链接可能会导致重定向。下面是判断是否重定向的方法:

                        HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection();

                        try{

                                InputStream in=new BufferedInputStream(urlConnection.getInputStream());

                                if(!url.getHost().equals(urlConnection.getURL().getHost())){

                                        //已经重定向了

                                }finally{

                                        urlConnection.disconnect();

                                }

                        }

                另外,Cookie的管理是通过CookieManager和CookieHandler进行的。

        (2)NIO简介

                按照UNIX网络编程划分,I/O模型可以分为阻塞I/O、非阻塞I/O、I/O复用、信号驱动I/O、异步I/O。按照POSIX标准来划分,I/O模型值分为两类:同步I/O、异步I/O。

                在Java中,目前有3中I/O方式可供选择,即BIO(Blockding I/O)NIO(New I/O)、AIO(Asynchronous I/O)。其中AIO于J2SE 7中引入,有时也称为NIO 2。NIO和AIO均为非阻塞I/O。

                在J2SE1.4中,Sun引入了NIO,在进行Java应用向Android移植时,会出现一些Java NIO的兼容性问题,通常是NIO对IPv6的支持存在问题,会抛出java.net.SocketException:Bad address family异常。目前解决的办法是,通过setProperty(propObj, put, "java,net.preferIPv6Addresses", "false")来禁止IPv6,然后进行Selector.open()等操作。

                注意,在Linux中,Java NIO是基于管道实现的。

                NIO主要使用了Channel和Selector来实现,其基于事件驱动的单线程处理可以有效地节省系统开销,并降低线程死锁的概率。

                NIO作为一种中高负载的I/O模型,相对于传统BIO并发的效率大大提高。在Android中,其涉及的包有java.nio、java.nio.channels、java.nio.channels.spi、java.nio.channels.spi、java.nio.charset、java.nio.charset.spi。

                在实际的开发中,NIO通常用于本地数据复制和网络传输中。

4.Apache网络编程接口

        在Apache中,主要通过HttpClient执行HTTP通信,通过HttpResponse获得服务器响应。执行GET请求的示例如下:

                String httpUrl=http://www.android.com;

                HttpGet httpRequest =new HttpGet(httpUrl).    //GET请求

                try{

                        HttpClient httpclient=new DefaultHttpClient();

                        HttpResponse httpResponse = httpclient.execute(httpRequest);

                        if(httpResponse.getStatusLine().getStatueCode==HttpStatus.SC_OK){

                        ...

                        }

                }

        为了在POST请求中传递参数,需要将参数封装到NameValuePair中。POST请求的示例如下:

                String url =http://www.google.com;

                HttpPost request=new HttpPost(url);       //POST请求

                List<NameValuePair> params=new ArralList<NameValuePair>();

                params.add(new BasicNameValuePair("name", "zhangsan"));

                HttpEntity entity=new UrlEncodeForEntity(params, "gb2312");

                request.setEntity(entity);

                HttpClient client=new DefaultHttpClient();

                HttpResponse response=client.execute(request);

                if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK{

                        ...

                }

5.Android网络编程接口

         Android的网络编程接口是对Apache的再封装和简化。目前Android支持RTP、SIP、HTTP、WiFi、SSL、代理等网络协议。

        (1)HTTP通信

                Android的HTTP通信和Apche的不同点主要体现在HttpClient的实现上。Android的HTTP通信主要是通过AndroidHttpClient而非DefaultHttpClient和Apache的HttpGet、HttpPost、HttpResponse等来是现代额。通过AndroidHttpClient可以创建一个HttpClient;通过HttpGet可以获取URI等信息,自动处理服务器的重定向;通过HttpResponse可以处理服务器的相应。通过AndroidHttpClient来实现HTTP通信的示例如下:

                        try{

                                AndroidHttpClient client=AndroidHttpClient.newInstance(userAgent());

                                HttpGet httpGet=new HttpGet(http://www.163.com/);        //GET请求

                                HttpResponse response=client.execute(httpGet);

                                if(response.getStatusLine().getStatusCode()!=HttpStatue.SC_OK){

                                        //正常相应

                                }

                               client.Close();

                        }

                Android的POST请求的实现请参考Apache的POST请求实现。

        (2)RTP协议

                实时传输协议主要用于VOIP、push-to-talk、电话会议等场景中。

                android.net.rtp包主要包括AudioCodec、AudioGroup、AudioStream、RtpStream等类,其中AudioCodec用于定义语音编码集;AudioGroup是扬声器、麦克风和AudioStream的集合,它对CPU、宽带的要求较高,当有多个AudioGroup对象运行时,只有具有MODE_ON_HOLD状态的AudioGroup才能运行。AudioGroup要求应用具有android.permission.RECORD_AUDIO权限;RtpStream和AudioStream要求应用具有android.permission.INTERNET权限。发起SIP语音通信的示例如下:

                        mAudioStream=new AudioStream(InetAddress.getByName(getLocalIp()));        //构建语音流

                        public void startAudio(){

                                try{

                                        startAudioInternal();

                                }

                         }

                         private synchronized void startAudioInternal() throws UnknownHostException{

                                 if(mpeerSd==null){

                                         throw new IllegalStateException("mPeerSd=null");

                                 }

                                 stopCall(DONT_RELEASE_SOCKET);

                                 mInCall=true;

                                 SimpleSessionDescription offer=new SimpleSeesionDescription(mPeerSd);

                                 AudioStream stream=mAudioStream;

                                 AudioCodec codec=null;

                                 for(Media media : offer.getMedia()){

                                         if((codec==null)&&(media.getPort() > 0) && "audio".equals(media.getType()) && "RTP/AVP".equals(media.getProtocol())){

                                                 for(int type : media.getRtpPayloadType()){

                                                         codec=AudioCodec.getCodec(type, media.getRtpmap(type), media.getFmtp(type);        //获得编码

                                                         if(codec!=null){

                                                                 break;

                                                         }

                                                 }

                                                 if(codec!=null){

                                                         String address=media.getAddress();

                                                         if(address==null){

                                                                 address=offer.getAddress();

                                                         }

                                                         stream.associate(InetAddress.getByName(address), media.getPort());        //绑定远程端点

                                                         stream.getDtmfType(-1);        // 设置DTMF

                                                         stream.setCodec(codec);        //设置编码

                                                         for(int type : media.getRtpPayloadTypes()){

                                                                 String rtpmap=media.getRtpmap(type);

                                                                 if((type!=codec.type)&&(rtpmap!=null) &&(rtpmap.startWith("telephone-event")){

                                                                         stream.setDtmfType(type);

                                                                 }

                                                         }

                                                         if(mHold){

                                                                 stream.setMode(RtpStream.MODE_NORMAL);        //设置模式

                                                         }else if(media.getAttribute("recvonly")!=null){

                                                                 stream,setMode(RtpStream.NODE_SEND)ONLY);

                                                         }else if(media.getAttribute("sendonly")!=null){

                                                                  stream.setMode(RtpStream.MODE_RECEIVE_ONLY);

                                                         }else if(offer.getAttribute("recvonly")!=null){

                                                                  stream.setMode(RtpMode(RtpStream.MODE_SEND_ONLY);

                                                         }else if(offer.getAttribute("sendonly")!=null){

                                                                  stream.setMode(RtpStream.MODE_RECEIVE_ONLY);

                                                         }else{

                                                                  stream.setMode(RtpStream.MODE_NORMAL);

                                                         }

                                                         break;

                                                 }

                                         }

                                 }

                                 if(codec==null){

                                         throw new IllegalStateException("Reject SDP: no suitable codecs");

                                 }

                                 if(isWifiOn()){

                                         grabWifiHighPerfLock();

                                 }

                                 if(!Hold){

                                         ((AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE)).setMode(AudioManager.MODE_NORMAL);

                                 }

                                 AudioGroup audioGroup = getAudioGroup();

                                 if(mHold){

                                 }else{

                                         if(audioGroup==null)

                                                 audioGroup=new AudioGroup();

                                         stream.join(audioGroup);

                                 }

                                 setAudioGroupMode();

                         }

                         private void setAudioGroupMode(){

                                 AuidoGroup audioGroup=getAudioGroup();

                                 if(audioGroup!=null){

                                         if(mHold){

                                                 audioGroup.setMode(AudioGroup.MODE_ON_HOLD);

                                         }else if(mMuted){

                                                 audioGroup.setMode(AudioGroup.MODE_MUTED);

                                         }else if(isSpeakerOn()){

                                                 audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);

                                         }else{

                                                 audioGroup.setMode(AudioGroup.MODE_NORMAL);

                                         }

                                 }

                         }

6.Web服务实现

        在移动互联网快熟发展的今天,为用户提供有价值的服务成为行业创新的重要特征,开发具有联网能力的客户端成为从业者的一个基本功能。在客户端和服务器端的通信实现方面,目前主要的两种方法,即HTTP协议和分布式计算。HTTP协议无须多言,在分布式计算中,目前最主要的技术是Web服务。
        (1)Web服务概述

        Web服务时基于XML和HTTPS的一种分布式计算服务,其通信协议主要基于SOAP,服务通过WSDL描述,发现和获得服务元数据则通过UDDI来描述。

        调用Web服务的方式包括RPC、SOA、REST等。Web服务的安全可以通过Web-Security、SSL、数字证书等3中方式来实现,通过Web-Reliability可进行可信赖的Web服务间消息传递。

        目前Web服务的框架有Ajax2、metro、cxf等。

        (2)KSOAP2的实现

                KSOAP2是一个针对Android平台的轻量级的SOAP库,适用于资源受限的环境,不支持Web-Security。

                KSOAP2比较重要的类包括AndroidHttpTransport、AndroidServiceConnection、SoapEnvelope、SoapObject等。SoapEnvelope目前支持3个版本,即1.0、1.1、1.2。

                KSOAP2的下载地址为http://code.google.com/p/ksoap2-android/,最新的版本为2.5.7。

                为了实现通过KSOAP2的Web服务调用,通常需要经过如下几个步骤:

                (a)准备参数。

                (b)配置SoapObject。

                (c)SoapEnvelope调用。

                下面详细介绍Web服务调用过程。

                1)为了实现Web服务,需要配置URL、名字空间、方法名等,示例如下:

                        public static String NAMESPACE=http://core.mfsp.com;

                        public static String URL=http://ws.demo.com/axis2/services/mfsp;

                        public static String METHOD_NAME="usersService";

                2)配置SoapObject

                        传送给服务器的信息要作为属性封装在SoapObject中,SoapObject的本质为一个KvmSerializable序列化对象。创建SoapObject的示例如下:

                                SoapObject soapObject=new SoapObject(NAMESPACE, METHOD_NAME);

                        设置SoapObject属性的方法如下:

                                public SoapObject addProperty(String name, Object value);

                3)SoapEnvelope调用

                        单有SoapObject是不够的,还要把SoapObject封装到SoapEnvelope才能发送给服务器,示例如下:

                                String xmlStr =null;

                                HttpTransportSE ht=new HttpTransportSE(URL);

                                SoapSerializationEnvelope envolope=new SoapSerializationEnvelope(SoapEnvelope.VER11);        //设置版本

                                envelope.dotNet =false;

                                envelope.setOutputSoapObject(soapObject);

                                ht.call(null, envelope);        //发送给服务器

                                xmlStr = envelope.getResponse().toString();        //获得返回值

7.XML解析

        在Android中,系统提供了3中XML解析器,即Pull解析器、DOM解析器、SAX解析器,开发者可以根据使用的场景进行选择。Pull解析器用于处理简单的XML数据,DOM解析器和SAX解析器均可解析复杂的数据,其中DOM解析器是完全加载后才能解析,而SAX解析器可以随时加载随时解析。

        在具体的解析器选择上,DOM解析器适合处理文件不大(比如10KB以内的文件),嵌套的分支比较多,需要反复查询的场景;相比DOM解析器将XML文件整个加入到RAM中,SAX更适合用于网络中数据无法一次完成获取的场景,其好处是占用的内存少;Pull解析器适用于进行一些简单的XML加载,如Android选项菜单的配置文件等。假设XML字符串xmlStr的内容如下:

                <response>

                        <result>...</result>

                        <body>...</body>

                </response>

        (1)Pull解析器

                Pull解析器是最简单的XML解析器,其主要通过依次分析XML标签来进行解析,解析过程主要通过XmlPullParserFactory构建的XmlPullParser进行的,下面是Pull解析器的一个示例:

                        try{

                                XmlPullParserFactory factory=XmlPullParseFactory.newInstance();

                                XmlPullParser parser=factory.newPullParser();        //创建Pull解析器

                                parser.setInput(new ByteArrayInputStream(xmlStr.getBytes()), "UTF-8");        //设置输入流

                                int type =parser.getEventType();

                                if(type==XmlPullParser.START_COCUMENT){

                                        do{

                                                type=parser.next();

                                                if(type==XmlPullParser.START_TAG){

                                                        String name=parser.getName();

                                                        if(name.equals("result")){

                                                                ...

                                                        }else if(name.equals("body")){

                                                                ...

                                                        }

                                                        while(type!=XmlPullParser.END_DOCUMENT);

                                                }

                                        }

                               }

        (2)DOM解析器

                DOM解析的实现非常简单,首先通过DocumentBuilderFactory构建DocumentBuilder对象,然后利用DocumentBuilder对象即可开始解析工作,对结点的处理主要通过结点遍历来实现。DOM解析XML字符串的示例如下:

                        DocumentBuilderFactory dbfactory=DocumentBuilderFactory.newInstance();

                        try{

                                DocumentBuilder db=dbfactory.newDocumentBuilder();        //创建DocumentBuilder

                                InputStream in=ByteArrayInputStream(xmlStr.getBytes());

                                InputSource is=new InputSource(in);

                                Document dom=db.parse(is);

                                org.w3c.dom.Element docEle=dom.getDocumentElement();

                                NodeList ni=docEle.getElementsByTagName("response");        //根结点

                                for(int i=0; i < n1.getLength(); i++){

                                        Node item=n1.item(i);

                                        NodeList properties=item.getChildNodes();

                                       for(int j=0; j < properties.getLength(); j++){        //遍历子结点

                                               Node property=properties.item(j);

                                               String name=property.getNodeName();

                                               if(name.equals("result")){        //第一个子节点

                                                       result=property.getFirstChild().getNodeValue();

                                               }else if(name.equals("body")){        //第二个子节点

                                                       if(property.getFirstChild()!=null){

                                                       }

                                              }

                                       }

                               }

                               in.close();

                        }

                在Honeycomb中,getElementsByTagName()方法无法获得NodeList,即DOM解析器在Honeycomb中会因严重的Bug而无法使用。

        (3)SAX解析器

                SAX的解析更加简单,通过SAXParserFactory构建SAXParser解析器,然后通过XMLReader即实现XML的解析,示例如下:

                        RootElement root =new RootElement("response");

                        Element result=root.getChild("result");

                        result.setEndTextElementListener(new EndTextElementListener(){

                                public void end(String body){

                                        ...

                                }

                        });

                        Element body =root.getChild("body");

                        body.setEndTextElementListener(new EndTextElementListener(){

                                public void end(String body){

                                        ...

                                }

                       });

                       SAXParserFactory factory=SAXParserFactory.newInstance();

                       try{

                               SAXParser parser=factory.newSAXParser();

                               XMLReader xmlreader=parser.getXMLReader();

                               xmlreader.setContextHandler(root.getContentHandler());        //设置内容句柄

                               InputSource is=new InputSource(new StringReader(xmlStr());

                               try{

                                       xmlreader.parse(is);        //解析输入流

                               }

                         }

8.套接字编程

        通过套接字可以实现基于TCP/UDP协议的通信,关于套接字的原理,下面介绍Android的套接字用法。

        (1)基于TCP协议的套接字

                客户端发起通信前,必须知道服务器端的IP地址或域名,Java通过InetAddress来表示IP地址,Java同时支持IPv4和IPv6两种协议。

                1)服务器端

                        服务器端的套接字通过ServerSocket实现,通过ServerSocket可以直接绑定端口,监听该端口传来的消息。

                        通过ServerSocket的accept方法可为传入的连接请求创建Socket实例,并将已成功连接的Socket实例返回给服务器套接字。如果没有连接请求,accept方法将阻塞等待。

                        通过Socket的getInputStream方法可获得输入流,查看传入的消息;通过Socket的getOutputStream方法可以相应客户端,实例如下:

                                ServerSocket aServerSocket=null;

                                Socket aSessionSocket=null;

                                DataInputStream aDataInput=null;

                                DataOutputStream aDataOutput=null;

                                try{

                                        aServerSocket=new ServerSocket(8000);        //监听8888端口

                                }catch(Exception e){

                                        e.printStackTrace();

                                }

                                while(true){

                                        try{

                                                aSessionSocket=aServerSocket.accept();        //有请求接入

                                                aDataInput=new DataInputStream(aSessionSocket.getInputStream());

                                                String msg=aDataInput.readUTF();        //读取消息

                                                aDataOutput=new DataOutputStream(aSessionSocket.getOutputStream());        //相应请求

                                                aDataOutput.write("ok");        //返回OK

                                        }catch(Exception e){

                                                e.printStackTrace();

                                        }

                                        aDataInput.close();

                                        aDataOutput.close();

                                        aSessionSocket.close();

                                }

                2)客户端

                        在客户端,可以设置代理、服务器域名、服务器IP地址、端口等信息。下面是客户端套接字实现示例:

                                Socket s=null;

                                DataOutputStream dout=null;

                                DataInputStream din=null;

                                try{

                                        s=new Socket(“your server ip", 8888);        //服务器的IP地址、端口

                                        dout=new DataOutputStream(s.getOutputStream());

                                        din=new DataInputStream(s.getInputStream());

                                        dout.writeUTF("reg");        //发送请求

                                        String msg=din.readUTF();        //读取相应消息

                                }catch(Exception e){

                                        e.printStackTrace();

                                }

                                dout.close();

                                din.close();

                                s.close();

        (2)基于UDP协议的套接字

                基于UDP协议的套接字主要是通过DatagramSocket进行的,DatagramSocket在通信的两端均适用。通过DatagramSocket可以绑定IP地址和端口等。

                DatagramSocket对象通过send方法发送消息,通过receive方法接收消息,通过connect方法建立和服务器的连接。

                1)服务器端

                        服务器端主要用来接收数据,方法如下:

                                try{

                                        DatagramSocket ds=new DatagramSocket(6000);

                                        byte[] buf=new byte[100];

                                        DatagramPacket dp=new DatagramPacket(buf, 100);

                                        ds.receive(dp);

                                        ds.close();

                                }catch(Exception ex){

                                        ex.ptintStackTrace();

                                }

                (2)客户端

                        客户端主要用来发送数据,示例如下:

                                try{

                                        DatagramSocket ds=new DatagramSocket();

                                        String str="this is lisi";

                                        DatagramPacket dp=new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("localhost"), 6000);

                                        ds.dend(dp);

                                        ds.close();

                               }catch(Exception ex){

                                       ex.printStackTrace();

                               }

9.Web应用实现

        在Android中,除了浏览器外,Google还提供了WebView控件来支持基于网页和本地应用的混合实现,这混合实现和基于Web服务的本地应用有着本质的不同,其在开发上更侧重于网页脚本语言和Java语言的混合。WebView的实现:

               

        WebView和Android浏览器一样均是基于Webkit渲染引擎的,在默认情况下,Webkit渲染引擎的部分功能被关闭,宽泛地讲,WebView是Android浏览器的子集。WebView要求应用具有android.permission.INTERNET权限。

        另外还提供了WebDriver用于更方便地测试基于WebView的应用

        (1)加载本地数据

                WebView支持本地HTML数据的加载,但在加载时需要指明数据的MIME类型和采用的编码方式,方法如下:

                        final String mimetype="text/html";

                        final String encoding="utf-8";

                        wv=(WebView)findViewById(R.id.wv1);

                        String data="<a href='x'>Hello Word! - 1</a>";

                        wv.loadData(data, mimeType, encoding);

        (2)自定义界面呈现

                通过设置WebChromeClient客户端,WebView可以影响网页的加载过程,如加载进度、JS对话框、上传文件、文字选中等,示例如下:

                        wv.setWebChromeClient(new WebChromeClient(){

                                ...

                        });

                要监听页面加载的进度,可以在onProgressChanged(WebView view, int newProgress)方法中进行,另外,在WebView中无法弹出JS对话框,需要开发者自行在本地应用中进行处理,下面是监听进度变化的方法:

                wv.setWebChromeClient(new WevChromeClient(){

                        public void onProgressChanges(WebView view, int progress){

                                setProgress(progress*1000);        //调用Activity的setProgress方法

                        }

                });

        (3)自定义渲染过程

                通过设置WebViewClient客户端,WebView可以影响网页内容的渲染过程,示例如下:

                       wv.setWebViewClient(new WebViewClient(){

                               ...

                        });

                要在页面加载前进行自定义的处理,可以在onLoadResource(WebView view, String url)方法中进行;要在页面开始加载时进行自定义的处理,可以在onPageStarted(WebView view, String url, Bitmap favicon)方法中进行;要在页面加载结束时进行自定义处理,可以在onPageFinished(WebView view,String url)方法中进行,要覆盖默认的按键处理过程,可以在shouldOverrideKeyEvent(WebView view, KeyEvent event)方法中进行,下面是一个示例:

                        wv.setWebViewClient(new WebViewClient(){

                                public void onPageStarted(WebView view, Sting url, Bitmap favicon){

                                        if(url.equals(....)){

                                                showDialog(DIALOG_WAIT);

                                        }

                                }

                                public void onPageFinished(WebView view, String url){

                                        if(url.equals(...)){

                                                dismissDialog(DIALOG_WAIT);

                                        }

                                 }

                                 public boolean shouldOverrideUrlLoading(WebView view, String url){

                                         view.loadUrl(url);

                                         return true;

                                 }

                         });

                另外,WebViewClient还支持对网页错误、鉴权等方面的处理。

        (4)WebView设置

                WebView的设置是通过WebSettings来实现的,方法如下:

                        public WebSettings getSettings()

                通过WebSettings可以设置或获取WebView的多种设置,如cache、数据库、字体及字体大小、编码、界面缩放、JS、插件、渲染速度等。下面仅介绍常用的几种WebView引擎设置:

                        wv.getSettings().sePluginState(PluginState.ON_DEMAND)        //插件

                        wv.getSettings().setJavaScriptEnable(true)        //JSP

                        wv.getSettings().setSavePassword(true)        //密码

                        wv.getSettings().setCacheMode(WebSettings.LOAD_NORMAL);        //cache模式

                WebView的cache模式包括LOAD_DEFAULT、LOAD_NORMAL、LOAD_CACHE_ELSE_NETWORK、LOAD_NO_CACHE、LOAD_CACHE_ONLY。

                通过WebSettings还可以设置与UI相关的配置,常见的几种配置如下:

                        wv.getSettings().setBuiltInZoomControls(true)        //缩放

                        wv.getSettings().setLightTouchEnable(true)        //触控事件

                        wv.getSettings().setDefaultZoon(WebSettings.ZoomDensity.NEDIUM)         //缩放密度

                其中和本地应用一样,WebView支持多种密度,其中ZoomDensity值包括FAR、DEDIUM、CLOSE,当然也可以自定义密度值。

        (5)与JSP交互

                考虑到越来越多的网页以JSP实现,Android还对Java和JSP之间的交互提供支持。为了支持JSP加载,必须进行如下设置:

                        wv.getSettings().setJavaScriptEnable(true);

                通过WebView可以将本地对象和JSP对象绑定,方法如下:

                        public void addJavascriptInterface(Object obj, String interfaceName)

                上述代码中obj为java对象,interfaceName为JSP中的对象。需要注意,绑定Java实现需另外调动线程来运行。

        (6)Cookie的管理

                WebView同样支持Cookie的管理,但是要注意,在进行下载等业务时,有时需要WebView与AndroidHttpClient配合使用。

                Cookie主要指为辨别用户身份,进行Session跟踪而储存在用户终端上的数据。

                Cookie以<key,value>键值对的形式存在,Cookie在生成时会被指定一个期限值,即Cookie的生命周期,当Cookie的生命周期结束时,Cookie即被自动清除。通过抓包工具可以分析报文中Cookie的信息。

                在Android中,Cookie的管理是通过CookieSyncManager来实现的,通常为了获得优异的性能,在RAM中保存Cookie,但是由于某种需要,通过CookieSyncManager可以将Cookie保存到永久存储空间中。

                CookieSyncManager在运行期间以单子模式出现,一单创建了CookieSyncManager,系统将单独启动一个线程来执行Cookie在RAM和永久存储空间之间的同步,系统会通过一个定时器没5min执行一次同步操作。

                创建CookieSyncManager实例的方法如下:

                        CookieSyncManager.createInstance(this);

                执行Cookie在RAM和永久存储之间的同步的方法如下:

                        CookieSyncManager.getInstance().startSync();

                        CookieSyncManager.getInstance().stopSync();

                上述代码中,startSync方法和stopSync方法均用于单次同步,通常在驻留应用的Activity的onResume方法中执行startSync方法,在Activity的onPause方法中执行stopSync方法。希望利用实时进行Cookie同步而非等待定时器时,可用如下方法实现:

                         CookieSyncManager.getInstance().sync();

                需要注意,sync方法是异步执行的,并不受UI线程的影响,通常在WebViewClient中调用,调用方法如下:

                        public void onPageFinished(WebView view, String url);

                对于上层应用而言,通过CookieSyncManager并不能直接管理Cookie,通常Cookie的管理是通过CookieManager来实现的。CookieManager在系统中也是以单子模式的方法运行的。创建CookieManager实例的方法如下:

                        CookieManager cookieManager=CookieManager.gerInstance();

                CookieManager对Cookie的管理本质上是对CookieSyncManager的封装,因此在管理Cookie时,必须创建CookieSyncManager示例。获取或设置Cookie的方法如下:

                        cookieManager.setCookie(url, value)        //设置Cookie

                        cookieManager.getCookie(url)                  //获取Cookie

                判断是否有Cookie存在的方法如下:

                        public synchronized booleam hasCookies()

                是否激活Cookie的方法如下:

                        public synchronized void setAcceptCookie(boolean accept)

                根据场景的不同,有时需要移除Cookie,相应的CookieManager的方法如下:

                        public void removeSeddionCookie()        //移除Session的Cookie

                        public void removeAllCookie()              //移除所有的Cookie

                        public void removeExpiredCookie()        //移除到期的Cookie

        (7)目标环境的适配

                考虑到Android设备的屏幕复杂多变,Google在WebView中同样支持密度的变化。WebView开始支持DOM、CSS和元标签等属性,以帮助开发者适配密度的变化。应用这些属性的方法如下:

                        对于DOM,相关的属性为window.devicePixelRatio,其值为1.0(MDPI)、1.5(HDPI)、0.75(LDPI)等。

                        对于CSS,相关的属性为-webkit-device-pixel-ratio,其值同DOM,下面是一个相关的示例:

                                <link rel=”stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" />

                        对于viewport的元标签,其相关的属性为target-densitydpi,其值为device-dpi(使用设备的原生DPI作为目标DPI)、high-dpi、medium-dpi、low-dpi等,下面是一个相关的示例:

                                <meta name="viewport" content="target-densitydpi=device-dpi" />

10.SIP服务

        SIP(Session Initiation Protocol)服务在Android 2.3中正式引用,能够支持VOIP。由于SIP和传统的通话不同,其通过的是PS域业务,因此本节将SIP服务归为多媒体部分进行介绍。

        不同于传统的H232,SIP的传输协议是基于IP协议的,在应用层基于XML,能够独立于底层传输协议TCP、UDP和SCIP(Stream Control Transmission Protocol),用于建立、修改和终止IP网上的双方和多方多媒体回话。SIP协议借鉴了HTTP、SMTP等协议,支持代理、重定向及登记定位用户等功能,支持用户移动。通过与RTP/RTCP、SDP、RTSP等协议及DNS配合,SIP支持语音、视频、数据、E-mail、状态、IM、聊天、游戏等。SIP协议支持TCP和UDP两种传输协议,由于SIP本身具有握手机制,因此首选UDP。

        SIP系统实际上有两部分:客户端和服务器。

        关于SIP协议栈的内容,可以参考RFC2543、RFC3261、RFC3262、RFC3263、RFC3264、RFC3265。

        使用SIP,要求应用具有android.permission.INTERNET和android.permission.USE_SIP权限。考虑到即时软件平台采用了Android2.3,在硬件配置上也可能会不完全支持SIP,所以在AndroidManifest.xml中还应声明android.hardware.sip.voip、android.hardware.wifi、android.hardware.microphone等。

        在Android中,SIP客户端的实现位于android.net.sip包中,主要的类包括SipManager、SipAudioCall、SipProfile、SipSession等。

        (1)SipManager

                SipManager用于初始化SIP连接和接入SIP服务等。通过SipManager能够创建SIP会话,比较常用的方法如下:

                        createSipSession()        //创建SIP回话

                        getCallID()                  //获得呼叫ID

                        makeAudioCall()   //发起语音呼叫

                        newInstance()       //创建一个SipManager示例

                        register()         //注册账号

                        takeAudioCall()       //接听电话

                        open()                           //打开SIP回话

                另外,支持SIP并不意味着一定支持VOIP,是否支持SIP协议栈和VOIP功能与硬件设备有关。在基于SIP发起语音呼叫前,还要判断是否支持SIP和语音呼叫,方法分别为isVoipSupported()和isApiSupported()。

        (2)SipAudioCall

                 SipAudioCall用于处理基于SIP的网络语音呼叫,通过SipManager的makeAudioCall()方法和takeAudioCall方法客户获得SipAudioCall的实例。

                 需要注意的是,SipAudioCall要求应用具有android.permission.INTERNET和android.permission.USE_SIP权限。

                 另外SipAudioCall的startAudio方法要求应用具有android.permission.ACCESS_WIFI_STATE、android.permission.WAKE_LOCK、android.permission.RECORD_AUDIO权限。SipAudioCall的setSpeakerMode方法要求应具有android.permission.MODIFY_PHONE_STATE权限。

                 SipAudioCall的常用方法如下:

                         startAudio()                       //在一个已建立的SIP链接中发起通话

                         setSpeakerMode()            //设置通话模式

                         toggleMute()                      //进行静音模式

                         getPeerProfile()                 //获取对方的信息

                         endCall()                           //结束一个语音呼叫SIP链接

                         answerCall()                          //应答一个语音呼叫SIP链接

                         makeCall()                          //发起一个语音呼叫SIP链接

                         sendDtmf()                           //发送DTMF

                 另外,通过设置SipAudioCall.Listener监听器可以监听到SIP链接建立和结束的消息。在SIP语音呼叫链接建立后,就可以传输RTP流了。在RFC3551中对SDP采用的RTP流进行明确的定义,其语音流为AMR格式。

        (3)SipProfile

                 SipProfile包含了一个SIP账户的账户名、密码、端口、SIP域和SIP服务器地址等信息。

                 通过SipProfile.Builder可以构建一个SipProfile实例,也可以从SipSession中获取本地和对方的SipProfile实例SipProfile的常用方法如下:

                         getPassword()       //获得密码

                         getUriString()                   //获得URI

                         getSipDomain()           //获得SIP域

                         getDisplayName()       //获得显示名

                         getUserName()        //获得账户名

        (4)SipSession

                 SipSession表示一个SIP会话,通过SipManager的createSipSession方法可以获得发起呼叫时的SIP会话,通过SipManager的getSessionFor方法可以获得收到呼叫时的SIP会话。

                 通过SipSession.Listener监听器可以监听到会话的信息,如呼叫忙、呼叫结束、呼叫建立、注册请求、铃声等。

                 通过SipSession.State监听器可以监听到会话的状态,SIP会话的状态目前有DEREGISTERING、INCOMING_CALL、INCOMING_CALL_ANSWERING、IN_CALL、NOT_DEFINED、OUTGOING_CALL、OUTGOING_CALL_ANCELING、OUTGOING_CALL_RING_BACK、PINGING、READ_TO_CALL、REGISTERING等。

                 SipSession的常用方法如下:

                         getPeerProfile()        //获取对方的信息

                         setListener()         //设置SipSession.Listener监听器

                         register()        //注册到SIP服务器

                在框架层,SIP协议栈的具体实现位于frameworks\base\voip中,和其他服务一样,在框架上采用的同样是C/S架构,其架构服务为SipService。

11.NFC通信

        作为一种超短距无线接入技术,其实际通信距离小于4cm,工作频率为13.56MHz,传输速率为106~848bps,通常用于移动支持等场景中。

        NFC的实现位于包android.nfc中。NFC支持的数据格式为Ndef。

        在Android 4.0中,Google为NFC增加了Android Beam功能,支持从一个设备向另一个设备传送Ndef消息,Ndef消息封装在NdefMessage中。

               

        要开发NFC相关的应用,需拥有权限android.permission.NFC,当然最重要的NFC的应用时移动终端支持NFC功能。设计在安装应用时判断硬件设备是否有NFC功能的方法如下:

                <uses-feature android:name="android.hardware.nfc" android:required="true"/>

        为了传送Ndef消息,Android提供了两种模式:一种是NfcAdapter的SetNdefPushMessage方法,根据用户的需要随时传送:一种是在Android Bean初始化时通过设置NfcAdapter.CreateNdefMessageCallback、传送。要了解Ndef消息是否传送完成,可以通过设置NfcAdapter.OnNdefPushCompleteCallback来监听,如果希望应用能够处理Ndef消息,那么需实现的Intent过滤器如下:

                <intent-filter>

                        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>

                        <data android:mimeType="mime/type“/>

               </intent-filter>

               <intent-filter>

                       <action android:name="android.nfc.action.TECH_DISCOVERED"/>

                       <meta-date android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter.xml"/>

               </intent-filter>

               <intent-filter>

                       <action android:name="android.nfc.action.TAG_DISCOVERED"/>

               </intent-filter>

        注意,在Android2.3中,仅支持基于android.nfc.action.TAG_DISCOVERED的过滤器。

12.RIL层处理

        RIL层是对底层蜂窝接入技术的封装,是Android和协议栈通信的接口。它提供了语音、数据、短信、SIM、卡管理以及STK应用等功能,其实现的接口遵循无线协议规范。

        在Android中,RIL层的代码主要位于hardware\ril中,包含6个子目录:include、libril、mock-ril、reference-cdma-sms、reference-ril、rild。

        (1)RIL框架

                从通信的角度讲,RIL框架上大致可以分为应用层。框架层、Linux用户控件层、Linux内核层、硬件层等,RIL层的内容主要分布在框架层和Linux用户空间层。从某种意义上讲,RIL层是通话服务(Telephony Service)和基带硬件中间的抽象层。RIL层在通信框架中的位置如下图所示:

                        

                1)框架层

                        框架层主要与RIL守护进程进行通信,其上层是与应用层通信的电话服务,其和RIL守护进程间的通信通过LocalSocket进行。

                        在RIL.java中,RIL类会维护一个RILRequest队列mRequestsList。通过RILSender发送RILRequest请求时,RIL会将请求加入mRequestList队列中,将待决请求计数加1;当RILReceiver中收到处理后的响应,会将待决请求计数减1。整个处理过程是以基于异步的方式进行的。RIL本质上是一个AT命令的实现。

                        向RIL守护进程发送RILRequest的过程如下:

                                private void send(RTLRequest rr){

                                        Message msg;

                                        if(mSocket==null){

                                                rr.onError(RADIO_NOT_AVAILABLE, null);

                                                rr.release();

                                                return;

                                        }

                                        msg=mSender.obtainMessage(EVENT_SEND, rr);

                                        acquireWakeLock();

                                        msg.sendToTarget();

                                }

                        接收到RIL守护进程返回的结果后进行如下处理:

                                private void processReponse(Paecel p){

                                        int type;

                                        type=p.readInt();        //数据传输方式为Parcel

                                        if(type==RESPONSE_UNSOLICITED){

                                                processUnsolicited(p);

                                        }else if(type==RESPONSE_SOLICITED){

                                                processSolicited(p);

                                        }

                                        releaseWakeLockIfDone();

                                }

                        从以上代码可以看出,相应类型分为RESPONSE_UNSOLICITED和RESPONSE_SOLICITED两种。其中RESPONSE_UNSOLICITED将会有BaseCommands中的助手对象直接处理,处理的内容主要是蜂窝模块主动上报的信号强度、基站信息等,共31种相应,这些相应在ril_unsol_commands.h中定义;RESPONSE_SOLICITED则通过RILReciver的异步通知机制传递命令的发送者并进行相应处理,如拨号等主动请求的动作。

                        RILRequest的请求共113种,这些请求在ril_commands.h中定义。另外,在SIMRecords.java中记录着系统支持的运营商信息,其MCC和MNC信息记录在MCCMNC_CODES_HAVING_3DIGITS_MNC数组中。

                2)RIL守护进程

                        RIL守护进程和框架层进行的通信是通过套接字实现的。套接字通信主要是通过RIL_register中调用套接字助手函数android_get_control_socket来实现的,具体的实现如下:

                                s_fdListen=android_get_control_socket(SOCKET_NAME_RIL);

                                if(s_fdListen<0){

                                        exit(-1);

                                }

                                ret=listen(s_fdListen, 4);

                                if(ret<0){

                                        exit(-1);

                                }

                        RIL守护进程对套接字的事件处理如下:

                                ril_event_set(&s_listen_event, s_fsListen, false, listenCallback, null);

                                rilEventAddWakeup(&s_listen_event);

                        在Init.goldfish.sh等配置脚本中,厂商可以根据实际情况对RIL进行配置,这主要是通过设置ro.radio.noril环境变量来实现的。如果ro.radio.noril环境变量设置为yes,表示设备不支持蜂窝通信,自然RIL守护进程不会启动。

                        RIL守护进程在启动时会先查找相应的rild.lib路径和rild.libargs系统属性来确定要用的VENDOR RIL及要提供给VENDOR RIL的初始化参数;然后加载相应的VENDOR RIL,即librefrence_ril.so并启动线程打开时间循环和监听事件,接着初始化VENDOR RIL,这是直接与Modem通信的部分;最后调用RIL_register在协议栈上注册并打开接收上层命令的套接字通道。

                        在Android中,通过RIL_startEventLoop打开时间队列,其中事件队列的实现采用了I/O多路转换技术,即先构造一个有关I/O描述符的列表,然后调用select函数,当I/O描述符列表中的一个描述符准备好进行读或写时,该函数返回,并告知可以读或写哪个描述符。通过这种方法既避免了阻塞I/O的阻塞问题又避免了非阻塞I/O对CPU时钟的浪费问题。

                        RIL_Init()是移植过程中必须实现的函数。VENDOR RIL通过AT命令通道和包数据通道来与基带进行通信,这也是直接和硬件打交道的地方。

        (2)TIL移植

                在Android源代码中,给出了一个VENDOR RIL的参考实现,即reference-ril.c,并最终将其编译成librefrence.so共享库。reference-ril.c负责与硬件的通信,将来自上层的命令转换为AT指令,然后传递给硬件。根据硬件厂商的不同,OEM厂商需要修改reference-ril.c。下面是AT命令的一个实现:

                        static void onRadioPowerOn()

                        {

                                #ifdef USE_TI_COMMANDS

                                at_send_command("AT%CPHS=1", NULL);

                                at_send_command("AT%CTZV=1",NULL);

                                #endif

                                pollSIMState(NULL);

                         }

                AT命令后的字母和数字表示具体的功能,其具体含义在3GPP协议栈中有定义。

                VENDOR RIL总体上包括reference-ril.c、archannel.c、misc.c、at_tok.c等。

                需要注意的是,如果定义了RIL_SHLIB,那么VENDOR RIL会被编译为共享库,或直接被编入RIL守护进程。

13.报文分析

        在进行网络编程时,经常会需要判断发送的报文是否正确,发送的时间是否恰当。这些可通过报文分析实现。业界已有开源的抓包工具可用,Wireshark就是一个网络抓包分析工具。

        Wiresshark可以用来分析ARP、TCP、UDP、IP、HTTP等数据包的信息。通过Capture菜单的Options选项可以设置捕获过滤器,如过滤协议、网卡等。在基于网络的应用开发时,要确认数据是否正确,即可使用Wireshark。


posted @ 2016-10-19 16:49  张同光  阅读(572)  评论(0编辑  收藏  举报