如何在 Android 中使用信号器

How to use signalr in Android

我正在尝试将 signalR 集成到 android 应用程序中,但没有成功。我一直在查看各种链接,但其中 none 提供了有关实施的正确信息。

我有以下问题。

我已经添加了三个库,即 signalr androidsignalr clientgson,但无法理解代码的工作原理,没有合适的文档来理解代码。

问了一些问题但信息不多

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

如果有人在本机应用程序信号方面有经验,那对我很有帮助。

更新

    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();

    }
}

2018 年更新:

如果您正在使用 SignalR.net Core,请使用 this library 否则您将在连接时出错。

服务器端:

以下是我的示例服务器端代码,大家可以关注public void Send(string message)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; }
}
}

客户端:

如果您还没有阅读我对以下问题的回答:

SignalR integration in android studio

然后,这是我的工作基本代码:

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;
    }
};
}

自定义消息 Class:

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

您还可以在 this GitHub link

查看我的示例客户端项目

调用响应更新:

我刚刚添加了新的示例方法:

服务器端:

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

客户端:

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());
            }
        });

这是屏幕截图:

对于那些在 android 中实现 signalR 客户端并且此处给出的答案对接收消息没有帮助的人可以查看 答案通过 rejnev.

答案实现了一个不同的方法connection.received(),在我的例子中,它能够从服务器接收消息回调。

这对我有用:完整源代码 Android(客户端)和服务器 GitHub

Server Slide 如果一个参数必须使用此接口 SubscriptionHandler1 如果两个参数必须使用此接口 SubscriptionHandler2,...

两个参数的示例,例如:

服务器幻灯片:

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);
        }

    }
}

客户端幻灯片:

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

要捕获所有消息可以使用这个:

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();
                    }
                });
            }
        });

SignalR 团队最近为 ASP.NET Core SignalR 发布了 Java 客户端。这是 link 入门文档 https://docs.microsoft.com/en-us/aspnet/core/signalr/java-client?view=aspnetcore-2.2

逐步完成本教程: https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?tabs=visual-studio-mac&view=aspnetcore-5.0

1.According 以上教程将您的聊天服务器发布到最喜欢的主机

2.add 对您的 android 样本的依赖:

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

3.add 这些权限 manifest.xml

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

4.below 代码是 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 代码为 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>