从零实现原子服务一键分享,免安装!

系统教程10个月前发布 Chen100315
5 00

从零实现原子服务一键分享,免安装!

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

一、前言

  • 原子化服务是鸿蒙的一大特性,在服务中心可以看见许多以卡片形式呈现的原子化服务,这些服务体积小,能够快速部署到手机上实现功能,达到免安装的效果。

从零实现原子服务一键分享,免安装!

  • 同样的,原子化服务另外一种呈现方式就是基于华为分享的,例如A同学希望分享他在京东上看到的一件商品,他可以通过华为分享将该服务页面快速迁移到B同学的手机上,而B同学的手机上并没有安装京东,也能看到呈现画面。

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

这里不知道是网络还是应用出现了BUG,总之就没显示出来画面,不过问题不大。接下来我们亲自用案例实现,这个案例首先实现华为分享分享服务,同时要发布测试态原子化服务,这样我们的应用才能够在服务中心以卡片形式呈现,并且实现免安装

二、原子化服务分享

1、华为分享

本案例在最新版本的Deveco上进行编写,我们需要建立携带原子化服务的JAVA工程,注意是JAVA工程,由于该功能目前还未迁移到JS上,我们需要用JAVA进行编写,同时勾选在服务中心显示。

从零实现原子服务一键分享,免安装!

具体的华为分享原理,其实就是两端在一个组网近场内,一端封装好要分享的数据通过华为分享传输给另一端。快速传输,免安装的体验感一是数据量小,二是华为分享本身过硬的技术两者结合带来的。多余不在赘述,详情参看官网文档,​​接入华为分享​​。

2、接入华为分享

(1)创建IDL接口

我们在JAVA同级目录下,创建两个idl接口:

  • IHwShareCallback
  • IHwShareService
    注意idl接口存放路径一定要是com/huawei/hwshare/third

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

(2)实现接口

  • IHwShareCallback
interface com.huawei.hwshare.third.IHwShareCallback {
[oneway] void notifyState([in] int state);
}
  • IHwShareService
sequenceable ohos.interwork.utils.PacMapEx;
interface com.huawei.hwshare.third.IHwShareCallback;

interface com.huawei.hwshare.third.IHwShareService {
int startAuth([in] String appId, [in] IHwShareCallback callback);
int shareFaInfo([in] PacMapEx pacMapEx);
}

3、ShareFaManager类

用于管理分享方与华为分享的连接通道和数据交互,建议不要DIY,DIY空间少,容易出错,直接参考官方文档。

