Reusing sfsClient between Activities

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

Moderators: Lapo, Bax

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

Reusing sfsClient between Activities

Postby sylhouette » 13 Oct 2011, 04:13

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: 244
Joined: 09 Nov 2010, 00:48
Contact:

Postby mmilen » 17 Oct 2011, 13:57

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

Postby stix » 17 Oct 2011, 15:35

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:

Postby sylhouette » 19 Oct 2011, 14:54

@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: 244
Joined: 09 Nov 2010, 00:48
Contact:

Postby mmilen » 19 Oct 2011, 15:01

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

Postby sylhouette » 19 Oct 2011, 15:44

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: 244
Joined: 09 Nov 2010, 00:48
Contact:

Postby mmilen » 19 Oct 2011, 16:31

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: 244
Joined: 09 Nov 2010, 00:48
Contact:

Postby mmilen » 19 Oct 2011, 19:57

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:

Postby sylhouette » 20 Oct 2011, 02:30

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: 244
Joined: 09 Nov 2010, 00:48
Contact:

Postby mmilen » 20 Oct 2011, 02:40

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:

Postby sylhouette » 20 Oct 2011, 04:04

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

Postby ThomasLund » 20 Oct 2011, 15:01

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:

Postby sylhouette » 20 Oct 2011, 15:09

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 :)

Return to “SFS2X Java / Android API”

Who is online

Users browsing this forum: No registered users and 2 guests