首页 文章 精选 留言 我的

精选列表

搜索[开源框架],共10011篇文章
优秀的个人博客,低调大师

ANDROID中使用开源框架EVENTBUS3.0实现FRAGMENT之间的通信交互

1.概述 在之前的博文中简单介绍过如何实现fragment之间的信息交互:《Android中Fragment与Activity之间的交互(两种实现方式)》,今天继续给大家介绍一种可以实现此效果的另外一种方式EventBus。(相比于handler,接口回调,bundle传参,这个简单好用到哭) EventBus是Android下高效的发布/订阅事件的消息总线。作用是可以代替传统的Intent,Handler,Broadcast或接口函数在Fragment、Activity、Service、线程之间传递数据进行通信,执行方法。做为消息总线,有三个主要元素: (1)Event:事件 (2)Subscriber:事件订阅者,接受特定的事件 (3)Publisher:事件发布者,用于通知Subscriber有事件发生 结合EventBus以上的三个元素,我们也可以称其为一种观察者设计模式。 EventBus 官网链接http://greenrobot.org/eventbus/ EventBus GitHub链接https://github.com/greenrobot/EventBus 前期相关博文链接: Android中Fragment与Activity之间的交互(两种实现方式) Android中Fragment的两种创建方式 2.Demo示例 (1)示例中左侧的按钮,潘侯爷与碧空海触发的事件为EventBus的普通事件发布 (2)左侧粘性事件按钮发布的为粘性事件 3.实现步骤 本次Demo架构: 3.1导依赖包 使用AndroidStudio2.2。仍然采用在build.gradle下中dependencies下直接添加如下代码: compile'org.greenrobot:eventbus:3.0.0' 同步后完成依赖添加。 3.2布局文件 (1)layout中主布局文件,activity_main.xml文件 <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context="com.mly.panhouye.eventbustest.MainActivity"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:background="#6f6669"> <Button android:layout_gravity="center_horizontal" android:id="@+id/panhouye" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=""/> <Button android:layout_gravity="center_horizontal" android:id="@+id/bikonghai" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=""/> <Button android:layout_gravity="center_horizontal" android:id="@+id/postSticky" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Д"/> </LinearLayout> <FrameLayout android:id="@+id/framelayout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="2"></FrameLayout> </LinearLayout> (2)layout中右侧的fragment布局文件fragment_msg.xml文件 <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="match_parent" android:text="nodata" android:textSize="50sp" android:gravity="center_horizontal"/> </LinearLayout> (3)layout中粘性事件的演示界面布局activity_main2.xml文件 <?xmlversion="1.0"encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main2" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.mly.panhouye.eventbustest.Main2Activity"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="30sp" android:gravity="center_horizontal" android:id="@+id/tv" android:text="nodata"/> </RelativeLayout> 3.3java实现代码 (1)自定义事件类 本次演示最简单事件的发布,事件仅发布字符串数据,MessageEvent.java文件如下: packagecom.mly.panhouye.eventbustest;/** *Createdbypanchengjiaon2017/2/190019.*/publicclassMessageEvent{ Stringdata;publicMessageEvent(Stringdata){this.data=data; } } (2)MsgFragment.java 右侧fragment对应的java类,除了在其中关联其对应的fragment布局外,还需要添加修改fragment中文本的方法,如下: packagecom.mly.panhouye.eventbustest;importandroid.os.Bundle;importandroid.support.annotation.Nullable;importandroid.support.v4.app.Fragment;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.ViewGroup;importandroid.widget.TextView;/** *Createdbypanchengjiaon2017/2/200020.*/publicclassMsgFragmentextendsFragment{ TextViewtv; @Nullable @OverridepublicViewonCreateView(LayoutInflaterinflater,@NullableViewGroupcontainer,@NullableBundlesavedInstanceState){ Viewview=inflater.inflate(R.layout.fragment_msg,container,false); tv=(TextView)view.findViewById(R.id.tv);returnview; }publicvoidsetText(Stringmessage){ tv.setText(message); } } (3)MainActivity.java MainActivity.java对应的布局为主布局,右侧的fragment附属于该布局,所以需要在该类中注册EventBus,将当前的Activity注册为事件订阅者,具体代码如下: packagecom.mly.panhouye.eventbustest;importandroid.content.Intent;importandroid.support.v7.app.AppCompatActivity;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importorg.greenrobot.eventbus.EventBus;importorg.greenrobot.eventbus.Subscribe;importorg.greenrobot.eventbus.ThreadMode;publicclassMainActivityextendsAppCompatActivityimplementsView.OnClickListener{ Buttonpanhouye,bikonghai,postSticky; MsgFragmentmsgFragment; @OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); panhouye=(Button)findViewById(R.id.panhouye); bikonghai=(Button)findViewById(R.id.bikonghai); postSticky=(Button)findViewById(R.id.postSticky); panhouye.setOnClickListener(this); bikonghai.setOnClickListener(this); postSticky.setOnClickListener(this);//添加fragment到右侧的帧布局中 msgFragment=newMsgFragment(); getSupportFragmentManager().beginTransaction().add(R.id.framelayout,msgFragment).commit(); }/*个人建议在onResume注册EventBus *在可见可交互状态下注册,尽可能少的占用内存*/ @OverrideprotectedvoidonResume(){super.onResume(); EventBus.getDefault().register(this); }/*个人建议在onPause注册EventBus(将当前Activity注册为事件订阅者) *不影响功能的情况下提早解除注册,尽可能少的占用内存*/ @OverrideprotectedvoidonPause(){super.onPause(); EventBus.getDefault().unregister(this); }/** *事件发布者(通过按钮点击事件进行事件发布) *@paramv*/ @OverridepublicvoidonClick(Viewv){switch(v.getId()){//(1)事件发布中所传参数可以作为右侧fragment文本的修改内容//(2)事件发布中所传参数也可以用作事件订阅者执行方法的区分通知 caseR.id.panhouye: EventBus.getDefault().post(newMessageEvent("潘侯爷"));break;caseR.id.bikonghai: EventBus.getDefault().post(newMessageEvent("碧空海"));break;caseR.id.postSticky://粘性事件发布 EventBus.getDefault().postSticky(newMessageEvent("粘性事件")); startActivity(newIntent(this,Main2Activity.class));break; } }/** *事件订阅者自定义的接收方法 *@paramevent*/ @Subscribe(threadMode=ThreadMode.MAIN)publicvoidonMessageEvent(MessageEventevent){////(1)将事件发布者发布的数据作为文本修改内容//msgFragment.setText(event.data);//(2)将事件发布者发布的数据作为方法执行的区分 switch(event.data){case"潘侯爷": msgFragment.setText("panhouye");break;case"碧空海": msgFragment.setText("bikonghai");break; } } } (4)Main2Activity.java 注意:此布局作为粘性事件发布的订阅者,同样需要注册EventBus packagecom.mly.panhouye.eventbustest;importandroid.support.v7.app.AppCompatActivity;importandroid.os.Bundle;importandroid.widget.TextView;importorg.greenrobot.eventbus.EventBus;importorg.greenrobot.eventbus.Subscribe;importorg.greenrobot.eventbus.ThreadMode;publicclassMain2ActivityextendsAppCompatActivity{ TextViewtv; @OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); tv=(TextView)findViewById(R.id.tv); } @OverrideprotectedvoidonResume(){super.onResume(); EventBus.getDefault().register(this); } @OverrideprotectedvoidonPause(){super.onPause(); EventBus.getDefault().unregister(this); } @Subscribe(threadMode=ThreadMode.MAIN,sticky=true)publicvoidonMessageEvent(MessageEventevent){////(1)将事件发布者发布的数据作为文本修改内容tv.setText(event.data);//(2)将事件发布者发布的数据作为方法执行的区分//switch(event.data){//case"粘性事件"://tv.setText("panhouye");//break;//}} } 发布的粘性事件在其新订阅者注册后将会自动传递给新订阅者,有时我们也需要移除粘性事件,以免它在传递下去。 MessageEventstickyEvent=EventBus.getDefault().getStickyEvent(MessageEvent.class);//Bettercheckthataneventwasactuallypostedbeforeif(stickyEvent!=null){//"Consume"thestickyeventEventBus.getDefault().removeStickyEvent(stickyEvent);//Nowdosomethingwithit} MessageEventstickyEvent=EventBus.getDefault().removeStickyEvent(MessageEvent.class);//Bettercheckthataneventwasactuallypostedbeforeif(stickyEvent!=null){//Nowdosomethingwithit} 4.线程模式 EventBus提供了四种线程模式: (1)postThread:用户将被调用在同一个线程中,这是发布事件(这是默认值)。事件传递意昧着最少的开销,因为它完全避免了线程切换。因此,这是推荐的模式,来处理简单的任务,如果是已知的完成是一个很短的时间,而不需要主线程。事件处理使用此模式必须迅速返回,以避免阻塞发布线程,这可能是主线程。 (2)MainThread:用户将被调用在主线程(UI线程)。如果发布线程是主线程,事件处理程序方法将直接调用。使用此模式的事件处理程序必须快速返回,避免阻塞主线程。 (3)BackgrounThread:将在后台线程中调用订阅者。如果发布线程不是主线程,则事件处理程序方法将被在发布线程中直接调用。如果线程是主线程,eventbus采用单独的一个后台线程,将按顺序调用所有的事件。使用此模式的事件处理程序应尝试快速返回,以避免阻塞后台线程。 (4)Async:事件处理程序方法在一个单独的线程中调用。这总是独立于发布线程和主线程。发布事件从来不会等待使用这种模式的事件处理程序方法。事件处理程序方法使用此模式,如果他们的执行可能需要一段时间,例如用于网络访问。避免触发大量在同一时间运行长时间运行的异步处理程序方法以限制并发线程的数目。eventbus使用一个线程池来有效地重用已完成的异步事件处理程序通知的线程。 本文转自帅气的头头博客51CTO博客,原文链接http://blog.51cto.com/12902932/1927602如需转载请自行联系原作者 sshpp

