评注:android 后台分进程保活方式的实践

Android 后台任务型App多进程架构演化

字数1621 阅读2790 评论8 喜欢35

什么是后台任务型app

类似音乐、录音机,需要用户长时间在后台使用的产品

背景:

笔者之前的项目一直在做跑步app, 用户的场景是这样的,用户开启跑步模式后,我们需要监听Gps 信号来统计用户的运动数据,包括距离,配速,时间。其实是看似很“简单"的用户场景, 起初笔者也这么认为,经过了一段时间的迭代完善,现在就来分享一些其中的”不简单“。笔者会从一个跑步app开发者的角度分享这样一个跑步App的架构演化。

最初的架构

笔者为了尽快实现产品经理的需求,马不停蹄的完成了app 的最初版,这时这个架构是这样的

Activity + Forground Service + Sqlite+Eventbus
其中: Activity 代表UI 层, Service 代表开启跑步模式时启动的forground service,用以记录运动数据,Sqlite 代表数据的存储层, eventbus 是一个事件总线的library,用于模块间解耦。

引来的问题

最初版发出之后,收到一些用户反馈,反应运动数据里程丢失,记录不准,这样的问题对于一款数据统计的运动app来说是致命的,那么为什么会有这样的问题呢?很容易猜到,因为我们app的进程被回收了

如何解决

主要做了UI进程与Service进程分离和一些service保活的策略,主要基于一下两点原因

  • Android进程管理机制
    这里就不得不提到Android 的对于进程管理的机制,Android 系统是通过Low Memory Killer 机制(参考)来管理进程的,对于进程分为几个优先级:

    • native
    • persistent
    • forground
    • visible
    • cache
      每个进程的优先级取决于系统计算oom_adj 的值,那么影响oom_adj的因素有哪些呢?主要是进程占用内存的大小
  • 便于系统回收资源
    对于跑步这类app而言,用户场景很长时间是处于后台运行的状态,前台UI只负责交互,后台的service负责业务的处理,而且UI进程的内存占远大于Sevice的内存占用,所以如果能够在app切换到后台的时候释放掉所有的UI资源,那么这个app运行时就能够 省出大量内存。

第二版的修改

基于以上两点原因, 于是有了第二版的重构,架构变成了这样:

