Reusing sfsClient between Activities

Post here your questions about the Java client / Android API for SFS2X

Moderators: Lapo, Bax

Post Reply
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Reusing sfsClient between Activities

Post by sylhouette »

What is the best approach for using the sfsClient in Android for multiple activites? Should I reuse them? Or should I re-init the sfsClient for each activity?

For example, I have activity A, activity B, activity C.
In activity A, I have an instance of sfsClient that handles the login process, etc.
After logging in from activity A, I log the user in to the "general zone" (in activity B). On click from activity B, I want the user to move to the game zone, therefore I disconnect-reconnect to move the user to the game zone.
In activity C, should I reuse the sfsClient from activity A? Because in activity B, I reuse the sfsClient from activity A to disconnect-reconnect. However, if I reuse the sfsClient from activity A, does that mean I have to define all the dispatch() for activtiy C in activity A? If I re-init, does that mean I have reconnect the user again so that the newly init smartFox can handle the dispatch() in activity C?

One thing I've noticed though, each time I call finish(), I would get disconnected from the server, so I have to put the activities to the stack.

Code: Select all

Intent mainMenuIntent = new Intent(getApplicationContext(), MainMenuActivity.class);
startActivity(mainMenuIntent);
// I cannot call finish here, because I would get disconnected. (the login session destroyed will be destroyed and I would be disconnected)


Sorry for the newbie question. I'm still quite novice and will love to learn more :) Thank you very much for the help.
mmilen
Posts: 311
Joined: 09 Nov 2010, 00:48
Contact:

Post by mmilen »

I need the same thing, and the solution is to have the sfsClient object as part of the global Application object. However the sfsClient can not be inited in the Application object, as it fails to find paths to some non public java API classes like sun.misc.BASE64Encoder. My guess is Android loads different set of classpaths when an activity is created and loaded..
stix
Posts: 9
Joined: 01 May 2010, 15:51

Post by stix »

To answer your question specifically, you should re-use the client across activities. Each activity will need to have a dispatch method defined to handle any events listened to in that particular activity. So, if activity C listens for SFSEvent.EXTENSION_RESPONSE but activity A does not, then activity C's dispatch method will be of the form:

Code: Select all

@Override
public void dispatch(final BaseEvent event) throws SFSException
{
 if (event.getType().equalsIgnoreCase(SFSEvent.EXTENSION_RESPONSE))
{
//...    
}
}
The dispatch method in activity A will not handle an extension response event.
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Post by sylhouette »

@mmilen: I haven't tried that though, it would be very interesting if I can do that, so that I can finish() all the unused activities in the stack :) Great idea. I'll google around.

@stix: I've tried that, and it failed. I still have to place dispatch() in the activity where I inited the smartfox client. I'll dig further. Anyway, thanks :)
mmilen
Posts: 311
Joined: 09 Nov 2010, 00:48
Contact:

Post by mmilen »

Where you able to init the sfsClient in the Application object?
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Post by sylhouette »

Code: Select all

public class SfsClient extends Application {
	private SmartFox sfsClient = new SmartFox(true);

	public SmartFox getSfsClient() {
		return sfsClient;
	}

	public void setSfsClient(SmartFox sfsClient) {
		this.sfsClient = sfsClient;
	}
	
}
this is my application code. Is this enough? I tried to call this from my activity through

Code: Select all

sfsClient = ((SfsClient) getApplicationContext()).getSfsClient();
and attaching all the event listeners I need afterwards. It works, but still, I cannot safely call finish() to switch from this activity to another activity. I still get disconnected (my session is destroyed). I wonder is there any way to safely call finish() and switch to another activity while maintaining my session throughout the other activities.. ~.~
mmilen
Posts: 311
Joined: 09 Nov 2010, 00:48
Contact:

Post by mmilen »

Code: Select all

sfsClient = ((SfsClient) getApplicationContext()).getSfsClient();
When I try the code above in my Activity I get Cannot make static reference to the non static method.

if I make the getSfsClient() and sfsClient static , I get the extension path error mentioned in my previous post.
mmilen
Posts: 311
Joined: 09 Nov 2010, 00:48
Contact:

Post by mmilen »

I did some more digging and looks like sfsClient needs to be abstracted into a service, rather that an application. Android promises to keep a service running if there is enough resources.
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Post by sylhouette »

Hmm..I'll try that..I'll try posting my code here after I succeed..please let me know if you have any success >,< thanks :)
mmilen
Posts: 311
Joined: 09 Nov 2010, 00:48
Contact:

Post by mmilen »

Here is what I came up with. A service wrapper where my activities do bind (for direct access to service methods) and register as broadcast listeners (to receive notifications). I still have to figure out a way to nicely pass SFSObjects via Intent interface. The example below connects, when I press a button in the main activity and bind it to the service (not the best place to do all that).

