moDev
moDev

Reputation: 5258

How to use signalr in Android

I am trying to integrate signalR in android app but no luck. I've been looking at various links but none of them provide proper information about implementation.

I've the following questions.

I've added three libraries i.e signalr android,signalr client and gson but unable to understand how code works, no proper documentation is available to understand the code.

Some of the questions asked but not much information

SignalR in Android Studio Unable to implement p2p chat using SignalR in Android

If anyone experienced in signal for native apps, it would be very helpful for me.

Update

    public class SignalRService extends Service {


    private static final String TAG = "Service";
    private HubConnection mHubConnection;
    private HubProxy mHubProxy;
    private Handler mHandler; // to display Toast message
    private final IBinder mBinder = new LocalBinder(); 

    private SharedPreferences sp;

    @Override
    public void onCreate() {
        super.onCreate();

        Utility.showLog(TAG, "Service Created");

        sp = getSharedPreferences(Utility.SHARED_PREFS, MODE_PRIVATE);
        mHandler = new Handler(Looper.myLooper());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        int result = super.onStartCommand(intent, flags, startId);
        startSignalR();
        return result;
    }

    @Override
    public IBinder onBind(Intent intent) {

        startSignalR();
        return mBinder;
    }

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        public SignalRService getService() {
            // Return this instance of SignalRService so clients can call public methods
            return SignalRService.this;
        }
    }

    /**
     * method for clients (activities)
     */
    public void sendMessage() {

        String SERVER_METHOD_SEND = "iAmAvailable";
        final String string = new String();

        mHubProxy.invoke(new String(), SERVER_METHOD_SEND, sp.getString("user_id", null), sp.getString("pass", null), "TransMedic").done(new Action() {
            @Override
            public void run(Object o) throws Exception {

                Utility.showLog(TAG, o.toString());

            }


        }).onError(new ErrorCallback() {
            @Override
            public void onError(Throwable throwable) {

            }
        });
    }

    private void startSignalR() {

        Platform.loadPlatformComponent(new AndroidPlatformComponent());

        String serverUrl = "http://transit.alwaysaware.org/signalr";

        mHubConnection = new HubConnection(serverUrl);

        String SERVER_HUB_CHAT = "ChatHub";

        mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);

        ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());

        SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);


        try {

            signalRFuture.get();

        } catch (InterruptedException | ExecutionException e) {

            e.printStackTrace();
            return;

        }

        sendMessage();
    }

    @Override
    public void onDestroy() {

        mHubConnection.stop();
        super.onDestroy();

    }
}

Upvotes: 39

Views: 68164

Answers (5)

Saeid Mohammadi
Saeid Mohammadi

Reputation: 449

do this tutorial step by step : https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?tabs=visual-studio-mac&view=aspnetcore-5.0

1.According above tutorial publish your chat server to favorite host

2.add this dependency to your android sample:

 implementation 'com.microsoft.signalr:signalr:3.0.0'
 

3.add these permission to manifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

4.below code is MainActivity.class:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HubConnection hubConnection = 
 HubConnectionBuilder.create("https://your_chat_server_url/chatHub").build();
        TextView textView = (TextView)findViewById(R.id.tvMain);
        ListView listView = (ListView)findViewById(R.id.lvMessages);
        Button sendButton = (Button)findViewById(R.id.bSend);
        EditText editText = (EditText)findViewById(R.id.etMessageText);

        List<String> messageList = new ArrayList<String>();
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(MainActivity.this,
                android.R.layout.simple_list_item_1, messageList);
        listView.setAdapter(arrayAdapter);


        hubConnection.on("ReceiveMessage", (user, message)-> {

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    arrayAdapter.add( user + " : " + message);
                    arrayAdapter.notifyDataSetChanged();
                }
            });
        }, String.class,String.class);

        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String message = editText.getText().toString();
                String user = "SAEID";
                editText.setText("");
                try {
                    hubConnection.send("SendMessage", user,message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        new HubConnectionTask().execute(hubConnection);
    }

    static class HubConnectionTask extends AsyncTask<HubConnection, Void, Void>{

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(HubConnection... hubConnections) {
            HubConnection hubConnection = hubConnections[0];
            hubConnection.start().blockingAwait();
            return null;
        }
    }
}

