【问题标题】:Android : Keep httpclient running in background and communicate with activity when message recieved by httpclientAndroid : 让 httpclient 在后台运行并在 httpclient 收到消息时与活动通信
【发布时间】:2016-04-18 06:20:10
【问题描述】:

抱歉标题过长,但我不知道该怎么做。我正在开发一个 Android 项目,在该项目中我试图使用一个名为 Cometd 的 PUSH 框架。现在 Cometd 使用 Bayuex API/规范来实现 PUSH 服务。

当您考虑用户登录或服务器端可扩展异步应用程序的普通网站部分时,Cometd 的工作简直太棒了,没有抱怨。我们面临的问题是在应用程序方面。没有可用的支持或文档。

经过一些努力,我能够编写代码以在 Android 应用程序中使用 Cometd,但不幸的是,它存在错误且不稳定。我将描述我如何构建它的结构,描述问题并展示代码。

有 2 个主要的 Activity 类,ConversationActivityChatMessagesActivity。在 ConversationActivity 中,我启动 httpclient 并通过握手连接到 PUSH 服务。

在同一个类中,有一个 ClientSessionChannel 监听器不断监听新消息。在 ConversationActivity 类中,我从服务器的数据库中检索登录用户的所有对话。这完成了,所以用户可以决定他/她想和谁聊天。

现在,当点击对话时,我会在另一个活动中为这两个用户打开聊天消息。

现在,不要忘记每个新消息都到达 ConversationActivity 中的 ClientSessionChannel 监听器,这就是为什么我必须在 ConversationActivity 和 ChatMessagesActivity 之间保持通信链接的原因,这会导致各种不稳定。

现在,我想做的是将监听器和 httpclient 推送到另一个线程/类/背景等中。 完成后,如何保持聊天消息之间的开放通信它们被打开,因此消息被更新。我希望问题很清楚。我现在将发布代码。请看:

public class ConversationActivity extends ApplicationDrawerLoader {

    static Context context;
List<PrivateChannel> privateChannelList = new ArrayList<>();

    List<Long> groupAccountList = new ArrayList<>();

    List<String> pushPrivateChannelList = new ArrayList<>();

    ListView conversationsList;

    ConversationAdapter conversationAdapter;

 protected static volatile BayeuxClient client;
    private ChatListener chatListener = new ChatListener();

    HttpClient httpClient = StaticRestTemplate.getHttpClient();

    String defaultURL = StaticRestTemplate.baseURL + "/cometd";