优秀的个人博客,低调大师

3个netty5的例子,java开源框架简单介绍netty的用法

这是一个netty快速入门的例子,也是我的学习笔记,比较简单,翻译于官方的文档整理后把所有代码注释放在每一行代码中间,简单明了地介绍一些基础的用法。 首页这是基于netty5的例子,如果需要使用请依赖netty5的包。maven引用方式 1 <dependency> 2 <groupId>io.netty</groupId> 3 <artifactId>netty-all</artifactId> 4 <version>5.0.0.Alpha2</version> 5 </dependency> 0.Netty Server packagecom.tjbsl.netty.demo0.server; importcom.tjbsl.netty.demo3.time.TimeServerHandler; importio.netty.bootstrap.ServerBootstrap; importio.netty.channel.ChannelFuture; importio.netty.channel.ChannelInitializer; importio.netty.channel.ChannelOption; importio.netty.channel.EventLoopGroup; importio.netty.channel.nio.NioEventLoopGroup; importio.netty.channel.socket.SocketChannel; importio.netty.channel.socket.nio.NioServerSocketChannel; /** *处理数据 */ publicclassNettyServer{ privateintport; publicNettyServer(intport){ this.port=port; } publicvoidrun()throwsException{ /*** *NioEventLoopGroup是用来处理I/O操作的多线程事件循环器, *Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。 *在这个例子中我们实现了一个服务端的应用, *因此会有2个NioEventLoopGroup会被使用。 *第一个经常被叫做‘boss’,用来接收进来的连接。 *第二个经常被叫做‘worker’,用来处理已经被接收的连接, *一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。 *如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现, *并且可以通过构造函数来配置他们的关系。 */ EventLoopGroupbossGroup=newNioEventLoopGroup(); EventLoopGroupworkerGroup=newNioEventLoopGroup(); System.out.println("准备运行端口:"+port); try{ /** *ServerBootstrap是一个启动NIO服务的辅助启动类 *你可以在这个服务中直接使用Channel */ ServerBootstrapb=newServerBootstrap(); /** *这一步是必须的,如果没有设置group将会报java.lang.IllegalStateException:groupnotset异常 */ b=b.group(bossGroup,workerGroup); /*** *ServerSocketChannel以NIO的selector为基础进行实现的,用来接收新的连接 *这里告诉Channel如何获取新的连接. */ b=b.channel(NioServerSocketChannel.class); /*** *这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。 *ChannelInitializer是一个特殊的处理类, *他的目的是帮助使用者配置一个新的Channel。 *也许你想通过增加一些处理类比如NettyServerHandler来配置一个新的Channel *或者其对应的ChannelPipeline来实现你的网络程序。 *当你的程序变的复杂时,可能你会增加更多的处理类到pipline上, *然后提取这些匿名类到最顶层的类上。 */ b=b.childHandler(newChannelInitializer<SocketChannel>(){//(4) @Override publicvoidinitChannel(SocketChannelch)throwsException{ //ch.pipeline().addLast(newDiscardServerHandler());//demo1.discard //ch.pipeline().addLast(newResponseServerHandler());//demo2.echo ch.pipeline().addLast(newTimeServerHandler());//demo3.time } }); /*** *你可以设置这里指定的通道实现的配置参数。 *我们正在写一个TCP/IP的服务端, *因此我们被允许设置socket的参数选项比如tcpNoDelay和keepAlive。 *请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识。 */ b=b.option(ChannelOption.SO_BACKLOG,128); /*** *option()是提供给NioServerSocketChannel用来接收进来的连接。 *childOption()是提供给由父管道ServerChannel接收到的连接, *在这个例子中也是NioServerSocketChannel。 */ b=b.childOption(ChannelOption.SO_KEEPALIVE,true); /*** *绑定端口并启动去接收进来的连接 */ ChannelFuturef=b.bind(port).sync(); /** *这里会一直等待,直到socket被关闭 */ f.channel().closeFuture().sync(); }finally{ /*** *优雅关闭 */ workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } publicstaticvoidmain(String[]args)throwsException{ intport; if(args.length>0){ port=Integer.parseInt(args[0]); }else{ port=8000; } newNettyServer(port).run(); //通过cmd窗口的telnet127.0.0.18000运行 } } 1.DISCARD服务(丢弃服务,指的是会忽略所有接收的数据的一种协议) packagecom.tjbsl.netty.demo1.discard; importio.netty.buffer.ByteBuf; importio.netty.channel.ChannelHandlerAdapter; importio.netty.channel.ChannelHandlerContext; importio.netty.util.CharsetUtil; importio.netty.util.ReferenceCountUtil; /** *服务端处理通道.这里只是打印一下请求的内容,并不对请求进行任何的响应 *DiscardServerHandler继承自ChannelHandlerAdapter, *这个类实现了ChannelHandler接口, *ChannelHandler提供了许多事件处理的接口方法, *然后你可以覆盖这些方法。 *现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。 * */ publicclassDiscardServerHandlerextendsChannelHandlerAdapter{ /*** *这里我们覆盖了chanelRead()事件处理方法。 *每当从客户端收到新的数据时, *这个方法会在收到消息时被调用, *这个例子中,收到的消息的类型是ByteBuf *@paramctx通道处理的上下文信息 *@parammsg接收的消息 */ @Override publicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ try{ ByteBufin=(ByteBuf)msg; /*while(in.isReadable()){ System.out.print((char)in.readByte()); System.out.flush(); }*/ //这一句和上面注释的的效果都是打印输入的字符 System.out.println(in.toString(CharsetUtil.US_ASCII)); }finally{ /** *ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。 *请记住处理器的职责是释放所有传递到处理器的引用计数对象。 */ ReferenceCountUtil.release(msg); } } /*** *这个方法会在发生异常时触发 *@paramctx *@paramcause */ @Override publicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause){ /*** *发生异常后,关闭连接 */ cause.printStackTrace(); ctx.close(); } } 以上是一个丢弃服务的处理方式,你可以运行后通过telnet来发送消息,来查看是否正常运行,注意console里会打印你的输入内容。 2.ECHO服务(响应式协议) 到目前为止,我们虽然接收到了数据,但没有做任何的响应。然而一个服务端通常会对一个请求作出响应。让我们学习怎样在ECHO协议的实现下编写一个响应消息给客户端,这个协议针对任何接收的数据都会返回一个响应。 和discard server唯一不同的是把在此之前我们实现的channelRead()方法,返回所有的数据替代打印接收数据到控制台上的逻辑。 说明NettyServer 还是用上面已经提供的类,只是把这段里的注销部分修改成如下。 packagecom.tjbsl.netty.demo2.echo; importio.netty.buffer.ByteBuf; importio.netty.channel.ChannelHandlerAdapter; importio.netty.channel.ChannelHandlerContext; importio.netty.util.CharsetUtil; /** *服务端处理通道. *ResponseServerHandler继承自ChannelHandlerAdapter, *这个类实现了ChannelHandler接口, *ChannelHandler提供了许多事件处理的接口方法, *然后你可以覆盖这些方法。 *现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。 *用来对请求响应 */ publicclassResponseServerHandlerextendsChannelHandlerAdapter{ /** *这里我们覆盖了chanelRead()事件处理方法。 *每当从客户端收到新的数据时, *这个方法会在收到消息时被调用, *ChannelHandlerContext对象提供了许多操作, *使你能够触发各种各样的I/O事件和操作。 *这里我们调用了write(Object)方法来逐字地把接受到的消息写入 *@paramctx通道处理的上下文信息 *@parammsg接收的消息 */ @Override publicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ ByteBufin=(ByteBuf)msg; System.out.println(in.toString(CharsetUtil.UTF_8)); ctx.write(msg); //cxt.writeAndFlush(msg) //请注意,这里我并不需要显式的释放,因为在进入的时候netty已经自动释放 //ReferenceCountUtil.release(msg); } /** *ctx.write(Object)方法不会使消息写入到通道上, *他被缓冲在了内部,你需要调用ctx.flush()方法来把缓冲区中数据强行输出。 *或者你可以在channelRead方法中用更简洁的cxt.writeAndFlush(msg)以达到同样的目的 *@paramctx *@throwsException */ @Override publicvoidchannelReadComplete(ChannelHandlerContextctx)throwsException{ ctx.flush(); } /** *这个方法会在发生异常时触发 * *@paramctx *@paramcause */ @Override publicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause){ /*** *发生异常后,关闭连接 */ cause.printStackTrace(); ctx.close(); } } 同样以上运行后,可以通过telnet发送数据,console里会打印出你发送的数据,同时你的命令行界面里应该也会接收到相同的数据。 3.TIME服务(时间协议的服务) 在这个部分被实现的协议是TIME协议。和之前的例子不同的是在不接受任何请求时他会发送一个含32位的整数的消息,并且一旦消息发送就会立即关闭连接。在这个例子中,你会学习到如何构建和发送一个消息,然后在完成时主动关闭连接。 因为我们将会忽略任何接收到的数据,而只是在连接被创建发送一个消息,所以这次我们不能使用channelRead()方法了,代替他的是,我们需要覆盖channelActive()方法,下面的就是实现的内容: 说明NettyServer 还是用上面已经提供的类,只是把这段里的注销部分修改成如下。 1 //ch.pipeline().addLast(newDiscardServerHandler()); 2 //ch.pipeline().addLast(newResponseServerHandler()); 3 ch.pipeline().addLast(newTimeServerHandler()); TimeServerHandler类的如下: packagecom.tjbsl.netty.demo3.time; importio.netty.buffer.ByteBuf; importio.netty.channel.ChannelFuture; importio.netty.channel.ChannelFutureListener; importio.netty.channel.ChannelHandlerAdapter; importio.netty.channel.ChannelHandlerContext; importio.netty.util.CharsetUtil; importjava.util.Scanner; publicclassTimeServerHandlerextendsChannelHandlerAdapter{ /** *channelActive()方法将会在连接被建立并且准备进行通信时被调用。 *因此让我们在这个方法里完成一个代表当前时间的32位整数消息的构建工作。 * *@paramctx */ @Override publicvoidchannelActive(finalChannelHandlerContextctx){ /*Scannercin=newScanner(System.in); System.out.println("请输入发送信息:"); Stringname=cin.nextLine();*/ Stringname="HelloWorld!"; /** *为了发送一个新的消息,我们需要分配一个包含这个消息的新的缓冲。 *因为我们需要写入一个32位的整数,因此我们需要一个至少有4个字节的ByteBuf。 *通过ChannelHandlerContext.alloc()得到一个当前的ByteBufAllocator, *然后分配一个新的缓冲。 */ finalByteBuftime=ctx.alloc().buffer(4); time.writeBytes(name.getBytes()); /*** *和往常一样我们需要编写一个构建好的消息 *。但是等一等,flip在哪?难道我们使用NIO发送消息时不是调用java.nio.ByteBuffer.flip()吗? *ByteBuf之所以没有这个方法因为有两个指针, *一个对应读操作一个对应写操作。 *当你向ByteBuf里写入数据的时候写指针的索引就会增加, *同时读指针的索引没有变化。 *读指针索引和写指针索引分别代表了消息的开始和结束。 *比较起来,NIO缓冲并没有提供一种简洁的方式来计算出消息内容的开始和结尾, *除非你调用flip方法。 *当你忘记调用flip方法而引起没有数据或者错误数据被发送时, *你会陷入困境。这样的一个错误不会发生在Netty上, *因为我们对于不同的操作类型有不同的指针。 *你会发现这样的使用方法会让你过程变得更加的容易, *因为你已经习惯一种没有使用flip的方式。 *另外一个点需要注意的是ChannelHandlerContext.write()(和writeAndFlush())方法会返回一个ChannelFuture对象, *一个ChannelFuture代表了一个还没有发生的I/O操作。 *这意味着任何一个请求操作都不会马上被执行, *因为在Netty里所有的操作都是异步的。 *因此你需要在write()方法返回的ChannelFuture完成后调用close()方法, *然后当他的写操作已经完成他会通知他的监听者。 */ finalChannelFuturef=ctx.writeAndFlush(time);//(3) /** *当一个写请求已经完成是如何通知到我们? *这个只需要简单地在返回的ChannelFuture上增加一个ChannelFutureListener。 *这里我们构建了一个匿名的ChannelFutureListener类用来在操作完成时关闭Channel。 */ f.addListener(newChannelFutureListener(){ @Override publicvoidoperationComplete(ChannelFuturefuture){ assertf==future; /*** *请注意,close()方法也可能不会立马关闭,他也会返回一个ChannelFuture。 */ ctx.close(); } }); } //接收结果 @Override publicvoidchannelRead(ChannelHandlerContextctx,Objectmsg) throwsException{ ByteBufbuf=(ByteBuf)msg; System.out.println("client:"+buf.toString(CharsetUtil.UTF_8)); } @Override publicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause){ cause.printStackTrace(); ctx.close(); } } 4.Time客户端 不像DISCARD和ECHO的服务端,对于TIME协议我们需要一个客户端因为人们不能把一个32位的二进制数据翻译成一个日期或者日历。在这一部分,我们将会讨论如何确保服务端是正常工作的,并且学习怎样用Netty编写一个客户端。 在Netty中,编写服务端和客户端最大的并且唯一不同的使用了不同的BootStrap和Channel的实现。 packagecom.tjbsl.netty.demo3.time.client; importio.netty.bootstrap.Bootstrap; importio.netty.channel.ChannelFuture; importio.netty.channel.ChannelInitializer; importio.netty.channel.ChannelOption; importio.netty.channel.EventLoopGroup; importio.netty.channel.nio.NioEventLoopGroup; importio.netty.channel.socket.SocketChannel; importio.netty.channel.socket.nio.NioSocketChannel; publicclassTimeClient{ publicstaticvoidmain(String[]args)throwsException{ Stringhost="127.0.0.1"; intport=8000; EventLoopGroupworkerGroup=newNioEventLoopGroup(); try{ /** *如果你只指定了一个EventLoopGroup, *那他就会即作为一个‘boss’线程, *也会作为一个‘workder’线程, *尽管客户端不需要使用到‘boss’线程。 */ Bootstrapb=newBootstrap();//(1) b.group(workerGroup);//(2) /** *代替NioServerSocketChannel的是NioSocketChannel,这个类在客户端channel被创建时使用 */ b.channel(NioSocketChannel.class);//(3) /** *不像在使用ServerBootstrap时需要用childOption()方法, *因为客户端的SocketChannel没有父channel的概念。 */ b.option(ChannelOption.SO_KEEPALIVE,true);//(4) b.handler(newChannelInitializer<SocketChannel>(){ @Override publicvoidinitChannel(SocketChannelch)throwsException{ ch.pipeline().addLast(newTimeClientHandler()); } }); //用connect()方法代替了bind()方法 ChannelFuturef=b.connect(host,port).sync(); //等到运行结束,关闭 f.channel().closeFuture().sync(); }finally{ workerGroup.shutdownGracefully(); } } }

优秀的个人博客,低调大师

PhalApi 2.8.1 发布,PHP 开源接口框架

PhalApi 2.8.1 [主要更新] 1、文件日志PhalApi\Logger\FileLogger区分隐式静默和显式异常两种模式,可通过\PhalApi\DI()->debug全局模式或初始化时指定调试模式。为调试模式时,若写入失败将500异常提示 1、文件配置PhalApi\Config\FileConfig区分隐式静默和显式异常两种模式,可通过\PhalApi\DI()->debug全局模式或初始化时指定调试模式。为调试模式时,若配置不存在将500异常提示 [辅助更新] [BUG修复] 1、URI路由匹配模式下默认接口读取不到 2、配置多个数据库时候, 调用get、update时会提示 dbs.tables should not be empty (issue 97)

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册