[文章]【木棉花】:Service核心技术精要直播之学习笔记(上)

阅读量0
推荐
0
5


前言
这篇文章是我观看张荣超老师8月12日Service核心技术精要的直播后的学习笔记,温故而知新,跟着敲代码,跟着学知识,冲冲冲O(∩_∩)O
概述

这是直播的主要内容,本文先对启动和停止本地设备的Service跨设备启动和停止Service两个Demo做点笔记记录
正文
一.Service的相关知识
1.三大Ability:PageAbility,ServiceAbility,DataAbility,主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即
使用户切换到其他应用,Service仍将在后台继续运行。
2.Service是在主线程中运行的,如果要进行一些耗时操作,建议在ServiceAbility中创建子线程,以免堵塞主线程(为什么不建议在PageAbility中创建子线程呢?因为PA容易被系统和用户销毁,被销毁后就不再持有该子线程的引用了,而Service是单实例的,可以与多个PA建立联系,除非系统要回收内存资源,否则系统是不会销毁service的)
3.onStart(),该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Intent应为空。
onCommand(),在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。参数boolean型的restart是用于表明service的启动模式,当值为true时表明service被系统销毁后重新启动,若值为false表明service是正常启动的;int型的startId表明service被启动的次数,每次service被启动是startId的值都会增1
onConnect​(),在Ability和Service连接时调用,该方法返回IRemoteObject对象,开发者可以在该回调函数中生成对应Service的IPC通信通道,以便Ability与Service交互。Ability可以多次连接同一个Service,系统会缓存该Service的IPC通信对象,只有第一个客户端连接Service时,系统才会调用Service的onConnect方法来生成IRemoteObject对象,而后系统会将同一个RemoteObject对象传递至其他连接同一个Service的所有客户端,而无需再次调用onConnect方法。
onDisconnect​(),在Ability与绑定的Service断开连接时调用。
onStop(),在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等
二.启动和停止本地设备的Service

1.界面布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  4.     ohos:height="match_parent"
  5.     ohos:width="match_parent"
  6.     ohos:alignment="center"
  7.     ohos:background_element="$graphic:background_ability_main"
  8.     >

  9.   <Button
  10.       ohos:id="$+id:btn_start"
  11.       ohos:height="match_content"
  12.       ohos:width="match_parent"
  13.       ohos:text="启动本地设备的Service"
  14.       ohos:text_color="#000000"
  15.       ohos:background_element="$graphic:background_button"
  16.       ohos:left_margin="30vp"
  17.       ohos:right_margin="30vp"
  18.       ohos:top_margin="30vp"
  19.       ohos:bottom_margin="30vp"
  20.       ohos:top_padding="10vp"
  21.       ohos:bottom_padding="10vp"
  22.       ohos:auto_font_size="true"
  23.       />

  24.   <Button
  25.       ohos:id="$+id:btn_stop"
  26.       ohos:height="match_content"
  27.       ohos:width="match_parent"
  28.       ohos:text="停止本地设备的Service"
  29.       ohos:text_color="#000000"
  30.       ohos:background_element="$graphic:background_button"
  31.       ohos:left_margin="30vp"
  32.       ohos:right_margin="30vp"
  33.       ohos:top_margin="30vp"
  34.       ohos:bottom_margin="30vp"
  35.       ohos:top_padding="10vp"
  36.       ohos:bottom_padding="10vp"
  37.       ohos:auto_font_size="true"
  38.       />
  39. </DirectionalLayout>
复制代码
2.创建Service

