本週末學習netty長鏈接,實現了一個netty安卓客戶端與服務器鏈接併發送消息的實例,
代碼位置:https://git.oschina.net/lpt20...html
主要實現:android
引入netty jar包git
創建鏈接客戶端代碼服務器
> 創建鏈接 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.ide
如何在handler類中收到服務器消息後更改ui中的控件內容函數
在安卓中,ui是主線程,子線程沒法直接控制ui中的元素,不然會報錯,那麼咱們想在其餘線程中控制ui元素應該怎麼辦呢?
我搜索了網絡中的解決辦法,其中使用handler比較方便oop
須要被線程更新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...