Android 4.x版本_子线程socket编程小实例

这几天在做一个android平台的关于网络通信的项目,之前android学习的不是很多,java语法也一知半解,所以在学习的过程中遇到了很多迷惑,一点点解决起来也十分困难。

所以首先建议初学者最好是先认真的学习java语言,然后再接触安卓开发,这样学习安卓的速度有很大提升,而且对于扎实的学习很有帮助。


在安卓4.0版本以后,无法在主线程中访问网络。因为访问网络的话,如果因为一些网速原因或者手机处理器慢等等原因,造成了延迟大于五秒(好像是这个时间),那么主线程(activity)会造成系统警报,报错。所以安卓4.0以后的系统版本将socket访问网络规定为只能在 子线程中进行。


下面是一个服务器和手机客户端的交互实例。


功能:手机端接受一条来自服务器的string数据并且显示。


界面:测试用的demo,所以随便拖了一个textview和button进去,IDE上随便用鼠标拖了一下位置。

代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:text="Button" />

</RelativeLayout>

服务器端代码:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

public class SerMachine {
	//PORT 变量代表服务器端口
 private static final int PORT = 40001;
 public static void main(String[] args) {
	//socket类的实例化对象建立,必须放在try里面,这是规定
   try {
    // 实例化服务器套接字 设置端口号9999
   ServerSocket server = new ServerSocket(PORT);
    //服务器端的ServerSocket对象是不需要关闭的,如代码最后所示,只需要关闭流对象就可以了
   while (true) {
	   //socket中的阻塞不是由while(true)产生的,而是由accept产生的
    Socket socket = server.accept();
    //如果accept没有接受到客户端的接入请求的话,就会一直阻塞在这里
    //所以一个服务器面向多个客户端的时候,都是用多线程socket的
    //就是说为每一个来访的客户端配置一个子线程,每个线程用来负责一个客户端
    //客户端的所有请求都在对应的子线程中处理
    //对应的,服务器接受客户端发来的数据可以使用BufferedReader + InputStreamReader
    //此处是向客户端发送数据,所以用BufferedWriter + OutputStreamWriter
    //曾经我试过OutputStream writer=(OutputStream) socket.getOutputStream();
    //但是我没有调出来,经过鉴定还是上述的B+I和B+O的组合最好用,可行性强,比较简单
    //上面OutputStream = socket.getOutputStream这种写法是我看李刚老师的疯狂android看到的
    //我还试过printStream等等好多流,反正只有B+I+O成功了
    //下面就是获取输出流
    BufferedWriter writer = new BufferedWriter(
      new OutputStreamWriter(socket.getOutputStream()));
    //向客户端发送string数据
    writer.write("Messages come from SerMachine");
    //每次发送完数据都要刷新一下,据说这这样比较好
    writer.flush();
    //关闭流
    writer.close();
   }
   //抛出异常是必须的!必须的!必须的!
  } catch (UnknownHostException e){
	  // TODO Auto-generated catch block
	  e.printStackTrace();
  } catch (IOException e) {
	  // TODO Auto-generated catch block
	  e.printStackTrace();
  } 
 }
}

下面是客户端的代码:

package com.example.service_behind2;

import android.os.Bundle;
import android.app.Activity;
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.net.Socket; 
import java.net.UnknownHostException; 
import android.annotation.SuppressLint; 
import android.os.Handler; 
import android.os.Message; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.TextView; 
 
@SuppressLint("HandlerLeak")
//上面这句话可有可无,网上有说明,大家可以自行百度
public class MainActivity extends Activity {

	//先建立两个对象,用来获取xml中的控件
 private Button receive_button;
 private TextView MessageShow;
 //这个string用来保存从服务器接受来的信息
 private String message_from_sermachine;
 
 //HOST表示IP地址,这是我的电脑联网时候的IP地址
 //服务器也是运行在我的电脑上,所以IP地址就是这个
 private static final String HOST = "121.229.136.83"; 
 //PORT表示对应端口,必须和服务器的ServeSocket端口一样
 private static final int PORT = 40001; 
 //查看IP的方法是:开始-》运行-》cmd-》ipconfig-》ppp适配器-》Ipv4

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //通过R.id找到对应的xml控件
  receive_button = (Button) findViewById(R.id.button1);
  MessageShow = (TextView) findViewById(R.id.textView1);
  //给receive_button添加事件监听器
  receive_button.setOnClickListener(new ReceiverListener());
 }
 
 @SuppressLint("HandlerLeak")
 //上面这行代码同理
 class ReceiverListener implements OnClickListener {
	 //实现OnClickListener接口
  @Override
  public void onClick(View v) {
   // TODO Auto-generated method stub
   new Thread() {
	   //点击button后,会开一个新的线程!
    @Override
    public void run() {
    	//重写线程中的run方法
     try {
      // 实例化Socket对象
      Socket socket = new Socket(HOST, PORT);
      // 获得输入流
      //此处同理,请见服务器端代码注释
      BufferedReader br = new BufferedReader(
        new InputStreamReader(socket.getInputStream()));
      message_from_sermachine = br.readLine();
      
      //message_from_sermachine.getBytes("utf-8");
      //这行代码注释掉是因为有没有都一样
      //从服务器接受到的string,编码方式和android不一样
      //android用的是utf-8编码
      //所以造成了服务器传输中文汉字时候不能被客户端正确解码的现象
      //会显示一堆乱码,所以我想用getBytes();函数来解决编码问题
      //但是失败了,这个实例到现在还是没办法正确显示服务器发来的汉子
      //希望有办法的朋友教教我
      br.close();
      //关闭流
      //抛出异常是必须的!
     } catch (UnknownHostException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
     //接受好的数据已经保存在了message_from_sermachine变量里面
     //但是就像主线程无法访问网络,只能在子线程访问一样
     //更新UI只能在主线程中做,不能在子线程中控制前台UI控件
     //所以需要handle类来解决这个问题
     //下面调用handler对象的sendEmptyMessage方法
     //事实上传出的是空消息
     //但是服务器发送来的数据,已经保存在了那个string里面
     handler.sendEmptyMessage(0);
    }
   }.start();
   //开启这个线程
  }

 }

 // 定义Handler对象
 private Handler handler = new Handler() {
	 
  @Override
  //只要接到消息发出,就会执行这个方法
  //包括空消息
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   // 处理前台的UI控件
   //将接受的数据,显示在textview里面
   MessageShow.setText(message_from_sermachine);
  }
 };
}


此外,千万不要忘了在androidManifest文件中添加访问网络的权限。

 <uses-permission android:name="android.permission.INTERNET"/>

这行代码有时候复制会报错,所以建议大家最好是自己重新打一遍。


以上代码的原始版本,最初在某偏僻网站找到,我还算认真的研究了一段时间吧。。。

并且做了适当的修改,是能够正确运行的。

测试的时候,我的API版本是4.2建立的工程。

有任何疑问欢迎大家交流。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值