import com.huawei.hwshare.third.HwShareCallbackStub;
import com.huawei.hwshare.third.HwShareServiceProxy;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.app.Context;
import ohos.bundle.ElementName;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.interwork.utils.PacMapEx;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteException;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class ShareFaManager {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, "ShareFa");
private static final String LOG_FORMAT = "%{public}s: %{public}s";
public static final String HM_FA_ICON = "ohos_fa_icon";
public static final String HM_FA_NAME = "ohos_fa_name";
public static final String HM_ABILITY_NAME = "ohos_ability_name";
public static final String HM_BUNDLE_NAME = "ohos_bundle_name";
public static final String SHARING_FA_TYPE = "sharing_fa_type";
public static final String SHARING_THUMB_DATA = "sharing_fa_thumb_data";
public static final String SHARING_CONTENT_INFO = "sharing_fa_content_info";
public static final String SHARING_EXTRA_INFO = "sharing_fa_extra_info";
private static final String TAG = "ShareHmFaManager";
private static final String SHARE_PKG_NAME = "com.huawei.android.instantshare";
private static final String SHARE_ACTION = "com.huawei.instantshare.action.THIRD_SHARE";
private static final long UNBIND_TIME = 20*1000L;
private Context mContext;
private String mAppId;
private PacMapEx mSharePacMap;
private static ShareFaManager sSingleInstance;
private HwShareServiceProxy mShareService;
private boolean mHasPermission = false;
private EventHandler mHandler = new EventHandler(EventRunner.getMainEventRunner());
private final IAbilityConnection mConnection = new IAbilityConnection() {
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
HiLog.error(LABEL_LOG, LOG_FORMAT, TAG, "onAbilityConnectDone success.");
mHandler.postTask(()->{
mShareService = new HwShareServiceProxy(iRemoteObject);
try {
mShareService.startAuth(mAppId, mFaCallback);
} catch (RemoteException e) {
HiLog.error(LABEL_LOG, LOG_FORMAT, TAG, "startAuth error.");
}
});
}
@Override
public void onAbilityDisconnectDone(ElementName elementName, int i) {
HiLog.info(LABEL_LOG, LOG_FORMAT, TAG, "onAbilityDisconnectDone.");
mHandler.postTask(()->{
mShareService = null;
mHasPermission = false;
});
}
};
private Runnable mTask = () -> {
if (mContext != null && mShareService != null) {
mContext.disconnectAbility(mConnection);
mHasPermission = false;
mShareService = null;
}
};
private final HwShareCallbackStub mFaCallback = new HwShareCallbackStub("HwShareCallbackStub") {
@Override
public void notifyState(int state) throws RemoteException {
mHandler.postTask(()->{
HiLog.info(LABEL_LOG, LOG_FORMAT, TAG, "notifyState: " + state);
if (state == 0) {
mHasPermission = true;
if (mSharePacMap != null) {
shareFaInfo();
}
}
});
}
};
/**
* 单例模式获取ShareFaManager的实例对象
*
* @param context 程序Context
* @return ShareFaManager实例对象
*/
public static synchronized ShareFaManager getInstance(Context context) {
if (sSingleInstance == null && context != null) {
sSingleInstance = new ShareFaManager(context.getApplicationContext());
}
return sSingleInstance;
}
private ShareFaManager(Context context) {
mContext = context;
}
private void shareFaInfo() {
if (mShareService == null) {
return;
}
if (mHasPermission) {
HiLog.info(LABEL_LOG, LOG_FORMAT, TAG, "start shareFaInfo.");
try {
mShareService.shareFaInfo(mSharePacMap);
mSharePacMap = null;
} catch (RemoteException e) {
HiLog.error(LABEL_LOG, LOG_FORMAT, TAG, "shareFaInfo error.");
}
}
// 不使用时断开
mHandler.postTask(mTask, UNBIND_TIME);
}
/**
* 用于分享服务
*
* @param appId 开发者联盟网站创建原子化服务时生成的appid
* @param pacMap 服务信息载体
*/
public void shareFaInfo(String appId, PacMapEx pacMap) {
if (mContext == null) {
return;
}
mAppId = appId;
mSharePacMap = pacMap;
mHandler.removeTask(mTask);
shareFaInfo();
bindShareService();
}
private void bindShareService() {
if (mShareService != null) {
return;
}
HiLog.error(LABEL_LOG, LOG_FORMAT, TAG, "start bindShareService.");
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName(SHARE_PKG_NAME)
.withAction(SHARE_ACTION)
.withFlags(Intent.FLAG_NOT_OHOS_COMPONENT)
.build();
intent.setOperation(operation);
mContext.connectAbility(intent, mConnection);
}
}

4、简单案例

这里我们编写一个简单案例,MainAbilitySlice实现的是从相册里面挑选一张照片,作为卡片信息分享出去,用于介绍要分享的内容给被分享者。

(1) 效果

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

但是这里必须两端都装了该HAP包才能实现,还不满足免安装的效果,必须得至少将原子化服务发布到测试态,后文将会详细介绍。

(2)实现

具体的样式不在这里给出,请参考附件,附上核心代码。

唯一需要主要的是,里面用到的APPID,是在AGC控制台上创建相应项目应用时得到的APPID,可在AGC控制台,我的项目中找到。

