• [问题求助] 【鲲鹏BoostKit ARM原生】【robox方案】android启动异常
    【功能模块】按照官网robox方案搭建android环境(地址:https://support.huaweicloud.com/prtg-robox-kunpengcps/kunpengcps_02_0002.html),按照操作步骤完成安装部署(除了ExaGear没有安装部署,因为不需要arm32转arm64)【操作步骤&问题现象】1、通过下面命令启动Xorg,启动成功    Xorg :0 -config /etc/X11/xorg.conf 2、通过下面命令启动robox,robox启动成功    ./robox -v start 1 3、进入docker内,通过下面命令查看android启动情况,结果发现没有完全启动,android镜像是从官网提供地址下载的    getprop | grep sys.boot.completed    ps查看有些android进程是启动成功的,有些好像没有成功,而且docker log -f 查看日志好像一直会重复打印一些错误信息,4、通过adb shell查看是否能发现android虚拟设备,要么没有设备,要么是offline状态,截图信息有结果显示【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [ARM原生] 发现robox启动脚本有个明显的错误,不知道你们是怎么做到能启动成功的???!!!
    基于官网robox方案搭建android环境,anbox可以启动,但是docker容器里android不能完全启动跟踪排查发现robox启动脚本有个明显的错误,binder对android来说如此重要,不知道别人都是怎么启动成功的
  • [ARM原生] robox方案下:no devices/emulators found
    安装部署好robox,执行adb sell有时输出:    error: no devices/emulators found  有时输出    error: device offline 按照华为云官网上步骤检查(地址:https://support.huaweicloud.com/prtg-robox-kunpengcps/kunpengrobox920_02_0010.html)发现只有进入docker容器内执行检查android是否完全启动的命令不符合要求,其他都是符合预期的:    getprop | grep sys.boot.completedandroid image是从官网提供地址下载的,ps检查看有些android有些进程是启动成功的,比如:
  • [ARM原生] robox容器可以启动,但是docker容器里的android镜像没有启动成功
    目前robox容器可以启动,但是进入docker容器里发现android镜像并没有启动成功,使用的是华为云官网上提供的android镜像:
  • [技术干货] Android之代码混淆
    应用混淆(ProGuard)ProGuard是一个免费的JAVA类文件压缩,优化,混淆器。它探测并删除没有使用的类,字段,方法和属性,它删除没有用的说明并使用字节码得到最大优化,它使用无意义的名字重命名类,字段和方法。我们先来介绍下ProGuard我们为啥要使用ProGuard?优化应用:创建紧凑的代码文档是为了更快的网络传输,快速装载和更小的内存占用;防止反向:创建的程序和程序库很难使用反向工程;预处理应用:充分利用JAVA的快捷加载的优点来提前检测和返回JAVA中存在的类文件。这是开发中用到的一些混淆规则,大家可以根据需要复制到自己的项目中的混淆规则的文件中即可。# Add project specific ProGuard rules here.# By default, the flags in this file are appended to flags specified# in D:\androidstudio\sdk\android-sdk/tools/proguard/proguard-android.txt# You can edit the include path and order by changing the proguardFiles# directive in build.gradle.## For more details, see#   http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following# and specify the fully qualified class name to the JavaScript interface# class:#-keepclassmembers class fqcn.of.javascript.interface.for.webview {#   public *;#}#-keepattributes EnclosingMethod ##--------------------------基本不用动区域--------------------------## -----------------------------基本 -----------------------------# # 指定代码的压缩级别 0 - 7(指定代码进行迭代优化的次数,在Android里面默认是5,这条指令也只有在可以优化时起作用。)-optimizationpasses 5# 混淆时不会产生形形色色的类名(混淆时不使用大小写混合类名)-dontusemixedcaseclassnames# 指定不去忽略非公共的库类(不跳过library中的非public的类)-dontskipnonpubliclibraryclasses# 指定不去忽略包可见的库类的成员-dontskipnonpubliclibraryclassmembers#不进行优化,建议使用此选项,-dontoptimize # 不进行预校验,Android不需要,可加快混淆速度。-dontpreverify# 屏蔽警告-ignorewarnings# 指定混淆是采用的算法,后面的参数是一个过滤器# 这个过滤器是谷歌推荐的算法,一般不做更改-optimizations !retCode/simplification/arithmetic,!field/*,!class/merging/*# 保护代码中的Annotation不被混淆-keepattributes *Annotation*# 避免混淆泛型, 这在JSON实体映射时非常重要-keepattributes Signature# 抛出异常时保留代码行号-keepattributes SourceFile,LineNumberTable #优化时允许访问并修改有修饰符的类和类的成员,这可以提高优化步骤的结果。# 比如,当内联一个公共的getter方法时,这也可能需要外地公共访问。# 虽然java二进制规范不需要这个,要不然有的虚拟机处理这些代码会有问题。当有优化和使用-repackageclasses时才适用。#指示语:不能用这个指令处理库中的代码,因为有的类和类成员没有设计成public ,而在api中可能变成public-allowaccessmodification#当有优化和使用-repackageclasses时才适用。-repackageclasses '' # 混淆时记录日志(打印混淆的详细信息) # 这句话能够使我们的项目混淆后产生映射文件 # 包含有类名->混淆后类名的映射关系-verbose ## ----------------------------- 默认保留 -------------------------# # 保持哪些类不被混淆#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆-keep public class * extends android.app.Activity-keep public class * extends android.app.Application-keep public class * extends android.app.Service-keep public class * extends android.content.BroadcastReceiver-keep public class * extends android.content.ContentProvider-keep public class * extends android.app.backup.BackupAgentHelper-keep public class * extends android.preference.Preference-keep public class * extends android.view.View # AndroidX的混淆-keep class com.google.android.material.** {*;}-keep class androidx.** {*;}-keep public class * extends androidx.**-keep interface androidx.** {*;}-dontwarn com.google.android.material.**-dontnote com.google.android.material.**-dontwarn androidx.** # javax.annotation #表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致-keepclasseswithmembernames class * {    native ;} #这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆#表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,#当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了-keepclassmembers class * extends android.app.Activity{    public void *(android.view.View);} #表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);} #表示不混淆任何一个View中的setXxx()和getXxx()方法,#因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。-keep public class * extends android.view.View{    *** get*();    void set*(***);    public (android.content.Context);    public (android.content.Context, android.util.AttributeSet);    public (android.content.Context, android.util.AttributeSet, int);}-keepclasseswithmembers class * {    public (android.content.Context, android.util.AttributeSet);    public (android.content.Context, android.util.AttributeSet, int);} #表示不混淆Parcelable实现类中的CREATOR字段,#毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。-keep class * implements android.os.Parcelable {  public static final android.os.Parcelable$Creator *;}# 这指定了继承Serizalizable的类的如下成员不被移除混淆-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamField[] serialPersistentFields;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}# 保留R下面的资源-keep class **.R$* { *;}#不混淆资源类下static的-keepclassmembers class **.R$* {    public static ;} # 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆-keepclassmembers class * {    void *(**On*Event);    void *(**On*Listener);} # 保留我们自定义控件(继承自View)不被混淆-keep public class * extends android.view.View{    *** get*();    void set*(***);    public (android.content.Context);    public (android.content.Context, android.util.AttributeSet);    public (android.content.Context, android.util.AttributeSet, int);}  ##-----混淆保护自己项目的部分代码以及引用的第三方jar包 -------------------##-libraryjars libs/source.aar#-libraryjars libs/tbs_sdk_thirdapp_v4.3.0.67_43967_sharewithdownloadwithfile_withoutGame_obfs_20200923_120452.jar#-libraryjars libs/com.heytap.msp-push-2.1.0.aar#-libraryjars libs/jcore-android-2.7.6.jar#-libraryjars libs/jpush-android-4.0.6.jar#-libraryjars libs/jpush-android-plugin-huawei-v4.0.6.jar#-libraryjars libs/jpush-android-plugin-meizu-v3.7.0.jar#-libraryjars libs/jpush-android-plugin-oppo-v4.0.6.jar#-libraryjars libs/jpush-android-plugin-vivo-v4.0.6.jar#-libraryjars libs/jpush-android-plugin-xiaomi-v4.0.6.jar#-libraryjars libs/meizu-push-3.9.0.jar#-libraryjars libs/MiPush_SDK_Client_3_8_5.jar#-libraryjars libs/push_sdk_v3.0.0.jar#-libraryjars libs/LiteAVSDK_Professional_8.4.9947.aar#-libraryjars libs/alipaysdk-15.8.02.210308182128.aar#-libraryjars libs/umeng-analytics-8.1.2.jar#-libraryjars libs/umeng-common-2.1.0.jar#-libraryjars libs/umeng-share-core-6.9.6.jar#-libraryjars libs/umeng-share-QQ-simplify-6.9.6.jar#-libraryjars libs/umeng-sharetool-6.9.6.jar#-libraryjars libs/umeng-share-wechat-simplify-6.9.6.jar#-libraryjars libs/wechat-sdk-android-with-mta-1.0.2.jar##--------------------- 腾讯X5 WebView -------------------#-dontwarn dalvik.**-dontwarn com.tencent.smtt.** -keep class com.tencent.smtt.** {    *;} -keep class com.tencent.tbs.** {    *;} #webView需要进行特殊处理 -keepclassmembers class * extends android.webkit.WebViewClient {    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);    public boolean *(android.webkit.WebView, java.lang.String);}-keepclassmembers class * extends android.webkit.WebViewClient {    public void *(android.webkit.WebView, jav.lang.String);}#在app中与HTML5的JavaScript的交互进行特殊处理#我们需要确保这些js要调用的原生方法不能够被混淆,于是我们需要做如下处理:-keepclassmembers class com.centit.theatre.dsBridge.JsApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsAudioApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsDeviceApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsEchoApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsNavigationApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsNotificationApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsPayApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsPermissionApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsPushApi {    ;}-keepclassmembers class com.centit.theatre.dsBridge.JsUtilApi {    ;} ##---------------------------------实体类------------------------------#--------(实体Model不能混淆,否则找不到对应的属性获取不到值)----------------#-dontwarn centit.theatre.model.**#对含有反射类的处理-keep class centit.theatre.model.** { *; } ## ----------------------------- 其他的 -----------------------------## 删除代码中Log相关的代码-assumenosideeffects class android.util.Log {    public static boolean isLoggable(java.lang.String, int);    public static int v(...);    public static int i(...);    public static int w(...);    public static int d(...);    public static int e(...);} # 保持测试相关的代码#-dontnote junit.framework.**#-dontnote junit.runner.**#-dontwarn android.test.**#-dontwarn android.support.test.**#-dontwarn org.junit.** ## ----------------------------- 第三方 -----------------------------# # FastJson-dontwarn com.alibaba.fastjson.**-keep class com.alibaba.fastjson.**{*; } # Retrofit-dontwarn retrofit2.**-keep class retrofit2.** { *; }-keepattributes Exceptions #-------------- okhttp3 --------------dontwarn com.squareup.okhttp.**-keep class com.squareup.okhttp.{*;}-keep interface com.squareup.okhttp.** { *; } -dontwarn com.squareup.okhttp3.**-keep class com.squareup.okhttp3.** { *;} -keep class okhttp3.** { *; }-keep interface okhttp3.** { *; }-dontwarn okhttp3.** # Okio-dontwarn com.squareup.**-dontwarn okio.**-keep public class org.codehaus.* { *; }-keep public class java.nio.* { *; }#----------okhttp end-------------- # butterknife-keep class butterknife.** { *; }-dontwarn butterknife.internal.**-keep class **$$ViewBinder { *; }-keepclasseswithmembernames class * {   @butterknife.* ;}-keepclasseswithmembernames class * {   @butterknife.* ;} #Glide-keep public class * implements com.bumptech.glide.module.GlideModule-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {  **[] $VALUES;  public *;} #eventbus-keepclassmembers class ** {    @org.greenrobot.eventbus.Subscribe ;}-keep enum org.greenrobot.eventbus.ThreadMode { *; }# Only required if you use AsyncExecutor-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {    (java.lang.Throwable);} # RxJava RxAndroid-dontwarn sun.misc.**-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {    long producerIndex;    long consumerIndex;}-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {    rx.internal.util.atomic.LinkedQueueNode producerNode;}-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {    rx.internal.util.atomic.LinkedQueueNode consumerNode;} # rxandroid-1.2.1 -keepclassmembers class rx.android.**{*;} # 路由框架 ARouter-keep public class com.alibaba.android.arouter.routes.**{*;}-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现-keep class * implements com.alibaba.android.arouter.facade.template.IProvider # 报错后添加-keep class * implements com.alibaba.android.arouter.facade.model.RouteMeta-dontwarn com.alibaba.android.arouter.facade.model.RouteMeta # autosize适配方案-keep class me.jessyan.autosize.** { *; }-keep interface me.jessyan.autosize.** { *; } # litepal数据库框架-keep class org.litepal.** {*;}-keep class * extends org.litepal.crud.DataSupport {*;}-keep class * extends org.litepal.crud.LitePalSupport {*;} #jpush极光推送-dontwarn cn.jpush.**-keep class cn.jpush.** { *; } # AndroidCodeUtils-keep class com.blankj.utilcode.** { *; }-keepclassmembers class com.blankj.utilcode.** { *; }-dontwarn com.blankj.utilcode.** # Bugly-dontwarn com.tencent.bugly.**-keep public class com.tencent.bugly.**{*;} # Addidional for x5.sdk classes for apps-keep class com.tencent.smtt.export.external.**{*;}-keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {*;}-keep class com.tencent.smtt.sdk.CacheManager {public *;}-keep class com.tencent.smtt.sdk.CookieManager {public *;}-keep class com.tencent.smtt.sdk.WebHistoryItem {public *;}-keep class com.tencent.smtt.sdk.WebViewDatabase {public *;}-keep class com.tencent.smtt.sdk.WebBackForwardList {public *;}-keep public class com.tencent.smtt.sdk.WebView {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebView$HitTestResult {    public static final ;    public java.lang.String getExtra();    public int getType();} -keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {    public ;} -keep public class com.tencent.smtt.sdk.WebView$PictureListener {    public ;    public ;} -keepattributes InnerClasses -keep public enum com.tencent.smtt.sdk.WebSettings$** {*;} -keep public enum com.tencent.smtt.sdk.QbSdk$** {*;} -keep public class com.tencent.smtt.sdk.WebSettings {    public *;} -keep public class com.tencent.smtt.sdk.ValueCallback {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebViewClient {    public ;    public ;} -keep public class com.tencent.smtt.sdk.DownloadListener {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebChromeClient {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {    public ;    public ;} -keep class com.tencent.smtt.sdk.SystemWebChromeClient{    public *;}# 1. extension interfaces should be apparent-keep public class com.tencent.smtt.export.external.extension.interfaces.* {    public protected *;} # 2. interfaces should be apparent-keep public class com.tencent.smtt.export.external.interfaces.* {    public protected *;} -keep public class com.tencent.smtt.sdk.WebViewCallbackClient {    public protected *;} -keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebIconDatabase {    public ;    public ;} -keep public class com.tencent.smtt.sdk.WebStorage {    public ;    public ;} -keep public class com.tencent.smtt.sdk.DownloadListener {    public ;    public ;} -keep public class com.tencent.smtt.sdk.QbSdk {    public ;    public ;} -keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {    public ;    public ;}-keep public class com.tencent.smtt.sdk.CookieSyncManager {    public ;    public ;} -keep public class com.tencent.smtt.sdk.Tbs* {    public ;    public ;} -keep public class com.tencent.smtt.utils.LogFileUtils {    public ;    public ;} -keep public class com.tencent.smtt.utils.TbsLog {    public ;    public ;} -keep public class com.tencent.smtt.utils.TbsLogClient {    public ;    public ;} -keep public class com.tencent.smtt.sdk.CookieSyncManager {    public ;    public ;} # Added for game demos-keep public class com.tencent.smtt.sdk.TBSGamePlayer {    public ;    public ;} -keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {    public ;    public ;} -keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {    public ;    public ;} -keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {    public ;    public ;} -keep public class com.tencent.smtt.utils.Apn {    public ;    public ;}-keep class com.tencent.smtt.** {*;}# end # TakePhoto,拍照、相册、裁剪-keep class com.jph.takephoto.** { *; }-dontwarn com.jph.takephoto.** -keep class com.darsh.multipleimageselect.** { *; }-dontwarn com.darsh.multipleimageselect.** -keep class com.soundcloud.android.crop.** { *; }-dontwarn com.soundcloud.android.crop.**  # Blankj工具类-keep class com.blankj.utilcode.** { *; }-keepclassmembers class com.blankj.utilcode.** { *; }-dontwarn com.blankj.utilcode.** # 微信过滤出去-dontwarn com.tencent.**-keep class com.tencent.** { *; } # BaseRecyclerViewAdapterHelper-keep class com.chad.library.adapter.** {*;}-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter-keep public class * extends com.chad.library.adapter.base.BaseViewHolder-keepclassmembers  class **$** extends com.chad.library.adapter.base.BaseViewHolder {     (...);} # banner 的混淆代码-keep class com.youth.banner.** {    *; }  #leakcanary -dontwarn com.squareup.haha.guava.** -dontwarn com.squareup.haha.perflib.** -dontwarn com.squareup.haha.trove.** -dontwarn com.squareup.leakcanary.** -keep class com.squareup.haha.** { *; } -keep class com.squareup.leakcanary.** { *; }  # Retrolambda -dontwarn java.lang.invoke.*  # Gson -keep class com.google.gson.stream.** { *; } -keepattributes EnclosingMethod -keep class org.xz_sale.entity.**{*;} -keep class com.google.gson.** {*;} -keep class com.google.**{*;} -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.stream.** { *; } -keep class com.google.gson.examples.android.model.** { *; }   # Android10标识 -keep class com.bun.miitmdid.core.** {*;} # 找不到类-dontwarn com.trello.rxlifecycle2.LifecycleProvider-dontwarn com.trello.rxlifecycle2.LifecycleTransformer-dontwarn com.trello.rxlifecycle2.OutsideLifecycleException-dontwarn com.trello.rxlifecycle2.RxLifecycle # 微信-keep class com.tencent.mm.opensdk.** {    *;}-keep class com.tencent.wxop.** {    *;}-keep class com.tencent.mm.sdk.** {    *;} #PictureSelector 2.0-keep class com.luck.picture.lib.** { *; } #Ucrop-dontwarn com.yalantis.ucrop**-keep class com.yalantis.ucrop** { *; }-keep interface com.yalantis.ucrop** { *; } # kongzue弹出框-keep class com.kongzue.dialog.** { *; }-dontwarn com.kongzue.dialog.**# AndroidX版本请使用如下配置:-dontwarn androidx.renderscript.**-keep public class androidx.renderscript.** { *; } # 乐播Lebo-keep class com.hpplay.**{*;}-keep class com.hpplay.**$*{*;}-dontwarn com.hpplay.** # 极光推送-dontwarn cn.jiguang.**-keep class cn.jiguang.** { *; }-dontwarn cn.jpush.**-keep class cn.jpush.** { *; }-keep public class com.sina.** {    *;} # mp4parser相关-dontwarn com.googlecode.mp4parser.**-keep public class com.googlecode.mp4parser.** {*;}-keep public class org.googlecode.mp4parser.** {*;}-keep public class com.coremedia.** {*;}-keep public class com.googlecode.** {*;} # permissions权限工具-dontwarn com.hjq.permissions.**-keep class com.hjq.permissions.** {*;}当设置完毕混淆规则后,紧接着在build.gradle配置如下:            // 移除无用的资源文件            shrinkResources true            // ZipAlign 优化            zipAlignEnabled true            // 设置混淆            minifyEnabled true             // 混淆配置        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-app.pro'把属性设置为tue就大功告成啦,接着就可以打包啦。
  • [技术干货] flutter 路由器实践 (上)
    flutter 应用的运行需要依赖 MaterialApp/CupertinoApp 这两个 Widget,他们分别对应着 android/ios 的设计风格,同时也为应用的运行提供了一些基本的设施,比如与路由相关的主页面、路由表等,再比如跟整体页面展示相关的 theme、locale 等。其中与路由相关的几项配置有 home、routes、initialRoute、onGenerateRoute、onUnknownRoute,它们分别对应着主页面 widget、路由表(根据路由找到对应 widget)、首次加载时的路由、路由生成器、未知路由代理(比如常见的 404 页面)。MaterialApp/CupertinoApp 的子结点都是 WidgetsApp,只不过他们给 WidgetsApp 传入了不同的参数,从而使得两种 Widget 的界面风格不一致。Navigator 就是在 WidgetsApp 中创建的,Widget build(BuildContext context) {  Widget navigator;    if (_navigator != null) {    navigator = Navigator(      key: _navigator,      // If window.defaultRouteName isn't '/', we should assume it was set      // intentionally via `setInitialRoute`, and should override whatever      // is in [widget.initialRoute].      initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName          ? WidgetsBinding.instance.window.defaultRouteName          : widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,      onGenerateRoute: _onGenerateRoute,      onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null        ? Navigator.defaultGenerateInitialRoutes        : (NavigatorState navigator, String initialRouteName) {          return widget.onGenerateInitialRoutes(initialRouteName);        },      onUnknownRoute: _onUnknownRoute,      observers: widget.navigatorObservers,    );  }  ...}在 WidgetsApp 的 build 中第一个创建的就是 Navigator,主要看一下它的参数,首先,_navigator 是一个 GlobalKey,使得 WidgetsApp 可以通过 key 调用 Navigator 的函数进行路由切换,也就是在 WidgetsBinding 中处理 native 的路由切换信息的时候,最终是由 WidgetsApp 完成的。另外这里的 _navigator 应该只在 WidgetsApp 中有使用,其他地方需要使用一般是直接调用 Navigator.of 获取,这个函数会沿着 element 树向上查找到 NavigatorState,所以在应用中切换路由是需要被 Navigator 包裹的,不过由于 WidgetsApp 中都有生成 Navigator,开发中也不必考虑这些。另外,就是关于底层获取上层 NavigatorElement 实例的方式,在 Element 树中有两种方式可以从底层获取到上层的实例,一种方式是使用 InheritedWidget,另一种就是直接沿着树向上查找(ancestorXXXOfExactType 系列),两种方式的原理基本是一致的,只不过 InheritedWidget 在建立树的过程中会一层层向下传递,而后者是使用的时候才向上查找,所以从这个角度来说使用 InheritedWidget 会高效些,但是 InheritedWidget 的优势不止如此,它是能够在数据发生改变的时候通知所有依赖它的结点进行更新,这也是 ancestorXXXOfExactType 系列所没有的。observers 是路由切换的监听列表,可以由外部传入,在路由切换的时候做些操作,比如 HeroController 就是一个监听者。Navigator 是一个 StatefulWidget,在 NavigatorState 的 initState 中完成了将 initRoute 转换成 Route 的过程,并调用 push 将其入栈,生成 OverlayEntry,这个会继续传递给下层负责显示页面的 Overlay 负责展示。在 push 的过程中,route 会被转换成 OverlayEntry 列表存放,每一个 OverlayEntry 中存储一个 WidgetBuilder,从某种角度来说,OverlayEntry 可以被认为是一个页面。所有的页面的协调、展示是通过 Overlay 完成的,Overlay 是一个类似于 Stack 的结构,它可以展示多个子结点。在它的 initState 中,void initState() {  super.initState();  insertAll(widget.initialEntries);}会将 initialEntries 都存到 _entries 中。Overlay 作为一个能够根据路由确定展示页面的控件,它的实现其实比较简单:Widget build(BuildContext context) {  // These lists are filled backwards. For the offstage children that  // does not matter since they aren't rendered, but for the onstage  // children we reverse the list below before adding it to the tree.  final List<Widget> onstageChildren = <Widget>[];  final List<Widget> offstageChildren = <Widget>[];  bool onstage = true;  for (int i = _entries.length - 1; i >= 0; i -= 1) {    final OverlayEntry entry = _entries[i];    if (onstage) {      onstageChildren.add(_OverlayEntry(entry));      if (entry.opaque)        onstage = false;    } else if (entry.maintainState) {      offstageChildren.add(TickerMode(enabled: false, child: _OverlayEntry(entry)));    }  }  return _Theatre(    onstage: Stack(      fit: StackFit.expand,      children: onstageChildren.reversed.toList(growable: false),    ),    offstage: offstageChildren,  );}build 函数中,将所有的 OverlayEntry 分成了可见与不可见两部分,每一个 OverlayEntry 生成一个 _OverlayEntry,这是一个 StatefulWidget,它的作用主要是负责控制当前页重绘,都被封装成 然后再用  _Theatre 展示就完了,在 _Theatre 中,可见/不可见的子结点都会转成 Element,但是在绘制的时候,_Theatre 对应的 _RenderTheatre 只会把可见的子结点绘制出来。判断某一个 OverlayEntry 是否能够完全遮挡上一个 OverlayEntry 是通过它的 opaque 变量判断的,而 opaque 又是由 Route 给出的,在页面动画执行时,这个值会被设置成 false,然后在页面切换动画执行完了之后就会把 Route 的 opaque 参数赋值给它的 OverlayEntry,一般情况下,窗口对应的 Route 为 false,页面对应的 Route 为 true。 所以说在页面切换之后,上一个页面始终都是存在于 element 树中的,只不过在 RenderObject 中没有将其绘制出来,这一点在 Flutter Outline 工具里面也能够体现。从这个角度也可以理解为,在 flutter 中页面越多,需要处理的步骤就越多,虽然不需要绘制底部的页面,但是整个树的基本遍历还是会有的,这部分也算是开销。_routeNamedflutter 中进行页面管理主要的依赖路由管理系统,它的入口就是 Navigator,它所管理的东西,本质上就是承载着用户页面的 Route,但是在 Navigator 中有很多函数是 XXXName 系列的,它们传的不是 Route,而是 RouteName,据个人理解,这个主要是方便开发引入的,我们可以在 MaterialApp/CupertinoApp 中直接传入路由表,每一个名字对应一个 WidgetBuilder,然后结合 pageRouteBuilder(这个可以自定义,不过 MaterialApp/CupertinoApp 都有默认实现,能够将 WidgetBuilder 转成 Route),便可以实现从 RouteName 到 Route 的转换。Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {  if (allowNull && widget.onGenerateRoute == null)    return null;  final RouteSettings settings = RouteSettings(    name: name,    arguments: arguments,  );  Route<T> route = widget.onGenerateRoute(settings) as Route<T>;  if (route == null && !allowNull) {    route = widget.onUnknownRoute(settings) as Route<T>;  }  return route;}这个过程分三步,生成 RouteSettings,调用 onGenerateRoute 从路由表中拿到对应的路由,如果无命中,就调用 onUnknownRoute 给一个类似于 404 页面的东西。onGenerateRoute 和 onUnknownRoute 在构建 Navigator 时传入,在 WidgetsApp 中实现,Route<dynamic> _onGenerateRoute(RouteSettings settings) {  final String name = settings.name;  final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null      ? (BuildContext context) => widget.home      : widget.routes[name];  if (pageContentBuilder != null) {    final Route<dynamic> route = widget.pageRouteBuilder<dynamic>(      settings,      pageContentBuilder,    );    return route;  }  if (widget.onGenerateRoute != null)    return widget.onGenerateRoute(settings);  return null;}如果是默认的路由会直接使用给定的 home 页面(如果有),否则就直接到路由表查,所以本质上这里的 home 页面更多的是一种象征,身份的象征,没有也无所谓。另外路由表主要的产出是 WidgetBuilder,它需要经过一次包装,成为 Route 才是成品,或者如果不想使用路由表这种,也可以直接实现 onGenerateRoute 函数,根据 RouteSetting 直接生成 Route,这个就不仅仅是返回 WidgetBuilder 这么简单了,需要自己包装。onUnknownRoute 主要用于兜底,提供一个类似于 404 的页面,它也是需要直接返回 Route。_flushHistoryUpdates不知道从哪一个版本开始,flutter 的路由管理引入了状态,与之前每一个 push、pop 都单独实现不同,所有的路由切换操作都是用状态表示,同时所有的 route 都被封装成 _RouteEntry,它内部有着关于 Route 操作的实现,但都被划分为比较小的单元,且都依靠状态来执行。状态是一个具有递进关系的枚举,每一个 _RouteEntry 都有一个变量存放当前的状态,在 _flushHistoryUpdates 中会遍历所有的 _RouteEntry 然后根据它们当前的状态进行处理,同时处理完成之后会切换它们的状态,再进行其他处理,这样的好处很明显,所有的路由都放在一起处理之后,整个流程会变得更加清晰,且能够很大程度上进行代码复用,比如 push 和 pushReplacement 两种操作,这在之前是需要在两个方法中单独实现的,而现在他们则可以放在一起单独处理,不同的只有后者比前者会多一个 remove 的操作。关于 _flushHistoryUpdates 的处理步骤:void _flushHistoryUpdates({bool rearrangeOverlay = true}) {  assert(_debugLocked && !_debugUpdatingPage);  // Clean up the list, sending updates to the routes that changed. Notably,  // we don't send the didChangePrevious/didChangeNext updates to those that  // did not change at this point, because we're not yet sure exactly what the  // routes will be at the end of the day (some might get disposed).  int index = _history.length - 1;  _RouteEntry next;  _RouteEntry entry = _history[index];  _RouteEntry previous = index > 0 ? _history[index - 1] : null;  bool canRemoveOrAdd = false; // Whether there is a fully opaque route on top to silently remove or add route underneath.  Route<dynamic> poppedRoute; // The route that should trigger didPopNext on the top active route.  bool seenTopActiveRoute = false; // Whether we've seen the route that would get didPopNext.  final List<_RouteEntry> toBeDisposed = <_RouteEntry>[];  while (index >= 0) {    switch (entry.currentState) {        // ...    }    index -= 1;    next = entry;    entry = previous;    previous = index > 0 ? _history[index - 1] : null;  }  // Now that the list is clean, send the didChangeNext/didChangePrevious  // notifications.  _flushRouteAnnouncement();  // Announces route name changes.  final _RouteEntry lastEntry = _history.lastWhere(_RouteEntry.isPresentPredicate, orElse: () => null);  final String routeName = lastEntry?.route?.settings?.name;  if (routeName != _lastAnnouncedRouteName) {    RouteNotificationMessages.maybeNotifyRouteChange(routeName, _lastAnnouncedRouteName);    _lastAnnouncedRouteName = routeName;  }  // Lastly, removes the overlay entries of all marked entries and disposes  // them.  for (final _RouteEntry entry in toBeDisposed) {    for (final OverlayEntry overlayEntry in entry.route.overlayEntries)      overlayEntry.remove();    entry.dispose();  }  if (rearrangeOverlay)    overlay?.rearrange(_allRouteOverlayEntries);}以上是除了状态处理之外,一次 _flushHistoryUpdates 的全过程,首先它会遍历整个路由列表,根据状态做不同的处理,不过一般能够处理到的也不过最上层一两个,其余的多半是直接跳过的。处理完了之后,调用 _flushRouteAnnouncement 进行路由之间的前后链接,比如进行动画的联动等,void _flushRouteAnnouncement() {  int index = _history.length - 1;  while (index >= 0) {    final _RouteEntry entry = _history[index];    if (!entry.suitableForAnnouncement) {      index -= 1;      continue;    }    final _RouteEntry next = _getRouteAfter(index + 1, _RouteEntry.suitableForTransitionAnimationPredicate);    if (next?.route != entry.lastAnnouncedNextRoute) {      if (entry.shouldAnnounceChangeToNext(next?.route)) {        entry.route.didChangeNext(next?.route);      }      entry.lastAnnouncedNextRoute = next?.route;    }    final _RouteEntry previous = _getRouteBefore(index - 1, _RouteEntry.suitableForTransitionAnimationPredicate);    if (previous?.route != entry.lastAnnouncedPreviousRoute) {      entry.route.didChangePrevious(previous?.route);      entry.lastAnnouncedPreviousRoute = previous?.route;    }    index -= 1;  }}
  • [问题求助] 【文字识别OCR】【使用Android SDK】参照OCR Android SDK开发工具包无法调用识别服务
    【功能模块】【文字识别OCR】【使用Android SDK】参照OCR Android SDK开发工具包无法调用识别服务【操作步骤&问题现象】1、参照OCR Android SDK开发工具包中的HWOcrClientAKSK类,调用requestOcrAkskService方法的85行(okhttp3.Request signedRequest = Client.signOkhttp(request);)报错,报错信息无法捕捉;2、AK、SK检查过都争取,region为:cn-south-1,uri为:/v2/0bb8d5f7ee0025752f9cc016a427a44a/ocr/web-image【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] Android 大文件并发分段上传合并出错
    Android 大文件并发分段上传,上传没有问题,最后合并请求出错。用的是示例里面的代码报错信息:2021-08-16 12:30:29.016 24894-25386 W/System.err: com.obs.services.exception.ObsException: OBS servcie Error Message. com.jamesmurty.utils.XMLBuilderRuntimeException: javax.xml.parsers.ParserConfigurationException: Failed to set 'external-general-entities' feature to false2021-08-16 12:30:29.018 24894-25386 W/System.err:     at com.obs.services.internal.utils.ServiceUtils.changeFromServiceException(ServiceUtils.java:622)2021-08-16 12:30:29.018 24894-25386 W/System.err:     at com.obs.services.ObsClient.doActionWithResult(ObsClient.java:2832)2021-08-16 12:30:29.018 24894-25386 W/System.err:     at com.obs.services.ObsClient.completeMultipartUpload(ObsClient.java:2423)
  • [行业资讯] 鸿蒙OS和Android到底有什么区别?
    一直都有人问 HarmonyOS 跟 Android 有什么区别?华为手机能够适配 Android 应用,那么我们做鸿蒙应用开发能够直接调用 JAR/AAR 包的 API 吗?接下我们就一起来一一验证。JAR 的 API 调用验证使用 HarmonyOS 开发工具 DevEco Studio 创建一个项目,我们到 entry→build.gradle→查看 dependencies 的引用包含了 JAR / HAR。那么是否如截图显示的直接导入 JAR 包就可以调用其 API 呢?我马上就把我厂 Express SDK 的 JAR 导进来。以下为详细步骤:①ZegoExpressEngine.jar 包放置到 project 中的 Entry 目录下 libs 文件夹中,在 gradle 中增加 sync project。 ②在程序调用该库(ZegoExpressEngine.jar)的 API。如图显示说明是可以直接调用 JAR 的 API 的: ③那么问题就来了,可以在鸿蒙 resources→layout→xml 里调用 Android 的控件吗?然后我又把 Android 的 v4 包给导进来了。如下图显示 v4 包是成功引入:resources→layou→长按右键→new→Layout Resource File:如上图所示,鸿蒙的 Layout 是不允许创建 Android 控件的。于是我就手动输入,看会有什么效果:在 Previewer 显示 Initial image data is Iost,说明是不支持这个包的功能。并且说明鸿蒙是可以直接进行 JAR 的 API 调用,能调用但不一定支持该功能。如,不支持 Android 的控件,它有自己的组件来进行 UI 的渲染。AAR 的 API 调用验证JAR 能够直接调用 API,那 AAR 呢?重复 JAR 的导入流程导入 AAR。entry→build.gradle→dependencies 的引用添加 AAR:以为很完美,这样就可以直接调用了:哇,我的 API 呢?证明鸿蒙是不能直接调用 AAR 的方法的。这样我们就放弃了吗?有没有其他的辅助的手段让我们能够调用 AAR 的方法。我们知 AAR 的本质是 class.jar + res,基于 JAR 包是可以直接在鸿蒙系统上调用的,所以我们提取 AAR 中的 class.jar 是不是就可以了。有了这个想法我们就要开始验证:①把 AAR 后缀修改为 zip 后解压 ,找到 class.jar 并修改昵称为 cardview.jar。如上图验证这样是可以直接调用,但是不能每次都手动解压再导入,于是就找了第三方的(aar2jar),为了解决鸿蒙依赖 AAR 库的问题,实现的原理也是解包 AAR,再依赖其中的 class.jar。②aar2jar,打开链接有依赖流程就不再累赘,新测有效。 https://gitee.com/andych008/aar2jar#https://www.jianshu.com/p/afa35b6a1098如上图,成功调用了 logger 的 AAR 库: https://github.com/orhanobut/loggerHAR 的编译过程创建鸿蒙的项目,默认包含的就是 JAR/HAR。HarmonyOS 库(HarmonyOS Ability Resources,简称 HAR)可以提供应用构建所需的一切内容,包括源代码、资源文件、HarmonyOS 配置文件以及第三方库。官网(HAR 的编译流程官网链接)已经详细的讲解了编译过程,就不再累赘。把我厂的 ZegoExpressEngine.jar 放到 HarmonyOS Library 编译:生成的 har 再放到项目的 libs 进行验证,是可以正常调用的,说明 har 编译成功。结语以下为本次验证总结:鸿蒙应用开发,不能直接使用 Android 上的第三方库。可以直接引用 JAR 的 API。但不能是运行时强依赖 Android 环境的,不然只能做移植,并做一定的修改。所以不依赖 Android 的 JAR 是可以直接在鸿蒙中使用的。鸿蒙的 gradle plugin 环境下不能直接依赖 AAR,最好的办法是重新整理 AAR 工程,编译成相应的 HAR。AAR 运行时不依赖 Android 环境的,可以提取其中的 class.jar,并做依赖。转载:鸿蒙技术社区
  • [开发资源] Android 媒体SDK
    Android SDK于2010714通过评审发布(非商用发布),用于ISV集成。使用方法: 将“android-001.zip”名称更改为“android.zip.001”,“android-002.zip”名称更改为“android.zip.002”,“android.zip.003”名称更改为“android.zip.003”后解压。
  • [技术干货] 【技术长文】JAVA 学到什么水平就可以转战 Android 了?
    原帖地址:(6条消息) CSDN 8000多人关注!27w+阅读开发者进阶难题:JAVA 学到什么水平就可以转战 Android 了?_wzh19950826的博客-CSDN博客说说我的情况吧第一个学过的语言是C,然后C学的很烂的情况下继续学习了C++,为什么没有先把C学好再学C++呢,因为课程设计==,我有自知之明,所以在上C++的前一个寒假就先开始看了,然后先看了一本《易学c++》, 虽然那本书在现在浅显至极,但当时还是感觉有点略难,我通过这本书熟练地掌握了std::iostream!==然后基本的对象模型有了一点概念,我比较好高骛远,于是就在此时想直接通过最后一个大作业——命令行下的RPG游戏 彻底掌握C++这门最难的语言!对,你没听错,我当时想的就是这个大作业要是搞懂了,C++还不是被我各种SM,但很不幸,虽然我一行行的把这个大作业的源码比着答案敲了一遍,但还是不懂,于是就在网上找了一个据说是清华的java课件开始学习,你还别说,除了当时感觉java里面把function叫做method(方法)感觉怪怪的以外,真的在不到3天的时间完全弄懂了对象的概念以及对象的继承,然后差不多就开学了==(当年不懂事,整个寒假就看了这10多天的书),所以java大法好。好,这是我人生入坑的开始,我说的寒假就是2018年寒假!,在接下来的学校的C++课程中我当然如鱼得水,然后一发不可收拾,很快我就自己把那本98清华版的《visual c++程序设计》弄烂了,确实那本书的只是我掌握的滚瓜烂熟,然后我就深入了解MFC,什么用GDI实现AlphaBlend,MFC的设计思想,MFC中数据库的使用什么的,然而现在回首看来这些并没有什么卵用。然后我就天天晚上回到宿舍打开电脑写代码,但显然我很快从社区 上得知MFC很烂,大家都说微软大法好!于是入了C#的坑,然后发现生活好美好===天天真是喜欢C#的不得了,不过有一天我成了软黑,winform绘图闪成狗,loadlibrary看着好蛋疼,以及用user32.dll时,蛋疼的语法,蛋疼的函数名字,API参数,然后我得知,原来罪魁祸首不是c#,是winAPI就长这样!然后就在知乎大声地教唆下,入了Qt教,入门是看的一个外国人录得视频,英文无字幕,但是他的声音好好听,于是就学了Qt,第一次发现各种Layout的自适应布局,通过掌握QGraphicsView熟悉了MVC模型,后来还用QGraphicsView到了真实的项目,发现好多,坐标系统是坑,事件系统是坑,总之,欲仙欲死,不过说实话,我至今还是认为Qt的设计挺好的,QML想学,但流产了,原因大概是没时间吧。然后解除了另外两端惨绝人寰的事情,第一个是第一次做音视频的实时网络传输的时候,资料很少,一开始甚至连Ffmpeg都不知道,后来知道了,就入了Ffmpeg的坑,好多莫名奇妙的错误,都需要右键去看源码,然后音视频的编解码以及Rtp/Rtsp协议弄的差不多,demo也出来的时候,为了广域网,又要入P2P Nat穿透的坑,但是很遗憾,没有完全成功。还有一些其他乱七八槽的事情回来补==今天先说重点。在今年4月份左右,我第一次下定决心要搞定Android,而且我真的做到了! 虽然以前断断续续看过android,但都没屁用,这次是真的白天看书,晚上写代码。郭霖老师的《第一行代码——android》只看了5章,但在这五章的学习过程中,我真的入了Android的门了,然后那本书我再也没有打开过,当然,Android只会画界面是远远不够的,现在的support.design.widget组件集让Android界面开发变得很轻松了,重要的是和服务器的交互,以及嵌入式本地数字信号处理,一不小心写了这么多,第一次发现自己打字这么快,哈哈~前言全网唯一一份,对标阿里P8 年薪60w+的Android高级工程师学习进阶路线(图未完全展开,怕大家看不清楚):本篇文章都会围绕这份脑图来写,详细的介绍你处于哪个阶段该如何进阶,以及年薪层次高低对应该学的的技术。需要相关知识点可以查看我的【GitHub】,对于已经掌握的可以忽略以节省时间。如果不方便查看,我已经整理成了一份PDF包含Android入门,基础—高级的全部系列知识点,还有新技术学习笔记。需要全套系列笔记可以直接,点击链接【https://jq.qq.com/?_wv=1027&k=OQA7ghiD】找群主大大免费获取!首先把学习Android技术的程序员先简单的分两种情况:一、有编程基础,或者科班出身的,直接上吧。直接推荐郭霖老师的《第一行代码-Android》然后按照5个阶段去学习:(以上都有对应的学习视频)第一阶段:所学知识点第二阶段:所学知识点第三阶段:所学知识点第四阶段:所学知识点第五阶段:所学知识点二、没编程基础先入门java如果天天都有比较多的空闲时间的话,这个入门时间要在2周完成,如果没有的话,1个月内完成吧,不要让拖延耽搁你的激情。Java基础知识Android基础Kotlin 部分计算机网络部分算法与数据结构部分**Flutter部分尾声一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。初学者,一句话,多练。需要相关知识点可以查看我的【GitHub】,对于已经掌握的可以忽略以节省时间。如果不方便查看,我已经整理成了一份PDF包含Android入门,基础—高级的全部系列知识点,还有新技术学习笔记。建议选一个自己相对比较擅长的领域。基础要 背!平时我并不太去留意要记住各种API,但是这里就是要背过。不然面试官就会想“这么简单的API都含糊不清还说擅长?”就是真的面试的时候记不清了,也不要打磕,要非常自信的说个差不多的,不要说“好像……”这样的话。要的就是自信,因为这个API面试官也不一定记得那么清楚。不过自己能背过才真的有底气。试着去了解这个领域市面上的技术。一般的话就是一些库或者框架。这里要记住,不要急着去看源码,要先掌握这些技术都有哪些优缺点,尤其是缺点!因为我们经常因为一个库有什么优点而去使用它,但是缺点往往是我们容易忽略的地方。而知不知道这个库的缺点,是你能不能驾驭这个库的一个关键。如果有时间的话,研究其中一个众所周知的库的源码。并试图找到它缺点的原因,并找到其解决方法。当然你如果正要准备面试了,肯定没有这个时间。那么就找一些现成的相关的文章来看看吧。然后记住。技术到家了,谁也淘汰不了你,选择权由谁决定就看谁更需要谁了!
  • [版本公告] 华为云会议V7.6.5版本新特性:智能客服全新上线,解决问题更高效!
    华为云会议V7.6.5版本新特性本月优化内容:会前1.新增智能客服,方便解决使用问题会中2.会中隐藏未开启视频画面,画面展示更美观(Windows)3.入会新增提示邀请与会者4.会中视频画面轻拨放大(移动端)会控平台5.支持被广播与被点名的用户观看指定画面订购6.网络研讨会支持按次订购一、新增智能客服,方便解决使用问题适用场景:当在使用时有一些使用问题需要快速找到解答PC端移动端二、会中隐藏未开启视频画面,画面显示更美观(Windows)适用场景:当在会议中所需展示画面想要保持美观和一致性,可以选择隐藏未开启视频的与会者,画面展示更统一美观三、入会新增提示邀请与会者适用场景:会议发起者在入会之后需要邀请其他与会者加入会议时,可以一键快速分享进行邀请四、会中视频画面轻拨放大(移动端)适用场景:当会中共享时,移动端显示屏幕过小不能清晰查看内容时五、支持被广播和被点名的用户观看指定画面适用场景:在一场正式会议中,当被点名和被广播时,所观看到的画面我们可以灵活选择,方便与主持人进行更好的互动六、网络研讨会支持按次订购适用场景:当公司开月度/年度大会等场景时,可以按次订购,操作灵活且性价比高
  • [技术干货] MindSpore部署图像分割示例程序
    MindSpore部署图像分割示例程序本端侧图像分割Android示例程序使用Java实现,Java层主要通过Android Camera 2 API实现摄像头获取图像帧,进行相应的图像处理,之后调用Java API 完成模型推理。此处详细说明示例程序的Java层图像处理及模型推理实现,Java层运用Android Camera 2 API实现开启设备摄像头以及图像帧处理等功能,需读者具备一定的Android开发基础知识。示例程序结构app├── src/main│ ├── assets # 资源文件| | └── deeplabv3.ms # 存放模型文件│ |│ ├── java # java层应用代码│ │ └── com.mindspore.imagesegmentation│ │ ├── help # 图像处理及MindSpore Java调用相关实现│ │ │ └── ImageUtils # 图像预处理│ │ │ └── ModelTrackingResult # 推理数据后处理│ │ │ └── TrackingMobile # 模型加载、构建计算图和推理│ │ └── BitmapUtils # 图像处理│ │ └── MainActivity # 交互主页面│ │ └── OnBackgroundImageListener # 获取相册图像│ │ └── StyleRecycleViewAdapter # 获取相册图像│ ││ ├── res # 存放Android相关的资源文件│ └── AndroidManifest.xml # Android配置文件│├── CMakeList.txt # cmake编译入口文件│├── build.gradle # 其他Android配置文件├── download.gradle # 工程依赖文件下载└── …配置MindSpore Lite依赖项Android 调用MindSpore Java API时,需要相关库文件支持。可通过MindSpore Lite源码编译生成mindspore-lite-{version}-minddata-{os}-{device}.tar.gz库文件包并解压缩(包含libmindspore-lite.so库文件和相关头文件),在本例中需使用生成带图像预处理模块的编译命令。version:输出件版本号,与所编译的分支代码对应的版本一致。device:当前分为cpu(内置CPU算子)和gpu(内置CPU和GPU算子)。os:输出件应部署的操作系统。本示例中,build过程由download.gradle文件自动下载MindSpore Lite 版本文件,并放置在app/src/main/cpp/目录下。若自动下载失败,请手动下载相关库文件,解压并放在对应位置:mindspore-lite-1.0.1-runtime-arm64-cpu.tar.gz 下载链接在app的build.gradle文件中配置CMake编译支持,以及arm64-v8a的编译支持,如下所示:android{defaultConfig{externalNativeBuild{cmake{arguments “-DANDROID_STL=c++_shared”}}    ndk{        abiFilters 'arm64-v8a'    }}1234}在app/CMakeLists.txt文件中建立.so库文件链接,如下所示。============== Set MindSpore Dependencies. =============include_directories(C M A K E S O U R C E D I R / s r c / m a i n / c p p ) i n c l u d e d i r e c t o r i e s ( {CMAKE_SOURCE_DIR}/src/main/cpp) include_directories(CMAKE S​     OURCE D​     IR/src/main/cpp)include d​     irectories({CMAKE_SOURCE_DIR}/src/main/cpp/M I N D S P O R E L I T E V E R S I O N / t h i r d p a r t y / f l a t b u f f e r s / i n c l u d e ) i n c l u d e d i r e c t o r i e s ( {MINDSPORELITE_VERSION}/third_party/flatbuffers/include) include_directories(MINDSPORELITE V​     ERSION/third p​     arty/flatbuffers/include)include d​     irectories({CMAKE_SOURCE_DIR}/src/main/cpp/M I N D S P O R E L I T E V E R S I O N ) i n c l u d e d i r e c t o r i e s ( {MINDSPORELITE_VERSION}) include_directories(MINDSPORELITE V​     ERSION)include d​     irectories({CMAKE_SOURCE_DIR}/src/main/cpp/M I N D S P O R E L I T E V E R S I O N / i n c l u d e ) i n c l u d e d i r e c t o r i e s ( {MINDSPORELITE_VERSION}/include) include_directories(MINDSPORELITE V​     ERSION/include)include d​     irectories({CMAKE_SOURCE_DIR}/src/main/cpp/M I N D S P O R E L I T E V E R S I O N / i n c l u d e / i r / d t y p e ) i n c l u d e d i r e c t o r i e s ( {MINDSPORELITE_VERSION}/include/ir/dtype) include_directories(MINDSPORELITE V​     ERSION/include/ir/dtype)include d​     irectories({CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/include/schema)add_library(mindspore-lite SHARED IMPORTED )add_library(minddata-lite SHARED IMPORTED )set_target_properties(mindspore-lite PROPERTIES IMPORTED_LOCATIONC M A K E S O U R C E D I R / s r c / m a i n / c p p / {CMAKE_SOURCE_DIR}/src/main/cpp/CMAKE S​     OURCE D​     IR/src/main/cpp/{MINDSPORELITE_VERSION}/lib/libmindspore-lite.so)set_target_properties(minddata-lite PROPERTIES IMPORTED_LOCATIONC M A K E S O U R C E D I R / s r c / m a i n / c p p / {CMAKE_SOURCE_DIR}/src/main/cpp/CMAKE S​     OURCE D​     IR/src/main/cpp/{MINDSPORELITE_VERSION}/lib/libminddata-lite.so)--------------- MindSpore Lite set End. --------------------Link target library.target_link_libraries(…# — mindspore —minddata-litemindspore-lite…)下载及部署模型文件从MindSpore Model Hub中下载模型文件,本示例程序中使用的终端图像分割模型文件为deeplabv3.ms,同样通过download.gradle脚本在APP构建时自动下载,并放置在app/src/main/assets工程目录下。若下载失败请手动下载模型文件,deeplabv3.ms 下载链接。编写端侧推理代码调用MindSpore Lite Java API实现端测推理。推理代码流程如下,完整代码请参见src/java/TrackingMobile.java。加载MindSpore Lite模型文件,构建上下文、会话以及用于推理的计算图。o 加载模型文件:创建并配置用于模型推理的上下文// Create context and load the .ms model named ‘IMAGESEGMENTATIONMODEL’model = new Model();if (!model.loadModel(Context, IMAGESEGMENTATIONMODEL)) {Log.e(TAG, “Load Model failed”);return;}o 创建会话// Create and init config.msConfig = new MSConfig();if (!msConfig.init(DeviceType.DT_CPU, 2, CpuBindMode.MID_CPU)) {Log.e(TAG, “Init context failed”);return;}// Create the MindSpore lite session.session = new LiteSession();if (!session.init(msConfig)) {Log.e(TAG, “Create session failed”);msConfig.free();return;}msConfig.free();o 构建计算图if (!session.compileGraph(model)) {Log.e(TAG, “Compile graph failed”);model.freeBuffer();return;}// Note: when use model.freeBuffer(), the model can not be compile graph again.model.freeBuffer();7. 将输入图片转换为传入MindSpore模型的Tensor格式。8. List inputs = session.getInputs();9. if (inputs.size() != 1) {10. Log.e(TAG, “inputs.size() != 1”);11. return null;12. }13.14. // bitmap is the picture used to infer.15. float resource_height = bitmap.getHeight();16. float resource_weight = bitmap.getWidth();17. ByteBuffer contentArray = bitmapToByteBuffer(bitmap, imageSize, imageSize, IMAGE_MEAN, IMAGE_STD);18.19. MSTensor inTensor = inputs.get(0);inTensor.setData(contentArray);20. 对输入Tensor按照模型进行推理,获取输出Tensor,并进行后处理。o 图执行,端侧推理。21. // After the model and image tensor data is loaded, run inference.22. if (!session.runGraph()) {23. Log.e(TAG, “Run graph failed”);24. return null;}o 获取输出数据。// Get output tensor values, the model only outputs one tensor.List tensorNames = session.getOutputTensorNames();MSTensor output = session.getOutputByTensorName(tensorNames.front());if (output == null) {Log.e(TAG, "Can not find output " + tensorName);return null;}o 输出数据的后续处理。// Show output as pictures.float[] results = output.getFloatData();ByteBuffer bytebuffer_results = floatArrayToByteArray(results);Bitmap dstBitmap = convertBytebufferMaskToBitmap(bytebuffer_results, imageSize, imageSize, bitmap, dstBitmap, segmentColors);dstBitmap = scaleBitmapAndKeepRatio(dstBitmap, (int) resource_height, (int) resource_weight);25. 图片处理及输出数据后处理请参考如下代码。26. Bitmap scaleBitmapAndKeepRatio(Bitmap targetBmp, int reqHeightInPixels, int reqWidthInPixels) {27. if (targetBmp.getHeight() == reqHeightInPixels && targetBmp.getWidth() == reqWidthInPixels) {28. return targetBmp;29. }30.31. Matrix matrix = new Matrix();32. matrix.setRectToRect(new RectF(0f, 0f, targetBmp.getWidth(), targetBmp.getHeight()),33. new RectF(0f, 0f, reqWidthInPixels, reqHeightInPixels), Matrix.ScaleToFit.FILL;34.35. return Bitmap.createBitmap(targetBmp, 0, 0, targetBmp.getWidth(), targetBmp.getHeight(), matrix, true);36. }37.38. ByteBuffer bitmapToByteBuffer(Bitmap bitmapIn, int width, int height, float mean, float std) {39. Bitmap bitmap = scaleBitmapAndKeepRatio(bitmapIn, width, height);40. ByteBuffer inputImage = ByteBuffer.allocateDirect(1 * width * height * 3 * 4);41. inputImage.order(ByteOrder.nativeOrder());42. inputImage.rewind();43. int[] intValues = new int[width * height];44. bitmap.getPixels(intValues, 0, width, 0, 0, width, height);45. int pixel = 0;46. for (int y = 0; y < height; y++) {47. for (int x = 0; x < width; x++) {48. int value = intValues[pixel++];49. inputImage.putFloat(((float) (value >> 16 & 255) - mean) / std);50. inputImage.putFloat(((float) (value >> 8 & 255) - mean) / std);51. inputImage.putFloat(((float) (value & 255) - mean) / std);52. }53. }54. inputImage.rewind();55. return inputImage;56. }57.58. ByteBuffer floatArrayToByteArray(float[] floats) {59. ByteBuffer buffer = ByteBuffer.allocate(4 * floats.length);60. FloatBuffer floatBuffer = buffer.asFloatBuffer();61. floatBuffer.put(floats);62. return buffer;63. }64.65. Bitmap convertBytebufferMaskToBitmap(ByteBuffer inputBuffer, int imageWidth, int imageHeight, Bitmap backgroundImage, int[] colors) {66. Bitmap.Config conf = Bitmap.Config.ARGB_8888;67. Bitmap dstBitmap = Bitmap.createBitmap(imageWidth, imageHeight, conf);68. Bitmap scaledBackgroundImage = scaleBitmapAndKeepRatio(backgroundImage, imageWidth, imageHeight);69. int[][] mSegmentBits = new int[imageWidth][imageHeight];70. inputBuffer.rewind();71. for (int y = 0; y < imageHeight; y++) {72. for (int x = 0; x < imageWidth; x++) {73. float maxVal = 0f;74. mSegmentBits[x][y] = 0;75. // NUM_CLASSES is the number of labels, the value here is 21.76. for (int i = 0; i < NUM_CLASSES; i++) {77. float value = inputBuffer.getFloat((y * imageWidth * NUM_CLASSES + x * NUM_CLASSES + i) * 4);78. if (i == 0 || value > maxVal) {79. maxVal = value;80. // Check whether a pixel belongs to a person whose label is 15.81. if (i == 15) {82. mSegmentBits[x][y] = i;83. } else {84. mSegmentBits[x][y] = 0;85. }86. }87. }88. itemsFound.add(mSegmentBits[x][y]);89.90. int newPixelColor = ColorUtils.compositeColors(91. colors[mSegmentBits[x][y] == 0 ? 0 : 1],92. scaledBackgroundImage.getPixel(x, y)93. );94. dstBitmap.setPixel(x, y, mSegmentBits[x][y] == 0 ? colors[0] : scaledBackgroundImage.getPixel(x, y));95. }96. }97. return dstBitmap;}————————————————原文链接:https://blog.csdn.net/wujianing_110117/article/details/113066483
  • [问题求助] 【鲲鹏920 robox】【android 容器 opengles 2.0】编译的镜像是否支持opengl es 3.0
    环境:运行的服务端内核和编译android镜像都没有打exagear补丁在android容器中执行opengl es3.0编写的应用程序会失败,但是opengl es2.0的就可以正常运行。检测到android系统目前是opengl es 2.0的版本。请问robox是否可以支持opengl es 3.0?还有exagear补丁有适配完成吗?
  • [ARM原生] 求32位内核转码补丁【ExaGear ARM32-ARM64 for Android VM】
    我根据《鲲鹏 BoostKit ARM 原生使能套件》编译安卓模拟器,进行到文档 3.5.2 Android内核编译时提示合入32位转码内核部分的补丁。 a. 联系华为一线工程师获取补丁goldfish-4.4-dev.patch,获取路径如下:https:// support.huawei.com/enterprise/zh/kunpeng-computing/kunpengcomputing-media-pid-251431619/software/251882957。此下载已失效, 请问哪里可以下载?
总条数:181 到第
上滑加载中