华为官方适配平行视界的文档:文档中心 最近在做华为平板的平行视界下的适配工作,从一开始的小白到网上查资料踩坑,到现在逐步实现了想要的功能。提一个比较坑的一点,就是网上不少人写的判断是否是平行世界模式,用的是


String config = this.getResources().getConfiguration().toString();
return config.contains("hwMultiwindow-magic") || config.contains("miui-magic-windows");或是config.contains("hwMultiwindow-magic")。然而,这是错的!!!一直是false


真正对的写法是config.contains("hw-magic-windows");这个也是官网上说明了。文档中心


说下我遇到的功能需求。由于开发的App安装到华为平板上后,会默认开启平行视界模式,也就是在平板的设置-平行视界中能看到App的开关是开着的。所以,不得已进行适配平行视界。 而App的启动流程原先是启动页--->主界面,但是启动页中需要做各种操作,为整个App功能和主界面的功能做一些初始化。这里其实有个先后关联关系,这就要求,启动页的处理不能随随便便。 关于分屏适配的心路历程:因为适配前,App的启动逻辑就是启动页SplashActivity-->主界面MainActivity。且启动页是要关闭的。然后就想着,适配的话,就需要一个辅助页MainPageActivity。App一启动的时候是启动的辅助页,辅助页设置为全屏,然后1秒后跳转到启动页,跳转后两个界面就分屏了(我预想应该可以这样的)。因为官方文档上说的可以设置某个界面默认的启动方式为全屏,我就有了这个想法。但是实际测试发现,如果启动页是全屏,即使设置了

"activityPairs": [ { "from": "com.cnki.android.cnkimoble.activity.MainPageActivity", "to": "com.cnki.android.cnkimoble.activity.SplashActivity" } ],
和
"Activities": [
          {
            "name": "com.cnki.android.cnkimoble.activity.MainPageActivity",
            "defaultFullScreen": "true"
          },
          {
            "name": "com.cnki.android.cnkimoble.activity.SplashActivity",
            "defaultFullScreen": "false"
          }
        ]

跳转到启动页后,并不会分屏,启动页依然是全屏。但是如果翻转为竖屏再翻转回来,就变成了分屏了。但是,这不是我想要的效果。 后来发现实在不行,就只好改为启动时双屏显示的方案。也就是 "defaultDualActivities": { "mainPages": "com.cnki.android.cnkimoble.activity.MainPageActivity", "relatedPage": "com.cnki.android.cnkimoble.activity.SplashActivity" }, 。启动时的显示问题解决了,也就是启动双屏,然后SplashActivity跳转到MainActivity,且SplashActivity关闭(注意,这里埋了一个坑,后面说),MainActivity打开二级界面,这一系列的显示都是ok的,如自己想要的一样。 然而,从主界面的二级界面返回后,本来想着应该是辅助页和主界面分屏显示啊,结果,只剩一个主界面了,当然,它是半屏的,中间的那种。那,辅助页怎么显示不出来了呢。我郁闷了。由于这个App本身功能复杂,配置也是各种配置,有可能不知道哪个地方影响到了呢,或者本来就是这个样?于是,我新建了一个干净的Demo,仿照这个启动逻辑重新搞了一遍。结果发现,当主界面的二级界面关闭后,Demo里的辅助页和主界面是正常显示的,并不会像自己开发的App显示的只剩主界面半屏中间的那样。 于是,我猜测应该就是App配置影响到了。仔细检查了清单文件,发现了猫腻。主界面的配置是这样的:

<activity
    android:name="com.cnki.android.cnkimoble.activity.MainActivity"
    android:clearTaskOnLaunch="true"
    android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize
       |screenLayout"
    android:label="@string/app_name"
    android:launchMode="singleTask"
    android:screenOrientation="unspecified"
    android:windowSoftInputMode="adjustPan">
 
</activity>

不寻常的配置有两项,android:clearTaskOnLaunch="true"和android:launchMode="singleTask"

而,android:clearTaskOnLaunch="true"这个就是启动的时候会把任务栈清空,也就是其他界面都移出去了,那么辅助页当然也被移出去了,所以,当二级界面回退到主界面时,只剩下主界面了,辅助页没有了。但是又有个问题,就是明明启动主界面时会把任务栈清空,那为啥那个时候辅助页还是存在的?界面显示正常啊。这应该是任务栈清空不会立即影响到界面,当需要重新显示辅助页时才会影响到,这里只是我的猜测。但这里不做深入讨论,毕竟现在我要把这个属性改一下。


