这个系列的文章就是为了防止以后自己开发的时候忘了怎么回事,提醒自己用的……由于自己Android开发非常非常菜,所以贴上来的代码很有可能像一坨shi一样,如果有新手朋友看到,希望谨慎地作为参考,同时如果有神牛看到的话,非常希望能批评指正。
到现在为止做了两个安卓的app,都跟网络通讯有关,通过WIFI连接电脑上的服务器,然后他们之间互相交流一点数据。目前我会的最简单的实现方法是利用socket,它就像一个文件一样,连接好以后写/读它就可以了。
要注意的是安卓凡是涉及网络的事情,都不允许在主线程中完成,都必须单开一个线程做,否则会抛出异常,名字的意思就是“主线程中有网络操作”。
另外需要注意的是要开启网络权限,第二个app我调了半天收不到数据,最后猛然想起是没有开网络权限导致的…… 这个下面说。
一般我们都是用一个线程的run函数来做我们希望做的网络操作,就像下面这个登录Activity一样:
代码大部分其实没什么用,看几个关键点就可以了……
有一点这个代码里没有的,就是socket的定义,直接
Socket socket = null;
就可以了。
public class LoginActivity extends Activity
{
ImageButton btn;
EditText editText;
GetThread getThread; //这个是网络线程
Handler handler; //网络线程要是想更新UI,需要一个handler来转消息
THUClient the_app; //这个东西是为了使用全局变量用的,不用看,THUClient是个类名
class GetThread implements Runnable //一般线程用内部类写,然后implements Runnable
{
public void getMsg()
{
try
{
Scanner in = new Scanner(the_app.socket.getInputStream());//输入流包装一下
String gotmsg = in.nextLine();//就像读控制台和读文件一样读就可以了
Message msg = new Message();
msg.obj = gotmsg;//用msg的obj域搭载消息
LoginActivity.this.handler.sendMessage(msg);//这句话就能把msg扔给handler让他去处理了
} catch (UnknownHostException e)
{
Message msg = new Message();
msg.obj = "net error";
LoginActivity.this.handler.sendMessage(msg);
} catch (IOException e)
{
Message msg = new Message();
msg.obj = "net error";
LoginActivity.this.handler.sendMessage(msg);
}
}
@Override
public void run() //启动线程时,启动这个函数
{
try
{
the_app.socket = new Socket(); //实例化socket
the_app.socket.connect(new InetSocketAddress(the_app.ip, the_app.port) , 5000);//connect函数,为什么要这么写呢?因为这样可以设置超时时间,比如这里就是5000毫秒 = 5秒
} catch (UnknownHostException e)
{
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
} catch (IOException e) {
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
}//凡是这种try-catch语句块,都要处理好必要的异常,否则程序很容易崩出去(比如UnknownHostException不处理的话如果服务器没开就崩出去了),我的处理方法是遇到网络异常的时候,通知主线程弹一个Toast,告诉一下用户
if( the_app.socket != null)
getMsg();
else
{
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_login);
the_app = (THUClient) getApplicationContext();
btn = (ImageButton)findViewById(R.id.buttonId);
editText = (EditText) findViewById(R.id.editTextId);
Context ctx = LoginActivity.this;//这些是干别的事用的不用看
SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
final String lastIP = sp.getString("IP", "none");
if(! lastIP.equals("none") )
{
the_app.ip = lastIP;
editText.setText(lastIP);
}
final Editor editor = sp.edit();
handler = new Handler()//handler是这么用的
{
public void handleMessage(Message msg)
{
String words = (String)msg.obj;
if( words.equals("ok") == true )
{
Toast.makeText(LoginActivity.this, "来自服务器的问候:登陆成功!!欢迎使用~",Toast.LENGTH_LONG).show();
finish();
}
else if(words.equals("did not login") )
{
Toast.makeText(LoginActivity.this,"ip输错了或者是主机没有开", Toast.LENGTH_SHORT).show();
}
}
};
btn.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
the_app.ip = editText.getText().toString();
if(! lastIP.equals(the_app.ip))
{
editor.putString("IP",the_app.ip);
editor.commit();
}
getThread = new GetThread();//点击按钮,发起线程
new Thread(getThread).start();
}
});
}
}
总的来看,过程就是这样的:
1)用户点击按钮,发起网络线程;
2)run函数进入,socket连接;
3)连接正常的话,收数据,
4)告诉handler更新UI。
有时候我们可能希望和服务器进行交互,下面是一个例子:
public void run()
{
try
{
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
, true); // 用PrintWriter包装一下输出流
out.println("1 "+ nowFrom +" "+ nowTo);//这样就可以向socket输出
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));//刚才用Scanner,用BufferedReader也行
String ans = in.readLine();
if(ans != null)//有时候会读进来null,我也不知道怎么回事,总之这个判断可以帮你的程序变得更健壮
{
<span style="white-space:pre"> </span>Message msg = new Message();
msg.obj = ans;
AskInputActivity.this.handler.sendMessage(msg);
}
} catch (IOException e) {
Message msg = new Message();
msg.obj = "net error";
AskInputActivity.this.handler.sendMessage(msg);
}
}
}
服务器端就是典型的java/c++网络编程,跟这篇文章要讨论的就没关系了。
一点经验是网络线程能不一直跑着就不要一直跑着,如果是严格的“请求-响应”工作模式的话,最好每次要发起连接的时候发起一个网络线程,发完-收完数据就结束,不然想结束一个线程不是那么轻松的事情。
最后,想正常利用网络,你需要在manifest里面申请网络权限:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" /> //这一句
<application
android:name="com.zero.client.THUClient"
android:allowBackup="true"
android:icon="@drawable/icon_mod"
android:label="@string/app_name"
android:theme="@style/AppTheme" > …………
一定要有,不然上面的写了等于白写,而且可恶的是他不告诉你……
暂且先记录这么多吧……