 @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_conversations);

        context = this;


        try {
            httpClient.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (isOnline()) {
            new getConversationsForLoggedInUser(this).execute();

            ClientTransport clientTransport = new LongPollingTransport(null, httpClient);
            client = new BayeuxClient(defaultURL, clientTransport);
            client.putCookie(new HttpCookie("JSESSIONID", StaticRestTemplate.getJsessionid()));
            client.getChannel(Channel.META_HANDSHAKE).addListener(new InitializerListener());
            client.getChannel(Channel.META_CONNECT).addListener(new ConnectionListener());
            client.handshake();
            boolean success = client.waitFor(2000, BayeuxClient.State.CONNECTED);
            if (!success) {
                Toast.makeText(getApplicationContext(), "Could not connect to PUSH service", Toast.LENGTH_LONG).show();
            }

 public class getConversationsForLoggedInUser extends AsyncTask<Void, Void, ResponseEntity<PrivateChannel[]>> {

client.getChannel("/person/"+privateChannel.getPrivateChannelName()).subscribe(chatListener);

// Here I get all conversations for user and load them in ListView using adapter
}
  public void sendMessageToChatActivity(final String channelName, final Map<String,Object> outputData) {
        // Here send the message to Chat activity.
        Log.d("Msg",String.valueOf(outputData.toString()));
        runOnUiThread(new Runnable() {
            public void run() {
                ChatMessagesActivity.recieveUpdatedMessage(channelName, outputData);
            }
        });
    }

    public static void recieveMessageFromChatActivity(String channelName, Map<String, Object> inputData){
        //client.putCookie(new HttpCookie("JSESSIONID", StaticRestTemplate.getJsessionid()));
        client.getChannel(channelName).publish(inputData);

    }

    private void initialize() {

    }

    private void connectionEstablished() {
        System.err.printf("system: Connection to Server Opened%n");

    }

    private void connectionClosed() {
        System.err.printf("system: Connection to Server Closed%n");
    }

    private void connectionBroken() {
        System.err.printf("system: Connection to Server Broken%n");
    }

    private class InitializerListener implements ClientSessionChannel.MessageListener {
        @Override
        public void onMessage(ClientSessionChannel channel, Message message) {
            if (message.isSuccessful()) {
                initialize();
            }
        }
    }

    private class ConnectionListener implements ClientSessionChannel.MessageListener {
        private boolean wasConnected;
        private boolean connected;

        public void onMessage(ClientSessionChannel channel, Message message) {
            if (client.isDisconnected()) {
                connected = false;
                connectionClosed();
                return;
            }
            wasConnected = connected;
            connected = message.isSuccessful();
            if (!wasConnected && connected) {
                connectionEstablished();
            } else if (wasConnected && !connected) {
                connectionBroken();
            }
        }
    }

// THe below method sends message to the ChatMessagesActivity.
   public class ChatListener implements ClientSessionChannel.MessageListener {
        public void onMessage(ClientSessionChannel channel, Message message) {
            sendMessageToChatActivity(channel.toString(),message);
        }
    }

ChatMessagesActivity :

public class ChatMessagesActivity extends ApplicationDrawerLoader {
    Context context;

    public ListView chatList;

    protected static ChatMessagesAdapter chatMessagesAdapter = null;
 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat_messages);

        chatList = (ListView) findViewById(R.id.chatList);
        context = this;

 sendMessageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
// Here I send message to ConversationActivity, which in turn sends it via CometdD.
                    sendMessageToConversationsActivity("/service/person/" + String.valueOf(conversationId), outputData);
}


    public static void recieveUpdatedMessage(String channelName, Map<String, Object> input) {

// Here I recieve the new message and add it to adapter. 
                    HashMap<String, String> insertMap = new HashMap<>();
// Add data to hashmap and add it to adapter 
 chatMessagesAdapter.add(insertMap);
                    chatMessagesAdapter.notifyDataSetChanged();
}
 public void sendMessageToConversationsActivity(String channelName, Map<String, Object> output) {
        ConversationActivity.recieveMessageFromChatActivity(channelName, output);
    }

    public class getPrivateChatsForUser extends AsyncTask<Void, Void, ResponseEntity<RestReplies[]>> {

        ChatMessagesActivity chatMessagesActivity = null;

        getPrivateChatsForUser(ChatMessagesActivity chatMessagesActivity) {
            this.chatMessagesActivity = chatMessagesActivity;

        }
// Here i get chat messages from network and load it in list-view using adapter.,


    public class ChatMessagesAdapter extends BaseAdapter {

// Adapter code to display, nothing fancy. 
}
}

我知道代码似乎有点长,但问题也是如此。我将不胜感激这方面的任何帮助。我不是 Android 专家,但只是想整合它。谢谢你。 :-)

编辑

public class CometdService extends Service {

    protected static volatile BayeuxClient client;

    String defaultURL = StaticRestTemplate.baseURL + "/cometd";


    HttpClient httpClient = StaticRestTemplate.getHttpClient();

    private ChatListener chatListener = new ChatListener();


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        try {
            httpClient.start();
        } catch (Exception e) {
            e.printStackTrace();
        }


        ClientTransport clientTransport = new LongPollingTransport(null, httpClient);
        client = new BayeuxClient(defaultURL, clientTransport);
        client.putCookie(new HttpCookie("JSESSIONID", StaticRestTemplate.getJsessionid()));
        client.getChannel(Channel.META_HANDSHAKE).addListener(new InitializerListener());
        client.getChannel(Channel.META_CONNECT).addListener(new ConnectionListener());
        client.handshake();
        boolean success = client.waitFor(2000, BayeuxClient.State.CONNECTED);
        if (!success) {
            Toast.makeText(getApplicationContext(), "Could not connect to PUSH service", Toast.LENGTH_LONG).show();
        }


