当前位置:首页 » 《休闲阅读》 » 正文

Android:使用Service处理息屏后的WebSocket的服务端推送消息并传递给前端

23 人参与  2024年12月30日 08:02  分类 : 《休闲阅读》  评论

点击全文阅读


前言


之前我们在使 RESTful 访问服务端时,一般都是客户端请求服务端应答的方式,这种通讯方式,对于需要持续获取数据的情形都是采用轮询的方式,但是这种方式对两边的性能消耗很大,特别是服务端的压力很大。现在当我们使用WebSocket时,这类问题迎刃而解了。服务端可以根据需要向客户端实时推送消息。
WebSocket的协议很简单,客户端的onMessage事件可以很方便的接收消息。我们可以收到后传递给前端的Activity处理更新UI.

现代手机为了省电,屏幕在很短的时候就关闭了,但有时候我们不希望屏幕一灭,应用也跟着睡着了,手机息屏后,我们仍然希望用户能通过语音给用户传递信息。就像你切换屏幕了,高德能继续给你语音导航一样。这种情况单独使用Activity是无能为力的,Android系统设计为只要屏幕一灭,任何Activity的UI活动都会睡眠。不过,息屏后,后台的WebSocket客户端是能够继续接受消息的,但是没办法传到前端的活动了。这时候我们需要引入Android的Service来处理。
在讲解正文之前我们先回顾一下WebSocket的基本使用。

WebSocket与回调接口

在之前的文章中我们介绍了WebSocket的基本使用。就是利用OkHttp3的WebSocket相关功能搭建自己的调用程序框架。这里为了适应Service的使用,我们对WebSocket程序稍微加以改造一下。
首先我们将静态方法改造为实例方法,并使用单例模式。
其次,我们引入一个回调接口,这个很关键。
WebSocketListenerCallback

public interface WebSocketListenerCallback {    void onDataReceived(String action, String data);    void onFailure(String text);}


它包含2个方法,
onDataReceived 处理收到消息后怎么办
onFailure 处理通讯失败

我们的WebSocket程序设计为可以设定多个Callback, 在onMessage 收到消息后,逐个执行每个Callback的onDataReceived方法。整个WebSocket程序的代码如下。使用前需要引入Okhttp3库。