android:clearTaskOnLaunch="false" 再重新运行,发现这个问题解决了。但后来发现,不是这个原因,貌似平板关机后重启,就没问题了。其实平行世界的这个系统功能还是不够稳定,不只我说,我看很多人也是吐槽,说功能还不稳定就放出来让用户用,而且还让开发者适配,这就坑到开发者了。 分屏显示时主界面退出后启动页重新创建的问题,用下面的代码解决: Intent home = new Intent(Intent.ACTION_MAIN); home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); home.addCategory(Intent.CATEGORY_HOME); startActivity(home); 适配过程中主要有几个点。 一、声明兼容平行视界模式 <meta-data android:name="EasyGoClient" android:value="true" /> 下面这个不加貌似也没问题 <meta-data android:name="android.allow_multiple_resumed_activities" android:value="true"/>


二、easyGo.json的配置


{
  "easyGoVersion": "1.0",
  "client": "com.cnki.android.cnkimobile",
  "logicEntities": [
    {
      "head": {
        "function": "magicwindow",
        "required": "true"
      },
      "body": {
        "mode": "0",
        "defaultDualActivities": {
          "mainPages": "com.cnki.android.cnkimoble.activity.MainPageActivity",
          "relatedPage": "com.cnki.android.cnkimoble.activity.SplashActivity"
        },
        "activityPairs": [
          {
            "from": "com.cnki.android.cnkimoble.activity.MainActivity",
            "to": "*"
          }
        ],

        "UX": {
          "supportRotationUxCompat": "true",
          "isDraggable": "true",
          "supportDraggingToFullScreen": "PAD"
        }
      }
    }
  ]
}


"mode": "0",表示是购物模式,官网中也说明了。我的功能是在购物模式下才能很好的实现。

三、启动的双屏配置

配置文件中

"defaultDualActivities": { "mainPages": "com.cnki.android.cnkimoble.activity.MainPageActivity", "relatedPage": "com.cnki.android.cnkimoble.activity.SplashActivity" }

决定着,启动界面应该是MainPageActivity

所以就有了以下配置


<activity
    android:name="com.cnki.android.cnkimoble.activity.MainPageActivity"
    android:label="@string/app_name"
    >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name="com.cnki.android.cnkimoble.activity.SplashActivity"
    android:label="@string/app_name"
    android:screenOrientation="unspecified"
    android:windowSoftInputMode="adjustPan">
 
</activity>


而主界面


<activity
    android:name="com.cnki.android.cnkimoble.activity.MainActivity"
    android:clearTaskOnLaunch="true"
    android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize
       |screenLayout"
    android:label="@string/app_name"
    android:launchMode="singleTask"
    android:screenOrientation="unspecified"
    android:windowSoftInputMode="adjustPan">
 
</activity>

注意,在适配平行视界之前,就是这个写法。有两点比较特殊,android:clearTaskOnLaunch="true"和android:launchMode="singleTask"

四、主界面退出时,App中调用了这个方法,来关闭。

/**
 * 退出程序关闭所有的Activity
 */
public static void finishActivitys() {
    LogSuperUtil.i(Constant.LogTag.advertise,"MyMainPage finish all activity",1);
    if(true) {
        Intent home = new Intent(Intent.ACTION_MAIN);
        home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        home.addCategory(Intent.CATEGORY_HOME);
        getInstance().startActivity(home);
        //return;
    }
    for (int i = 0; i < mActivityList.size(); i++) {
        if (null != mActivityList.get(i).get()) {
            String activityName=mActivityList.get(i).get().getLocalClassName();
            if(activityName!=null&&activityName.equals(MainPageActivity.class.getName())) {

            }else {

            }
            LogSuperUtil.i(Constant.LogTag.advertise,i+" MyMainPage finish activity="+activityName);
            mActivityList.get(i).get().finish();
        }

    }
    mActivityList.clear();//这个必须有,因为有的地方判断MainActivity是否在运行要遍历mActivityList
}


五、对于解决我需求中的Bug的比较重要的知识点


/**
 * 判断是否处于平行视界
 * @return true/false
 */
protected boolean isInMagicWindow(){
   //String config = this.getResources().getConfiguration().toString();
   //return config.contains("hwMultiwindow-magic") || config.contains("miui-magic-windows");
   String config = this.getResources().getConfiguration().toString();
   boolean isInMagicWindow = config.contains("hw-magic-windows");//这个才是官网上说的判定标准
   return isInMagicWindow;
}
 
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);