package com.huawei.hwshare.third.slice;
import com.huawei.hwshare.third.MainAbility;
import com.huawei.hwshare.third.ResourceTable;
//import com.huawei.hwshare.third.ShareFaManager;
import com.huawei.hwshare.third.ShareFaManager;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.global.resource.NotExistException;
import ohos.global.resource.RawFileDescriptor;
import ohos.interwork.utils.PacMapEx;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;
import java.io.*;
public class MainAbilitySlice extends AbilitySlice {
private static final int imgRequestCode = 1001;
//核心: 显示分享的图片
private Image photo;
private Uri uri;
private byte [] picByte;
InputStream resource;
private Image cardimage;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button btn = (Button) findComponentById(ResourceTable.Id_btn);
Button btn1 = (Button) findComponentById(ResourceTable.Id_btn1);
cardimage = (Image) findComponentById(ResourceTable.Id_cardimg1);
btn1.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
selectPhoto();
}
});
photo = (Image) findComponentById(ResourceTable.Id_photo);
btn.setClickedListener(new Component.ClickedListener() {
@Override
/*华为分享功能的核心代码*/
public void onClick(Component component) {
/*数据包*/
PacMapEx pacMap = new PacMapEx();
/*分享的服务类型 默认为0 当前也仅支持0*/
pacMap.putObjectValue(ShareFaManager.SHARING_FA_TYPE, 0);
/*分享服务的包名,必选参数*/
pacMap.putObjectValue(ShareFaManager.HM_BUNDLE_NAME, getBundleName());
/*额外的信息 非必选*/
pacMap.putObjectValue(ShareFaManager.SHARING_EXTRA_INFO, "原子化服务分享");
/*分享的服务的Ability类名,必选参数*/
pacMap.putObjectValue(ShareFaManager.HM_ABILITY_NAME, MainAbility.class.getName());
/*卡片展示的服务介绍信息,必须参数*/
pacMap.putObjectValue(ShareFaManager.SHARING_CONTENT_INFO, "分享成功!");
/*卡片展示服务介绍图片,最大长度153600,必选参数。*/
pacMap.putObjectValue(ShareFaManager.SHARING_THUMB_DATA, picByte);
// byte[] iconImg = getResourceBytes(ResourceTable.Media_icon);
/*服务图标 非必选参数*/
//pacMap.putObjectValue(com.huawei.hwshare.third.ShareFaManager.HM_FA_ICON, iconImg);
// pacMap.putObjectValue(com.huawei.hwshare.third.ShareFaManager.HM_FA_ICON, iconByte);
/*卡片展示的服务名称*/
pacMap.putObjectValue(ShareFaManager.HM_FA_NAME, "华为分享服务测试");
// 第一个参数为appid,在华为AGC创建原子化服务时自动生成。
ShareFaManager.getInstance(MainAbilitySlice.this).shareFaInfo("942536802034526976", pacMap);
}
});
}
private byte[] getResourceBytes(int resId) {
ByteArrayOutputStream outStream = null;
try {
resource = getResourceManager().getResource(resId);
outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = resource.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
outStream.close();
resource.close();
return outStream.toByteArray();
} catch (IOException | NotExistException e) {
// HiLog.error(TAG, "get resource occurs io exception!");
} finally {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
// HiLog.error(TAG, "close input stream occurs io exception!");
}
}
if (outStream != null) {
try {
resource.close();
} catch (IOException e) {
// HiLog.error(TAG, "close output stream occurs io exception!");
}
}
}
return null;
}
//选择图片
private void selectPhoto() {
//调起系统的选择来源数据视图
Intent intent = new Intent();
Operation opt=new Intent.OperationBuilder().withAction("android.intent.action.GET_CONTENT").build();
intent.setOperation(opt);
intent.addFlags(Intent.FLAG_NOT_OHOS_COMPONENT);
intent.setType("image/*");
startAbilityForResult(intent, imgRequestCode);
}
/*选择图片回调*/
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
if(requestCode==imgRequestCode && resultData!=null)
{
//被选择的图片的uri地址
String img_uri=resultData.getUriString();
//定义数据能力帮助对象
DataAbilityHelper helper=DataAbilityHelper.creator(getContext());
//定义图片来源对象
ImageSource imageSource = null;
//选择的Img对应的Id
String img_id=null;
/*
*如果是选择文件则getUriString结果为dataability:///com.android.providers.media.documents/document/image%3A437,其中%3A437是":"的URL编码结果,后面的数字就是image对应的Id
*/
/*
*如果选择的是图库则getUriString结果为dataability:///media/external/images/media/262,最后就是image对应的Id
*/
//判断是选择了文件还是图库
if(img_uri.lastIndexOf("%3A")!=-1){
img_id = img_uri.substring(img_uri.lastIndexOf("%3A")+3);
}
else {
img_id = img_uri.substring(img_uri.lastIndexOf('/')+1);
}
//获取图片对应的uri,前缀是content,替换成对应的dataability前缀
uri=Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,img_id);
try {
//读取图片
FileDescriptor fd = helper.openFile(uri, "r");
//RawFileDescriptor rfd = helper.openRawFile(uri,"r");
imageSource = ImageSource.create(fd, null);
//创建位图
PixelMap pixelMap = imageSource.createPixelmap(null);
//组件显示选择的图片
photo.setPixelMap(pixelMap);
picByte = new byte[153600];
//打包图片
ImagePacker packer = null;
try{
packer= ImagePacker.create();
ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
//图片格式信息
packingOptions.format = "image/jpeg";
//图片质量
packingOptions.quality = 90;
//打包
packer.initializePacking(picByte, packingOptions);
packer.addImage(pixelMap);
//完成打包
packer.finalizePacking();
}finally {
packer.release();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (imageSource != null) {
imageSource.release();
}
}
}
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}