UI进程 + Remote进程(service 进程

那么问题来了,app从单进程变成多进程会存在哪些坑呢?笔者主要遇到了三个问题

  • 1.进程间如何通信
  • 2.两个进程如何访问数据保证进程安全
  • 3.如何保证进程安全的操作sharepreference

针对第一个问题,多进程通信的方式:
1.Broadcast :
这种方式的所有通讯协议都需要放在intent里面发送和接受,是一种异步的通讯方式,即调用后无法立刻得到返回结果。另外还需要在UI和service段都要注册receiver才能达到他们之间的相互通讯。

2.Messager
Messenger的使用 方法比较简单,定义一个Messenger并指定一个handler作为通讯的接口,在onBind的时候返回Messenger的getBinder方 法,并在UI利用返回的IBinder也创建一个Messenger,他们之间就可以进行通讯了。这种调用方法也属于异步调用

3.ResultReceiver 跨组件的异步通讯,常用于请求-回调模式.

4.重写Binder
这种通过aidl进行通信
我们选择了最后一种方案:
主进程通过bindservice 调起remote 进程,并在onServiceConnection时,注册一个remote 进程的callback 回调,用于监听,接收remote进程的消息。

  • 首先在AndroidManifest.xml 中声明

    1. <serviceandroid:name=".RemoteService"
    2. android:process=":remote"
    3. android:label="@string/app_name" />
  • 声明aidl接口
    1. //aidl service 进程持有的对象
    2. interface IRemoteService {
    3. void registerCallback(IRemoteCallback cb);
    4. void unregisterCallback(IRemoteCallback cb);
    5. }
    1. //回调更新UI进程数据的接口
    2. interface IRemoteCallback {
    3. void onDataUpdate(double distance,double duration, double pace, double calorie, double velocity);
    4. }
  • 重写RemoteService Binder
    1. LocalBinder mBinder = new LocalBinder();
    2. IRemoteCallback mCallback;
    3. class LocalBinder extends IRemoteService.Stub {
    4. @Override
    5. public void registerCallback(IRemoteCallback cb) throws RemoteException {
    6. mCallback = cb;
    7. }
    8. @Override
    9. public void unregisterCallback(IRemoteCallback cb) throws RemoteException {
    10. mCallback = null;
    11. }
    12. public IBinder asBinder() {
    13. return null;
    14. }
    15. }
  • 重写UI进程的Binder
    1. public class RemoteCallback extends IRemoteCallback.Stub {
    2. @Override
    3. public void onActivityUpdate(final double distance, final double duration, final double pace, final double calorie, final double velocity) throws RemoteException {
    4. //do something
    5. }
    6. }
  • onServiceConnection 时将UI 进程的binder 注册到remote进程
    1. @Override
    2. public void onServiceConnected(ComponentName name, IBinder service) {
    3. try {
    4. mService = IRemoteService.Stub.asInterface(service);
    5. mService.registerCallback(mCallback);
    6. } catch (RemoteException e) {
    7. e.printStackTrace();
    8. }
    9. }
    10. @Override
    11. public void onServiceDisconnected(ComponentName name) {
    12. try {
    13. if (mService != null) {
    14. mService.unregisterCallback(mCallback);
    15. }
    16. } catch (RemoteException e) {
    17. e.printStackTrace();
    18. }
    19. mService = null;
    20. }

第二个问题,两个进程如何访问数据保证一致性:ContentProvider
在Sqlite 上层封装一层ContentProvider
于是现有的架构变成了:

UI process: Activity + eventbus
Remote process : Service + ContentProvider + Sqlite + Eventbus

还有第三个问题:
用户需求:多个进程需要获取跑步的状态信息,比如跑步中,跑步暂停还是跑步结束。
一个进程的时候使用SharePreference存储一个持久化的状态,分进程之后,开始使用MODE_MULTI_PROCESS, 而后来发现文档注释被废弃掉了,multi_process 模式下sharepreference工作不会可靠,同步数据不会一致,如下描述:

SharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.

那么如何解决呢?
两种方案

性能比较
DPreference setString
called 1000 times cost : 375 ms getString
called 1000 times cost : 186 ms
Tray setString
called 1000 times cost : 13699 ms getString
called 1000 times cost : 3496 ms

方案1还有一个缺点,如果将老的SharePreference 数据迁移到 用sqlite的方式需要全部拷贝,而方案二天然的避免了这样的问题,并且读写性能更佳,于是采用了方案二。
于是架构变成了这样:

UI process: Activity + eventbus
Remote process : Service + (ContentProvider + Sqlite)+ (ContentProvider + SharePreference) + Eventbus

以上就是笔者在多进程开发中遇到的一些问题和解决方案,希望可以对大家有所帮助

转: Android 后台任务型App多进程架构演化的更多相关文章

  1. ArchSummit分享 | 高德地图App架构演化与实践

    讲师介绍 郝仁杰,高德地图无线开发专家.在7月13日落幕的2019年ArchSummit峰会上就高德地图近几年的App架构演化和实践进行了分享. 背景概述 高德是国内领先的数字地图内容.导航和位置服务 ...

  2. [转]Android App整体架构设计的思考

    1. 架构设计的目的 对程序进行架构设计的原因,归根到底是为了提高生产力.通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点, ...

  3. Android App的架构设计:从VM、MVC、MVP到MVVM

    随着Android应用开发规模的扩大,客户端业务逻辑也越来越复杂,已然不是简单的数据展示了.如同后端开发遇到瓶颈时采用的组件拆分思想,客户端也需要进行架构设计,拆分视图和数据,解除模块之间的耦合,提高 ...

  4. Android(4)—Mono For Android 第一个App应用程序

    0.前言 年前就计划着写这篇博客,总结一下自己做的第一个App,却一直被新项目所累,今天抽空把它写完,记录并回顾一下相关知识点,也为刚学习Mono的同学提供佐证->C#也是开发Android的! ...

  5. 在Android应用中使用Clean架构

    自从开始开发安卓应用,我一直感觉我可以做得更好.我看过不少烂代码,其中当然有我写的.安卓系统的复杂性加上烂代码势必酿成灾祸,所以从错误中成长就很重要.我Google了如何更好地开发应用,发现了这个叫做 ...

  6. 转:Android开发中的MVP架构(最后链接资源不错)

    Android开发中的MVP架构 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解MVP和DDD,但是我们的新项目还是决定通过MVP来构建. 这篇文章是我通过研究和 ...

  7. Android系统简介(中):系统架构

    Android的系统架构栈分为4层,从上往下分别是Applications.Application framework.Libraries  & Android Runtime.Linux  ...

  8. 高焕堂《android从程序员到架构师之路》 YY讲坛直面大师学习架构设计

    <android从程序员到架构师之路>YY讲坛活动:  sundy携手高焕堂老师全程YY答疑 与大师一起,分享android技术 时间:7月21日下午2:00   报名联系QQ:22243 ...

  9. android上instant app介绍 类似于微信小程序

    android上instant app介绍 类似于微信小程序instant app 是谷歌推出的类似于微信小程序(或者说小程序类似于instant app)的一项技术,用户无须安装应用,用完就走,同时 ...

随机推荐

  1. 网页上的JS call Unity3d里的function——SendMessage

    注意: sendmessage只可以从网页发信息到unity游戏里,但是没有返回值 只可以发布三种类型的data,不可以其他复杂的强类型 发信息的时不会做编译检测 SendMessage Workfl ...

  2. UI:登录窗的自定义键盘

    在创建一个自定义键盘的时候遇到的错误 //双重for循环,对于Button上的数字用二维数组 //    NSArray * butArr[4][3] = {@[@"1",@&qu ...

  3. 文件字符读写函数fscanf()和 fgets() 比较

    一. 文件格式化读入函数 fscanf()  int  fscanf(文件指针,格式化字符串,输入列表); 返回值: 整形,输入列表中定义字符串的个数. 1, 例如读取字符串: char  str1[ ...

  4. 也来说说C#异步委托(转)

    原文地址: http://www.cnblogs.com/lxblog/archive/2012/12/11/2813893.html 前些日子,看到园子里面有人用老王喝茶的例子讲解了一下同步和异步, ...

  5. PHP网址

    15个魔术方法的总结: http://blog.csdn.net/bossdarcy/article/details/6210794 PHP代码重构:http://blog.csdn.net/tony ...

  6. 查看sid

    查看用户sid: whoami /user 查看系统sid: 使用PSTools工具中的 psgetsid.exe命令查看

  7. C#操作Word (2)-- 打开&关闭Word文档

    本文正式开始在VS2010中使用C#语言操作Word2007. 不是十分了解Word对象模型的朋友,请参考上一篇文章,或者下载:C#操作Word2007.pdf. ------------------ ...

  8. xmf 翻译

    避免在详细信息视图的确认对话框显示? https://documentation.devexpress.com/#Xaf/CustomDocument3160 我如何获得从登录窗口应用程序的数据库? ...

  9. hdu 5029 Relief grain(树链剖分+线段树)

    题目链接:hdu 5029 Relief grain 题目大意:给定一棵树,然后每次操作在uv路径上为每一个节点加入一个数w,最后输出每一个节点个数最多的那个数. 解题思路:由于是在树的路径上做操作, ...

  10. Understanding page frames and pages

    Memory in Linux is organized in the form of pages (typically 4 KB in size). Contiguous linear addres ...