为 TV 开发的 App,你说要运行在手机上?

企业动态
Android 智能电视,不知道你接触过没有?近两年生产的电视,基本上都属于智能电视,而因为 Android 的开放性,这些电视很大一部分都是搭载的 Android 系统。

[[219110]]

一、前言

Android 智能电视,不知道你接触过没有?近两年生产的电视,基本上都属于智能电视,而因为 Android 的开放性,这些电视很大一部分都是搭载的 Android 系统。

而除了 Android 智能电视之外,还有一些智能盒子,例如:小米盒子、天猫魔盒等,其实都是属于 Android 阵营的,接上一台显示器,就可以当一个智能电视使用。

在国内的环境下,开发 TV App 其实并没有遵循标准的 Google TV 的开发规范,而是把它当成一个普通的横屏 Android App 来开发。可是在这个过程中,是需要额外处理一些手机和电视的差异的,例如:焦点的控制、选中态的控制、屏幕的适配等等。

如果这些适配都已经做的非常好了的话,是可以在 Android 手机上,不需要做任何改动和配置,就***的运行一个原本为 Android TV 而开发的 App 的。

而在某些场景下,你可能需要对你原本想为 TV 开发的 App,做一些手机上的适配,让它在运行在手机上的时候,呈现出另外的 UI 效果或者执行分支的逻辑。

举个比较实际的例子:简单的微信登录功能,TV App 来实现这个功能,一般是展示一个登录二维码,让用户通过手机扫码登录,但是如果这个 App 运行在手机上的话,你可能需要的是一个按钮,点击吊起微信去登录。

你别问为什么用户要在手机上安装一个 TV App?为什么不能让用户截图然后去微信里扫描截图登录?

需求下来了,就问你能不能实现?

那么,本文就来讨论一下,如何在运行时,通过一些标识来区分当前 App 是运行在手机上还是 TV 上。

二、如何区分

既然这是一个运行时的区分,肯定是需要获取一些设备上的差异值,来判定当前的运行环境。

那么首先提个问题给自己,手机和 TV 到底存在哪些差异?

手机和电视的差异性:

  • 屏幕物理尺寸不同。
  • 布局尺寸不同。
  • SIM 卡的支持不同。
  • 电源接入的方式不同。
  • 系统参数不同。

差不多就这些差异了,接下来我们进行详细分析。

1、屏幕物理尺寸

手机和电视的屏幕物理尺寸是完全不一样的,但是我们也不能完全使用买电视的时候介绍的 Xx寸 来区分屏幕物理尺寸。实际上完全可以将 Android TV 当成一个大号的平板。

这里以一个电视英寸数的计算公式,计算屏幕对角线的长度,来做一个参考的数值。

  1. /** 
  2.  * 检查当前屏幕的物理尺寸 
  3.  * 小于 6.4 人为是手机,否则人为是电视 
  4.  * 
  5.  * @return true 手机,false TV 
  6.  */ 
  7. private static boolean checkScreenIsPhone(Context ctx) { 
  8.     WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); 
  9.     Display display = wm.getDefaultDisplay(); 
  10.     DisplayMetrics dm = new DisplayMetrics(); 
  11.     display.getMetrics(dm); 
  12.     double x = Math.pow(dm.widthPixels / dm.xdpi, 2); 
  13.     double y = Math.pow(dm.heightPixels / dm.ydpi, 2); 
  14.     // 屏幕尺寸 
  15.     double screenInches = Math.sqrt(x + y); 
  16.     return screenInches < 6.5; 

对于智能电视而言,我想最小应该都在 32 英寸,而这里的 6.4英寸以下,主要是基于手机的一个参数判断。

不过手机的屏幕尺寸越做越大,各大厂商现在也都在上线全面屏的产品,随手找了小米 Mix2 的参数,尺寸为 5.99 英寸,霸么就这个 6.4 英寸的判断条件,在现阶段来看是合理的。

2、布局尺寸

既然屏幕的尺寸有差异,那么从不同的布局中获取布局文件也是不一样的,可以通过 screenLayout 参数来区分出当前运行环境下***那一套。

规则如下:

截图来自官方文档,有兴趣的可以通篇阅读一下。

https://developer.android.com/guide/practices/screens_support.html?hl=zh-cn