5.below code is activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:orientation="vertical" >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tvMain" />
    <ListView
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_width="fill_parent"
        android:id="@+id/lvMessages"
        android:transcriptMode="alwaysScroll">
    </ListView>
    <EditText
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:id="@+id/etMessageText"
        android:hint="Enter Message" />
    <Button
        android:text="Send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bSend" />
</LinearLayout>

Upvotes: 1

Mikael Mengistu
Mikael Mengistu

Reputation: 342

The SignalR team recently released a Java client for ASP.NET Core SignalR. Here is a link to getting started docs https://learn.microsoft.com/en-us/aspnet/core/signalr/java-client?view=aspnetcore-2.2

Upvotes: 2

BNK
BNK

Reputation: 24114

UPDATE 2018:

If you are using SignalR.net Core use this library otherwise you will get error on connection.

SERVER SIDE:

The following is my sample server-side code, you can pay attention to public void Send(string message) and public void SendChatMessage(string to, string message).

  • Server-side app: public void SendChatMessage(string to, string message)

    • Android client app: mHubProxy.invoke("SendChatMessage", receiverName, message);
  • Server-side app: public void Send(string message)

    • Android client app: mHubProxy.invoke("Send", message);
namespace SignalRDemo
{
    public class ChatHub : Hub
    {
        private static ConcurrentDictionary<string, string> FromUsers = new ConcurrentDictionary<string, string>();         // <connectionId, userName>
        private static ConcurrentDictionary<string, string> ToUsers = new ConcurrentDictionary<string, string>();           // <userName, connectionId>
        private string userName = "";

    public override Task OnConnected()
    {
        DoConnect();
        Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online" });
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        if (stopCalled) // Client explicitly closed the connection
        {
            string id = Context.ConnectionId;
            FromUsers.TryRemove(id, out userName);
            ToUsers.TryRemove(userName, out id);
            Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline" });
        }
        else // Client timed out
        {
            // Do nothing here...
            // FromUsers.TryGetValue(Context.ConnectionId, out userName);            
            // Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline By TimeOut"});                
        }

        return base.OnDisconnected(stopCalled);
    }

    public override Task OnReconnected()
    {
        DoConnect();
        Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online Again" });
        return base.OnReconnected();
    }

    private void DoConnect()
    {
        userName = Context.Request.Headers["User-Name"];
        if (userName == null || userName.Length == 0)
        {
            userName = Context.QueryString["User-Name"]; // for javascript clients
        }
        FromUsers.TryAdd(Context.ConnectionId, userName);
        String oldId; // for case: disconnected from Client
        ToUsers.TryRemove(userName, out oldId);
        ToUsers.TryAdd(userName, Context.ConnectionId);
    }

    public void Send(string message)
    {
        // Call the broadcastMessage method to update clients.            
        string fromUser;
        FromUsers.TryGetValue(Context.ConnectionId, out fromUser);
        Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = fromUser, Message = message });
    }

    public void SendChatMessage(string to, string message)
    {
        FromUsers.TryGetValue(Context.ConnectionId, out userName);
        string receiver_ConnectionId;
        ToUsers.TryGetValue(to, out receiver_ConnectionId);

        if (receiver_ConnectionId != null && receiver_ConnectionId.Length > 0)
        {
            Clients.Client(receiver_ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = message });
        }
    }        
}

public class ChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}
}

CLIENT SIDE:

If you have not read my answer at the following question:

SignalR integration in android studio

Then, here is my working basic code:

public class SignalRService extends Service {
    private HubConnection mHubConnection;
    private HubProxy mHubProxy;
    private Handler mHandler; // to display Toast message
    private final IBinder mBinder = new LocalBinder(); // Binder given to clients

public SignalRService() {
}

@Override
public void onCreate() {
    super.onCreate();
    mHandler = new Handler(Looper.getMainLooper());
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    int result = super.onStartCommand(intent, flags, startId);
    startSignalR();
    return result;
}

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

@Override
public IBinder onBind(Intent intent) {
    // Return the communication channel to the service.
    startSignalR();
    return mBinder;
}

/**
 * Class used for the client Binder.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with IPC.
 */
public class LocalBinder extends Binder {
    public SignalRService getService() {
        // Return this instance of SignalRService so clients can call public methods
        return SignalRService.this;
    }
}

/**
 * method for clients (activities)
 */
public void sendMessage(String message) {
    String SERVER_METHOD_SEND = "Send";
    mHubProxy.invoke(SERVER_METHOD_SEND, message);
}    

private void startSignalR() {
    Platform.loadPlatformComponent(new AndroidPlatformComponent());

    Credentials credentials = new Credentials() {
        @Override
        public void prepareRequest(Request request) {
            request.addHeader("User-Name", "BNK");
        }
    };

    String serverUrl = "http://192.168.1.100";
    mHubConnection = new HubConnection(serverUrl);
    mHubConnection.setCredentials(credentials);
    String SERVER_HUB_CHAT = "ChatHub";
    mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
    ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
    SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);

