【问题标题】:How to use signalr in Android如何在 Android 中使用信号器
【发布时间】:2015-12-10 23:41:45
【问题描述】:

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

我有以下问题。

  • SignalR 集成必须在 Service/Intent Service 内部完成?
  • 如果我们想通过相同的调用方法接收响应,那么如何获取?

我添加了三个库,即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();

    }
}

【问题讨论】:

  • 你的“mHubProxy.invoke”看起来和我的不一样,不知道是不是和你的服务器Hub定义一样。它的第一个参数是一个空白字符串?此外,你在哪里使用“字符串”变量?
  • @BNK 我想从调用接收回调,为此我使用了 new String()
  • 嗨!现在我了解了您对调用响应的想法,您使用 public &lt;E&gt; SignalRFuture&lt;E&gt; invoke(final Class&lt;E&gt; resultClass, final String method, Object... args) :)
  • 为什么不试试String.class 而不是new String() 比如mHub.invoke(String.class, "hello", "123").done(new Action&lt;String&gt;() { @Override public void run(String s) throws Exception { } }).onError(new ErrorCallback() { @Override public void onError(Throwable throwable) { } });
  • 我刚刚用你的服务器Url http://transit.alwaysaware.org/signalr测试过,可能你的服务器Hub名字不是ChatHub,因为我的app得到了[InvalidOperationException]: &amp;#39;chathub&amp;#39; Hub could not be resolved.

标签: c# android .net asp.net-mvc signalr


【解决方案1】:

2018 年更新:

如果您使用的是SignalR.net Core,请使用this library,否则您会收到连接错误。

服务器端:

以下是我的示例服务器端代码,可以关注public void Send(string message)public void SendChatMessage(string to, string message)

  • 服务器端应用程序:public void SendChatMessage(string to, string message)

    • Android 客户端应用:mHubProxy.invoke("SendChatMessage", receiverName, message);
  • 服务器端应用:public void Send(string message)

    • Android 客户端应用: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);
}
}

活动:

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

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

您还可以在this GitHub link 上查看我的示例客户端项目


INVOKE 响应更新:

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

服务器端:

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

这是截图:

【讨论】:

  • 如何检查连接是否建立
  • 基本上我有两个不同的电话给signalR。在一个调用中,它发送少量参数并接收响应,而在另一个调用中,它接收来自不同方法的响应,就像您在广播消息中提到的那样。第一次来电怎么办?
  • 我收到错误java.util.concurrent.ExecutionException: microsoft.aspnet.signalr.client.transport.NegotiationException: There was a problem in the negotiation with the server
  • 我找到了 JSON 的解决方案。感谢您的帮助。
  • 看起来closed只在服务器终止连接时调用,我稍后尝试停止并启动IIS,然后得到以下logcat 12-04 11:43:52.555 4105-5449/com.example.signalrclient E/SignalRClient: cec7f6e8-0d9b-43f0-abdd-18c1265d6452 connectionSlow! 12-04 11:43:59.256 4105-5707/com.example.signalrclient E/SignalRClient: cec7f6e8-0d9b-43f0-abdd-18c1265d6452 reconnecting! 12-04 11:43:59.296 4105-5710/com.example.signalrclient E/SignalRClient: cec7f6e8-0d9b-43f0-abdd-18c1265d6452 Disconnected!
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    这项工作适合我:完整源代码 Android(客户端)和服务器 GitHub

    服务器幻灯片 如果一个参数必须使用这个接口 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();
                        }
                    });
                }
            });
    

    【讨论】:

    • 谢谢我需要这部分“为了抓住所有消息可以使用这个”
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      1.根据上面的教程将您的聊天服务器发布到喜欢的主机

      2.将此依赖项添加到您的 android 示例中:

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

      3.将这些权限添加到 manifest.xml

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

      4.下面的代码是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.下面的代码是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>
      

      【讨论】:

      • @Saied Mohammadi 为什么你认为信号 r 在 redmi 设备中不起作用我正在发送消息但在聊天中它没有显示
      • @sashabeliy 你在其他设备上测试过吗?
      猜你喜欢
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 2021-10-14
      • 2021-12-05
      • 1970-01-01
      相关资源
      最近更新 更多