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。

Android网络篇相关推荐

  1. android 网络篇简单介绍

    1 简介 本文简单介绍android 开发中常用的webview .url. volley. json解析等网络工具.由于篇幅问题,这里只做简单介绍并不做详解. 2 WebView的用法 2.1 简单 ...

  2. ym—— Android网络框架Volley(终极篇)

    转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103).谢谢支持! 没看使用过Volley的同学能够,先看看 Android网络框架Volley(体验篇) ...

  3. Android面试题网络篇

    Android面试题网络篇,由本人整理汇总,后续将继续推出系列篇,如果喜欢请持续关注和推荐. 系列文章目录: Android面试题View篇 Android面试题进程篇 Android面试题线程篇 T ...

  4. 腾讯T3亲自教你!2021年Android网络编程总结篇,最强技术实现

    前言 Android常用知识体系是什么鬼?所谓常用知识体系,就是指对项目中重复使用率较高的功能点进行梳理.注意哦,不是Android知识体系. 古语道:学而不思则罔,思而不学则殆.如果将做项目类比为& ...

  5. Android网络编程系列 一 Socket抽象层

    在<Android网络编程>系列文章中,前面已经将Java的通信底层大致的描述了,在我们了解了TCP/IP通信族架构及其原理,接下来我们就开始来了解基于tcp/ip协议层的Socket抽象 ...

  6. Android网络之数据解析----SAX方式解析XML数据

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  7. Android中级篇之百度地图SDK v3.5.0-一步一步带你仿各大主流APP地图定位移动选址功能

    from: http://blog.csdn.net/y1scp/article/details/49095729 定位+移动选址 百学须先立志-学前须知: 我们经常在各大主流APP上要求被写上地址, ...

  8. Android - 网络基础

    Android网络编程(一)HTTP协议原理 Android网络请求心路历程 HttpURLConnection和HttpClient对比: http://blog.csdn.net/guolin_b ...

  9. android 怎么调用js项目_APP逆向神器之Frida【Android初级篇】

    说到逆向APP,很多人首先想到的都是反编译,但是单看反编译出来的代码很难得知某个函数在被调用时所传入的参数和它返回的值,极大地增加了逆向时的复杂度,有没有什么办法可以方便地知道被传入的参数和返回值呢? ...

最新文章

  1. RHEL在戴尔系统上p1p1 ......命名规则
  2. @ImportResource
  3. 数据库管理工具 Navicat使用教程:导航窗格提示和技巧 - 管理连接
  4. nanopc t3开发板系统烧录_基础教程18 定制 Arduino 系统
  5. 解决FireFox(火狐浏览器)占用资…
  6. 三大国产操作系统,到底哪个最好用
  7. 泛微 linux mobile手册,泛微E-Mobile5.0服务端安装手册.doc
  8. 对话腾讯17级员工张正友博士:有关梦想、成长和焦虑
  9. 项目经理不得不知道的里程碑计划及其重要用途
  10. Aerial Cactus Identification(空中仙人掌鉴定)
  11. word背景图片设置a4纸大小教程
  12. mybatis一级缓存,二级缓存的开启、关闭、清除及使用说明
  13. css3书页卷角使用教程,css - 这个卷角的效果怎么做?
  14. 在京东工作是一种什么体验
  15. 使用Python+Appuim 清理微信
  16. python怎么输入三引号_python三引号如何输入
  17. 面试:View加载流程setContentView
  18. JVM中引用计数法与可达性分析
  19. 高级Spring之Scope 详解
  20. BZOJ 1193 HNOI2006 马步距离

热门文章

  1. JavaWeb通过http下载多个文件,打包下载,压缩下载
  2. MySQL - 电商网站开发数据表结构设计(表结构,表关系,索引,时间戳)
  3. Macbook 商店下载xcode点击无反应不下载
  4. 报错Can‘t pickle <function <lambda> at……
  5. 计算机取证工具应用-数据恢复(实验部分)
  6. 台湾电子供应商技嘉被勒索软件攻击
  7. Jmeter访问需要登录的接口如何处理
  8. Python课 #06号作业
  9. STM32L0xx_HAL_Driver库的使用——UART续
  10. javascript原生—悬浮导航栏