I'm working on a simple multiplayer game on android. I have NetworkManager class which opens connection to the server and provides methods like send and receive.
public class NetworkManager {
private Socket socket;
public NetworkManager() throws IOException
{
socket = new Socket(HOST, PORT);
...
}
public void send(String msg) throws IOException { ... }
}
In one activity I connect to the server by creating new instance of NetworkManager. Then, I have to use that instance in several other activities. Is it possible to pass reference to it when starting new activity?
No, and you should not do it anyway. Use a service to do the background work: https://developer.android.com/guide/components/services.html - or at least an AsyncTask, but for your use case a service should be a better option.
You can only pass primitive or Parcelable types from one Activity to another.
Related
I want to create a communication system with two clients and a server in Netty nio. More specifically, firstly, I want when two clients are connected with the server to send a message from the server and after that to be able to exchnage data between the two clients. I am using the code provided from this example. My modifications in the code can be found here: link
It seems that the channelRead in the serverHandler works when the first client is connceted so it always return 1 but when a second client is connected does not change to 2. How can I check properly from the server when both clients are connected to the server? How can I read this value dynamically from my main function of the Client? Then which is the best way to let both clients communicate?
EDIT1: Apparently it seems that the client service is running and close directly so every time that I am running a new NettyClient is connected but the connection is closed after that. So the counter is always chnages from zero to one. As I was advised in the below comments I tested it using telnet in the same port and the counter seems to increasing normally, however, with the NettyClient service no.
EDIT2: It seems that the issue I got was from future.addListener(ChannelFutureListener.CLOSE); which was in channelRead in the ProcessingHandler class. When I commented it that out it seems that the code works. However, am not sure what are the consequences of commented that out. Moreover, I want from my main function of the client to check when the return message is specific two. How, could I create a method that waits for a specific message from server and meanwhile it blocks the main functionality.
static EventLoopGroup workerGroup = new NioEventLoopGroup();
static Promise<Object> promise = workerGroup.next().newPromise();
public static void callClient() throws Exception {
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(), new ClientHandler(promise));
}
});
ChannelFuture f = b.connect(host, port).sync();
} finally {
//workerGroup.shutdownGracefully();
}
}
I want inside the main function to call the method and return the result and when it is 2 to continue with the main functionality. However, I cannot call callClient inside the while since it will run multiple times the same client.
callBack();
while (true) {
Object msg = promise.get();
System.out.println("Case1: the connected clients is not two");
int ret = Integer.parseInt(msg.toString());
if (ret == 2){
break;
}
}
System.out.println("Case2: the connected clients is two");
// proceed with the main functionality
How can I update the promise variable for the first client. When I run two clients, for the first client I always received the message :
Case1: the connected clients is not two
seems that the promise is not updated normally, while for the second client I always received the:
Case2: the connected clients is two
If my memory is correct, ChannelHandlerContext is one per channel and it can have multiple ChannelHandlers in it's pipeline. Your channels variable is an instance variable of your handler class. And you create a new ProcessingHandler instance for each connection. Thus each will have one and only one connection in channels variable once initialized - the one it was created for.
See new ProcessingHandler() in initChannel function in the server code (NettyServer.java).
You can either make channels variable static so that it is shared between ProcessingHandler instances. Or you can create a single ProcessingHandler instance elsewhere (e.g. as a local variable in the run() function) and then pass that instance to addLast call instead of new ProcessingHandler().
Why the size of ChannelGroup channels is always one. Even if I connect
more clients?
Because child ChannelInitializer is called for every new Channel (client). There you are creating new instance of ProcessingHandler, so every channel see its own instance of ChannelGroup.
Solution 1 - Channel Attribute
Use Attribute and associate it with Channel.
Create attribute somewhere (let's say inside Constants class):
public static final AttributeKey<ChannelGroup> CH_GRP_ATTR =
AttributeKey.valueOf(SomeClass.class.getName());
Now, create ChannelGroup which will be used by all instances of ProcessingHandler:
final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Update your child ChannelInitializer in NettyServer :
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new RequestDecoder(),
new ResponseDataEncoder(),
new ProcessingHandler());
ch.attr(Constants.CH_GRP_ATTR).set(channels);
}
Now you can access instance of ChannelGroup inside your handlers like this:
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final ChannelGroup channels = ctx.channel().attr(Constants.CH_GRP_ATTR).get();
channels.add(ctx.channel());
This will work, because every time new client connects, ChannelInitializer will be called with same reference to ChannelGroup.
Solution 2 - static field
If you declare ChannelGroup as static, all class instances will see same ChannelGroup instance:
private static final ChannelGroup channels =
new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Solution 3 - propagate shared instance
Introduce parameter into constructor of ProcessingHandler:
private final ChannelGroup channels;
public ProcessingHandler(ChannelGroup chg) {
this.channels = chg;
}
Now, inside your NettyServer class create instance of ChannelGroup and propagate it to ProcessingHandler constructor:
final ChannelGroup channels = new
DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new RequestDecoder(),
new ResponseDataEncoder(),
new ProcessingHandler(channels)); // <- here
}
Personally, I would choose first solution, because
It clearly associate ChannelGroup with Channel context
You can access same ChannelGroup in other handlers
You can have multiple instances of server (running on different port, within same JVM)
Im new to Android programming, my app is supposed create a socket in order to maintain constant contact with a server (get status and updates etc) while the user changes to different activities while using the app in general.
Im wondering how its possible for the user to change to different activity classes while still have a constant connection to the server.
I though maybe in main activity to create some background handler that always runs even if the player changes to a different activity.
That handler would be the entity that would maintain contact with the server, like a sort of background task, and also communicate status to show in whatever activity that happen to be running at the time.
something like
OnCreate(....)
{
handler=new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
tick();
handler.postDelayed(this,1000);
}
},1000);
}
void tick()
{
communicate with server
}
But what if the user changes to a different activity, does the handler still run? Can the socket still receive data from server even when a different activity is running if i set up some kind of socket call back that calls a function when it gets data, but that call back would be in the main activity, how can it get called if another activity is active?
Also how to communicate new information gotten from the socket to any activity that happens to be running as to update its UI. I would imagine having a data structure that all activities can access, that is populated by the thread or whatever is communicating with the server.
It seems pretty complicated, anybody have example or know the standard way to do it?
Thanks
This works for me, but I'm pretty sure there are better ways to keep the socket connection alive when changing activities, like using services(as I have read but not yet tried). You can have a look at an example of services here: Services Example
In my Main Activity (Where I create socket)
public static Socket socket;
public static Socket getSocket(){
return socket;
}
public static synchronized void setSocket(Socket socket){
MainActivity.socket = socket;
}
public class ClientThread implements Runnable {
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
setSocket(socket);
if(socket!=null){
Intent goToNextActivity = new Intent(getApplicationContext(), SecondActivity.class);
startActivity(goToNextActivity);
}
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
In my second activity, I just call it like this to use the same socket I created (for example)
MainActivity.getSocket().getOutputStream().write(String.valueOf(multiplier).getBytes());
Works well for me!
I checked source code of ZMQueue class from JeroMQ which implements Runnable interface looks like:
private final Socket inSocket;
private final Socket outSocket;
public ZMQQueue( Context context, Socket inSocket, Socket outSocket ){
this.inSocket = inSocket;
this.outSocket = outSocket;
}
#Override
public void run(){
zmq.ZMQ.proxy( inSocket.base(), outSocket.base(), null );
}
As you can see inside the run() only one statement is there, i.e. calling a
ZMQ.proxy() - what happens here?
And in constructor,it's taking a Context instance as a parameter and doing nothing with it.
can any one explains, for what purpose this class has implemented?
It's simple proxy that works in separate thread, it takes a msg from one socket and puts it to another, ZMQueue class is just a some kind of high-level api in jeromq/jzmq library.
Also you can use proxy without ZMQueue class (doc).
Or you can implement something more complicated by yourself with any processing you need.
I am sending an object called event inside my application but to another process.
I am using IPC EventBus. When I register I cannot receive events back.
This is what I am doing:
public class UserActivity extends Activity
implements IIpcEventBusConnectionListener, IIpcEventBusObserver {
#Override
public void onConnected(IIpcEventBusConnector connector) {
connector.registerObserver(this);
}
...
}
How can I receive the events?
Are you calling conn.startConnection();? If you don't then it will not work.
IIpcEventBusConnector conn =
ConnectorFactory.getInstance().buildConnector(context, this, "com.myapp");
conn.startConnection();
I have a very basic question. I have just started with vertx Framework. There is one thing I am not able to understand like
vertx.createNetServer().connectHandler(new Handler<NetSocket>() {
#Override
public void handle(final NetSocket socket) {
socket.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
/* I can access the socket object inside this handler */
socket.write("Hello");
}
});
}
}).listen(1234);
but if I write the handler function separately like this
Handler<Message> socketHandler = new Handler<Buffer>() {
public void handle(Buffer buffer) {
/* How can i access the socket object now */
socket.write("Hello"); //this will give error as it does not recognize the socket object
}
};
vertx.createNetServer().connectHandler(new Handler<NetSocket>() {
#Override
public void handle(final NetSocket socket) {
socket.dataHandler(socketHandler);//now I write handler separately
}
}).listen(1234);
Handler can be anywhere in the same java class or other java class. Please help me me how I can write handler separately and still access the socket object.
In your second example, you're not passing socket to socketHandler, so you cannot expect socketHandler to know about it.
You could solve this by creating a method like createSocketHandler(final Socket socket) that returns a Handler<Message> and that replaces your socketHandler variable.
To learn more, and to find out why you need socket to be final, look up "anonymous inner classes"