> 编写:[awong1900](https://github.com/awong1900) - 原文:[http://developer.android.com/training/tv/start/hardware.html](http://developer.android.com/training/tv/start/hardware.html)
TV硬件和其他安卓设备有实质性的不同。TV不包含一些其他安卓设备具备的硬件特性,如触摸屏,摄像头,和GPS。TV也完全依赖于其他辅助硬件设备。为了让用户与TV应用交互,他们必须使用遥控器或者游戏手柄。当您创建TV应用时,必须小心的考虑到TV硬件的限制和操作要求。
本节课程讨论如何检查应用是不是运行在TV上,怎样去处理不支持的硬件特性,和讨论处理TV设备控制器的要求。
### TV设备的检测
如果你创建的应用同时支持TV设备和其他设备,你可能需要检测应用当前运行在哪种设备上,并调整应用的执行。例如,如果你有一个应用通过[Intent](http://developer.android.com/reference/android/content/Intent.html)启动,你的应用应该检查设备特性然后决定是应该启动TV导向的[activity](# "An activity represents a single screen with a user interface.")还是手机的[activity](# "An activity represents a single screen with a user interface.")。
检查应用是否运行在TV设备上,推荐的方式是用[UiModeManager.getCurrentModeType()](http://developer.android.com/reference/android/app/UiModeManager.html#getCurrentModeType())方法检测设备是否运行在TV模式。下面的示例代码展示了如何检查你的应用是否运行在TV设备上:
~~~
public static final String TAG = "DeviceTypeRuntimeCheck";
UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
Log.d(TAG, "Running on a TV Device")
} else {
Log.d(TAG, "Running on a non-TV Device")
}
~~~
### 处理不支持的硬件特性
基于设计和应用的功能,你可能需要在某些硬件特性不可用的情况下工作。这节讨论哪些硬件特性对于TV是典型不可用的,如何去检测缺少的硬件特性,并且建议替代方法去用这些特性。
### 不支持的TV硬件特性
TV有不同于其他设备的用途,因此它们没有一些其他安卓设备通常有的硬件特性。由于这个原因,TV设备的安卓系统不支持以下特性:
| 硬件 | 安卓特性描述 |
|-----|-----|
| 触屏 | `android.hardware.touchscreen` |
| 触屏模拟器 | `android.hardware.faketouch` |
| 电话 | `android.hardware.telephony` |
| 摄像头 | `android.hardware.camera` |
| 蓝牙 | `android.hardware.bluetooth` |
| 近场通讯(NFC) | `android.hardware.nfc` |
| GPS | `android.hardware.location.gps` |
| 麦克风 **[1]** | `android.hardware.microphone` |
| 传感器 | `android.hardware.sensor` |
> **[1]** 一些TV控制器有麦克风,但不是这里描述的麦克风硬件特性。控制器麦克风是完全被支持的。
查看[Features Reference](http://developer.android.com/guide/topics/manifest/uses-feature-element.html#features-reference)获得完全的特性列表,子特性,和他们的描述。
### 声明TV硬件需求
安卓应用能通过在manifest中定义硬件特性需求来确保应用不能被安装在不提供这些特性的设备上。如果你正在扩展应用到TV上,仔细地审查你的manifest的硬件特性需求,它有可能阻止应用安装到TV设备上。
即使你的应用使用了TV上不存在的硬件特性(如触屏或者摄像头),应用也可以在没有那些特性的情况下工作,需要修改应用的manifest来表明这些特性不是必须的。接下来的manifest代码片段示范了如何声明在TV设备中不可用的硬件特性,尽管你的应用在非TV设备上可能会用上这些特性。
~~~
<uses-feature android:name="android.hardware.touchscreen"
android:required="false"></uses>
<uses-feature android:name="android.hardware.faketouch"
android:required="false"></uses>
<uses-feature android:name="android.hardware.telephony"
android:required="false"></uses>
<uses-feature android:name="android.hardware.camera"
android:required="false"></uses>
<uses-feature android:name="android.hardware.bluetooth"
android:required="false"></uses>
<uses-feature android:name="android.hardware.nfc"
android:required="false"></uses>
<uses-feature android:name="android.hardware.gps"
android:required="false"></uses>
<uses-feature android:name="android.hardware.microphone"
android:required="false"></uses>
<uses-feature android:name="android.hardware.sensor"
android:required="false"></uses>
~~~
> **Note**:一些特性有子特性,如`android.hardware.camera.front`,参考:[Feature Reference](http://developer.android.com/guide/topics/manifest/uses-feature-element.html#features-reference)。确保应用中任何子特性也标记为`required="false"`。
所有想用在TV设备上的应用必须声明触屏特性不被需要,在[创建TV应用的第一步](http://developer.android.com/training/tv/start/start.html#no-touchscreen)有描述。如果你的应用使用了一个或更多的上面列表上的特性,改变manifest特性的`android:required`属性为`false`。
> **Caution**:表明一个硬件特性是必须的,设置它的值为`true`可以阻止应用在TV设备上安装或者出现在安卓TV的主屏幕launcher上。
一旦你决定了应用的硬件特性选项,你必须检查在运行时那些特性的可用性,然后调整你的应用的行为。下一节讨论如何检查硬件特性和改变应用行为的建议处理。
更多关于filter和在manifest里声明特性,参考:[uses-feature](http://developer.android.com/guide/topics/manifest/uses-feature-element.html)。
### 声明权限会隐含硬件特性
一些[uses-permission](http://developer.android.com/guide/topics/manifest/uses-permission-element.html) manifest声明隐含了硬件特性。这些行为意味着在应用中请求一些权限能导致应用不能安装和使用在TV设备上。下面普通的权限请求包含了一个隐式的硬件特性需求:
| 权限 | 隐式的硬件需求 |
|-----|-----|
| [RECORD_AUDIO]() | `android.hardware.microphone` |
| [CAMERA]() | `android.hardware.camera`_and_`android.hardware.camera.autofocus` |
| [ACCESS_COARSE_LOCATION]() | `android.hardware.location`_and_`android.hardware.location.network` |
| [ACCESS_FINE_LOCATION]() | `android.hardware.location`_and_`android.hardware.location.gps` |
包含隐式硬件特性需求的完整权限需求列表,参考:[uses-feature](http://developer.android.com/guide/topics/manifest/uses-feature-element.html#permissions-features)。如果你的应用请求了上面列表上的特性的任何一个,在manifest中设置它的隐式硬件特性为不需要(`android:required="false"`)。
### 检查硬件特性
在应用运行时,Android framework能告诉你硬件特性是否可用。用[hasSystemFeature(String)](http://developer.android.com/reference/android/content/pm/PackageManager.html#hasSystemFeature(java.lang.String))方法在运行时检查特定的特性。这个方法只需要一个字符串参数,即想检查的特性名字。
接下来的示例代码展示了如何在陨石检测硬件特性的可用性:
~~~
// Check if the telephony hardware feature is available.
if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
Log.d("HardwareFeatureTest", "Device can make phone calls");
}
// Check if android.hardware.touchscreen feature is available.
if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
Log.d("HardwareFeatureTest", "Device has a touch screen.");
}
~~~
### 触屏
因为大部分的TV没有触摸屏,在TV设备上,安卓不支持触屏交互。此外,用触屏交互和坐在离显示器3米外观看是相互矛盾的。
在TV设备中,你应该设计应用交互模式支持遥控器上的方向键(D-pad)操作。更多关于正确地支持TV友好的控制器操作的信息,参考[Creating TV Navigation](http://developer.android.com/training/tv/start/navigation.html)。
### 摄像头
尽管TV通常没有摄像头,但是你仍然可以提供拍照相关的TV应用,如果应用有拍照,查看和编辑图片功能,在TV上可以关闭拍照功能但仍可以允许用户查看甚至编辑图片。如果你决定在TV上使用摄像相关的应用,在manifest里添加接下来的特性声明:
~~~
<uses-feature android:name="android.hardware.camera" android:required="false" ></uses>
~~~
如果在缺少摄像头情况下运行你的应用,在你的应用中添加代码去检测是否摄像头特性可用,并且调整应用的操作。接下来的示例代码展示了如何检测一个摄像头的存在:
~~~
// Check if the camera hardware feature is available.
if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
Log.d("Camera test", "Camera available!");
} else {
Log.d("Camera test", "No camera available. View and edit features only.");
}
~~~
### GPS
TV是固定的室内设备,并且没有内置的全球定位系统(GPS)接收器。如果你的应用使用定位信息,你仍可以允许用户搜索位置,或者用固定位置提供商代替,如在TV设置中设置邮政编码。
~~~
// Request a static location from the location manager
LocationManager locationManager = (LocationManager) this.getSystemService(
Context.LOCATION_SERVICE);
Location location = locationManager.getLastKnownLocation("static");
// Attempt to get postal or zip code from the static location object
Geocoder geocoder = new Geocoder(this);
Address address = null;
try {
address = geocoder.getFromLocation(location.getLatitude(),
location.getLongitude(), 1).get(0);
Log.d("Zip code", address.getPostalCode());
} catch (IOException e) {
Log.e(TAG, "Geocoder error", e);
}
~~~
### 处理控制器
TV设备需要辅助硬件设备与应用交互,如一个基本形式的遥控器或者游戏手柄。这意味着你的应用必须支持D-pad(十字方向键)输入。它也意味着你的应用可能需要处理手柄掉线和更多类型的手柄输入。
### D-pad必须的按键
默认的TV设备控制器是D-pad。通常,你可以用遥控器的上,下,左,右,选择,返回,和Home键操作应用。如果你的应用是一个游戏而需要游戏手柄额外的控制,你的应用也应该尝试允许用D-pad操作。这种情况下,你的应用也应该警告用户需要手柄,并且允许他们用D-pad优雅的退出游戏。更多关于在TV设备如理D-pad的操作,参考[Creating TV Navigation](http://developer.android.com/training/tv/start/navigation.html)。
### 处理手柄掉线
TV的手柄通常是蓝牙设备,它为了省电而定期的休眠并且与TV设备断开连接。这意味着如果不处理这些重连事件,应用可能被中断或者重新开始。这些事件可以发生在下面任何情景中:
- 当在看几分钟的视频,D-Pad或者游戏手柄进入了睡眠模式,从TV设备上断开连接并且随后重新连接。
- 在玩游戏时,新玩家用不是当前连接的游戏手柄加入游戏。
- 在玩游戏时,一个玩家离开游戏并且断开游戏手柄。
任何TV应用[activity](# "An activity represents a single screen with a user interface.")受制于断开连接和重连事件,这些必须在manifest中配置为处理重连事件。。接下来的示例代码展示了如何打开一个[activity](# "An activity represents a single screen with a user interface.")去处理配置改变,包括键盘或者操作设备连接,断开连接,或者重新连接:
~~~
<activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|navigation"
android:theme="@style/Theme.Leanback">
<intent-filter>
<action android:name="android.intent.action.MAIN" ></action>
<category android:name="android.intent.category.LEANBACK_LAUNCHER" ></category>
</intent-filter>
...
</activity>
~~~
这个配置改变允许应用通过重连事件继续运行,比较而言Android framework强制重启应用会导致一个不好的用户体验。
### 处理D-pad变种输入
TV设备用户可能有超过一种类型的控制器来操纵TV。例如,一个用户可能有基本D-pad控制器和一个游戏控制器。游戏控制器用于D-pad功能的按键代码可能和物理十字键提供的不相同。
你的应用应该要处理游戏控制器D-pad的变种输入,因此用户不需要通过物理开关控制器去操作你的应用。更多信息关于处理这些变种输入,参考[Handling Controller Actions](http://developer.android.com/training/tv/start/hardware.html)。
[下一节: 创建TV布局](#)
- 序言
- Android入门基础:从这里开始
- 建立第一个App
- 创建Android项目
- 执行Android程序
- 建立简单的用户界面
- 启动其他的Activity
- 添加ActionBar
- 建立ActionBar
- 添加Action按钮
- 自定义ActionBar的风格
- ActionBar的覆盖层叠
- 兼容不同的设备
- 适配不同的语言
- 适配不同的屏幕
- 适配不同的系统版本
- 管理Activity的生命周期
- 启动与销毁Activity
- 暂停与恢复Activity
- 停止与重启Activity
- 重新创建Activity
- 使用Fragment建立动态的UI
- 创建一个Fragment
- 建立灵活动态的UI
- Fragments之间的交互
- 数据保存
- 保存到Preference
- 保存到文件
- 保存到数据库
- 与其他应用的交互
- Intent的发送
- 接收Activity返回的结果
- Intent过滤
- Android分享操作
- 分享简单的数据
- 给其他App发送简单的数据
- 接收从其他App返回的数据
- 给ActionBar增加分享功能
- 分享文件
- 建立文件分享
- 分享文件
- 请求分享一个文件
- 获取文件信息
- 使用NFC分享文件
- 发送文件给其他设备
- 接收其他设备的文件
- Android多媒体
- 管理音频播放
- 控制音量与音频播放
- 管理音频焦点
- 兼容音频输出设备
- 拍照
- 简单的拍照
- 简单的录像
- 控制相机硬件
- 打印
- 打印照片
- 打印HTML文档
- 打印自定义文档
- Android图像与动画
- 高效显示Bitmap
- 高效加载大图
- 非UI线程处理Bitmap
- 缓存Bitmap
- 管理Bitmap的内存
- 在UI上显示Bitmap
- 使用OpenGL ES显示图像
- 建立OpenGL ES的环境
- 定义Shapes
- 绘制Shapes
- 运用投影与相机视图
- 添加移动
- 响应触摸事件
- 添加动画
- View间渐变
- 使用ViewPager实现屏幕侧滑
- 展示卡片翻转动画
- 缩放View
- 布局变更动画
- Android网络连接与云服务
- 无线连接设备
- 使得网络服务可发现
- 使用WiFi建立P2P连接
- 使用WiFi P2P服务
- 执行网络操作
- 连接到网络
- 管理网络
- 解析XML数据
- 高效下载
- 为网络访问更加高效而优化下载
- 最小化更新操作的影响
- 避免下载多余的数据
- 根据网络类型改变下载模式
- 云同步
- 使用备份API
- 使用Google Cloud Messaging
- 解决云同步的保存冲突
- 使用Sync Adapter传输数据
- 创建Stub授权器
- 创建Stub Content Provider
- 创建Sync Adpater
- 执行Sync Adpater
- 使用Volley执行网络数据传输
- 发送简单的网络请求
- 建立请求队列
- 创建标准的网络请求
- 实现自定义的网络请求
- Android联系人与位置信息
- Android联系人信息
- 获取联系人列表
- 获取联系人详情
- 使用Intents修改联系人信息
- 显示联系人头像
- Android位置信息
- 获取最后可知位置
- 获取位置更新
- 显示位置地址
- 创建和监视地理围栏
- Android可穿戴应用
- 赋予Notification可穿戴特性
- 创建Notification
- 在Notifcation中接收语音输入
- 为Notification添加显示页面
- 以Stack的方式显示Notifications
- 创建可穿戴的应用
- 创建并运行可穿戴应用
- 创建自定义的布局
- 添加语音功能
- 打包可穿戴应用
- 通过蓝牙进行调试
- 创建自定义的UI
- 定义Layouts
- 创建Cards
- 创建Lists
- 创建2D-Picker
- 创建确认界面
- 退出全屏的Activity
- 发送并同步数据
- 访问可穿戴数据层
- 同步数据单元
- 传输资源
- 发送与接收消息
- 处理数据层的事件
- Android TV应用
- 创建TV应用
- 创建TV应用的第一步
- 处理TV硬件部分
- 创建TV的布局文件
- 创建TV的导航栏
- 创建TV播放应用
- 创建目录浏览器
- 提供一个Card视图
- 创建详情页
- 显示正在播放卡片
- 帮助用户在TV上探索内容
- TV上的推荐内容
- 使得TV App能够被搜索
- 使用TV应用进行搜索
- 创建TV游戏应用
- 创建TV直播应用
- TV Apps Checklist
- Android企业级应用
- Ensuring Compatibility with Managed Profiles
- Implementing App Restrictions
- Building a Work Policy Controller
- Android交互设计
- 设计高效的导航
- 规划屏幕界面与他们之间的关系
- 为多种大小的屏幕进行规划
- 提供向下和横向导航
- 提供向上和历史导航
- 综合:设计样例 App
- 实现高效的导航
- 使用Tabs创建Swipe视图
- 创建抽屉导航
- 提供向上的导航
- 提供向后的导航
- 实现向下的导航
- 通知提示用户
- 建立Notification
- 当启动Activity时保留导航
- 更新Notification
- 使用BigView风格
- 显示Notification进度
- 增加搜索功能
- 建立搜索界面
- 保存并搜索数据
- 保持向下兼容
- 使得你的App内容可被Google搜索
- 为App内容开启深度链接
- 为索引指定App内容
- Android界面设计
- 为多屏幕设计
- 兼容不同的屏幕大小
- 兼容不同的屏幕密度
- 实现可适应的UI
- 创建自定义View
- 创建自定义的View类
- 实现自定义View的绘制
- 使得View可交互
- 优化自定义View
- 创建向后兼容的UI
- 抽象新的APIs
- 代理至新的APIs
- 使用旧的APIs实现新API的效果
- 使用版本敏感的组件
- 实现辅助功能
- 开发辅助程序
- 开发辅助服务
- 管理系统UI
- 淡化系统Bar
- 隐藏系统Bar
- 隐藏导航Bar
- 全屏沉浸式应用
- 响应UI可见性的变化
- 创建使用Material Design的应用
- 开始使用Material Design
- 使用Material的主题
- 创建Lists与Cards
- 定义Shadows与Clipping视图
- 使用Drawables
- 自定义动画
- 维护兼容性
- Android用户输入
- 使用触摸手势
- 检测常用的手势
- 跟踪手势移动
- Scroll手势动画
- 处理多触摸手势
- 拖拽与缩放
- 管理ViewGroup中的触摸事件
- 处理键盘输入
- 指定输入法类型
- 处理输入法可见性
- 兼容键盘导航
- 处理按键动作
- 兼容游戏控制器
- 处理控制器输入动作
- 支持不同的Android系统版本
- 支持多个控制器
- Android后台任务
- 在IntentService中执行后台任务
- 创建IntentService
- 发送工作任务到IntentService
- 报告后台任务执行状态
- 使用CursorLoader在后台加载数据
- 使用CursorLoader执行查询任务
- 处理查询的结果
- 管理设备的唤醒状态
- 保持设备的唤醒
- 制定重复定时的任务
- Android性能优化
- 管理应用的内存
- 代码性能优化建议
- 提升Layout的性能
- 优化layout的层级
- 使用include标签重用layouts
- 按需加载视图
- 使得ListView滑动顺畅
- 优化电池寿命
- 监测电量与充电状态
- 判断与监测Docking状态
- 判断与监测网络连接状态
- 根据需要操作Broadcast接受者
- 多线程操作
- 在一个线程中执行一段特定的代码
- 为多线程创建线程池
- 启动与停止线程池中的线程
- 与UI线程通信
- 避免出现程序无响应ANR
- JNI使用指南
- 优化多核处理器(SMP)下的Android程序
- Android安全与隐私
- Security Tips
- 使用HTTPS与SSL
- 为防止SSL漏洞而更新Security
- 使用设备管理条例增强安全性
- Android测试程序
- 测试你的Activity
- 建立测试环境
- 创建与执行测试用例
- 测试UI组件
- 创建单元测试
- 创建功能测试
- 術語表