定时获取来自服务器的消息

定时获取来自服务器的消息

功能描述:

  界面中有一个消息功能,点击消息可以查看消息列表。
定时向服务器发送请求接口获取最新消息,并将消息数量以角标得形式显示到消息功能上,
从而用户可以直观得了解到接收了几条消息。

思路

step 1:开一个服务,定时发送请求到服务器,获取消息
    知识点:定时发送:observable.interval();
            网络请求:okhttp;
            后台运行:service;

step 2:得到消息实体后,通知界面更新(activity或者fragment)

    
step 3:显示消息数量的角标到对应控件
    知识点:badgeview;

实现

1,在后台不停跑的service-MonitorService

public class MonitorService extends Service {
    public MonitorService() {
    }

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

    @Override
    public void onCreate() {
        LogUtil.d("onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.d("onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        LogUtil.d("onDestroy");
        super.onDestroy();
    }

    private MonitorBinder binder = new MonitorBinder();
    public class MonitorBinder extends Binder{

        /**
         * 每隔几秒向服务器发送请求,获取最新消息数量
         * @param  intervalSecond 时间间隔,单位秒
         */
        public Observable<String> getNewMessage(int intervalSecond){
            // 默认5秒
            if (intervalSecond<1) intervalSecond = 5;
            return io.reactivex.Observable
                    .interval(intervalSecond, TimeUnit.SECONDS)
                    .flatMap(new Function<Long, ObservableSource<String>>() {
                        @Override
                        public ObservableSource<String> apply(Long aLong) throws Exception {
                            return io.reactivex.Observable                    .create(new ObservableOnSubscribe<String>() {
                                @Override
                                public void subscribe(final ObservableEmitter<String> emitter) throws Exception {
                                    LogUtil.e("------subscribe-------");
                                    MessageModel model = new MessageModel();
                                    model.loadData(  "", new MessageModel.OnMessageRequestListener() {
                                        @Override
                                        public void onFailed(IOException e) {
                                            emitter.onNext("");
                                        }

                                        @Override
                                        public void onSuccess(String json) {
                                            emitter.onNext(json);
                                        }
                                    });
                                }
                            });
                        }
                    });
        }

    }
}

2,网络请求需要的Module

public class MessageModel {

    private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient();

    public void loadData(String url, final OnMessageRequestListener listener){
        LogUtil.e(url);
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = OK_HTTP_CLIENT.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                listener.onFailed(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                listener.onSuccess(response.body().string());
            }
        });

    }

    public interface OnMessageRequestListener{
        void onFailed(IOException e);
        void onSuccess(String json);
    }
}

3,设置角标的view

public class BadgeView extends TextView {
    private boolean mHideOnNull;

    public BadgeView(Context context) {
        this(context, (AttributeSet)null);
    }

    public BadgeView(Context context, AttributeSet attrs) {
        this(context, attrs, 16842884);
    }

