看了一些android scoket的实例,觉得大部分的socket编程只是实现了一个客户端发送数据到服务器,基本上可以说建立完成。可是实际我们在应用中常见的应该是双向通信,那么这里我主要说一些在做客户端的细节问题,尤其对readline和3.0系统以上建立连接进行介绍。。
源码:http://download.csdn.net/detail/feiyangxiaomi/5573915
1、关于安卓高版本访问网络出 现.NetworkOnMainThreadException异常的问题 ,我们可以采用三种方式:
自从Android 2.3之后,系统增加了一个类:StrictMode。这个类对网络的访问方式进行了一定的改变。Android的官方文档给出了这个类设置的目的:StrictMode是一个系统提供的开发工具,用以检测在开发过程中因为偶然的事故从而造成的系统潜在的问题,进而提示开发者对其进行修复。StrictMode通常用于捕获磁盘访问或者网络访问中与主进程之间交互产生的问题,因为在主进程中,UI操作和一些动作的执行是最经常用到的,它们之间会产生一定的冲突问题。将磁盘访问和网络访问从主线程中剥离可以使磁盘或者网络的访问更加流畅,提升响应度和用户体验。
显然,大多数初学者在进行网络开发时,会选择将访问网络的代码直接放到主进程中,由于和主进程的首要工作——UI交互——相矛盾,因此,必须设置一定的检测机制,以保证系统运行的流畅,所有的异常都可以被检测。
① 使用 AsyncTask 不做介绍可参考点击打开链接
② 开启 Worker Thread(接下来我要采用的)
③ 在 onCreate 方法中加以下代码:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath()
.build());
这里我主要讲的是第二种方法:
直接上源码,回来我在具体说明:
package com.example.client;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@SuppressLint("HandlerLeak")
public class MainActivity extends Activity{
private static final String TAG = "MainActivity";
private Button mBtnRegister;
private TextView mTxtId;
private EditText mEtDevice;
private TextView mTxtIp;
private Button mBtnConnect;
private Socket socket=null; //使用socket连接
private BufferedReader in=null; //缓冲数据
private BufferedWriter out =null;
private ObjectInputStream socketin=null; //缓冲数据
private ObjectOutputStream socketout =null;
private Thread socketThread;
private Thread readlineThread;
private boolean socketIsRunning = true;
private boolean readIsRunning = false;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "-------------this is activity_main-------------");
mBtnRegister = (Button)findViewById(R.id.register);
mTxtId = (TextView)findViewById(R.id.id);
mEtDevice = (EditText)findViewById(R.id.device);
mTxtIp = (TextView)findViewById(R.id.ip);
mBtnConnect = (Button)findViewById(R.id.connect);
ClickListener c1 = new ClickListener();
mBtnConnect.setOnClickListener(c1);
mBtnRegister.setOnClickListener(c1);
/*************************************************************************
* handler 事件处理
*/
handler = new Handler(){
@Override
public void handleMessage(Message msg){
switch(msg.what){
case 1://接收回来的数据
Log.i(TAG, msg.toString());
Toast.makeText(MainActivity.this, "Data:"+msg.obj, Toast.LENGTH_LONG).show();
mTxtId.setText((String)msg.obj);
break;
case 2:
readIsRunning = true;
socketIsRunning = false;
readlineThread.start();
}
}
};
readlineThread = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
while(readIsRunning){
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(!socket.isInputShutdown()){
try {
Log.i(TAG, "read……");
String formServerStr=null;
char[] buffer = new char[10];
int count = 0;
if((count = in.read(buffer))>0)
{
Log.i(TAG, count+"");
Log.i(TAG, buffer[0]+"");
formServerStr = getInfoBuff(buffer, count);//消息换行
Message msg = new Message();
msg.what = 1;
msg.obj = formServerStr;
handler.sendMessageDelayed(msg, 100);
}
/***********
* 这块不能接收是个极大的问题 看手册 看readline 和 read区别
*
if((formServerStr = in.readLine())!=null){//通过直接读取,会造成阻塞
formServerStr+='\n';
Message msg = new Message();
msg.what = 1;
msg.obj = formServerStr;
Log.i(TAG, "success: "+formServerStr.toString());
handler.sendMessageDelayed(msg, 100);
}*/
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
});
socketThread = new Thread(new Runnable(){
public void run(){
while(socketIsRunning){
if(socket == null){
try{
socket = new Socket("10.10.100.254", 8899); //创建 socket
socket.setSoTimeout(3000); //防止阻塞
if(socket.isConnected()){
Log.i(TAG, "socket: is created "+socket.toString());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Message msg = new Message();
msg.what = 2;
handler.sendMessageDelayed(msg, 120);
}
}catch(Exception e){
e.printStackTrace();
Log.i(TAG, "error: "+e.toString());
}
}
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/*************************************************************************
* 内部监听类,监听OnClickListener事件
*/
class ClickListener implements OnClickListener{
public void onClick(View v) {
// TODO Auto-generated method stub
if(v == mBtnConnect){
Log.i(TAG, "connectButton is pressed");
if(socketIsRunning == true){
socketThread.start();
}else{
Toast.makeText(MainActivity.this, "socket已经连接", Toast.LENGTH_LONG).show();
}
}
/*********************
* register device
*/
if(v == mBtnRegister){
Log.i(TAG, "register is pressed");
}
}
}
private String getInfoBuff(char[] buff, int count)
{
char[] temp = new char[count];
for(int i=0; i<count; i++)
{
temp[i] = buff[i];
}
return new String(temp);
}
}
上面程序用了两个线程实现创建socket和实现socket接收。用了一个handle消息处理机制,按键监听功能这里不做讲解。
2、主要集中的两个问题:
(1)为什么程序中应用in.readline()会阻塞住线程,并且不能读取到正确数据,使用read()则可以
BufferedReader.read(buffer)从这个缓冲区中读取字符,并将它们存储在字符数组buffer,偏移0开始,如果数据读取完毕,返回实际读的字符数目或-1(无数据)。
buffer = BufferedReader.readLine():则是返回读者缓冲区中的下一行。A行开始是由零个或多个字符为'\ n','\ r',“\ R\ N”等作为起始字符。该字符串不包括换行符序列。
在过程中我是用readline()造成了程序的阻塞。
(2)对与BufferedReader和BufferedWriter进行讲解
BufferedReader继承了java.io.Reader包装了一个阅读器和一个缓冲器,用于做数据接收或是文件读取缓冲,默认情况下可缓冲8192个字节:
BufferedReader buf = new BufferedReader(new FileReader("file.java"));
主要的Reader有
对于数据接收或是文件读取缓冲可通过下列方法,进行读出:
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
void |
close()
Closes this reader.
| ||||||||||
void |
mark(int markLimit)
Sets a mark position in this reader.
| ||||||||||
boolean |
markSupported()
Indicates whether this reader supports the
mark()
and
reset()
methods.
| ||||||||||
int |
read()
Reads a single character from this reader and returns it with the two higher-order bytes set to 0.
| ||||||||||
int |
read(char[] buffer, int offset, int length)
Reads at most
length
characters from this reader and stores them at
offset
in the character array
buffer .
| ||||||||||
String |
readLine()
Returns the next line of text available from this reader.
| ||||||||||
boolean |
ready()
Indicates whether this reader is ready to be read without blocking.
| ||||||||||
void |
reset()
Resets this reader's position to the last
mark()
location.
| ||||||||||
long |
skip(long byteCount)
Skips
byteCount
bytes in this stream.
|
BufferedWriter和BufferedReader是一个原理,不做介绍。