        return Service.START_REDELIVER_INTENT;
    }

    @Override
    public void onDestroy() {

    }

    private class InitializerListener implements ClientSessionChannel.MessageListener {
        @Override
        public void onMessage(ClientSessionChannel channel, Message message) {
            if (message.isSuccessful()) {
                initialize();
            }
        }
    }

    private class ConnectionListener implements ClientSessionChannel.MessageListener {
        private boolean wasConnected;
        private boolean connected;

        public void onMessage(ClientSessionChannel channel, Message message) {
            if (client.isDisconnected()) {
                connected = false;
                connectionClosed();
                return;
            }
            wasConnected = connected;
            connected = message.isSuccessful();
            if (!wasConnected && connected) {
                connectionEstablished();
            } else if (wasConnected && !connected) {
                connectionBroken();
            }
        }
    }

    private void initialize() {

    }

    private void connectionEstablished() {
        System.err.printf("system: Connection to Server Opened%n");

    }

    private void connectionClosed() {
        System.err.printf("system: Connection to Server Closed%n");
    }

    private void connectionBroken() {
        System.err.printf("system: Connection to Server Broken%n");
    }

    public class ChatListener implements ClientSessionChannel.MessageListener {
        public void onMessage(ClientSessionChannel channel, Message message) {
           // sendMessageToChatActivity(channel.toString(), message);
        }
    }
}

一个新创建的服务,正在进行中。

【问题讨论】:

  • In ConversationActivity, I start a httpclient and connect to the PUSH service by performing a handshake. 错误,这是一个应该启动这样一个连接的Service
  • @pskink :好的,正如 csenga 也提到的,我应该在服务中启动 httpclient。只有一件事,ConnectionListener 类是否也应该在 Service 中?
  • 是的,不像其他人说不要使用BroadcastReceivers,而是阅读“绑定服务”
  • @pskink :好的,这肯定需要一些时间来实现,我会随着时间的推移进行编辑。非常感谢。 :-)
  • @pskink :我创建了一个新服务并将连接和相关代码放在那里。要使用 PUSH 框架,我必须订阅频道。现在的问题是,我必须使用 BayeuxClient 实例化,但是因为它已移至 Service,所以我无法订阅 ConversationActivity 中的频道。您能否检查一下 ConversationActivity 中的 AsyncTask 和我添加的 Service 类。谢谢.. :-)

标签: java android push comet cometd


【解决方案1】:

使用如下服务在后台运行。

public class BackgorundService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        new HttpCall().execute();
    }


    private class HttpCall extends AsyncTask<String, String, String> {

        @Override
        protected String doInBackground(String... strings) {
            String response = "";
            // DO the HttpConnection here
            return response;
        }

        @Override
        protected void onPostExecute(String s) {
            Intent in = new Intent();
            in.setAction("MyAction");
            in.putExtra("name", s);
            sendBroadcast(in);
        }
    }

}

在您的活动中调用上述服务并注册您的静态广播接收器。

公共类 MyActivity 扩展 AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_lesson);
    startService(new Intent(this, BackgroundService.class));
    registerReceiver(new MyReceiver(),new IntentFilter("MyAction"));
}

public class MyReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals("MyAction")){
            String value = intent.getExtras().getString("name");
             // Response from the background service..
        }

    }
}

}

【讨论】:

    【解决方案2】:

    似乎IntentService 可能有助于与服务器的通信。

    【讨论】:

    • 您能详细说明一下吗?谢谢。
    • 另外,我如何推送 httpclient 和 messageListener,它们在其他类的后台运行。这样,即使用户正在使用其他功能,我也可以显示通知。
    • 您可以只初始化服务中的所有内容,并让活动与服务通信,而不是彼此通信。对于与服务的通信,您可以使用广播接收器或 EventBus link
    【解决方案3】:

    我没有阅读你的整个问题,因为标题说了很多,我建议你使用BroadcastReceiver来达到这个目的。

    步骤:

    1. 在您的活动中注册广播接收器。
    2. 从您的 HTTPClient 发送广播消息。

    您可以阅读它here。除此之外,您还可以在线阅读许多关于如何使用它们的简单教程。

    【讨论】:

    • 好的,你是说我应该注册 ChatMessagesActivity 来接收广播,每当有新消息时,我应该通过广播发送它?当我移动到另一个时,ConversationActivity 不会死吗?
    • @WeareBorg 好吧,你可以搜索如何以编程方式注册广播接收器。然后,您可以在任何您想要的活动中注册它,并在不再需要时取消注册。
    【解决方案4】:

    您应该使用Service 并在那里启动您的httpclient。要更新您的 Activties,您应该使用 BroadcastReceiver 和/或 Activity 的 onResume 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-06
      • 1970-01-01
      • 1970-01-01
      • 2018-01-12
      相关资源
      最近更新 更多