    public BadgeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mHideOnNull = true;
        this.init();
    }

    private void init() {
        if (!(this.getLayoutParams() instanceof LayoutParams)) {
            LayoutParams layoutParams = new LayoutParams(-2, -2, 53);
            this.setLayoutParams(layoutParams);
        }

        this.setTextColor(-1);
        this.setTypeface(Typeface.DEFAULT_BOLD);
        this.setTextSize(2, 11.0F);
        this.setPadding(this.dip2Px(5.0F), this.dip2Px(1.0F), this.dip2Px(5.0F), this.dip2Px(1.0F));
        this.setBackground(9, Color.parseColor("#d3321b"));
        this.setGravity(17);
        this.setHideOnNull(true);
        this.setBadgeCount(0);
    }

    public void setBackground(int dipRadius, int badgeColor) {
        int radius = this.dip2Px((float)dipRadius);
        float[] radiusArray = new float[]{(float)radius, (float)radius, (float)radius, (float)radius, (float)radius, (float)radius, (float)radius, (float)radius};
        RoundRectShape roundRect = new RoundRectShape(radiusArray, (RectF)null, (float[])null);
        ShapeDrawable bgDrawable = new ShapeDrawable(roundRect);
        bgDrawable.getPaint().setColor(badgeColor);
        this.setBackgroundDrawable(bgDrawable);
    }

    public boolean isHideOnNull() {
        return this.mHideOnNull;
    }

    public void setHideOnNull(boolean hideOnNull) {
        this.mHideOnNull = hideOnNull;
        this.setText(this.getText());
    }

    public void setText(CharSequence text, BufferType type) {
        if (!this.isHideOnNull() || text != null && !text.toString().equalsIgnoreCase("0")) {
            this.setVisibility(0);
        } else {
            this.setVisibility(8);
        }

        super.setText(text, type);
    }

    public void setBadgeCount(int count) {
        this.setText(String.valueOf(count));
    }

    public Integer getBadgeCount() {
        if (this.getText() == null) {
            return null;
        } else {
            String text = this.getText().toString();

            try {
                return Integer.parseInt(text);
            } catch (NumberFormatException var3) {
                return null;
            }
        }
    }

    public void setBadgeGravity(int gravity) {
        LayoutParams params = (LayoutParams)this.getLayoutParams();
        params.gravity = gravity;
        this.setLayoutParams(params);
    }

    public int getBadgeGravity() {
        LayoutParams params = (LayoutParams)this.getLayoutParams();
        return params.gravity;
    }

    public void setBadgeMargin(int dipMargin) {
        this.setBadgeMargin(dipMargin, dipMargin, dipMargin, dipMargin);
    }

    public void setBadgeMargin(int leftDipMargin, int topDipMargin, int rightDipMargin, int bottomDipMargin) {
        LayoutParams params = (LayoutParams)this.getLayoutParams();
        params.leftMargin = this.dip2Px((float)leftDipMargin);
        params.topMargin = this.dip2Px((float)topDipMargin);
        params.rightMargin = this.dip2Px((float)rightDipMargin);
        params.bottomMargin = this.dip2Px((float)bottomDipMargin);
        this.setLayoutParams(params);
    }

    public int[] getBadgeMargin() {
        LayoutParams params = (LayoutParams)this.getLayoutParams();
        return new int[]{params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin};
    }

    public void incrementBadgeCount(int increment) {
        Integer count = this.getBadgeCount();
        if (count == null) {
            this.setBadgeCount(increment);
        } else {
            this.setBadgeCount(increment + count);
        }

    }

    public void decrementBadgeCount(int decrement) {
        this.incrementBadgeCount(-decrement);
    }

    public void setTargetView(TabWidget target, int tabIndex) {
        View tabView = target.getChildTabViewAt(tabIndex);
        this.setTargetView(tabView);
    }

    public void setTargetView(View target) {
        if (this.getParent() != null) {
            ((ViewGroup)this.getParent()).removeView(this);
        }

        if (target != null) {
            if (target.getParent() instanceof FrameLayout) {
                ((FrameLayout)target.getParent()).addView(this);
            } else if (target.getParent() instanceof ViewGroup) {
                ViewGroup parentContainer = (ViewGroup)target.getParent();
                int groupIndex = parentContainer.indexOfChild(target);
                parentContainer.removeView(target);
                FrameLayout badgeContainer = new FrameLayout(this.getContext());
                android.view.ViewGroup.LayoutParams parentlayoutParams = target.getLayoutParams();
                badgeContainer.setLayoutParams(parentlayoutParams);
                target.setLayoutParams(new android.view.ViewGroup.LayoutParams(-1, -1));
                parentContainer.addView(badgeContainer, groupIndex, parentlayoutParams);
                badgeContainer.addView(target);
                badgeContainer.addView(this);
            } else if (target.getParent() == null) {
                Log.e(this.getClass().getSimpleName(), "ParentView is needed");
            }

        }
    }

    private int dip2Px(float dip) {
        return (int)(dip * this.getContext().getResources().getDisplayMetrics().density + 0.5F);
    }
}

4,activity中实现

public class MainActivity extends AppCompatActivity {

   
    private MonitorService.MonitorBinder binder;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("test","------onServiceConnected-------");
            binder = (MonitorService.MonitorBinder) service;
            // 假设json 不为空。为:有消息。
            disposable = binder.getNewMessage(6)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(json -> {
                        if (!TextUtils.isEmpty(json)) {
                            Log.e("test","------subscribe  success-------");
                            // 假设json 不为空。为:有消息。
                            View view = findViewById(R.id.tv_menu);
                            BadgeView badgeView = new BadgeView(MainActivity.this);
                            badgeView.setTargetView(view);
                            badgeView.setBadgeCount(1);
                        }
                    });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initToolbar();
        bindService(new Intent(this,MonitorService.class),serviceConnection,BIND_AUTO_CREATE);
 

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(disposable!=null&&!disposable.isDisposed()){
            disposable.dispose();
        }
        unbindService(serviceConnection);
    }

注意:

