本周末学习netty长连接,实现了一个netty安卓客户端与服务器连接并发送消息的实例,
代码位置:https://git.oschina.net/lpt20...
主要实现:
引入netty jar包
建立连接客户端代码
> 建立连接
public class HelloClientInitializer extends ChannelInitializer<SocketChannel> {
private Handler view;
public HelloClientInitializer(Handler view){
this.view=view;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new HelloClientHandler(view));
}
}
此处获取服务器发送的消息,并对ui进行更改
public class HelloClientHandler extends SimpleChannelInboundHandler<String> {
private Handler handler;
public HelloClientHandler(Handler view) {
this.handler = view;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, final String msg) throws Exception {
Message message = new Message();
//message.what=11;
message.obj=msg;
handler.sendMessage(message);
System.out.println("Server say : " + msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client active ");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client close ");
super.channelInactive(ctx);
}
}
封装连接和断开
public class HelloClient {
public static String host = "192.168.3.29";
public static int port = 7946;
Channel ch;
private Handler handler;
public HelloClient() {
// TODO Auto-generated constructor stub
}
public HelloClient(Handler handler) {
// TODO Auto-generated constructor stub
this.handler=handler;
}
/**
* @param
* @throws InterruptedException
* @throws IOException
*/
public void run() throws InterruptedException, IOException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new HelloClientInitializer(handler));
// 连接服务端
ch = b.connect(host, port).sync().channel();
ch.closeFuture().sync(); // 异步关闭,这方法执行完不关闭连接
} finally {
// The connection is closed automatically on shutdown.
group.shutdownGracefully();
}
}
public void sendMSg(String line) {
/*
* 向服务端发送在控制台输入的文本 并用"\r\n"结尾 之所以用\r\n结尾 是因为我们在handler中添加了 DelimiterBasedFrameDecoder 帧解码。
* 这个解码器是一个根据\n符号位分隔符的解码器。所以每条消息的最后必须加上\n否则无法识别和解码
*/
ch.writeAndFlush(line + "\r\n");
}
public void closeChannel(){
ch.close();
}
}
ui主线程,在界面中显示服务器的消息
public class MainActivity extends AppCompatActivity {
TextView textView;
HelloClient hc;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg);
textView.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView= (TextView) findViewById(R.id.textview);
View run = this.findViewById(R.id.run);
run.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hc=new HelloClient(handler);
new Thread(new Runnable() {
@Override
public void run() {
try {
hc.run();
} catch (InterruptedException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
});
View close = this.findViewById(R.id.close);
close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hc.closeChannel();
}
});
}
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
}
遇到的问题:
ERROR/AndroidRuntime(1222):android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
如何在handler类中收到服务器消息后更改ui中的控件内容
在安卓中,ui是主线程,子线程无法直接控制ui中的元素,否则会报错,那么我们想在其他线程中控制ui元素应该怎么办呢?
我搜索了网络中的解决办法,其中使用handler比较方便
需要被线程更新UI的Activity 中声明一个android.os.Handler 类的变量,
private Handler handler
并初始化:
@Override
public void onCreate(Bundle savedInstanceState) {
//其他代码……
handler=new Handler(){
public void handleMessage(Message msg){
String message=(String)msg.obj; //根据message中的信息对主线程的UI进行改动
//……
}
}
};
子线程类中需要获得activity中的handler对象,可以持有表示上下文的Context类对象,实际应用中这个引用就是指向要更新UI的Activity对象,一般声明为:
private Context ctx
,然后在子线程类构造函数或其它函数中初始化ctx,就可以得到Activity对象中的Handler对象。也可以通过传递handler获取它。在子线程运行到某个地方,需要向Activity传递消息的时候,创建android.os.Message 类的对象,将要传送的对象加入message ,通过Handler发布传送给主线程,代码示例如下:
String str=message"
Message message = Message.obtain();
message.obj=str;
//通过Handler发布传送消息,handler
handler.sendMessage(message);//这里的handler跟Activity中的handler是同一个对象
还有其他的方法在非ui线程中控制ui,参见http://www.2cto.com/kf/201405...