在 Android 上使用 RxNetty
在 Android 上使用 RxNetty
Netty是由JBOSS提供的一個Java開源架構,是一個支援TCP/UDP/HTTP等網路通訊協定的通訊架構,和Mina類似,廣泛應用於RPC架構。RxNetty則是支援RxJava的Netty開源架構,現在我們來看一下在Android上如何使用RxNetty。
添加RxNetty
在 Android Studio 中添加 RxNetty 的依賴:
把RxNetty的tcp包加入到依賴,直接這樣編譯會有兩個問題,第一個問題是jar重複:
com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK THIRD-PARTYFile1: C:\Users\XXX.gradle\caches\modules-2\files-2.1\org.openjdk.jmh\jmh-core\1.11.2\f4f8cd9874f5cdbc272b715a381c57e65f67ddf2\jmh-core-1.11.2.jarFile2: C:\Users\XXX.gradle\caches\modules-2\files-2.1\org.openjdk.jmh\jmh-generator-annprocess\1.11.2\72d854bf76ba5e59596d4c887a6de48e7003bee2\jmh-generator-annprocess-1.11.2.jar
解決辦法:
dependencies { ... compile('io.reactivex:rxnetty-tcp:0.5.2-RC1') { exclude group: 'org.openjdk.jmh' } ...}
另一個問題是引用的netty包中META-INF/下的部分檔案重複。
解決辦法:
packagingOptions { ... exclude 'META-INF/INDEX.LIST' exclude 'META-INF/BenchmarkList' exclude 'META-INF/io.netty.versions.properties' exclude 'META-INF/CompilerHints' ... }
到這裡RxNetty就成功添加到項目模組中了。接下來看看到底如何使用RxNetty。
如何使用
拿TCP協議舉例,用過Netty的都清楚建立串連的步驟:
workerGroup = new NioEventLoopGroup(); Bootstrap boot = new Bootstrap(); boot.group(workerGroup) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast("decoder", new MessageDecoder()); p.addLast("encoder", new MessageEncoder()); p.addLast("handler", new MessageHandler()); } }); ChannelFuture f = boot.connect("localhost", 8888).syncUninterruptibly(); channel = f.channel();
自訂的協議需要我們自己實現編碼解碼Handler,還有最後處理資料的MessageHandler:
@Sharablepublic class MessageHandler extends SimpleChannelInboundHandler { @Override public void messageReceived(ChannelHandlerContext ctx, Message msg) throws Exception { //處理訊息 }}
在RxNetty中可以不實現MessageHandler,因為通過註冊的觀察者可以得到最終解碼後的協議對象。
下面是RxNetty建立串連的方法:
Connection mConnection; public Observable connect(final String url, final int port) { return Observable.create(new Observable.OnSubscribe() { @Override public void call(final Subscriber subscriber) { TcpClient.newClient(url, port).addChannelHandlerLast("decoder", new Func0() { @Override public ChannelHandler call() { return new StringDecoder(); } }).addChannelHandlerLast("encoder", new Func0() { @Override public ChannelHandler call() { return new StringEncoder(); } }).createConnectionRequest().subscribe(new Observer>() { @Override public void onCompleted() { subscriber.onCompleted(); } @Override public void onError(Throwable e) { subscriber.onError(e); } @Override public void onNext(Connection connection) { mConnection = connection; subscriber.onNext(true); } }); } }); }
上面的TCP用戶端建立了一個字串解碼器、一個字串編碼器,然後建立連結,在連結建立成功後把連結化物件connection儲存到mConnection方便後面發送資料,同時通知訂閱者socket串連成功。
在Android中不能在UI線程建立網路連結,就連InetSocketAddress類都不能在UI線程中建立,TcpClient.newClient(url, port)...createConnectionRequest()本身是一個Observable,但是由於方法newClient(url, port)中建立了InetSocketAddress類,Android嚴苛模式會報異常,所以上面建立連結的TcpClient方法在外層又包裹了一個Observable,讓它運行在IO線程等其它非UI線程才可以正常建立socket連結。
用來接收資料、發送資料的方法同樣返回一個Observable,代碼如下:
public Observable receive() { if (mConnection != null) { return mConnection.getInput(); } return null; } public Observable send(String s) { return mConnection.writeString(Observable.just(s)); }
測試上面方法的用戶端代碼:
public void rxNettyClientTest() { connect("localhost", 60000).subscribe(new Observer() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { //reconnect Observable.timer(1, TimeUnit.SECONDS).subscribe(new Action1() { @Override public void call(Long aLong) { if (mConnection != null) mConnection.closeNow(); rxNettyClientTest(); } }); System.out.println("reconnect"); } @Override public void onNext(Boolean aBoolean) { //send data send("hello world!").subscribe(new Action1() { @Override public void call(Void aVoid) { System.out.println("send success!"); } }); //receive data receive().subscribe(new Observer() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { //reconnect Observable.timer(1, TimeUnit.SECONDS).subscribe(new Action1() { @Override public void call(Long aLong) { if (mConnection != null) mConnection.closeNow(); rxNettyClientTest(); } }); System.out.println("reconnect"); } @Override public void onNext(String s) { System.out.println("receive:" + s); } }); } }); }
上面的程式碼封裝涵了讀、寫資料和重連等主要功能。
然後是建立服務端的代碼:
public void rxNettyServerTest() { TcpServer server; server = TcpServer.newServer(60000).addChannelHandlerLast("string-decoder", new Func0() { @Override public ChannelHandler call() { return new StringDecoder(); } }).addChannelHandlerLast("string-encoder", new Func0() { @Override public ChannelHandler call() { return new StringEncoder(); } }).start(new ConnectionHandler() { @Override public Observable handle(Connection newConnection) { return newConnection.writeStringAndFlushOnEach( newConnection.getInput().map(new Func1() { @Override public String call(String s) { System.out.println("receive:" + s); return "echo=> " + s; } })); } }); server.awaitShutdown(); }
服務端代碼比較簡單,直接echo用戶端發來的資料。
關於線程,在Android中處理網路需要subscribeOn(Schedulers.io()),如果需要在UI線程展示則observeOn(AndroidSchedulers.mainThread())。
最後,在Android上使用RxNetty大多數是因為沒有合適的socket用戶端架構,RxNetty也支援Http協議,Android上的Http協議的可選架構比較多,所以就不在這裡介紹了,想要瞭解的可以到這裡RxNetty。