    try {
        signalRFuture.get();
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
        return;
    }

    String HELLO_MSG = "Hello from Android!";
    sendMessage(HELLO_MSG);

    String CLIENT_METHOD_BROADAST_MESSAGE = "broadcastMessage";
    mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
            new SubscriptionHandler1<CustomMessage>() {
                @Override
                public void run(final CustomMessage msg) {
                    final String finalMsg = msg.UserName + " says " + msg.Message;
                    // display Toast message
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
            , CustomMessage.class);
}
}

Activity:

public class MainActivity extends AppCompatActivity {

private final Context mContext = this;
private SignalRService mService;
private boolean mBound = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = new Intent();
    intent.setClass(mContext, SignalRService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
    // Unbind from the service
    if (mBound) {
        unbindService(mConnection);
        mBound = false;
    }
    super.onStop();
}    

public void sendMessage(View view) {
    if (mBound) {
        // Call a method from the SignalRService.
        // However, if this call were something that might hang, then this request should
        // occur in a separate thread to avoid slowing down the activity performance.
        EditText editText = (EditText) findViewById(R.id.edit_message);            
        if (editText != null && editText.getText().length() > 0) {                
            String message = editText.getText().toString();
            mService.sendMessage(message);
        }
    }
}

/**
 * Defines callbacks for service binding, passed to bindService()
 */
private final ServiceConnection mConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className,
                                   IBinder service) {
        // We've bound to SignalRService, cast the IBinder and get SignalRService instance
        SignalRService.LocalBinder binder = (SignalRService.LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        mBound = false;
    }
};
}

CustomMessage Class:

public class CustomMessage {
    public String UserName;
    public String Message;
}

You can also see my sample client project at this GitHub link


UPDATE FOR RESPONSE FROM INVOKE:

I have just added new sample methods:

Server side:

public string iAmAvailable(string username, string password, string message)
{            
     return "BNK Response for testing Android INVOKE";
}

Client side:

mHubProxy.invoke(String.class, "iAmAvailable", "username", "password", "TransMedic").done(new Action<String>() {
            @Override
            public void run(String s) throws Exception {
                Log.w("SimpleSignalR", s);
            }
        }).onError(new ErrorCallback() {
            @Override
            public void onError(Throwable throwable) {
                Log.e("SimpleSignalR", throwable.toString());
            }
        });

And here is the screenshot:

Android SignalR Invoke Response

Upvotes: 44

Ahmad Aghazadeh
Ahmad Aghazadeh

Reputation: 17131

This work for me : Full source Android (Client) & Server GitHub

Server Slide If one argument must use this interface SubscriptionHandler1 if two argument must use this interfaceSubscriptionHandler2 ,...

Sample for two argument like :

Server slide :

using Microsoft.AspNet.SignalR;
namespace SignalRChat
{
    public class ChatHub : Hub
    {
        public void Send(string name, string message)
        {
            // Two argument must use this interfaceSubscriptionHandler2 .
            Clients.All.broadcastMessage(name, message);
        }

    }
}

Client slide :

mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
                new SubscriptionHandler2<String, String>() {
                    @Override
                    public void run(final String name,final String msg) {
                        final String finalMsg =  msg.toString();
                        // display Toast message
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                }
                , String.class,String.class);

For catch all message can use this :

mHubConnection.received(new MessageReceivedHandler() {

            @Override
            public void onMessageReceived(final JsonElement json) {
                Log.e("onMessageReceived ", json.toString());
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), json.toString(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

Upvotes: 2

Umar Farooque
Umar Farooque

Reputation: 2059

For those who are implementing signalR client in android and the given answer here doesn't help in receiving the messages can check out this answer by rejnev.

The answer implements a different method connection.received() which is able to receive message callbacks from the server in my case.

Upvotes: 0

Related Questions