而代码如下:

  1. /** 
  2.  * 检查当前设备的局部尺寸 
  3.  * 如果是 SIZE_LARGE 就人为是大屏幕的 
  4.  */ 
  5. private static boolean checkScreenLayoutIsPhone(Context ctx) { 
  6.     return (ctx.getResources().getConfiguration().screenLayout 
  7.             & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) 
  8.             <= Configuration.SCREENLAYOUT_SIZE_LARGE; 

3、SIM 支持的模式

对于电视而言,就现在所了解到的,还没有一款智能电视或者智能盒子,是可以插 SIM 卡的,所以判断 SIM 支持的模式,基本上就可以区分出电视还是手机了。

SIM 卡支持的模式可以使用 TelephonyManager 来获取当前的状态。

  1. /** 
  2.  * 检查 SIM 卡的状态,如果没有检查到,认为是电视 
  3.  * 
  4.  * @param ctx 
  5.  * @return 
  6.  */ 
  7. private static boolean checkTelephonyIsPhone(Context ctx) { 
  8.     TelephonyManager telecomManager = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE); 
  9.     return telecomManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; 

可以看到 getPhoneType() 可以获取当前设备支持的 Radio 的模式。

  1. /** No phone radio. */ 
  2.     public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE; 
  3.     /** Phone radio is GSM. */ 
  4.     public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM; 
  5.     /** Phone radio is CDMA. */ 
  6.     public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA; 
  7.     /** Phone is via SIP. */ 
  8.     public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP; 

一般而言,识别不到 SIM 的模式,就可以认为是一款不支持 SIM 插卡的设备了。

4、电源的接入方式

对于电视的电源,有什么特点?

  1. 永远没有耗电的变动,获取到的电量永远是满的。
  2. 电源接入的方式,使用 AC 交流电,而非 USB(充电) 或者电池。

获取当前电源和充电的接入方式,没什么好说的,基本上依据这两个条件,就可以区分出当前到底是电视还是手机/平板了。

  1. /** 
  2.  * 检查当前电源的接入状态,电视一定是 AC 交流电 
  3.  * 
  4.  * @param ctx 
  5.  * @return 
  6.  */ 
  7. private static boolean checkBatteryIsPhone(Context ctx) { 
  8.     IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 
  9.     Intent batteryStatus = ctx.registerReceiver(null, filter); 
  10.     // 当前电池的状态 
  11.     int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); 
  12.     boolean isChanging = status == BatteryManager.BATTERY_STATUS_FULL; 
  13.     // 当前充电的状态 
  14.     int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); 
  15.     boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; 
  16.     // 电视的状态 当前点亮一定是满的 兵器是 AC 交流电接入 才认为是电视 
  17.     return !(isChanging && acCharge); 

这种方式去判断也有缺陷,因为对于智能电视类的设备来说,还有一种设备容易被忽略,那就是投影,对于投影而言,有一些是会内置电池的。

5、UI Mode

使用 UI Mode 的方式去判断,就需要用到一个系统服务 UIModeManager,它和一般的系统服务一样,需要我们通过 Context.getSystemService() 方法获取到。

这是一个官方给出的判断方式,但是在国内的环境下,并不可取。因为大部分厂商的智能电视,只是拿普通的 Android 系统改了改,其实并没有遵循 Google TV 的标准,所以这种方式在某些设备上可能会判断出错。

既然文档介绍了,这里还是简单介绍一下。没什么好说的,直接上代码就好了。

  1. /** 
  2.  * 检查当前设备的 UI MODE 来判定运行环境是 TV 还是 Phone 
  3.  */ 
  4. private static boolean checkUIModeIsPhone(Context ctx) { 
  5.     UiModeManager uiModeManager = (UiModeManager) ctx.getSystemService(Context.UI_MODE_SERVICE); 
  6.     return uiModeManager.getCurrentModeType() != Configuration.UI_MODE_TYPE_TELEVISION; 

有兴趣可以直接阅读完整的官方文档中的相关部分。

https://developer.android.com/training/tv/start/hardware.html#runtime-check

三、设计原则

这里提供的几种方法,其实都是猜测,都是有缺陷的。例如可能出现某些厂商的奇葩设备,出货屏幕尺寸就是大的手机,或者有一些奇葩的电视或者盒子,就是可以支持插 SIM 卡,再或者有其实还有一些智能投影的设备,其实是内带电池的,是有电量的消耗的。

所以最稳妥的方式,就是组合起来判断。

  1. private static boolean sIsChecked = false
  2. private static boolean sIsPhoneRunCache = false
  3. public static boolean isPhoneRunning(Context ctx) { 
  4.     if (!sIsChecked) { 
  5.         sIsPhoneRunCache = checkScreenIsPhone(ctx) 
  6.                 && checkScreenLayoutIsPhone(ctx) 
  7.                 && checkTelephonyIsPhone(ctx) 
  8.                 && checkBatteryIsPhone(ctx); 
  9.         sIsChecked = true
  10.     } 
  11.     return sIsPhoneRunCache; 

这里的判断,是基于当前 App 是主要发布在 Android 电视的应用市场中,所以这里的判断条件是对手机进行严格判断,其他的都认为是 Android TV 。这样即便是误判了,影响也不会太大。

【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2021-04-21 10:22:56

Python 开发编程语言

2012-02-22 09:09:04

Ubuntu for UbuntuAndroid

2011-01-24 15:04:01

Windows Pho

2011-10-21 10:26:51

Howdy明信片

2014-09-18 09:20:18

WPS高德地图美图秀秀

2023-02-14 15:04:00

Linux虚拟机物理机

2020-09-27 10:13:53

Windows 功能系统

2021-01-28 10:12:02

鸿蒙JSJava

2012-12-26 10:29:02

2016-11-24 08:59:10

LinuxTOP500计算机

2012-05-10 09:34:27

TV

2020-09-05 16:35:20

AndroidPython软件开发

2015-07-20 17:01:12

APP崩溃云智慧

2023-07-04 13:50:21

自然语言手机

2023-03-28 15:48:39

开源模型

2022-03-14 17:35:55

数据中心

2018-01-18 16:17:05

Android TVApp工具

2012-08-08 11:20:18

苹果Apple TV

2013-07-05 13:48:47

App

2014-06-11 13:22:44

点赞
收藏

51CTO技术栈公众号