import androidx.annotation.NonNull;import androidx.annotation.Nullable;import com.bob.app.C;import java.util.ArrayList;import java.util.List;import java.util.concurrent.TimeUnit;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import okhttp3.WebSocket;import okhttp3.WebSocketListener;public class WSClient {    private static WSClient instance;    private WebSocket socket;    private final List<WebSocketListenerCallback> callbacks = new ArrayList<>();    private final OkHttpClient client;    private WSClient() {        OkHttpClient.Builder cb = new OkHttpClient.Builder();        cb.readTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);        cb.connectTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);        cb.writeTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);        client = cb.build();    }    public static synchronized WSClient getInstance() {        if (instance == null) {            instance = new WSClient();        }        return instance;    }    public synchronized void init() {        Request request = new Request.Builder().url(C.serverUrl).build();        WSListener listener = new WSListener();        socket = client.newWebSocket(request, listener);    }    public void addCallback(WebSocketListenerCallback callback) {        if (!callbacks.contains(callback)) {            callbacks.add(callback);        }    }    public void removeCallback(WebSocketListenerCallback callback) {        callbacks.remove(callback);    }    public synchronized void send(String action, Object data) {        long txNo = System.currentTimeMillis();        String message = action + "$$" + txNo + "$$" + U.toJSONString(data);        if (socket != null) {            socket.send(message);        }    }    public void disconnect(int code, String reason) {        if (socket != null) {            socket.close(code, reason);            socket = null;        }    }    private class WSListener extends WebSocketListener {        @Override        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {            for (WebSocketListenerCallback callback : callbacks) {                String[] str = text.split("\\$\\$");                String action = str[0];//操作指令,                String message = str.length > 2 ? str[2] : "";//信息正文                callback.onDataReceived(action, message);            }        }        @Override        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {            for (WebSocketListenerCallback callback : callbacks) {                callback.onFailure("Error: " + t.getMessage());//错误信息传出去            }        }    }}

C是一个常数类,里面有些常数,比如SOCKET_TIMEOUT = 5;


Service接收推送消息


关于Service的基本使用,网上有很多资料。我们这里通过实际的场景应用来消化理解。

我们现在的应用场景就是:收到后台消息后,通过Service传递给前端应用,然后语音播报。
首先自己的Service继承基类,并且需要实现回调方法WebSocketListenerCallback
在初始化WebSocket的时候我们就把Callback传进去。并初始化。

整个Service的完整代码如下:

import android.app.Notification;import android.app.NotificationChannel;import android.app.NotificationManager;import android.app.Service;import android.content.Intent;import android.os.IBinder;import androidx.annotation.Nullable;import androidx.core.app.NotificationCompat;import androidx.localbroadcastmanager.content.LocalBroadcastManager;import com.bob.app.common.WSClient;import com.bob.app.common.WebSocketListenerCallback;public class ActiveDataService extends Service implements WebSocketListenerCallback {    @Override    public void onCreate() {        super.onCreate();        WSClient wsClient = WSClient.getInstance();//获取实例        wsClient.addCallback(this);//传入Callback        wsClient.init();//执行初始化    }    public void onDataReceived(String action, String str) {        if (A.ACTIVE_DATA.equals(action)) {//如果指令是推送新消息            Intent intent = new Intent(BC.BROADCAST_ACTIVE_DATA);//收到最新实时消息            intent.putExtra("data", str);            LocalBroadcastManager.getInstance(this).sendBroadcast(intent);        }    }    public void onFailure(String str) {        Intent intent = new Intent(BC.BROADCAST_SERVER_OFFLINE);//服务端访问异常        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        startForeground(1, createNotification());        return START_STICKY;    }    private Notification createNotification() {        NotificationChannel channel = new NotificationChannel("channel", "Service", NotificationManager.IMPORTANCE_LOW);        NotificationManager manager = getSystemService(NotificationManager.class);        if (manager != null) {            manager.createNotificationChannel(channel);        }        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel")                .setContentTitle(getString(R.string.app_name))                .setContentText(getString(R.string.fetching_data))                .setSmallIcon(android.R.drawable.ic_notification_overlay)                .setAutoCancel(true)                .setOngoing(false)                .setPriority(NotificationCompat.PRIORITY_DEFAULT);  // Make sure the priority is LOW        return builder.build();    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onDestroy() {        super.onDestroy();    }}

其中BC.BROADCAST_ACTIVE_DATA和BC.BROADCAST_SERVER_OFFLINE是2个常数字符串,可以自己取名字。

在Android里面,由于Activity和Service的生命周期并不相同,Service息屏后还能活动,因此Service并不能直接引用Activity也不能直接与Activity交互,只能通过广播间接交互。意思就是Service发送广播,Activity可以订阅后处理。上面的代码展示了怎样发送广播。

createNotification是一个常规方法,创建一个通知信息栏。这个会让用户知道一个后台服务在干嘛。有的手机会显示比如华为等,有的手机也默认不显示,比如Oppo等

Activity接收Service广播信息

Service发送的广播,Activity可以通过订阅的方式来使用。

具体的订阅方法如下:

 private final BroadcastReceiver receiver1 = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            String data = intent.getStringExtra("data");            finishGetActiveData(data);//接收到最新数据,传递到处理方法        }    };    private final BroadcastReceiver receiver2 = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            showLogin();//接收到网络异常,退回到登录界面        }    };

先声明BroadcastReceiver, receiver的目的就是声明收到数据后怎么办。在onReceive方法里接收数据,然后传递到实现方法里面。这里我们声明了2个receiver,一个是收到正常的最新信息后怎么办。另一个是收到网络错误信息后怎么办。

然后在onCreate方法里注册receiver

Intent serviceIntent = new Intent(this, ActiveDataService.class);ContextCompat.startForegroundService(this, serviceIntent);LocalBroadcastManager.getInstance(this).registerReceiver(receiver1, new IntentFilter(BC.BROADCAST_ACTIVE_DATA));LocalBroadcastManager.getInstance(this).registerReceiver(receiver2, new IntentFilter(BC.BROADCAST_SERVER_OFFLINE));

注意其中的 IntentFilter的用法,一般一个receiver只过滤接收一种消息。

这样我们就完成把消息传送给Activity这个数据链路了。

然后我们可以在finishGetActiveData方法里面干该干的事情了,比如调用TTS语音播报啥的。

看到这里,有的小伙伴可能会问,这个Callback我能不能在Activity里将WebSocket初始化,然后传入Callback呢,答案是可以的,可以将Activity实现WebSocketListenerCallback后传入WebSocket,也是一样的效果,还更简单,不用广播。不过前面我们讲过,这种直接使用Activity初始化WebSocket的程序只能在亮屏时功能正常,息屏了程序就跟着睡眠了。

另外,需要强调的一点时,要实现息屏后服务正常,需要手机在设置里关闭省电功能,并且允许应用后台活动。否则写了也白搭。只有用户才有最终决定权。

总结

要想息屏后,部分手机功能正常接收后台推送消息并传递给Activity处理,需要做如下处理:

- WebSocket引入回调接口并在onMesssge中调用。

- 创建一个Service实现回调接口,在回调方法中发送广播

- Activity中声明接收广播的receiver,并实现处理receiver收到的的数据。


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/209048.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1