右击包名,New → Ability → Empty ServiceAbility
在onStart中创建子线程来播放音乐,代码如下
  1. public class ServiceAbility extends Ability {
  2.     private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
  3.     private Player player=new Player(this);
  4.     @Override
  5.     public void onStart(Intent intent) {
  6.         HiLog.info(LABEL_LOG, "onStart()被调用了");
  7.         super.onStart(intent);

  8.         EventRunner eventRunner = EventRunner.create(true);
  9.         EventHandler eventHandler = new EventHandler(eventRunner);
  10.         eventHandler.postTask(new Runnable() {
  11.             @Override
  12.             public void run() {
  13.                 try {
  14.                     RawFileDescriptor rawFileDescriptor = getResourceManager()
  15.                             .getRawFileEntry("resources/rawfile/test.mp3")
  16.                             .openRawFileDescriptor();
  17.                     Source source = new Source(rawFileDescriptor.getFileDescriptor(),
  18.                             rawFileDescriptor.getStartPosition(),
  19.                             rawFileDescriptor.getFileSize());
  20.                     player.setSource(source);
  21.                     player.prepare();
  22.                     player.play();

  23.                 }catch(IOException e){
  24.                     e.printStackTrace();
  25.                 }
  26.             }
  27.         });
  28.     }

  29.     @Override
  30.     public void onCommand(Intent intent, boolean restart, int startId) {
  31.         HiLog.info(LABEL_LOG, "onCommand()被调用了,第%{public}d次后被启动",startId);
  32.     }

  33.     @Override
  34.     public void onBackground() {
  35.         super.onBackground();
  36.         HiLog.info(LABEL_LOG, "onBackground()被调用了");
  37.     }

  38.     @Override
  39.     public void onStop() {
  40.         super.onStop();
  41.         HiLog.info(LABEL_LOG, "onStop()被调用了");

  42.         if(player.isNowPlaying()){
  43.             player.stop();
  44.             player.release();
  45.         }
  46.     }
  47. }
