-
这是百度面试官问的一个问题,当时没答上来。我们知道AIDL底层是基于Binder机制通信的,而Binder本身是C/S架构的。Activity写个AIDL接口可以实现跟Service的通信,那么Service如何主动回调或者主动推送消息到Activity呢?1|0定义通信接口这个接口是Activity发数据给Service用的,addPerson会在Service中的List中新增一个数据,getPersonList返回Person列表。registerCallback注册回调对象,等一会儿会说。package com.billshen.offerlearn.service; import com.billshen.offerlearn.service.IPersonCallBack; interface IPerson { void addPerson(String name); List<String> getPersonList(); void registerCallback(IPersonCallBack cb); void unregisterCallback(IPersonCallBack cb); }IPersonCallBack.aidl这个接口是Service主动回调Activity用的,用于获取Activity中的时间。package com.billshen.offerlearn.service; interface IPersonCallBack { String getTime(); }在AndroidManifest中定义service为另一个进程,并指明action<service android:name="com.billshen.offerlearn.service.AIDLService" android:enabled="true" android:exported="true" android:process=":aidl_service"> <intent-filter> <action android:name="com.aidl.person" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>定义完成后,在AS中build一下,编译器会自动为我们生成binder抽象对象,当然你也可以自己写。IPerson是个interface继承于IInterface 内部包含我刚才在AIDL里面声明的四个方法。 还有三个内部静态类Default,Stub和Proxy。其中Stub交给服务方实现,Proxy交给客户端持有。Proxy主要是怎么往内核空间写数据和拿数据,Default是默认实现,基本不用。 2|0实现具体方法编译器为我们生成的IPerson.java文件中有个Stub()内部静态类,这是个抽象类,具体实现需要我们自己去写。RemoteCallbackList是一个回调对象列表,可以把Service想象成一个服务器,多个Activity为客户端。Service在建立连接的时候保存了客户端的回调对象。public class AIDLService extends Service { private String TAG = AIDLService.class.getSimpleName(); private RemoteCallbackList<IPersonCallBack> iPersonCallBacks = new RemoteCallbackList<>();//回调对象列表 List<String> mPersons = new ArrayList<>(); public AIDLService() { mPersons.add("初始人"); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind: " + mPersons); BroadcastThread broadcastThread = new BroadcastThread(); broadcastThread.start(); return new IPerson.Stub() { @Override public void addPerson(String name) throws RemoteException { mPersons.add(name); } @Override public List<String> getPersonList() throws RemoteException { return mPersons; } @Override public void registerCallback(IPersonCallBack cb) throws RemoteException { iPersonCallBacks.register(cb);//注册回调对象 } @Override public void unregisterCallback(IPersonCallBack cb) throws RemoteException { iPersonCallBacks.unregister(cb);//反注册回调对象 } }; } class BroadcastThread extends Thread { @Override public void run() { while (true) { try { Thread.sleep(1000); int len = iPersonCallBacks.beginBroadcast();//开始发送广播 for (int i = 0; i < len; i++) {//遍历回调对象,将拿回来的数据打印再控制台上 Log.i(TAG, "run: " + iPersonCallBacks.getBroadcastItem(0).getTime()); } iPersonCallBacks.finishBroadcast(); } catch (Exception e) { e.printStackTrace(); } } } } } Activity中实现客户端的回调函数和开启新线程不断让Service增加Person并拿回数据显示在TextView上。public class AIDLActivity extends BaseMvpActivity { IPerson iPerson = null; TextView resultTv; Handler mHandler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { try { resultTv.setText(iPerson.getPersonList().toString()); } catch (RemoteException e) { e.printStackTrace(); } super.handleMessage(msg); } }; @Override public void initView() { Log.i(TAG, "initView: "); resultTv = findViewById(R.id.result_tv); } @Override public void initData() { Log.i(TAG, "initData: "); Intent intent = new Intent(); intent.setPackage("com.billshen.offerlearn"); intent.setAction("com.aidl.person"); ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iPerson = IPerson.Stub.asInterface(service); IPersonCallBack iPersonCallBack = new IPersonCallBack.Stub() { @Override public String getTime() throws RemoteException { //回调函数,返回时间给服务端 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return df.format(System.currentTimeMillis()); } }; try { iPerson.registerCallback(iPersonCallBack);//在Service中注册回调 } catch (RemoteException e) { e.printStackTrace(); } new PersonThread().start(); } @Override public void onServiceDisconnected(ComponentName name) { } }; bindService(intent, serviceConnection, BIND_AUTO_CREATE); } class PersonThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { try { iPerson.addPerson("person" + i); Thread.sleep(1000); mHandler.sendEmptyMessage(0); } catch (RemoteException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } } @Override public int getLayoutId() { return R.layout.activity_aidl; } @Override public void showLoading() { } @Override public void hideLoading() { } @Override public void refreshView() { } @Override public void onError(String errMessage) { } }
-
透过「历史上的今天」,从过去看未来,从现在亦可以改变未来。今天是 2022 年 3 月 19 日,在 1474 年的今天,威尼斯共和国颁布了世界上第一部专利法,并依法颁发了世界上的第一号专利。该法令是现代专利法的基本典范和代表,伽利略就曾在威尼斯取得了他的扬水灌溉机械的专利权。而英国引人专利制度的时间是 1674 年,恰好是在 200 年以后。回顾科技历史上的 3 月 19 日,这一天还发生过哪些有着深远影响的关键事件呢?1927 年 3 月 19 日:图灵奖人工智能先驱 Allen Newell 出生艾伦·纽厄尔(Allen Newell)出生于 1927 年 3 月 19 日,他是计算机科学和认知信息学科学家、人工智能先驱、IPL 共同发明人、早期 AI 程序“逻辑理论家”和“通用问题解决器”共同作者。因为 AI 领域的贡献,纽厄尔与老师司马贺共同获 1975 年图灵奖。1954 年,信息处理语言(Information Processing Language,IPL)诞生;IPL 是纽厄尔和司马贺共同设计与实现的语言,是史上第一种用于研究人工智能的语言,启发了 Lisp 的发明。IPL 是第一种列表处理语言,也是第一种支持递归的语言。纽厄尔在斯坦福大学完成了他的物理学学士学位;随后在 1950 年,他在普林斯顿大学攻读研究生,转而学习数学。由于他很早就接触到博弈论等数学领域,导致他确信自己更喜欢实验和理论研究的结合,而不是纯粹的数学研究。1950 年,他离开普林斯顿,加入了位于圣莫尼卡的兰德公司,在那里他为一个研究空军后勤问题的小组工作。1954 年 9 月,纽厄尔参加了一个研讨会,了解到了当时计算机界对于人工智能领域的最新看法;也就是从那时开始,艾伦相信人工智能系统可以被创建并具有适应能力。着迷于人工智能之后,纽厄尔于 1955 年撰写了《国际象棋机器:通过适应处理复杂任务的示例》,其中“概述了计算机程序以人形方式下棋的富有想象力的设计”。他的工作引起了经济学家(也是未来的诺贝尔奖获得者)赫伯特·西蒙的注意,这位赫伯特·西蒙成为了艾伦·纽厄尔的老师,他在未来多次访华交流讲学及合作研究,为自己取了一个中文名字:司马贺。司马贺与纽厄尔带着他们研发的 IPL 语言,一起参加了 1956 年的达特茅斯会议,这是一次对用机器模拟智能感兴趣的研究人员的非正式聚会;这次会议,现在被广泛认为是“人工智能的诞生”,具有巨大的影响力,与会的人在未来皆成为未来二十年人工智能研究的领导者。会议结束后,两人建立了持久的伙伴关系;他们在卡内基梅隆大学建立了一个人工智能实验室,并在五六十年代末期产生了一系列重要的程序和理论见解。1975 年,纽厄尔和司马贺一起因人工智能方面的基础贡献而被授予图灵奖。2007 年 3 月 19 日:Adobe 推出 Adobe AIR2007 年 3 月 19 日,Adobe 公司发布 AIR public preview(当时还被称为 Apollo)及其软件开发工具包(SDK),这便是 Adobe AIR 的前身;Adobe AIR(Adobe Integrated Runtime)是一个跨操作系统运行环境,用来建造 RIA,使用 Adobe Animate、Flex、HTML 与 AJAX,可部署为桌面应用程序。AIR 是 Adobe 针对网络与桌面应用的结合所开发出来的技术,可以不必经由浏览器而对网络上的云端程序做控制,也由于这是 Adobe 所开发的技术,因此能很顺利的与 Adobe 旗下的 Photoshop 等应用程序来进行开发。Adobe 表示,截至 2014 年 5 月,已经在 AIR 上构建了超过 100,000 个独特的应用程序,并且从世界各地的用户那里记录了超过 10 亿次相同的安装。使用 AIR,开发人员可以访问功能,包括文本、矢量图形、光栅图形、视频、音频、相机和麦克风功能。AIR 还包括其他功能,例如文件系统集成、本机客户端扩展、桌面集成和对连接设备的访问。AIR 使应用程序能够以不同的方式处理数据,包括使用本地文件、本地 SQLite 数据库(AIR 具有内置支持)、数据库服务器或 AIR 随附的加密本地存储。开发人员可以通过构建 AIR Native Extensions 来访问其他功能,它可以访问以本机语言编程的完整设备功能。2019 年 6 月,Adobe 宣布将开始把 Adobe AIR 的持续支持和开发转移到 Harman。Adobe 将继续为 32 位及更早版本提供支持,到 2020 年底之后,软件支持开始由 Harman 管理。2009 年 3 月 19 日:微软发布 Internet Explorer 8Windows Internet Explorer 8(IE8)是微软所开发的网页浏览器,该软件于 2009 年 3 月 19 日首次发行适用于 Windows 的版本,虽然它的后续版本 IE9 已推出,但由于不支持 Windows XP,所以 IE8 在初期还是十分普及。IE8 内置于 Windows 7 以及 Windows Server 2008 R2 中,作为默认的网页浏览器,同时也发行了 Windows XP SP2 和 Windows Vista 的版本供用户下载。IE8 开发于 2006 年 3 月之前。2008 年 2 月,微软发出了 IE8 Beta 1 的私人邀请,并于 2008 年 3 月 5 日向公众发布了 Beta 1,专注于 Web 开发人员。该版本与 Windows Internet Explorer 8 Readiness Toolkit 网站一起发布,该网站宣传 IE8 白皮书、相关软件工具和新功能,以及 Beta 下载链接。2009 年 1 月 5 日,为了 Debug,微软提供了一个工具来阻止通过 Windows Update 自动安装 IE8。IE8 新增和移除了许多功能,其中值得一提的是,从 IE8 开始,用户无法再让 Internet Explorer 在下次启动时自动打开当前会话,而是手动执行此操作。IE8 是首款通过 Acid2 测试也是最后一个通过该测试的主要的网页浏览器。虽然它在 Acid3 测试中仅获得 24/100 的分数,但据微软所说,安全性、易用性和对 RSS、CSS 及 Ajax 的改善与支持是 IE8 的优势。此外,IE8 也是最后一款支持 Windows XP 的版本的 IE。用户界面上,IE7 和 IE8 十分相似,因为 IE8 的改进主要在于引擎和技术方面。微软方面,在 2011 年 3 月 14 日推出 Internet Explorer 9。在 2012 年 7 月时,Internet Explorer 8 使用率为 13.78%左右;随后,据 2012 年 6 月的统计,IE9 在此时超过 IE8,成为最多人使用的 IE 版本。2015 年 3 月 19 日:Android Auto 发布Android Auto 是谷歌(Google)推出的专为汽车所设计的软件,其需要连接 Android Lollipop 以上版本操作系统的手机使用。其预览版本在 2014 年 6 月 26 日的 Google I/O 之开幕式主题演讲中被首度公之于众;Android Auto 目前仅在美国等少数国家及地区提供下载与服务。 2015 年 3 月 19 日,Android Auto 正式发布。2019 年,Android Auto for Phone Screens 推出,允许用户使用手机屏幕来作为车载系统;但从 Android 12 开始, Android Auto for Phone Screens 将无法使用,Android Auto 从此仅适用于车载屏幕上。Android Auto 是 Open Automotive Alliance 的一部分,该联盟由 28 家汽车制造商共同组成,Nvidia 作为技术供应商;该软件旨在取代汽车制造商的原生车载系统来执行 Android 应用与服务并访问与访问 Android 手机内容。目前,Android Auto 能够与 Android 设备集成的几项功能有:一、Google 助理,谷歌的个人智能助理;二、Google 地图,提供卫星定位与语音导航;三、音乐控制,透过 Google Play 音乐或 Pandora、Spotify 等音乐应用程序访问音乐;四、语音操作,联动前面的谷歌助理。在 2016 年 11 月,谷歌添加了将 Android Auto 作为常规应用程序在 Android 设备上运行的选项,即不连接到汽车的主机,这允许它在 Android 驱动的主机上使用,或者只是在一个车内的个人手机或平板电脑。截至 2022 年 2 月,Android Auto 目前可在 46 个国家/地区使用。2019 年 5 月,意大利提起了针对 Android Auto 的反垄断投诉,理由是谷歌仅允许第三方媒体和消息应用在该平台上的政策,阻止了 Enel 提供用于定位汽车充电站的应用。最初,谷歌不允许第三方将他们的地图应用程序与 Android Auto 集成,只有自己的应用程序谷歌地图和 Waze 可用。但自 2020 年以来,谷歌放开限制,允许使用 Sygic 等第三方地图应用程序;随后,谷歌宣布将于 2020 年 8 月向部分合作伙伴发布新的 SDK,并于 2020 年底全面推出,允许第三方修改其应用程序以使用 Android Auto。原文链接:https://blog.csdn.net/Byeweiyang/article/details/123588078
-
目前一些"民族资产解冻类"诈骗在微信群、QQ群里面由开始死灰复燃,不外乎就是让大家交少量的钱,承诺给与高额的回报进行诈骗,曾经许多中老年人不停的报单交钱,那些平台到哪里去了,利箭在行动从莱州市人民法院公布的一起判决书中获悉:自2017年以来,被告人韩某某先后加入“人民军”、“华夏瑞星”、“中华民族事业慈善基金会”等非法组织,帮助组织通过建立微信群发展大量会员,成为骨干成员,利用推出的虚假项目鼓动会员报单,收取报单款并从中牟利。具体犯罪事实如下:1、2017年前后,被告人韩某某经他人介绍加入“人民军”非法组织,该非法组织仿照军队架构,下设军、师、旅、团、营、连、排等不同层级,韩某某先后被任命为第2司令部1军军长、8军军长、12军军长、副司令等职务,通过许以高额回报,推出“9+1”、“10+1”等项目的方式利用微信群发展下级会员1300余人,要求会员交纳报单款或者捐款等。经查,2019年7月期间韩某某微信账户收到每笔为10元的报单款354笔、共计3540元,后将报单款转给上级人员。2、2020年前后,被告人韩某某经他人介绍加入“中华民族事业慈善基金会”非法组织并担任团队长,通过许以高额回报、虚构项目等方式利用微信群发展会员1000余人,要求会员通过微信扫码支付报单款。经查,2020年1月11日至2020年2月2日期间韩某某微信账户收到每笔为7.6元的报单款3804笔、共计28910.4余元,并陆续提现至其微信绑定的中国工商银行账户内,后将其中25000元转给上级人员。被告人韩某某供述,2020年1月韩某通过河北人韩某彬加入了“中华民族事业慈善基金会”,否认韩某是这个基金会的会长,韩某在这个“中华民族事业慈善基金会”里担任过群里的团队长,团队长需要每人报单100元钱,可以享受每月2万元钱的工资,该群里大约有1000多人的会员,会员每人向其报单7.6元钱,在其向总部报单后总部没有兑现会员每月2000元,以及团队长每月20000元的工资待遇。该共收到会员报单款2万多元,该款分两次转账给了崔某玲。2018年年底、2019年年初韩某某加入“华夏瑞星ipv9”项目,任37部03厅厅长,不需要加入的会员花钱,只需要介绍人加入项目,厅长需要花2434元钱进行报单,该群需要会员填写个人身份信息。韩某某在群里发布过让会员学会向公安机关人员保密。韩某某在“人民军”群里担任过副司令的职务,韩某在“人民军”中向会员收取过每人10元的报单费,还有一些会员的捐款,该会员共有7700多人,报单款该通过微信转账和银行卡转账给总部的统计,“人民军”并没有兑现每人2000元的补贴。韩某某对公安统计的自2019年7月至2020年5月仅10元钱的报单一共是369笔、一共是3690元没有异议,韩某辩称都通过微信转给了总部统计。韩某某还在全球物联网平台交过1699元报单费;在黑金卡项目中该向会员收取每人69元和114元的报单费;在“中境集团”项目中,该收取会员34500元报单费,并将这笔钱通过银行转给了司某京。莱州市人民法院认为,被告人韩某某帮助他人利用“中华民族事业慈善基金会”等项目为幌子,以非法占有为目的骗取钱财,数额较大,其行为侵犯了公民的私人财产所有权,构成诈骗罪。公诉机关指控被告人韩某某诈骗的事实及罪名成立。但被告人韩某某归案后基本能够如实供述主要犯罪事实,系坦白,依法可以从轻处罚,自愿认罪认罚,可以从宽处理,被告人在整个诈骗犯罪中系从犯,依法应当从轻处罚。根据本案事实与情节,依法判决如下:一、被告人韩某某犯诈骗罪,判处有期徒刑八个月,缓刑一年。并处罚金人民币5000元。二、追缴被告人韩某某违法所得人民币3910.4元,上缴国库(已交纳)目前还在忽悠大家交钱的类似平台,都是骗局,请不要上当!
-
目前一些"民族资产解冻类"诈骗在微信群、QQ群里面由开始死灰复燃,不外乎就是让大家交少量的钱,承诺给与高额的回报进行诈骗,曾经许多中老年人不停的报单交钱,那些平台到哪里去了,利箭在行动从莱州市人民法院公布的一起判决书中获悉:自2017年以来,被告人韩某某先后加入“人民军”、“华夏瑞星”、“中华民族事业慈善基金会”等非法组织,帮助组织通过建立微信群发展大量会员,成为骨干成员,利用推出的虚假项目鼓动会员报单,收取报单款并从中牟利。具体犯罪事实如下:1、2017年前后,被告人韩某某经他人介绍加入“人民军”非法组织,该非法组织仿照军队架构,下设军、师、旅、团、营、连、排等不同层级,韩某某先后被任命为第2司令部1军军长、8军军长、12军军长、副司令等职务,通过许以高额回报,推出“9+1”、“10+1”等项目的方式利用微信群发展下级会员1300余人,要求会员交纳报单款或者捐款等。经查,2019年7月期间韩某某微信账户收到每笔为10元的报单款354笔、共计3540元,后将报单款转给上级人员。2、2020年前后,被告人韩某某经他人介绍加入“中华民族事业慈善基金会”非法组织并担任团队长,通过许以高额回报、虚构项目等方式利用微信群发展会员1000余人,要求会员通过微信扫码支付报单款。经查,2020年1月11日至2020年2月2日期间韩某某微信账户收到每笔为7.6元的报单款3804笔、共计28910.4余元,并陆续提现至其微信绑定的中国工商银行账户内,后将其中25000元转给上级人员。被告人韩某某供述,2020年1月韩某通过河北人韩某彬加入了“中华民族事业慈善基金会”,否认韩某是这个基金会的会长,韩某在这个“中华民族事业慈善基金会”里担任过群里的团队长,团队长需要每人报单100元钱,可以享受每月2万元钱的工资,该群里大约有1000多人的会员,会员每人向其报单7.6元钱,在其向总部报单后总部没有兑现会员每月2000元,以及团队长每月20000元的工资待遇。该共收到会员报单款2万多元,该款分两次转账给了崔某玲。2018年年底、2019年年初韩某某加入“华夏瑞星ipv9”项目,任37部03厅厅长,不需要加入的会员花钱,只需要介绍人加入项目,厅长需要花2434元钱进行报单,该群需要会员填写个人身份信息。韩某某在群里发布过让会员学会向公安机关人员保密。韩某某在“人民军”群里担任过副司令的职务,韩某在“人民军”中向会员收取过每人10元的报单费,还有一些会员的捐款,该会员共有7700多人,报单款该通过微信转账和银行卡转账给总部的统计,“人民军”并没有兑现每人2000元的补贴。韩某某对公安统计的自2019年7月至2020年5月仅10元钱的报单一共是369笔、一共是3690元没有异议,韩某辩称都通过微信转给了总部统计。韩某某还在全球物联网平台交过1699元报单费;在黑金卡项目中该向会员收取每人69元和114元的报单费;在“中境集团”项目中,该收取会员34500元报单费,并将这笔钱通过银行转给了司某京。莱州市人民法院认为,被告人韩某某帮助他人利用“中华民族事业慈善基金会”等项目为幌子,以非法占有为目的骗取钱财,数额较大,其行为侵犯了公民的私人财产所有权,构成诈骗罪。公诉机关指控被告人韩某某诈骗的事实及罪名成立。但被告人韩某某归案后基本能够如实供述主要犯罪事实,系坦白,依法可以从轻处罚,自愿认罪认罚,可以从宽处理,被告人在整个诈骗犯罪中系从犯,依法应当从轻处罚。根据本案事实与情节,依法判决如下:一、被告人韩某某犯诈骗罪,判处有期徒刑八个月,缓刑一年。并处罚金人民币5000元。二、追缴被告人韩某某违法所得人民币3910.4元,上缴国库(已交纳)目前还在忽悠大家交钱的类似平台,都是骗局,请不要上当!
-
中缀运算 中缀运算定义了两个栈,数字栈和符号栈;分别存储用户输入的数字(例如:1,2,3)和输入的符号(例如:+,-); 下列视频以1+2+3/2*3%3为例;首先输入1,然后输入+,通过对运算符点击事件监听,将其分别纳入数字栈和符号栈,然后在输入2和+,即对+进行监听,并取出符号栈栈顶元素,判断其是否为初始化元素,若为否,则将数字栈栈顶元素取出,并获取拼接字符作为另外一个运算数字;(数字栈栈顶元素 &(代表符号栈栈顶元素)拼接字符串)(1+2);得到结果3之后将其压入数字栈中,并将第二个+号压入符号栈,用于下次运算;以此类推…效果视频运算自定义圆形TextView效果图建立attr文件通过对控件手势动作进行监听,改变按钮的样式;即按下为白色,松开为橙色<resources> <declare-styleable name="SetCircle"> <attr name="CircleColor" format="color"/> <attr name="SelectCircle" format="color"/> </declare-styleable></resources>绘制圆形 protected void onDraw(Canvas canvas) { //判断手势动作,改变控件状态 if (isSelect){ CirclePaint.setColor( SelectCircle ); }else { CirclePaint.setColor( CircleColor ); } //设置填充方式 CirclePaint.setStyle( Paint.Style.FILL ); //设置抗锯齿 CirclePaint.setAntiAlias( true ); RectF rectF = new RectF(); //设置半径,比较长宽,取最大值 int radius = getMeasuredWidth() > getMeasuredHeight() ? getMeasuredWidth() : getMeasuredHeight(); rectF.set(getPaddingLeft(),getPaddingTop(),radius-getPaddingRight(),radius-getPaddingBottom()); //绘制圆弧 canvas.drawArc(rectF,0,360,false,CirclePaint); super.onDraw(canvas); }字符拼接通过StringBuilder将用户输入的数字进行拼接,段尾对复位按钮进行判断,将数字栈和符号栈以及拼接字符串的内容全部清空;private class NumOnClick implements View.OnClickListener{ @Override public void onClick(View view) { switch (view.getId()){ case R.id.Zero: numBuilder.append('0'); break; case R.id.One: numBuilder.append('1'); break; case R.id.Two: numBuilder.append('2'); break; case R.id.Three: numBuilder.append('3'); break; case R.id.Four: numBuilder.append('4'); break; case R.id.Five: numBuilder.append('5'); break; case R.id.Six: numBuilder.append('6'); break; case R.id.Seven: numBuilder.append('7'); break; case R.id.Eight: numBuilder.append('8'); break; case R.id.Nine: numBuilder.append('9'); break; case R.id.Point: numBuilder.append('.'); break; case R.id.Reset: isReset = true; } if (isReset){ PopStack(); numBuilder.delete(0,numBuilder.length()); ResultBox.setText("0"); operatorStack.push('#'); isReset = false; }else { ResultBox.setText(numBuilder.toString()); } } }清空栈内元素清空数字栈和符号栈内元素 private void PopStack(){ while (numStack.isEmpty()){ numStack.pop(); } while (operatorStack.isEmpty()){ operatorStack.pop(); } }运算执行手势监听此处以加运算符为例,对按下和松开两个事情进行监听,分别改变控件样式,并传入相应运算符进行运算 private class OperatorOnClick implements View.OnTouchListener{ @Override public boolean onTouch(View view, MotionEvent motionEvent) { boolean isPress = false; if (motionEvent.getAction() == MotionEvent.ACTION_DOWN){ isPress = true; } switch (view.getId()){ case R.id.Add: if (isPress){ mAdd.IsSelect(true); mAdd.setTextColor(getResources().getColor(R.color.normal)); StartOperation(ADD); }else { mAdd.IsSelect(false); mAdd.setTextColor(getResources().getColor(R.color.select)); } break;入栈&&出栈在初始化时,将符号栈压入‘#’符号,即代表第一次执行,不进行结果运算operatorStack.push('#');1 第一次运行时,即将符号栈栈顶元素取出,即‘#’,假如输入1和+,此时无法构成算式,因为确实另外一个运算数,即直接将其压入栈中,不进行结果运算;然后将拼接字符串清空,因为假如输入完了100和+,因为+号是不显示在用户界面的,如果不进行清空,之后输入的字符会追加在其之后,例如在输入50,即不清空为10050,会造成用户体验不良以及使用麻烦等缺点; 假如输入了100和+,然后输入50和-,构成100+50-算式,第一次不进行运算,如上释所示,第二次输入的-,即取出符号栈顶元素+,和运算数100和50,并将其传入EXEOperation()方法,开始运算结果 private void StartOperation(char symbol){ char operator = operatorStack.pop(); if (operator == EQUAL){ operatorStack.push(symbol); } else if (operator == '#'){ numStack.push(numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString())); operatorStack.push(symbol); numBuilder.delete(0,numBuilder.length()); }else { switch (operator){ case ADD: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),ADD); break; case SUB: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),SUB); break; case MULTIPLY: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),MULTIPLY); break; case DIVISION: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),DIVISION); break; case MOD: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),MOD); break; case EQUAL: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString())); break; } } }运算结果 三个参数分别为第一个运算数,和另外一个运算数,以及符号栈栈顶元素,对符号进行判断,并进行结果运算,然后将结果压入栈中以及第二个运算符压人符合栈; 重点在于对等于键进行判断: 1:例如输入算式1+1-,对第二个运算符进行监听即可得到前一结果,然后在输入数字,符号,得到前一结果… 2:例如输入算式1+1=,此时并没有对等于符号进行监听,无法完成运算,解决办法为不将等于压入符号栈,直接结果入栈,相当于需要执行第一步才能完成运算小数位判断对结果字符串进行子串截取,判断小数点之后是否存在小数位,因为为double类型,默认会存在小数位。例如:(1)1.0,则省略小数点后的0,直接输出0;(2)1.05,则不进行小数位省略,直接输出 //判断小数位之后是否有数字 if (str.substring(str.indexOf('.') + 1, str.length() - 1).isEmpty()) { str = str.substring(0, str.indexOf('.')); }运算代码 private void EXEOperation(double front,double rear,char operator) { double result = 0; String str; /** * 对连续点击运算符,而运算数字并未符合标准时进行判断 * 例如:12+ * 此时12和+分别进栈,此时若再点击运算符+,则无法进行运算,因为rear运算数为空*/ switch (operator) { case ADD: result = front + rear; break; case SUB: result = front - rear; break; case MULTIPLY: result = front * rear; break; case DIVISION: result = front / rear; break; case MOD: result = front < rear ? front :front % rear; break; } numStack.push(result); if (isReturn){ operatorStack.push(EQUAL); }else { operatorStack.push(operator); isReturn = false; } str = String.valueOf(result); //判断小数位之后是否有数字 if (str.substring(str.indexOf('.') + 1, str.length() - 1).isEmpty()) { str = str.substring(0, str.indexOf('.')); } ResultBox.setText(str); //前运算符清空,为后运算符输入做准备 numBuilder.delete(0,numBuilder.length()); }任意进制转换通过输入十进制数,分别转化为相应的二进制,八进制,十六进制数效果视频转换进制进制转换因为需要分别转化多个进制,无法对其数组长度进行判断,即将每个数字首元素作为存储数组长度的地址 private int[] Conversion(int num,int binary){ int[] remainder = new int[255]; int count = 1; do { remainder[count++] = num%binary; num /= binary; } while (num != 0); remainder[0] = count; return remainder; }结果逆置通过对解析的进制数组进行逆置,因为十六进制的10——A,11——B…,所以需要对其进行判断,然后使用字符串拼接,最终返回一个结果字符串 private String Inversion(int[] array){ StringBuilder builder = new StringBuilder(); for (int i = array[0]-1; i >=1 ; i--) { if (array[i] == 10){ builder.append("A"); }else if (array[i] == 11){ builder.append("B"); }else if (array[i] == 12){ builder.append("C"); }else if (array[i] == 13){ builder.append("D"); }else if (array[i] == 14){ builder.append("E"); }else if (array[i] == 15){ builder.append("F"); }else { builder.append(array[i]); } } return builder.toString(); }结果返回int num = Integer.parseInt(ResultBox.getText().toString());Binary_2.setText(Inversion(Conversion(num,2)));Binary_8.setText(Inversion(Conversion(num,8)));Binary_10.setText(Inversion(Conversion(num,10)));Binary_16.setText(Inversion(Conversion(num,16)));原文链接:https://blog.csdn.net/News53231323/article/details/123234328
-
很荣幸参加鲲鹏众智的安卓应用兼容4期项目,项目主要是在kbox2.0上兼容几款办公和娱乐的应用软件。项目中我主要负责应用兼容问题的分析和问题解决,并输出问题的分析报告和解决方案。作为一个习惯在物理机上开发的android系统工程师而言,首次遇到容器手机方案时感觉特别的新奇。与常规模拟器的方案相比,我更好奇它的实现原理、I/O通信、界面渲染的实现等。所以在项目初期我学习了一些与kbox相关知识。这些对于分析定位kbox上兼容app问题起了很大的作用。下面是我在项目中遇到的一些问题及解决办法,Android中为我们提供了logcat日志,以及一些分析工具,可以快速的帮我们定位问题。所以出现问题的时候最直接的方式就是看android的logcat日志,有些问题我们那可以直接从logcat中看出,比如飞书中共享桌面崩溃时的logcat,我们看到红框位置提示没有找到submix,直接到系统中查找submix的对应的库文件是否存在。我们在编译的android系统中新编译submix库,崩溃问题就解决了。还有的问题除了要分析logcat日志以外,还需要同步分析tombstone日志。Tombstone日志可以告诉我们具体报错的进程。如果此进程在打包的镜像中包含它的调试符号信息,我们可以使用addr2line命令获取具体报错的位置。如下日志告诉我们发生崩溃的是surfaceflinger进程。可以根据tid和pid知道进程调用关系。这里可以用到kbox渲染相关的知识。还有一种是比较麻烦的兼容适配问题,就是应用本身会出现问题。项目中有一个应用在kbox上测试出现错误日志打印,虽然可以修改android系统源码规避此问题,但是不能从根本上解决问题。好在应用的官方发布了升级版本,在升级版本中就没有此问题。出现这样的情况,最好的方式就是在物理机上同步测试,如果物理机上也出现,大概率可以判断是应用本身问题,可以不用花费大量精力去分析日志了。在项目过程中发现kbox方案可以更好的解决和替代之前工作中遇到的一些实用方案。期待kbox开源部分功能。
-
非常荣幸能参加鲲鹏众智Android应用兼容4期项目,项目主要是在国产鲲鹏PC上搭建Android 容器,在容器内兼容几款常用办公和娱乐应用。在项目中,我主要是负责功能和性能测试。因为之前做过手机应用相关的性能测试,再结合项目对性能测试的具体要求,很快就决定使用monkey测试和python3+weditor+uiautomator2来进行性能测试。下面着重介绍下性能测试使用的方法和技术。Monkey测试又被称作猴子测试,是指通过向系统发送伪随机用户事件流对Android 系统和应用做测试,用来检测应用稳定性和健壮性,主要目的是检测应用是否会出现crash、冻屏和资源异常。monkey测试测试环境搭建和使用这里不在细聊,想了解可以直接问度娘。测试时使用的命令如下:adb shell monkey -p 包名 --hprof --pct-touch 50 --pct-motion 30 --pct appswitch 10 --pct-majornav 10 --ignore-timeouts --ignore-security-exceptions --monitor-native-crashes --ignore-crashes --throttle 500 -v -v -v 190000 1>/home/kylin/Desktop/monkey_test_info.log 2>/home/kylin/Desktop/monkey_log/monkey_test_error.logpython3+weditor+uiautomator2 主要是结合项目性能要求,默认用户正常操作来对应用的稳定性进行测试。在使用前需要安装python3环境和adb环境,然后在python内使用pip 安装python 三方库weditor和uiautomator2。安装完成后在pycharm新建python项目,使用venv虚拟环境,cmd进入到venv虚拟环境目录,执行下面命令激活虚拟环境:python -m venv env;source env/bin/acticate。在虚拟环境venv目录输入weditor,会弹出一个weditor网页界面。最左侧是手机通过adb连接的画面,通过鼠标在上面进行操作,相关操作的详细信息会被记录在中间一栏;最右侧是自动录制的脚本,可以直接回放。下面是项目中结合性能要求使用的一段代码,主要是实现对应用主要功能进行遍历测试,仅供参考。代码如下:import uiautomator2 as u2import timeusb_connect_addr="0.0.0.0:8501"d=u2.connect(usb_connect_addr)d.loggerfor i in range (5000): try: d.xpath( '//*[@resource-id="com.pingan.smt:id/app_navigation"]/android.widget.LinearLayout[1]/android.support.v7.app.ActionBar-b[2]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]').click() time.sleep(0.5) d.xpath( '//*[@resource-id="com.pingan.smt:id/recycler_view"]/android.widget.FrameLayout[1]/android.view.ViewGroup[1]/android.widget.ImageView[1]').click() time.sleep(0.5) d.xpath( '//*[@resource-id="com.pingan.smt:id/officehall_recyclerView"]/android.widget.LinearLayout[3]/android.widget.ImageView[1]').click() time.sleep(1) d.xpath('//*[@text="二、三级运动员授予"]').click() time.sleep(4) d.xpath('//*[@resource-id="com.pingan.smt:id/iv_title_left"]').click() d.xpath('//*[@resource-id="com.pingan.smt:id/iv_title_left"]').click() time.sleep(0.5) d.xpath('//*[@resource-id="com.pingan.smt:id/iv_title_left"]').click() d.xpath( '//*[@resource-id="com.pingan.smt:id/app_navigation"]/android.widget.LinearLayout[1]/android.support.v7.app.ActionBar-b[1]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]').click() time.sleep(30) print(i) print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) d.p except: pass湖南麒麟信安科技股份有限公司-华为鲲鹏众智项目团队--黄美建指导老师:邓旺波
-
Androi中的日志工具类是Log(android.util.Log),这个类提供了如下5种方法来供我们打印日志。方法作用Log.v()用于打印那些最为繁琐的、意义最小的日志信息。对应级别是verbose,是Android日志里面级别最低的一种。Log.d()用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的。对应级别debug,比verbose高一级。Log.i()用于打印一些比较重要的数据,这些数据应该是你非常想看到的、可以帮助你分析用户行为数据。对应级别info,比debug高一级。Log.w()用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别warn,比info高一级。Log.e()用于打印程序中的错误信息,比如程序进入catch语句当。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别error,比warn高一级。@TOC一、日志方法我们先来演示一下Log.d()的用法: 它需要传入两个参数,第一个参数是tag,一般传入当前的类名就好,主要用于对打印信息进行过滤;第二个参数是msg,即想要打印信息的具体内容。Log.d("MainActivity","Hello Debug");我们运行之后来看下控制台Logcat的内容: 不仅包含了打印日志内容和tag名,还有包含程序的包名、打印时间以及应用进程的进程号。二、快捷键对于java选手来说,sout就是最喜欢的快捷键。那么对于Android选手,打印日志也有快捷键,只需要输入logd,然后按下Tab键,就会自动帮你补全一条完整的打印语句。同理,输入logi、logw等等。另外,由于Log的所有打印方法都要求传入一个tag参数,每次都写一遍显然太过麻烦,这里有个小技巧,我们在onCreate()的方法外面输入logt,然后按下Tab键补全,自动生成一个TAG常量。三、过滤器除了快捷键以外,logcat中还可以轻松添加过滤器,可以看到目前我们所有的过滤器:目前有4个过滤器:show only selected application表示只显示当前选中程序的日志。Firebase是谷歌提供的一个分析工具,我们可以不用管它。No Filters相当于没有过滤器,会把所有日志显示出来。Edit Filter Configuration是自定义过滤器,比如下面的MainActivity就是我自定义的一个过滤器。我们来演示下如何自定义过滤器:点击Edit Filter Configuration,会弹出一个过滤器配置界面。如何给过滤器起名为MainActivity,并且让它对MainActivity的tag进行过滤。我们点击这个过滤器,发现我们的日志被筛选为先几条tag名为MainActivity的日志了。四、日志级别控制学完了过滤器,我们再来看下logcat中日志级别的控制吧。logcat主要有6个级别,在第一部分中已经介绍过了。给大家整理了一下日志等级表:log等级提示颜色verbose冗余信息 ,级别最低黑色debug调试信息蓝色info普通信息绿色warning警告信息橙色error错误信息红色assert断言,级别最高当选择级别是verbose时,也就是最低等级。不管我们使用哪一个方法打印日志,这条日志一定会显示出来。如果我们选中debug,那么只有使用debug以上级别的方法打印的日志才会显示出来,以此类推。String returnData=data.getStringExtra("data_return"); Log.v(TAG,returnData); Log.d(TAG,returnData); Log.i(TAG, returnData); Log.w(TAG, returnData);我们运行程序,打印以上四种级别的信息,此时选择verbose级别:我们选择info等级,发现只显示info以及warn的日志:五、关键字过滤使用关键字可以进一步过滤我们想看的日志信息,而且关键字过滤支持正则表达式,有了这个特性,我们可以构建出更加丰富的过滤条件。链接:https://bbs.huaweicloud.com/blogs/335874
-
概述此示例应用演示了如何快速的使用Cloud DB构建简单的图书管理服务。通过快速入门和示例应用,您将会了解到如下信息:如何使用Cloud DB进行应用开发。应用数据如何写入到Cloud DB。如何实现数据的查询。实时侦听数据的更改。体验端云数据同步等功能。开发准备使用Cloud DB构建应用服务,需要完成以下准备工作:您已经在开发者联盟官网注册帐号并通过实名认证,详细请参见帐号注册认证。您已经在AppGallery Connect控制台上创建项目和应用,详细请参见创建项目。示例应用使用了认证用户的相关权限,需要开通AppGallery Connect认证服务中“匿名帐号”服务,详细请参见认证服务。您已经获取到示例代码,请从示例代码获取。您已在本地安装Android Studio。启用服务使用Cloud DB服务前,您需要先启用服务。登录AppGallery Connect网站,选择“我的项目”。在项目列表页面中选择项目,单击项目下需要启用云数据库服务的应用。在导航树上选择“构建 > 云数据库”。单击“立即开通”,开通云数据库服务。如您还未选择数据处理位置,需要您先设置数据处理位置,Cloud DB支持多个数据处理位置,您可以设置一个数据处理位置,也可以设置多个数据处理位置,具体操作请参见设置数据处理位置。服务初始化成功后,即启用云数据库服务成功。 说明: 您进入云数据库界面后展现的是默认数据处理位置的云数据库,当您的应用需要支持多个数据处理位置时,请在“数据处理位置”选择其他存储地后再分别进行操作。新增和导出对象类型您需要基于AppGallery Connect控制台创建对象类型,请您遵循操作步骤创建示例中涉及的对象类型,并导出用于Android应用开发的java格式对象类型文件。登录AppGallery Connect网站,选择“我的项目”。在项目列表页面中选择项目,单击项目下需要创建对象类型的应用。在导航树上选择“构建 > 云数据库”。单击“新增”,进入创建对象类型页面。输入对象类型名为“BookInfo”后,单击“下一步”。单击 + 新增如下字段后,单击“下一步”。表1 字段定义表字段类型类型主键非空加密默认值idinteger√√--bookNameString----authorString----priceDouble----pulisherString----shadowFlagBoolean---true单击+ 新增索引,设置索引名为“bookName”,索引字段为“bookName”后,单击“下一步”。按照如下要求设置各角色权限后,单击“下一步”。表2 权限配置表角色queryupsertdelete所有人√--认证用户√√√数据创建者√√√管理员√√√单击“确定”。创建完成后返回对象类型列表中,可以查看已创建的对象类型。单击“导出”。选择导出文件格式,选择“java格式”。选择java文件类型,选择“android”。输入包名称,即java文件中的package名称。包名称只能包含以下3种类型:字母(A-Z或a-z)数字(0-9)特殊字符:_和.单击“导出”。文件将会导出至本地,其内包含该版本中所有的对象类型。导出的java格式文件在后续步骤用于添加至本地开发环境。新增存储区您可基于AppGallery Connect控制台在云侧创建数据存储区,请您遵循操作步骤创建一个存储区名称为“QuickStartDemo”的存储区。登录AppGallery Connect网站,选择“我的项目”。在项目列表页面中选择项目,单击项目下需要创建存储区的应用。在导航树上选择“构建 > 云数据库”。选择“存储区”页签。单击“新增”,进入创建存储区页面。输入存储区名称为“QuickStartDemo”。单击“确定”。创建完成后返回存储区列表中,可以查看已创建的存储区。配置开发环境使用Android Studio打开示例项目。集成AGC SDK,详细请参见集成AGC SDK。添加Cloud DB SDK至应用级build.gradle文件。在<项目名>/app/build.gradle文件中dependencies部分添加Cloud DB SDK。dependencies { // 添加Cloud DB SDK implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300' implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300' }在应用级build.gradle文件中设置Java源码兼容模式为JDK1.8版本。compileOptions { sourceCompatibility = 1.8 targetCompatibility = 1.8 }添加对象类型文件在开发应用时,可直接将AppGallery Connect控制台上导出的java格式文件添加至本地开发环境,并通过AGConnectCloudDB类中的createObjectType()方法实现对象类型的定义和创建。您在进行本地应用开发时,无需再次创建对象类型。将已在AppGallery Connect控制台上导出的全部java格式文件添加至本地开发环境,如已存在,请覆盖原文件。文件位置:/app/src/main/java/com/huawei/agc/clouddb/quickstart/model。初始化Cloud DB,通过AGConnectCloudDB类中的createObjectType()方法实现对象类型的定义和创建,详细请参见初始化。初始化在添加对象类型文件后,您就可以使用云数据库进行应用开发。您开发应用时,需要先执行初始化操作,初始化对应数据处理位置的AGConnectCloudDB、创建Cloud DB zone和对象类型。通过initialize()方法初始化AGConnectCloudDB。Java:public static void initAGConnectCloudDB(Context context) { AGConnectCloudDB.initialize(context); }Kotlin:fun initAGConnectCloudDB(context: Context?) { AGConnectCloudDB.initialize(context!!) }2. 通过setRoutePolicy设置数据处理位置、buildInstance指定配置生成新的AGConnectInstance实例、getInstance(AGConnectInstance connectInstance, AGConnectAuth auth)方法获取对应数据处理位置的AGConnectCloudDB实例,并使用createObjectType()创建对象类型。Java:AGConnectInstance instance = AGConnectInstance.buildInstance(new AGConnectOptionsBuilder().setRoutePolicy(AGCRoutePolicy.CHINA).build(mContext)); mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance(instance)); mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());Kotlin:var instance = AGConnectInstance.buildInstance(AGConnectOptionsBuilder().setRoutePolicy(AGCRoutePolicy.CHINA).build(context)); mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance(instance)) mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo())3.创建Cloud DB zone配置对象,并打开该Cloud DB zone(以Cloud DB zone的同步属性为缓存模式、访问属性为公共存储区为例),详细请参考CloudDBZoneConfig。Java:mConfig = new CloudDBZoneConfig("QuickStartDemo", CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE, CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC); mConfig.setPersistenceEnabled(true); Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true); openDBZoneTask.addOnSuccessListener(new OnSuccessListener<CloudDBZone>() { @Override public void onSuccess(CloudDBZone cloudDBZone) { Log.i(TAG, "open cloudDBZone success"); mCloudDBZone = cloudDBZone; // Add subscription after opening cloudDBZone success addSubscription(); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.w(TAG, "open cloudDBZone failed for " + e.getMessage()); } });Kotlin:mConfig = CloudDBZoneConfig("QuickStartDemo", CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE, CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC) mConfig!!.persistenceEnabled = true val task = mCloudDB.openCloudDBZone2(mConfig!!, true) task.addOnSuccessListener { Log.i(TAG, "Open cloudDBZone success") mCloudDBZone = it // Add subscription after opening cloudDBZone success addSubscription() }.addOnFailureListener { Log.w(TAG, "Open cloudDBZone failed for " + it.message) }写入数据在本节主要介绍如何在应用程序中进行数据写入操作,以便您了解如何使用Cloud DB SDK实现数据的写入。在应用界面中,增加了“添加”按钮,用于用户新增数据,并在代码中通过executeUpsert()实现数据的写入。Java:public void upsertBookInfos(BookInfo bookInfo) { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it"); return; } Task<Integer> upsertTask = mCloudDBZone.executeUpsert(bookInfo); upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() { @Override public void onSuccess(Integer cloudDBZoneResult) { Log.i(TAG, "Upsert " + cloudDBZoneResult + " records"); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { mUiCallBack.updateUiOnError("Insert book info failed"); } }); }Kotlin:fun upsertBookInfos(bookInfo: BookInfo?) { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it") return } val upsertTask = mCloudDBZone!!.executeUpsert(bookInfo!!) upsertTask.addOnSuccessListener { cloudDBZoneResult -> Log.i(TAG, "Upsert $cloudDBZoneResult records") }.addOnFailureListener { mUiCallBack.updateUiOnError("Insert book info failed") } }查看数据获取数据变化用户在应用界面中新增的数据,将会被存储在云侧。在端侧注册数据变化侦听器,当云侧数据发生变化时,端侧能够感知数据变化,及时刷新本地应用数据。通过查询条件与subscribeSnapshot()方法组合使用,可以指定侦听对象,当侦听对象的数据发生变化时,端侧会收到通知,根据快照获取变化的数据信息,从云侧同步数据至端侧应用。Java:private OnSnapshotListener<BookInfo> mSnapshotListener = new OnSnapshotListener<BookInfo>() { @Override public void onSnapshot(CloudDBZoneSnapshot<BookInfo> cloudDBZoneSnapshot, AGConnectCloudDBException e) { if (e != null) { Log.w(TAG, "onSnapshot: " + e.getMessage()); return; } CloudDBZoneObjectList<BookInfo> snapshotObjects = cloudDBZoneSnapshot.getSnapshotObjects(); List<BookInfo> bookInfos = new ArrayList<>(); try { if (snapshotObjects != null) { while (snapshotObjects.hasNext()) { BookInfo bookInfo = snapshotObjects.next(); bookInfos.add(bookInfo); updateBookIndex(bookInfo); } } mUiCallBack.onSubscribe(bookInfos); } catch (AGConnectCloudDBException snapshotException) { Log.w(TAG, "onSnapshot:(getObject) " + snapshotException.getMessage()); } finally { cloudDBZoneSnapshot.release(); } } }; public void addSubscription() { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it"); return; } try { CloudDBZoneQuery<BookInfo> snapshotQuery = CloudDBZoneQuery.where(BookInfo.class) .equalTo(BookEditFields.SHADOW_FLAG, true); mRegister = mCloudDBZone.subscribeSnapshot(snapshotQuery, CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY, mSnapshotListener); } catch (AGConnectCloudDBException e) { Log.w(TAG, "subscribeSnapshot: " + e.getMessage()); } }Kotlin:private val mSnapshotListener = OnSnapshotListener<BookInfo> { cloudDBZoneSnapshot, e -> if (e != null) { Log.w(TAG, "onSnapshot: " + e.message) return@OnSnapshotListener } val snapshotObjects = cloudDBZoneSnapshot.snapshotObjects val bookInfoList: MutableList<BookInfo> = ArrayList() try { if (snapshotObjects != null) { while (snapshotObjects.hasNext()) { val bookInfo = snapshotObjects.next() bookInfoList.add(bookInfo) updateBookIndex(bookInfo) } } mUiCallBack.onSubscribe(bookInfoList) } catch (snapshotException: AGConnectCloudDBException) { Log.w(TAG, "onSnapshot:(getObject) " + snapshotException.message) } finally { cloudDBZoneSnapshot.release() } } private fun addSubscription() { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it") return } try { val snapshotQuery = CloudDBZoneQuery.where(BookInfo::class.java) .equalTo(BookEditFields.SHADOW_FLAG, true) mRegister = mCloudDBZone!!.subscribeSnapshot(snapshotQuery, CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY, mSnapshotListener) } catch (e: AGConnectCloudDBException) { Log.w(TAG, "subscribeSnapshot: " + e.message) } }数据查询和排序在应用界面中,增加了“查询”按钮和排序功能,通过executeQuery()、addOnSuccessListener()和addOnFailureListener()方法组合,实现异步方式查询数据。Java:public void queryBooks(CloudDBZoneQuery<BookInfo> query) { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it"); return; } Task<CloudDBZoneSnapshot<BookInfo>> queryTask = mCloudDBZone.executeQuery(query, CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY); queryTask.addOnSuccessListener(new OnSuccessListener<CloudDBZoneSnapshot<BookInfo>>() { @Override public void onSuccess(CloudDBZoneSnapshot<BookInfo> snapshot) { processQueryResult(snapshot); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { mUiCallBack.updateUiOnError("Query failed"); } }); }Kotlin:fun queryBooks(query: CloudDBZoneQuery<BookInfo>) { if (mCloudDBZone == null) { Log.w(TAG, "CloudDBZone is null, try re-open it") return } val queryTask = mCloudDBZone!!.executeQuery(query, CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY) queryTask.addOnSuccessListener { snapshot -> processQueryResult(snapshot) } .addOnFailureListener { mUiCallBack.updateUiOnError("Query failed") } }通过查询与limit()方法组合,实现限制查询数据显示条数的功能;与orderByAsc()方法或者orderByDesc()方法组合来实现数据的排序功能。Java:private void queryWithOrder() { invalidateViewInNormalMode(); CloudDBZoneQuery<BookInfo> query = CloudDBZoneQuery.where(BookInfo.class); if (!mQueryInfo.bookName.isEmpty()) { query.contains(BookEditFields.BOOK_NAME, mQueryInfo.bookName); } query.greaterThanOrEqualTo(BookEditFields.PRICE, mQueryInfo.lowestPrice); if (mQueryInfo.lowestPrice != mQueryInfo.highestPrice || mQueryInfo.lowestPrice != 0.0) { query.lessThanOrEqualTo(BookEditFields.PRICE, mQueryInfo.highestPrice); } if (mQueryInfo.showCount > 0) { query.limit(mQueryInfo.showCount); } if (mSortState.state == SortState.State.UP) { query.orderByAsc(mSortState.field); } else { query.orderByDesc(mSortState.field); } mHandler.post(() -> mCloudDBZoneWrapper.queryBooks(query)); }Kotlin:private fun queryWithOrder() { invalidateViewInNormalMode() val query = CloudDBZoneQuery.where(BookInfo::class.java) if (mQueryInfo.bookName!!.isNotEmpty()) { query.contains(BookEditFields.BOOK_NAME, mQueryInfo.bookName!!) } query.greaterThanOrEqualTo(BookEditFields.PRICE, mQueryInfo.lowestPrice) if (mQueryInfo.lowestPrice != mQueryInfo.highestPrice || mQueryInfo.lowestPrice != 0.0) { query.lessThanOrEqualTo(BookEditFields.PRICE, mQueryInfo.highestPrice) } if (mQueryInfo.showCount > 0) { query.limit(mQueryInfo.showCount) } if (mSortState.state == SortState.State.UP) { query.orderByAsc(mSortState.field!!) } else { query.orderByDesc(mSortState.field!!) } mHandler.post { mCloudDBZoneWrapper.queryBooks(query) } }应用编译运行到此,您已经了解了示例应用的开发流程。您可以编译并生成APK包,在Android手机上安装、运行示例应用。如需体验示例应用,您可以在A手机上打开应用,并新增数据;然后您可以在B手机上打开应用,查看在A手机上写入的数据。
-
场景:openeye呼叫接入码进线到IVR流程,流程中播放视频和音频,音频播放正常,视频无法展示,openeye上显示异常。问题原因:支持9:16竖屏、16:9横屏、4:3横屏、1:1横屏多种屏幕比例,以及如下分辨率,帧率建议至少支持30fps支持9:16竖屏,分辨率 540*960、576*1024、720*1280支持16:9 横屏,分辨率640*360、854*480、960*540、1024*576支持4:3 横屏,分辨率640*480、800*600、1024*768支持1:1 横屏,分辨率640*640、720*720、800*800、960*960文件必须是3gp格式帧率是 30帧语音文件常见问题UAP侧分析放音文件的平均采样率未符合UAP要求,当前的平均采样率是16000,UAP要求是8000UAP侧分析放音文件的平均采样率未符合UAP要求,当前的平均采样率是16000,UAP要求是8000音频采样率 设置为8000 不然视频里面的语音,听不到
-
编译出包1.配置编译环境。2.开启联邦编译选项,在mindspore根目录进行编译,编译包含aarch64和aarch32的AAR包代码如下:export MSLITE_ENABLE_FL=onbash build.sh -A on -j32获取生成的Android AAR包。如下:mindspore-lite-maven-{version}.zip运行时要注意运行依赖:所以要构建运行依赖:将文件mindspore-lite-maven-{version}.zip解压后,所得到的目录结构如下所示:mindspore-lite-maven-{version}└── mindspore└── mindspore-lite└── {version}└── mindspore-lite-{version}.aar # MindSpore Lite训练框架AAR包由此可知联邦学习相关的AAR包路径是:mindspore/output/mindspore/mindspore-lite/{version}/mindspore-lite-{version}.aar其中AAR包中与联邦学习相关的目录结构如下:mindspore-lite-{version}├── jni│ ├── arm64-v8a│ │ ├── libjpeg.so # 图像处理动态库文件│ │ ├── libminddata-lite.so # 图像处理动态库文件│ │ ├── libmindspore-lite.so # MindSpore Lite推理框架依赖的动态库│ │ ├── libmindspore-lite-jni.so # MindSpore Lite推理框架依赖的jni动态库│ │ ├── libmindspore-lite-train.so # MindSpore Lite训练框架依赖的动态库│ │ ├── libmindspore-lite-train-jni.so # MindSpore Lite训练框架依赖的jni动态库│ │ └── libturbojpeg.so # 图像处理动态库文件│ └── armeabi-v7a│ ├── libjpeg.so # 图像处理动态库文件│ ├── libminddata-lite.so # 图像处理动态库文件│ ├── libmindspore-lite.so # MindSpore Lite推理框架依赖的动态库│ ├── libmindspore-lite-jni.so # MindSpore Lite推理框架依赖的jni动态库│ ├── libmindspore-lite-train.so # MindSpore Lite训练框架依赖的动态库│ ├── libmindspore-lite-train-jni.so # MindSpore Lite训练框架依赖的jni动态库│ └── libturbojpeg.so # 图像处理动态库文件├── libs│ ├── mindspore-lite-java-common.jar # MindSpore Lite训练框架jar包│ └── mindspore-lite-java-flclient.jar # 联邦学习框架jar包└── classes.jar # MindSpore Lite训练框架jar包注意,由于生成Android环境中的联邦学习jar包时未包含所依赖的第三方开源软件包,因此在Android环境中,使用AAR包前,需要用户在Android工程下的app/build.gradle文件的dependencies{}字段中添加相关依赖语句,用于加载联邦学习所依赖的三个开源软件,如下所示:dependencies {undefined//添加联邦学习所依赖第三方开源软件implementation group: ‘com.squareup.okhttp3’, name: ‘okhttp’, version: ‘3.14.9’implementation group: ‘com.google.flatbuffers’, name: ‘flatbuffers-java’, version: ‘2.0.0’implementation(group: ‘org.bouncycastle’,name: ‘bcprov-jdk15on’, version: ‘1.68’)}联邦学习依赖的第三方开源软件bcprov-jdk15on包含多版本class文件,为防止低版本jdk编译高版本class文件出错,在Android工程的gradle.properties文件中可添加如下设置语句:android.jetifier.blacklist=bcprov工程中设置好了如上所示依赖之后,只需依赖 AAR包即可调用联邦学习提供的相关接口x86环境编译出包3.配置编译环境。4.在mindspore根目录进行编译,编译x86架构相关包。bash build.sh -I x86_64 -j32获取生成的x86架构相关包。如下:mindspore/output/mindspore-lite-{version}-linux-x64.tar.gz构建运行依赖环境:代码如下:mindspore-lite-{version}-linux-x64├── tools│ ├── benchmark_train # 训练模型性能与精度调测工具│ ├── converter # 模型转换工具│ └── cropper # 库裁剪工具│ ├── cropper # 库裁剪工具可执行文件│ └── cropper_mapping_cpu.cfg # 裁剪cpu库所需的配置文件└── runtime├── include # 训练框架头文件│ └── registry # 自定义算子注册头文件├── lib # 训练框架库│ ├── libminddata-lite.a # 图像处理静态库文件│ ├── libminddata-lite.so # 图像处理动态库文件│ ├── libmindspore-lite-jni.so # MindSpore Lite推理框架依赖的jni动态库│ ├── libmindspore-lite-train.a # MindSpore Lite训练框架依赖的静态库│ ├── libmindspore-lite-train.so # MindSpore Lite训练框架依赖的动态库│ ├── libmindspore-lite-train-jni.so # MindSpore Lite训练框架依赖的jni动态库│ ├── libmindspore-lite.a # MindSpore Lite推理框架依赖的静态库│ ├── libmindspore-lite.so # MindSpore Lite推理依赖的动态库│ ├── mindspore-lite-java.jar # MindSpore Lite训练框架jar包│ └── mindspore-lite-java-flclient.jar # 联邦学习框架jar包└── third_party└── libjpeg-turbo└── lib├── libjpeg.so.62 # 图像处理动态库文件└── libturbojpeg.so.0 # 图像处理动态库文件其中联邦学习所需的相关x86包名如下:libjpeg.so.62 # 图像处理动态库文件libminddata-lite.so # 图像处理动态库文件libmindspore-lite.so # MindSpore Lite推理框架依赖的动态库libmindspore-lite-jni.so # MindSpore Lite推理框架依赖的jni动态库libmindspore-lite-train.so # MindSpore Lite训练框架依赖的动态库libmindspore-lite-train-jni.so # MindSpore Lite训练框架的jni动态库libturbojpeg.so.0 # 图像处理动态库文件mindspore-lite-java-flclient.jar # 联邦学习框架jar包在x86中设置环境变量(export LD_LIBRARY_PATH=/resource/x86libs/:$LD_LIBRARY_PATH————————————————原文链接:https://blog.csdn.net/qq_36893844/article/details/121914450
-
原文链接:https://blog.csdn.net/m0_50065287/article/details/122943351还记得在今年年初盘点“Bug 连连”的 Android 12 时,XDA Developers 的前主编 Mishaal Rahman 曾预言:“Android 12 的 Bug 修复对谷歌而言已压力不小,由此可能导致下一版本的开发周期遭到缩减,从而引发恶性循环。”结果不到一个月,Mishaal Rahman 就被“打脸”了:上周,谷歌正式发布了 Android 13 首个开发者预览版。不可否认的是,在感受过“Android 历史上最大设计变更”的 Android 12 后,首个 Android 13 预览版似乎处在一种“珠玉在前,瓦石难当”的尴尬处境:在看过谷歌的介绍后,许多人吐槽 Android 13 “平淡无奇”、“鸡肋平庸”。但就在昨天,Android 和 Web 开发人员 @kdrag0n 发现了隐藏在 Android 13 预览版中的闪光点:谷歌 Pixel 6 等设备在安装 Android 13 预览版后,可实现完全虚拟化!这意味着,现在我们可以在 Pixel 6 或其他基于 Tensor 处理器的设备上,以接近原生的速度运行几乎任意操作系统,包括 Windows 11、Ubuntu 或 Arch Linux Arm 等 Linux 发行版。一、将 KVM 引入 Android早在 2 个月之前,Mishaal Rahman 就曾“谷歌将如何在 Android 13 中使用虚拟化”这一话题进行过分析,他坚定认为:“谷歌多年碎片化战争的下一个战场是虚拟化。”说起 Android 的虚拟化,Android 系统团队的 Will Deacon 将之称为“碎片化的狂野西部”。因为不论虚拟机是否存在于设备上,它们的作用通常都不是用来运行其他操作系统,而是用于试图增强内核安全或在 Android 操作系统之外运行杂项代码(例如 DRM、密码学和其他闭源二进制文件的第三方代码)。但在谷歌看来,“在 Android 操作系统之外运行杂项代码”这一点存在重大隐患:从上面这张 ARMv8/v9 异常模型中可以看出,虚拟机程序运行在 EL2 层,而在 ARM 命名法中,数字越大,特权级别越高,即 EL2 比行在 EL0、EL1 的代码权限更高。这也就意味着,许多闭源二进制文件的第三方代码的运行权限,比操作系统和内核还高。这显然是个安全隐患,因为在较高 EL 上运行的代码可访问低级别的所有寄存器。为解决这一安全问题,谷歌一直在寻求一个通用的虚拟化解决方案,以实现解除第三方代码的特权,并将该代码与 Android 和其他第三方程序隔离开来。关于这点,KVM 是个不错的选择。KVM,全名 Kernel-based Virtual Machine,是一个开源的系统虚拟化模块,自 Linux 2.6.20 之后,便广泛集成在各个主流 Linux 发行版中,主要使用 Linux 自身的调度器进行管理。而 Android 同样也基于 Linux 内核构建,所以谷歌自然而然会选择将 KVM 部署为通用虚拟机管理程序。但谷歌并非照搬全收,它实际上是在扩展具有额外安全功能的 KVM,即 pKVM:受保护的 KVM。当时,Mishaal Rahman 就已预测了 Android 13 可能出现的变化:“目前,市场上没有任何 Android 设备配备虚拟化模块,包括谷歌自己的 Pixel 6 也没有,但这将随着即将发布的 Android 13 版本而改变。”他补充道,据他了解,谷歌有计划在 Android 13 中引入 pKVM 和虚拟机框架的第一个版本。二、Mishaal Rahman 的预测成功这次,Mishaal Rahman 的预测成功了:通过加入全新虚拟化框架的 Android 13 预览版,@kdrag0n 完成了在 Pixel 6 中运行 Windows 11 及部分 Linux 发行版的尝试。@kdrag0n 表示:“在 Pixel 6 + Android 13 DP1 上,拥有 KVM 管理程序(接近本机性能)的成熟虚拟机。”Ta 还分享道,目前在设备上可获得完整的 EL2,而“pKVM”是可选项,可在每个 VM 的基础上启用,但对于未受保护的 VM,似乎可使用完整的 KVM 功能。为证明其运行成功,@kdrag0n 在推特上发布了一则各种 Linux 发行版作为 VM 在 Pixel 6 上启动的视频,其中包括 Ubuntu 21.10、Arch Linux Arm、Void Linux 和 Alpine Linux,甚至还有一段在 Arch 上为 arm64 编译 Linux 5.17-rc3 allnoconfig 的录屏。此后,@kdrag0n 又通过 Android 13 虚拟化,让 Pixel 6 得以运行 Windows 11 并进行了优化:“稍微提高了性能,Windows VM 现在真的可以使用了,尽管不支持硬件 GPU 加速,但 CPU、I/O 和内存压力已缓和许多。”不仅如此,@kdrag0n 还成功通过 Pixel 6 的 Windows 虚拟机,连接到电脑上(为进行键盘输入),十分顺畅地玩起了 1993 年的老游戏《毁灭战士》。但 @kdrag0n 也补充道,这是 Windows on ARM,而非 x86,并且不支持嵌套虚拟化,所以也不支持 WSA。尽管这远非完美的体验,也不如本地安装操作系统那样流畅,但能够良好运行已然不易。相信等到 Android 13 正式版支持 pKVM 时,其虚拟机体验将再度完善,届时各类操作系统在 Android 手机上的表现也值得期待。参考链接:https://blog.esper.io/android-dessert-bites-5-virtualization-in-android-13-351789/https://www.cnx-software.com/2022/02/14/android-13-virtualization-lets-pixel-6-run-windows-11-linux-distributions/https://twitter.com/kdrag0n/status/1492754683445669893
-
【操作步骤&问题现象】1、下载 Android platform-tools 到本地后 ,执行adb指令,提示执行异常【截图信息】
-
公司最近开发了一个Android版手游应用,想了解一下上线以来玩家充值情况,就让我接入华为分析的Android SDK。今天我就来给大家分享一下如何将Analytics Kit添加到安卓应用中去。本文是基于Android平台,关于iOS应用接入华为分析服务SDK的方法,可以参考:接入华为分析的iOS SDK。我接入的版本是5.0.0.301。 当我们开始接SDK时,我们要注意以下两个方面:参考华为开发者联盟最新的SDK接入文档,并注意你接入的SDK版本不要遗漏文档中需要复制的代码细节 具体步骤包括:配置AppGallery Connect集成HMS Core SDK接入Analytics1 配置AppGallery Connect 请按照如下步骤为你的安卓应用配置AppGallery Connect。如果你的应用同时拥有Android和iOS版本,你可以将两个应用放在同一个项目内,后续可以灵活切换,以对全量用户做跨平台、跨设备的统一数据分析。(一)开发前准备为你的安卓应用准备好包名(向开发人员询问)。登录 AppGallery Connect 网站 并创建一个新项目。 项目创建好后,你会进入一个页面,左侧导航栏展示了AGC的多种功能菜单。3. 点击“添加应用“按钮,在项目下创建Android应用。点击确定后会直接进入到“设置SDK”的引导页面中,我建议大家先去检查并确保已经完成高级分析服务的开通。(二)开通华为分析服务 点击“API管理“,看到Analystics Kit已默认开启。回到左侧导航栏,转到“华为分析”,你将看到各种数据报告,包括用户分析、行为分析、受众分析等,点击任意菜单即可开通分析服务。 然后进入“项目接入设置”页面,设置数据存储位置、时区、货币、设置用户数据留存时间、设置自然周定义后,单击“完成”,即完成Analytics Kit服务开通。第一步:添加AppGallery Connect配置文件a) 下载配置文件“agconnect-services.json”b) 将“agconnect-services.json”文件拷贝到应用级根目录下第二步:添加SDK 在Gradle文件中设置AppGallery Connect的Gradle插件以及AppGallery Connect SDK基础包。a) 设置项目级build.gradleallprojects { repositories { //Add Maven maven {url 'http://developer.huawei.com/repo/'} } } ... buildscript{ repositories { //Add Maven maven { url 'http://developer.huawei.com/repo/' } } dependencies { // Add this line classpath 'com.huawei.agconnect:agcp:1.1.1.300' } }b) 设置模块级build.gradledependencies { // Add this line implementation 'com.huawei.agconnect:agconnect-core:1.0.0.300' } ... // Add to the bottom of the file apply plugin: 'com.huawei.agconnect'1234567c)单击“Sync now”或“Sync Project with Gradle Files”,开始构建工程。接入Analytics在你的应用中初始化Analytics SDK最后一步是在你的应用中添加初始化代码。通过 getInstance 接口SDK根据配置完成初始化。通过 onEvent 接口记录定义事件。如果你不确定如何标记事件,请查看事件说明。参考“API说明”,调用接口实现各类功能。另外,在开发过程中,推荐启用调试模式,借助华为分析的应用调试功能,可以实时查看事件的上报情况,观察上报结果并做调测。开启/停用调试模式开启调试模式:在Android设备上执行如下命令行,开启调试模式。调试模式打开后会保持启用状态,所有事件将实时上报。adb shell setprop debug.huawei.hms.analytics.app package_name1停用调试模式:您可执行如下命令行,停用调试模式:adb shell setprop debug.huawei.hms.analytics.app .none.1 数据成功上报后,华为分析的应用调试界面会展示出数据,如下图:
-
公司最近开发了一个Android版手游应用,想了解一下上线以来玩家充值情况,就让我接入华为分析的Android SDK。今天我就来给大家分享一下如何将Analytics Kit添加到安卓应用中去。本文是基于Android平台,关于iOS应用接入华为分析服务SDK的方法,可以参考:接入华为分析的iOS SDK。我接入的版本是5.0.0.301。 当我们开始接SDK时,我们要注意以下两个方面:参考华为开发者联盟最新的SDK接入文档,并注意你接入的SDK版本不要遗漏文档中需要复制的代码细节 具体步骤包括:配置AppGallery Connect集成HMS Core SDK接入Analytics1 配置AppGallery Connect 请按照如下步骤为你的安卓应用配置AppGallery Connect。如果你的应用同时拥有Android和iOS版本,你可以将两个应用放在同一个项目内,后续可以灵活切换,以对全量用户做跨平台、跨设备的统一数据分析。(一)开发前准备为你的安卓应用准备好包名(向开发人员询问)。登录 AppGallery Connect 网站 并创建一个新项目。 项目创建好后,你会进入一个页面,左侧导航栏展示了AGC的多种功能菜单。3. 点击“添加应用“按钮,在项目下创建Android应用。点击确定后会直接进入到“设置SDK”的引导页面中,我建议大家先去检查并确保已经完成高级分析服务的开通。(二)开通华为分析服务 点击“API管理“,看到Analystics Kit已默认开启。回到左侧导航栏,转到“华为分析”,你将看到各种数据报告,包括用户分析、行为分析、受众分析等,点击任意菜单即可开通分析服务。 然后进入“项目接入设置”页面,设置数据存储位置、时区、货币、设置用户数据留存时间、设置自然周定义后,单击“完成”,即完成Analytics Kit服务开通。第一步:添加AppGallery Connect配置文件a) 下载配置文件“agconnect-services.json”b) 将“agconnect-services.json”文件拷贝到应用级根目录下第二步:添加SDK 在Gradle文件中设置AppGallery Connect的Gradle插件以及AppGallery Connect SDK基础包。a) 设置项目级build.gradleallprojects { repositories { //Add Maven maven {url 'http://developer.huawei.com/repo/'} } } ... buildscript{ repositories { //Add Maven maven { url 'http://developer.huawei.com/repo/' } } dependencies { // Add this line classpath 'com.huawei.agconnect:agcp:1.1.1.300' } }b) 设置模块级build.gradledependencies { // Add this line implementation 'com.huawei.agconnect:agconnect-core:1.0.0.300' } ... // Add to the bottom of the file apply plugin: 'com.huawei.agconnect'1234567c)单击“Sync now”或“Sync Project with Gradle Files”,开始构建工程。接入Analytics在你的应用中初始化Analytics SDK最后一步是在你的应用中添加初始化代码。通过 getInstance 接口SDK根据配置完成初始化。通过 onEvent 接口记录定义事件。如果你不确定如何标记事件,请查看事件说明。参考“API说明”,调用接口实现各类功能。另外,在开发过程中,推荐启用调试模式,借助华为分析的应用调试功能,可以实时查看事件的上报情况,观察上报结果并做调测。开启/停用调试模式开启调试模式:在Android设备上执行如下命令行,开启调试模式。调试模式打开后会保持启用状态,所有事件将实时上报。adb shell setprop debug.huawei.hms.analytics.app package_name1停用调试模式:您可执行如下命令行,停用调试模式:adb shell setprop debug.huawei.hms.analytics.app .none.1 数据成功上报后,华为分析的应用调试界面会展示出数据,如下图:
上滑加载中
推荐直播
-
OpenHarmony应用开发之网络数据请求与数据解析
2025/01/16 周四 19:00-20:30
华为开发者布道师、南京师范大学泰州学院副教授,硕士研究生导师,开放原子教育银牌认证讲师
科技浪潮中,鸿蒙生态强势崛起,OpenHarmony开启智能终端无限可能。当下,其原生应用开发适配潜力巨大,终端设备已广泛融入生活各场景,从家居到办公、穿戴至车载。 现在,机会敲门!我们的直播聚焦OpenHarmony关键的网络数据请求与解析,抛开晦涩理论,用真实案例带你掌握数据访问接口,轻松应对复杂网络请求、精准解析Json与Xml数据。参与直播,为开发鸿蒙App夯实基础,抢占科技新高地,别错过!
回顾中 -
Ascend C高层API设计原理与实现系列
2025/01/17 周五 15:30-17:00
Ascend C 技术专家
以LayerNorm算子开发为例,讲解开箱即用的Ascend C高层API
回顾中
热门标签