1,记得在androidManifest.xml中配置MonitorService服务
2,如果需要设置的目标view是button的话可能会不显示,代码中的tv_menu 是textview。
<think>好的,我需要帮助用户实现一个基于树莓派的客户端程序,定时获取温度数据并上报到服务器。首先,我得理清楚整个流程需要哪些步骤。 首先,温度数据的获取。用户提到参考了引用[1]和[2],其中提到了树莓派通过DS18B20传感器获取温度数据。DS18B20是常用的数字温度传感器,使用单总线协议,树莓派可以通过GPIO接口连接。根据引用[1],可能需要修改温度采样文件的路径,特别是如果用户不是在树莓派上运行的话。不过用户现在明确使用树莓派,所以应该先确认传感器是否正确连接,并且系统已经加载了相应的模块。 接下来是定时获取数据。通常可以使用Linux的cron定时任务,或者用Python的定时库如APScheduler。用户可能更倾向于用Python代码实现,因为后续上报服务器可能需要整合到同一个脚本里。引用[2]提到了发送数据时要发一条删一条,避免数据堆积,这点需要注意。 然后是数据上报到服务器。需要考虑通信协议,比如HTTP POST请求或者MQTT。根据引用[2],需要确保数据发送的可靠性,比如处理服务器断开的情况,可能需要加入重试机制。同时,发送后要删除已发送的数据,避免重复或丢失。用户可能需要一个本地数据库或缓存来临时存储数据,尤其是在网络不稳定时。 数据存储方面,可以选择SQLite这样的轻量级数据库,或者简单的文本文件。但根据引用[2]的建议,发送一条删除一条,可能需要事务处理来确保数据一致性。 异常处理也很重要,比如网络中断时的重试逻辑,以及日志记录,方便排查问题。引用[2]提到发送失败可能导致数据问题,所以必须处理这些情况。 最后,部署和测试。确保脚本在树莓派上开机自启动,可以使用systemd服务或者rc.local。测试各个部分是否正常工作,尤其是定时采集和网络通信。 现在需要将这些步骤整合成一个方案,可能需要分模块编写代码:传感器读取、定时触发、数据存储、服务器通信、异常处理等。同时参考用户提供的引用内容,确保数据上报的可靠性和正确性。</think>根据您的需求,这里提供分步骤实现方案(整合引用[1][2]内容): 一、硬件环境搭建 1. 连接DS18B20传感器到树莓派GPIO4引脚(默认单总线接口) 2. 启用单总线协议:`sudo raspi-config` → Interfacing Options → 1-Wire 3. 确认温度采样路径:`/sys/bus/w1/devices/28-*/w1_slave`[^1] 二、软件架构设计 ```python # 数据结构定义(参考引用[1]结构体概念) class TemperatureData: def __init__(self, timestamp, value): self.timestamp = timestamp # 时间戳 self.value = value # 温度值 # 数据库表结构建议 CREATE TABLE IF NOT EXISTS temps ( id INTEGER PRIMARY KEY AUTOINCREMENT, temp REAL NOT NULL, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ``` 三、核心代码实现 ```python import sqlite3 import requests import time from datetime import datetime def read_temperature(): """读取温度传感器数据(来自引用[1])""" try: with open('/sys/bus/w1/devices/28-011931d2d2aa/w1_slave', 'r') as f: contents = f.read() if 'YES' in contents: temp_pos = contents.find('t=') if temp_pos != -1: temp = float(contents[temp_pos+2:])/1000 return TemperatureData(datetime.now(), temp) except Exception as e: print(f"读取失败: {str(e)}") return None def send_to_server(data): """带重试机制的上传(参考引用[2]建议)""" retries = 3 for attempt in range(retries): try: response = requests.post('http://yourserver.com/api/temp', json={'temp': data.value, 'time': data.timestamp}, timeout=5) if response.status_code == 200: return True except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): if attempt == retries - 1: return False time.sleep(2**attempt) # 指数退避 return False def data_handler(): """带事务的数据处理(优化引用[2]建议)""" conn = sqlite3.connect('temps.db') try: # 获取未发送数据 cursor = conn.execute("SELECT * FROM temps ORDER BY created LIMIT 1") row = cursor.fetchone() if row and send_to_server(TemperatureData(row[2], row[1])): conn.execute("DELETE FROM temps WHERE id=?", (row[0],)) conn.commit() finally: conn.close() if __name__ == "__main__": # 定时任务配置(APScheduler) from apscheduler.schedulers.blocking import BlockingScheduler scheduler = BlockingScheduler() @scheduler.scheduled_job('interval', minutes=5) def collect_job(): if (temp := read_temperature()): with sqlite3.connect('temps.db') as conn: conn.execute("INSERT INTO temps (temp) VALUES (?)", (temp.value,)) @scheduler.scheduled_job('interval', minutes=1) def upload_job(): data_handler() scheduler.start() ``` 四、可靠性增强措施 1. 网络异常处理:采用指数退避重试机制(代码中已实现) 2. 数据持久化:SQLite本地存储保证数据不丢失[^2] 3. 进程守护:使用systemd服务配置 ```bash # /etc/systemd/system/temp_report.service [Unit] Description=Temperature Reporting Service [Service] ExecStart=/usr/bin/python3 /path/to/script.py Restart=always RestartSec=10s [Install] WantedBy=multi-user.target ``` 五、部署步骤 1. 安装依赖:`sudo apt install python3-pip && pip install apscheduler requests` 2. 初始化数据库:`sqlite3 temps.db < schema.sql` 3. 启用服务:`sudo systemctl enable temp_report.service`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值