复制代码
3.启动Service
DeviceId:表示设备ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表,
BundleName:表示包名称。
AbilityName:表示待启动的Ability名称。
打开MainAbilitySlice,添加代码
  1. public class MainAbilitySlice extends AbilitySlice {
  2.     @Override
  3.     public void onStart(Intent intent) {
  4.         super.onStart(intent);
  5.         super.setUIContent(ResourceTable.Layout_ability_main);

  6.         Intent serviceIntent = new Intent();
  7.         Operation serviceOperation = new Intent.OperationBuilder()
  8.                 .withDeviceId("")
  9.                 .withBundleName(getBundleName())
  10.                 .withAbilityName(ServiceAbility.class.getName())
  11.                 .build();
  12.         serviceIntent.setOperation(serviceOperation);

  13.         Button btnStart = (Button)findComponentById(ResourceTable.Id_btn_start);
  14.         btnStart.setClickedListener(component -> {
  15.             startAbility(serviceIntent);
  16.         });

  17.         Button btnStop = (Button)findComponentById(ResourceTable.Id_btn_stop);
  18.         btnStop.setClickedListener(component -> {
  19.             stopAbility(serviceIntent);
  20.         });
  21.     }
复制代码
执行上述代码后,Ability将通过 startAbility() 方法来启动Service。
如果Service尚未运行,则系统会先调用 onStart() 来初始化Service,再回调Service的 onCommand() 方法来启动Service。
如果Service正在运行,则系统会直接回调Service的 onCommand() 方法来启动Service。
Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。开发者可以在Service中通过 terminateAbility() 停止本Service或在其他Ability调用 stopAbility()停止Service
连接真机去调试,日志打印如下




三.跨设备启动和停止Service
1.添加权限
第一个权限是获取分布式组网内的设备列表和设备信息,第二个权限是用于获取分布式组网内的设备的状态变化,第三个权限是用于查询其他应用的信息,第四个权限用于不同设备间的数据交换;前三个为非敏感权限,第四个为敏感权限。
已在config.json文件中声明的非敏感权限,会在应用安装时自动授予,该类权限的授权方式为系统授权(system_grant)。
敏感权限需要应用动态申请,通过运行时发送弹窗的方式请求用户授权,该类权限的授权方式为用户授权(user_grant)。
代码分别如下:
/config.json/
  1. "reqPermissions": [
  2.       {
  3.         "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
  4.       },
  5.       {
  6.         "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
  7.       },
  8.       {
  9.         "name": "ohos.permission.GET_BUNDLE_INFO"
  10.       },
  11.       {
  12.         "name": "ohos.permission.DISTRIBUTED_DATASYNC"
  13.       }
  14.     ]
复制代码
/MainAbility/
将所有要动态申请的权限存放在一个字符串数组中,设置一个可以被动态申请权限的列表,然后遍历所有要动态申请的权限,进行判断:如果当前应用没有被用户授予指定的权限,再进行判断:该权限是否可以被动态申请,如果可以就放在上述列表中

  1. public class MainAbility extends Ability {
  2.     @Override
  3.     public void onStart(Intent intent) {
  4.         super.onStart(intent);
  5.         super.setMainRoute(MainAbilitySlice.class.getName());

  6.         String[] permissions = { "ohos.permission.DISTRIBUTED_DATASYNC"};
  7.         List<String> canRequestPermissions = new ArrayList<>();
  8.         for(String permission:permissions){
  9.             if(verifySelfPermission(permission)!= IBundleManager.PERMISSION_GRANTED){
  10.                 if(canRequestPermission(permission)){
  11.                     canRequestPermissions.add(permission);
  12.                 }
  13.             }
  14.         }
  15.         requestPermissionsFromUser(canRequestPermissions.toArray(new String[0]),0);
  16.     }
  17. }
复制代码
2.找到组网内的其他设备
新建一个包命名为utils,新建一个类DeviceUtils,其方法用来返回组网内的设备列表
代码如下
  1. public class DeviceUtils {
  2.     public static List<String> getOnlineDeviceIdList(){
  3.         List<DeviceInfo> onlineDeviceInfoList =
  4.                 DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
  5.         if(onlineDeviceInfoList == null || onlineDeviceInfoList.isEmpty()){
  6.             return null;
  7.         }
  8.         List<String> onlineDeviceIdList = new ArrayList<>(onlineDeviceInfoList.size());
  9.         onlineDeviceInfoList.forEach(onlineDeviceInfo -> {
  10.             onlineDeviceIdList.add(onlineDeviceInfo.getDeviceId());
  11.         });
  12.         return onlineDeviceIdList;
  13.     }
  14. }
复制代码
3.修改MainAbilitySlice
修改的部分如下,将获取到的设备Id传到withDeviceId,增加withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)设置支持分布式的标记,从而这个应用可以支持分布式的调度
重命名Intent,选中后右击Refactor -> Rename
  1. public class MainAbilitySlice extends AbilitySlice {
  2.     @Override
  3.     public void onStart(Intent intent) {
  4.         super.onStart(intent);
  5.         super.setUIContent(ResourceTable.Layout_ability_main);

  6.         Intent remoteIntent = new Intent();

  7.         List<String> onlineDeviceIdList = DeviceUtils.getOnlineDeviceIdList();
  8.         if(onlineDeviceIdList != null || !onlineDeviceIdList.isEmpty()){
  9.             String remoteTargetDeviceId = onlineDeviceIdList.get(0);
  10.             Operation serviceOperation = new Intent.OperationBuilder()
  11.                     .withDeviceId(remoteTargetDeviceId)
  12.                     .withBundleName(getBundleName())
  13.                     .withAbilityName(ServiceAbility.class.getName())
  14.                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  15.                     .build();
  16.             remoteIntent.setOperation(serviceOperation);
  17.         }

  18.         Button btnStart = (Button)findComponentById(ResourceTable.Id_btn_start);
  19.         btnStart.setClickedListener(component -> {
  20.             startAbility(remoteIntent);
  21.         });

  22.         Button btnStop = (Button)findComponentById(ResourceTable.Id_btn_stop);
  23.         btnStop.setClickedListener(component -> {
  24.             stopAbility(remoteIntent);
  25.         });
  26.     }

复制代码
最后就可以连上两台真机调试了,不过我没有两台设备所以就没有调试了。
结语
以上就是我这次的小分享啦❀❀!!2022,学习路上继续前进!
更多资料请关注我们的项目 : Awesome-Harmony_木棉花


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友