Google工程师关于开发多窗口应用的5点建议

hmijh8125 8年前
   <p>这两天,关于Android最大的新闻莫过于Android 7.0正式版的发布,开发多窗口应用将成为广大Android开发者必须get的新技能点。在这篇文章里,google工程师Ian Lake给出了5点建议,帮助你快速掌握多窗口应用的开发。 <a href="/misc/goto?guid=4959677318464455545" rel="nofollow,noindex">原文在这里</a></p>    <p>如果你之前浏览过 <a href="https://www.油Tube.com/watch?v=CsulIu3UaUM&utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog" rel="nofollow,noindex">Android N的新特性</a> (需KX上网),你应该已经发现了Android N对多窗口的支持。</p>    <p>当把屏幕拆分成多窗口后,两个APP可以并排可见,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/e9500870acdc8fbc8547780b216b7d91.png"></p>    <p>sidebysideApp.png</p>    <p>我很兴奋,很想知道背后的原理,于是迅速查看了说明文档,寻找那些实现这一魔术般效果的新的API。</p>    <p>结果有点小失望,我没有发现许多新的API,新的系统只增加了一些XML属性和一些Activity方法。XML属性可以用来说明你的应用是否支持多窗口,Activity方法用来帮你检查Activity是否正处于多窗口模式中。那么,神奇的多窗口功能在哪里实现的呢?答案是Android一直都具备实现多窗口的功能,只不过刚被挖掘出来而已。</p>    <p>秘密就隐藏在Android的资源系统中,该系统最重要的能力就是根据应用所处设备的配置条件和应用中的资源标识符,提供不同的资源,如layouts,drawables,menus等等。举例来说,通过创建一个drawable-sw600dp文件夹,并在其中放置合适的layout资源文件,你的应用可以在大屏幕设备和小屏幕设备上呈现不一样的布局。</p>    <p>多窗口功能的实现正是利用了资源系统的这一特性,根据你的Activity窗口的尺寸调整相应的设备配置条件----最明显的条件就是屏幕尺寸,但同时,最小宽度(即sw值,屏幕宽和高中的较小值)和屏幕方向(orientation)也将随着Activity窗口尺寸的调整而更新。</p>    <p>这就引出了我们的第一条建议。</p>    <h2>建议1:使用正确的Context</h2>    <p>加载合适的资源需要合适的Context。如果你使用Activity Context来inflate你的layouts,获取资源等等,一切都是正确无误的,尽可放心。</p>    <p>然而,如果你使用 Application context来做任何与UI相关的事,比如加载一个drawable,你将会发现该context完全不知道多窗口这回事,自然也就无法帮你加载合适的资源,除了不使用你为不同配置条件设置的Activity主题外,还可能加载 <strong>完全错误</strong> 的资源!!!所以,在多窗口应用中,当你做UI相关的事情时, <strong>切记切记</strong> 使用Activity Context。</p>    <h2>建议2:正确处理配置变化</h2>    <p>当你使用正确的context时,你就始终可以根据Activity的窗口尺寸获取到正确的资源,不管该窗口是处于全屏状态还是处于与其他应用共享屏幕的分屏状态。每次重新加载资源的过程都基于你怎样处理运行时的配置变化。</p>    <p>应对配置变化的默认处理是销毁整个Activity并重建它、恢复你在onSaveInstanceState()中保存的任何状态、重新加载所有的资源和layouts。这样做的好处有两点:</p>    <ul>     <li> <p>新建的Activity将会与新的配置条件保持一致。</p> </li>     <li> <p>每一种配置的变化都在重建中得到了处理。</p> </li>    </ul>    <p>毫无疑问,每种配置的变化都应当得到快速连贯的处理,为此,请不要在onResume中做过多的工作,并考虑使用 <a href="https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog" rel="nofollow,noindex">loaders</a> 来确保你的数据不会因配置变化而丢失。</p>    <p>你也可以自己处理配置的变化,在这种情况下,你的Activity(或者Fragment)将不会销毁重建,相反,你将会收到一个onConfigurationChanged()回调,你需要在回调中手动更新你的views,重新加载资源等等。</p>    <p>为了能够catch住多窗口相关的配置变化,你至少需要在清单文件中为你的Activity的android:configChanges属性添加如下值:</p>    <p>&lt;activity android:name=&quot;.MyActivity&quot; android:configChanges=&quot;screenSize|smallestScreenSize |screenLayout|orientation&quot; /&gt;</p>    <p>当你自己处理配置变更时,请确保处理好每一个可能发生变化的资源。</p>    <p>这里所说的“每一个可能发生变化的资源”包括一些在单窗口环境中被认为不会发生变化的资源。比如,你在values和values-sw600dp文件下有一个dimension。在单窗口应用时代,你永远不会在应用运行时切换这个dimension,因为最小宽度(smallest width)是不会变化的,它永远是你的设备的高和宽中的较小值。然而,在多窗口应用时代,当你的应用发生resize时,你将不得不在不同的dimension之间切换。因为此时, <strong>最小宽度</strong> 这项配置将不再是一个常量,而是一个变量。</p>    <h2>建议3:处理所有的方向(orientations)</h2>    <p>本文开始时,我们提到,当Activity的窗口resize时,orientation这项配置将会发生变化。是这样的:即使设备处于水平(landscape)方向,你的应用也可能处于“竖直(portrait)”方向。</p>    <p>这说明:“竖直(portrait)”真正的含义是Activity的窗口的高度大于宽度,“水平(landscape)”的真正含义是窗口的宽度大于高度,牢记这一点后,你就可以在Activity的窗口resize时,正确地从一种状态过渡到另一种状态。</p>    <p>这也同时意味着,不同的orientations间的过渡应尽可能地平滑。这里引用 <a href="https://www.google.com/design/spec/layout/split-screen.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#split-screen-layout" rel="nofollow,noindex">分屏材料设计规范</a> 中的一段话:</p>    <p>改变一个设备的方向不应该引起应用的UI产生非预料的变化。举例来说,如果一个应用正在多窗口模式下播放一个视频,此时设备处于竖直状态。当把设备旋转到水平状态时,该应用不应该转为全屏播放。</p>    <p>说明:如果你说,假如我的应用一开始是在竖直全屏播放,设备变成水平时,我想让应用变为水平全屏播放怎么办?你可以用inMultiWindowMode()来检查Activity当前所处的状态。</p>    <p>以前,你可以通过android:screenOrientation属性锁定屏幕方向,但现在情况发生了变化。如果你的应用的目标(target)不是Android N,那么这个属性意味着你的应用将完全不支持多窗口。在用户使用你的应用时,将会强迫用户退出多窗口模式。如果你的应用的目标(target)是Android N,那么当用户在多窗口模式下使用你的应用时,这个属性将被忽略。</p>    <p>另外,请切记,多窗口模式下,企图通过setRequestedOrientation()在运行时锁定屏幕方向是没有任何效果的,无论你的应用的目标(target)是不是Android N。</p>    <p>在Activity的清单文件中添加 android:immersive属性,产生的效果与android:screenOrientation属性是一样一样的。</p>    <h2>建议4:为所有的屏幕尺寸创建响应式的UI</h2>    <p>在设计多窗口应用时,Orientation是你必须要注意考虑的问题,但仅考虑Orientation是不够的。在多窗口时代,你的平板UI将会被缩放到一个微小的尺寸,在以前这是不会发生的。</p>    <p>如果你正在构建一套响应式UI,以便应对不同的available space,并且为手机和平板创建的layout比较相似,你的应用将很容易迁移到多窗口环境中。根据建议,你现在可以将UI缩放到220dp的宽/高,并以该尺寸为基础,往上构建全屏尺寸的UI。</p>    <p><img src="https://simg.open-open.com/show/40055a339c2cc1d8482ac31bb094d198.png"></p>    <p>SingleResponseUI.png</p>    <p>但是,如果你的手机和平板的UI差异很大,请不要强制进行转换(即不要把手机的UI强制转换到平板上展示,也不要把平板的UI强制转换到手机上展示)。你有很多种 <a href="https://www.google.com/design/spec/layout/responsive-ui.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#responsive-ui-patterns" rel="nofollow,noindex">响应式UI模式</a> 可选择,以便为用户提供良好顺滑的resize体验,并且不需要任何APIs。</p>    <h2>建议5:被其他应用启动的Activity必须始终支持多窗口</h2>    <p>在多窗口的世界里,一个任务(task)是用 <strong>一个单独的</strong> 窗口来表示的,也就是说,窗口(Window)与任务(Task)是一一对应的。所以,当你想启动一个Activity,并让它与当前Activity分享屏幕时,你必须启动一个新的任务,新的窗口。</p>    <p>反过来也成立,当你在当前Activity所属任务栈中启动另一个Activity时,这个Activity将会在同一个窗口中替代当前Activity,并继承它所有的多窗口属性。</p>    <p>这意味着,当你有一个可被其他应用启动的Activity时,你的Activity将会继承启动它的Activity的多窗口属性,包括最小尺寸等。当你的Activity是以startActivityForResult()方式启动时,你的Activity还必须成为启动它的Activity所在任务栈的一部分。也就是说,这时候不能以新的任务栈的方式启动你的Activity。</p>    <p>即使当你的Activity是被一个隐式Intent启动时,你也不能确定该Intent就一定包含 <a href="http://developer.android.com/reference/android/content/Intent.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#FLAG_ACTIVITY_NEW_TASK" rel="nofollow,noindex"> <em>FLAG_ACTIVITY_NEW_TASK</em> </a> 标记,这就是说,你的Activity还是可能会继承启动它的Activity的多窗口属性。</p>    <p>因此,每一个被其他应用启动的Activity都必须支持多窗口,所以要进行彻底的测试!</p>    <h2>测试所有的方面</h2>    <p>应对多窗口最好的办法就是全面测试你的应用。最好的测试就是把你的应用安装到一个Android N 的设备或者模拟器上,这种办法不需要你改动代码,也不需要你设置Android N SDK,但确实是开始测试的最好的一步。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/e4d2a0d6bcd8</p>    <p> </p>