Any ideas of any efficient way to move BaseEvent to the service listeners via Intent or any other mechanism would be great.

Here is my code

Code: Select all

public class NetworkProtocol extends Service implements IEventListener{

	//these are ID of various request that can be sent the NetworkProtocol to action
	public static final int APP_DELEGATE=1; 
	
	private SmartFox sfsClient=new SmartFox(true);

	//Login properties
	private String username=null;
	private String password=null;

	public class NP_Binder extends Binder implements IBinder {
		public NetworkProtocol getService() {
            return NetworkProtocol.this;
        }
    }

	// This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private IBinder mBinder=null;
	
	@Override
	public IBinder onBind(Intent intent) {
		switch (intent.getIntExtra("sender", 0)){
		case APP_DELEGATE:  {
			if (!sfsClient.isConnected()) this.connectToServer("some.server.host", 9933);
			Toast.makeText(this, "Main Activity binded...", Toast.LENGTH_LONG).show();
			break;
		}
		default: break;
		}
		if (mBinder==null) mBinder = new NP_Binder();
		return mBinder;
	}

	/** iBRIDGEBARON protocol methods
	 * 
	 */
	
	/** Connect to server
	 * 
	 */
	
	
	
	
	/** END iBRIDGEBARON protocol methods
	 * 
	 */
	
	
	@Override
	public void onCreate() {
		super.onCreate();
//		mBinder = new NP_Binder();
	    sfsClient = new SmartFox(true);		
		// Add event handlers
		sfsClient.addEventListener(SFSEvent.CONNECTION, this);
		sfsClient.addEventListener(SFSEvent.CONNECTION_LOST, this);
		sfsClient.addEventListener(SFSEvent.LOGIN, this);
		sfsClient.addEventListener(SFSEvent.ROOM_JOIN, this);
		sfsClient.addEventListener(SFSEvent.USER_ENTER_ROOM, this);
		sfsClient.addEventListener(SFSEvent.USER_EXIT_ROOM, this);
		sfsClient.addEventListener(SFSEvent.PUBLIC_MESSAGE, this);
		Toast.makeText(this, "Service created...", Toast.LENGTH_LONG).show();
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// super.onStartCommand(intent,flags, startId);
		// We want this service to continue running until it is explicitly
		// stopped, so return sticky.
		
		return START_STICKY;
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		Toast.makeText(this, "Service destroyed...", Toast.LENGTH_LONG).show();
	}

	@Override
	public void dispatch(final BaseEvent event) throws SFSException
	{
		Log.v("dispatch", event.getType());
		if(event.getType().equalsIgnoreCase(SFSEvent.CONNECTION))
		{
			//if the connections is successful login dialog is shown
			if(event.getArguments().get("success").equals(true))
			{
		    	sendBroadcast(new Intent(event.getType()));
			}
			//otherwise error message is shown
			else
			{
			}			    	
		}
		//When the connection is lost
		//the UI is disabled and message is shown
		else if(event.getType().equalsIgnoreCase(SFSEvent.CONNECTION_LOST))
		{
		}
		else if(event.getType().equalsIgnoreCase(SFSEvent.LOGIN))
		{					
			//safe the user/pass for other login requests
			this.username="";
			this.password="";
			
			// Join The Lobby room
			sendBroadcast(new Intent(event.getType()));   

		}
		//if the login is not successful then error message and
		//the login dialog are shown until it's successful
		else if(event.getType().equalsIgnoreCase(SFSEvent.LOGIN_ERROR))
		{
			sendBroadcast(new Intent(SFSEvent.CONNECTION));
		}
		//When the user joins new room then a message is added to
		//the chat history and the UI is enabled
		else if(event.getType().equalsIgnoreCase(SFSEvent.ROOM_JOIN))
		{
			Room room = (Room)event.getArguments().get("room");
			for(User user : room.getUserList()) {
				//		    			mUsers.add(user.getName());
			}
		}
		//When a user enter the room the user list is updated
		else if(event.getType().equals(SFSEvent.USER_ENTER_ROOM))
		{
			User user = (User)event.getArguments().get("user");
		}
		//When a user leave the room the user list is updated
		else if(event.getType().equals(SFSEvent.USER_EXIT_ROOM))
		{
			User user = (User)event.getArguments().get("user");
		}
		//When public message is received it's added to the chat history
		else if(event.getType().equals(SFSEvent.PUBLIC_MESSAGE))
		{
			User sender = (User)event.getArguments().get("sender");
			String msg = event.getArguments().get("message").toString();
		}
	}