三、测试原子化服务

1、手动签名

首先我们对工程进行手动签名,得到.cer和.p12文件,这里不再赘述手动签名。

​手动签名​​。

2、申请原子化服务

从零实现原子服务一键分享,免安装!

3、创建服务

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

箭头所指,填工程包名即可,其他自拟,点击确定。

从零实现原子服务一键分享,免安装!

至此,服务创建完成,接下来配置服务。

4、申请证书

这里要申请两个证书,.csr和.p7b.

(1)申请.csr证书

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

申请完证书后,记得点击下载,我们会获得一个.cer证书。

从零实现原子服务一键分享,免安装!

(2)申请.p7b证书

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

申请完后一定记得点击下载,我们会获得.p7b证书。

从零实现原子服务一键分享,免安装!

至此,我们手上有.p12,.p7b,.cer,.csr四种证书,这里非常重要!

5、工程签名

这里用到刚刚申请好的证书,在release一栏进行签名,不是debug哦!

从零实现原子服务一键分享,免安装!

接着编译整个工程,是build APP,不是build hap哦!

从零实现原子服务一键分享,免安装!

这样,我们就获得了APP包!

从零实现原子服务一键分享,免安装!

到这里,我们就能够理解实现原子化服务为啥要APP包,说白了还是把APP上传服务器,用的时候再下载,只不过体积小,实现了无感安装的过程!

6、发布为测试态

我们回到刚刚的服务平台,继续配置服务。

从零实现原子服务一键分享,免安装!

(1)上传APP包

上传刚刚编译好的APP包,这里由于我已经上传过了,界面稍微不一样,总之就是有个按钮上传就完事了。

从零实现原子服务一键分享,免安装!

(2)其余信息

其余信息大部分自拟,如果是要最终实现发布的话,要涉及到很多证书或者专利,如果是只是为了简单测试,就可以像我这样(乱填)填一些信息就行了,关键的在后面。

从零实现原子服务一键分享,免安装!

(3)分发

这里提供了几种找到该服务的方式,这里根据个人情况探索即可。

从零实现原子服务一键分享,免安装!

(4)测试

这里才是第二重要的地方了,我们需要添加测试设备。

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

新增一个组别,信息自拟。

从零实现原子服务一键分享,免安装!

点击查看,添加测试设备的手机号

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!

最后返回到测试界面,点击保存。

从零实现原子服务一键分享,免安装!

最最最后,发布为测试态。

从零实现原子服务一键分享,免安装!

四、结果

完成前文所有步骤后,稍等5-10分钟,大概就能在测试设备的服务中心看到我们制作的原子化服务了。从屏幕右下角往屏幕中心划,可呼出服务中心,注意看图。

从零实现原子服务一键分享,免安装!

从零实现原子服务一键分享,免安装!从零实现原子服务一键分享,免安装!

至此,我们真正实现了免安装功能。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

© 版权声明

相关文章