jjzjj

运动App如何实现端侧后台保活,让运动记录更完整?

HMS Core 2023-03-28 原文

你在锻炼健身时,有没有遇到这样的情况?辛辛苦苦锻炼了几小时,却发现App停止了运行,本次运动并没有被记录到App上,从而失去了一个查看完整运动数据的机会?

运动类App是通过手机或者穿戴设备的传感器,来识别运动状态并反馈给用户的,App能否在手机后台时刻保持运行是影响运动数据完整性的关键因素。为了满足用户查看完整运动数据的需求,运动类App都希望在设备后台保活,并通过传感器实时记录用户的运动数据。但大部分手机厂商为了节省电量,一旦应用处于后台就会被系统限制甚至强制关闭,导致最终呈现给用户的运动记录不完整。

运动类App要想实现端侧后台保活,目前通常有两种解决办法:

  1. 引导用户在手机上手动设置保活,如关闭电池优化,允许App后台运行。这种方法缺点在于操作步骤较复杂,用户学习成本较高。

  2. 可以通过集成华为运动健康服务来解决此问题,运动健康服务提供支持后台保活的运动记录API,集成该能力后应用能够在用户的锻炼过程中在华为手机后台保持运行,从而实现用户锻炼过程中的运动记录不间断。

那如何实现后台保活功能呢?以下是详细的集成步骤。

集成步骤

  1. 请参考开发准备完成申请Health Kit服务,勾选产品必需申请的数据权限并集成SDK。

  2. 调用后台保活功能需申请运动记录读取权限,再获取用户授权完成权限申请。

  3. 为保证您的应用不被系统冻结,需要开启一个前台服务Foreground services,在前台服务中调用ActivityRecordsController方法创建允许后台运行的运动记录;

  4. 调用ActivityRecordsController的beginActivityRecord接口开始允许后台运行的运动记录,默认会申请允许应用后台运行时长10分钟;

// 请注意此处的this为Activity对象
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this); 

// 1.构造新运动记录开始时间
long startTime = Calendar.getInstance().getTimeInMillis(); 
// 2.构造ActivityRecord对象,设置运动记录开始时间 
ActivityRecord activityRecord = new ActivityRecord.Builder() 
    .setId("MyBeginActivityRecordId") 
    .setName("BeginActivityRecord") 
    .setDesc("This is ActivityRecord begin test!") 
    .setActivityTypeId(HiHealthActivities.RUNNING) 
    .setStartTime(startTime, TimeUnit.MILLISECONDS) 
    .build(); 

// 3.构建应用运动记录运行中展示的页面, MyActivity需替换成自身的Activity类
ComponentName componentName = new ComponentName(this, MyActivity.class);

// 4.构建运动记录后台运行状态变化监听器
OnActivityRecordListener activityRecordListener = new OnActivityRecordListener() {
    @Override
    public void onStatusChange(int statusCode) {
        Log.i("ActivityRecords", "onStatusChange statusCode:" + statusCode);
    }
};

// 5.调用启动新运动记录API接口beginActivityRecord
Task<Void> task1 = activityRecordsController.beginActivityRecord(activityRecord, componentName, activityRecordListener); 
// 6.添加启动ActivityRecord成功 
task1.addOnSuccessListener(new OnSuccessListener<Void>() { 
    @Override 
    public void onSuccess(Void aVoid) { 
        Log.i("ActivityRecords", "MyActivityRecord begin success"); 
    } 
// 7.添加启动ActivityRecord失败
}).addOnFailureListener(new OnFailureListener() { 
    @Override 
    public void onFailure(Exception e) { 
        String errorCode = e.getMessage(); 
        String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode)); 
        Log.i("ActivityRecords", errorCode + ": " + errorMsg); 
    } 
});
  1. 若用户运动时间较长,每临近10分钟(小于10分钟)需调用ActivityRecordsController的continueActivityRecord接口续申请后台保活10分钟;
// 请注意此处的this为Activity对象
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this); 

// 调用continueActivityRecord方法为指定运动记录续申请允许后台运行,入参为ActivityRecord的ID字符串 
Task<Void> endTask = activityRecordsController.continueActivityRecord("MyBeginActivityRecordId");
endTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.i("ActivityRecords", "continue backgroundActivityRecord was successful!");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Log.i("ActivityRecords", "continue backgroundActivityRecord error");
    }
});
  1. 当用户运动结束时,调用ActivityRecordsController的endActivityRecord接口停止该运动记录,同时取消应用后台保活;
// 请注意此处的this为Activity对象
final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);

// 调用endActivityRecord接口停止运动记录,入参为ActivityRecord的ID字符串或者null
// 入参为ID字符串时,停止当前应用指定ID的运动记录
// 入参为null时,停止该应用当前所有的未停止运动记录
Task<List<ActivityRecord>> endTask = activityRecordsController.endActivityRecord("MyBeginActivityRecordId");
endTask.addOnSuccessListener(new OnSuccessListener<List<ActivityRecord>>() {
    @Override
    public void onSuccess(List<ActivityRecord> activityRecords) {
        Log.i("ActivityRecords","MyActivityRecord End success");
        // 返回停止成功的运动记录列表
        if (activityRecords.size() > 0) {
            for (ActivityRecord activityRecord : activityRecords) {
                DateFormat dateFormat = DateFormat.getDateInstance();
                DateFormat timeFormat = DateFormat.getTimeInstance();
                Log.i("ActivityRecords", "Returned for ActivityRecord: " + activityRecord.getName() + "\n\tActivityRecord Identifier is "
                    + activityRecord.getId() + "\n\tActivityRecord created by app is " + activityRecord.getPackageName()
                    + "\n\tDescription: " + activityRecord.getDesc() + "\n\tStart: "
                    + dateFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + " "
                    + timeFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: "
                    + dateFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + " "
                    + timeFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + "\n\tActivity:"
                    + activityRecord.getActivityType());
            }
        } else {
            // 没有停止成功返回null
            Log.i("ActivityRecords","MyActivityRecord End response is null");
        }
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        String errorCode = e.getMessage();
        String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
        Log.i("ActivityRecords",errorCode + ": " + errorMsg);
    }
});

需要注意的是,由于端侧后台保活API属于敏感权限,运动类应用接入时需进行人工审核,确保数据安全、流程合规才能上架。

获取端侧后台保活能力开发文档

华为运动健康场景解决方案

了解更多详情>>

访问华为开发者联盟官网
获取开发指导文档
华为移动服务开源仓库地址:GitHubGitee

关注我们,第一时间了解 HMS Core 最新技术资讯~

有关运动App如何实现端侧后台保活,让运动记录更完整?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