	/** private SmartFox methods
	 * 
	 */
	
	/**
	 * Connects to SmartFoxServer instance.
	 * 
	 * @param ip the server IP.
	 * @param port the server port. 
	 */
	private void connectToServer(final String ip, final int port) {
		this.sfsClient.connect(ip, port);
	}
	
}

This is the activity code.

Code: Select all

public class BridgeBaronActivity extends Activity{
    /** Called when the activity is first created. */
	private NetworkProtocol mService;
    private Boolean isLoggedIn=false;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  Because we have bound to a explicit
            // service that we know is running in our own process, we can
            // cast its IBinder to a concrete class and directly access it.
        	@SuppressWarnings("unused")
			String tempstr=className.toShortString();
        	NetworkProtocol.NP_Binder tempBinder=((NetworkProtocol.NP_Binder) service);
        	mService = tempBinder.getService();

            // Tell the user about this for our demo.
            Toast.makeText(getApplicationContext(), "Binder call back ...",Toast.LENGTH_LONG).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
        	mService = null;
            Toast.makeText(getApplicationContext(), "Disconnected from service ...",
                    Toast.LENGTH_SHORT).show();
        }
    };

	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    
    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }


    @Override
    public void onRestart() {
        super.onRestart();
    }

	@Override
	public void onResume() {
		super.onResume();		
//		startService(npIntent);
//		registerReceiver(broadcastReceiver, new IntentFilter(NetworkProtocol.BROADCAST_ACTION));
	}
 
	@Override
	public void onPause() {
		super.onPause();
//		unregisterReceiver(broadcastReceiver);
//		stopService(npIntent); 		
	}
    
    public void bActionOnlineHelp(View view) { 
  /* Show table after good login
    	Intent myIntent = new Intent(view.getContext(), BridgeTable.class);
        startActivity(myIntent);
  */      
    	Intent npIntent=new Intent(getApplicationContext(), NetworkProtocol.class);
    	npIntent.putExtra("sender", NetworkProtocol.APP_DELEGATE);
    	startService(npIntent);
    	bindService(npIntent, mConnection, Context.CONTEXT_IGNORE_SECURITY);
	    registerReceiver(timelToLoginReceiver, new IntentFilter(SFSEvent.CONNECTION));
	    registerReceiver(timelToLoginReceiver, new IntentFilter(SFSEvent.LOGIN_ERROR));
       
/*    	WebView webview = new WebView(this);
    	 setContentView(webview);
    	 webview.loadUrl("http://www.greatgameproducts.com/ibridgebaron/help.cfm");
*/
    }

    private BroadcastReceiver timelToLoginReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           	Intent myIntent = new Intent(getApplicationContext(), LoginBox.class);
            startActivity(myIntent);
        	Toast.makeText(getApplicationContext(), "Time to login ...", Toast.LENGTH_LONG).show();       
        }
    };
    
}
[/code]
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Post by sylhouette »

I found out something interesting..can this help? I am still looking at it though
http://www.eigo.co.uk/Managing-State-in ... ivity.aspx
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Post by ThomasLund »

I'll fix the Base64 stuff in the next release. Just FYI

/Thomas
Full Control - maker of Unity/C# and Java SFS API and indie games
Follow on twitter: http://twitter.com/thomas_h_lund
sylhouette
Posts: 20
Joined: 09 Oct 2011, 01:29
Location: Indonesia
Contact:

Post by sylhouette »

Here's my successful approach:
  • I put the instance of SmartFox client into a class that extends Application and specify that in AndroidManifest.xml, in the <application android:name="myClass">

    I call the instance of sfsClient like this:

    Code: Select all

    sfsClient = ((SfsClient) getApplicationContext()).getSfsClient();
    I have to remove all event listeners onPause() of each activity.

    I have to re-attach event listeners each onCreate() of each activity, so that the events will properly dispatched to the current activity you are in.

    The drawback is that we cannot call sfsClient.disconnect() in onDestroy(). Therefore, I have to specify in the zone configurator the idle time for users is 15 minutes, otherwise, they will be disconnected.
I have successfully implemented this approach and it works. If anyone is able to come up with a better approach, please let me know :) Thank you :)
mihi
Posts: 12
Joined: 03 May 2018, 18:38

Re: Reusing sfsClient between Activities

Post by mihi »

Anyone found useful solution?
User avatar
Lapo
Site Admin
Posts: 23438
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Reusing sfsClient between Activities

Post by Lapo »

Hi,
this is a 10 year old post, so it would be best to post a new topic.
In any case, generally speaking an instance of the SmartFox client is valid until disconnection, then you need to create a new instance.

Cheers
Lapo
--
gotoAndPlay()
...addicted to flash games
Post Reply