首页 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
资讯 业界资讯 软件杂谈 编程开发 网站建设 网络观查 搜索引擎 移动应用 网站运营 网络地图
开发 移动开发 Web前端 架构设计 编程语言 互联网 数据库 系统运维 云计算 开发杂谈
[互联网] Android基于Cling开发DLNA应用
Android基于Cling开发DLNA应用

DLNA,Digital Living Network Alliance的简称,即数字生活网络联盟。其由消费性电子、移动电话以及电脑厂商组成。目标在于创建一套可以使得各厂商的产品互相连接,互相适应的工业标准,从而为消费者实现数字化生活。
UPnP/DLNA library for Java and Android。
GitHub最多关注,当前仍在维护,许可协议为LGPL或CDDL。

以下为中文译文:

Android上的Cling
Cling Core为Android应用提供了UPnP栈。由于如今大部分Android系统都是小型手持设备,所以通常你需要写控制端应用。然而你也可以写Android上的UPnP服务应用,其所有特性Cling Core都支持。
 
```
Android模拟器上的Cling
在写此时,Android模拟器还不支持接收UDP组播。不过,可以发送UDP组播。你能够发送一个组播UPnP搜寻,并接收UDP单播回应,继而发现正运行的设备。你发现不了在搜寻后新开启的设备,并且在设备关闭时也收不到消息。另外,其他在你网络的控制端应用,则不能发现你本地的Android设备或服务。在你测试应用时,所有这些情况都会使你感到困惑,所以除非你真得理解哪些有作用、哪些没有,不然你应当使用一个真正的设备。
这章阐述了你如何整合Cling到你的Android应用,使其成为一个共享的部件。
```
 
 配置应用服务
你可以在Android应用主activity中实例化Cling UpnpService。另一方面,如果你好些activities都要要求访问UPnP栈,那么最好采用后台服务,android.app.Service。之后,任何想要访问UPnP栈的activity,都能够在需要时绑定或解绑该服务。
 
该服务组件的接口是org.teleal.cling.android.AndroidUpnpService:

view sourceprint?
1.public interface AndroidUpnpService {
2.public UpnpService get();
3.public UpnpServiceConfiguration getConfiguration();
4.public Registry getRegistry();
5.public ControlPoint getControlPoint();
6.}
activity通常访问已知UPnP设备的注册表,或者通过ControlPoint查询和控制UPnP设备。
你必须在AndroidManifest.xml内配置内建的服务实现:

view sourceprint?
01.<manifest ...>
02. 
03.<uses-permission android:name="android.permission.INTERNET"/>
04.<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
05.<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
06.<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
07. 
08.<application ...>
09. 
10.<activity ...>
11....
12.</activity>
13. 
14.<service android:name="org.teleal.cling.android.AndroidUpnpServiceImpl"/>
15. 
16.</application>
17. 
18.</manifest>
此Cling UPnP服务要求设备WiFi接口的访问权限,事实上其也只将会绑定网络接口。
此服务将会自动检测WiFi接口的关闭,并优雅地处理这种情形:任何客户端操作都会导致"no response from server"状态,而你的代码必须预料并处理该状态。
 
当服务组件创建或销毁时,会相应开启和关闭UPnP系统。这依赖于在你的activities里是如何访问此组件的。
 
activity如何访问服务
service的生命周期在Android中很好的被定义了。如果service还没启动的话,第一个绑定服务的activity将会启动它。当不再有activity绑定到service上时,操作系统将会销毁此service。
 
让我们写一个简单的UPnP浏览activity。它用于将所有你网络内的设备显示在一个列表内,并有一个菜单选项来触发搜寻。activity连接UPnP服务之后,会一直监听注册表内设备的增加和删除,所以显示的设备列表会实时更新。
 
以下是activity类的骨架:
 

view sourceprint?
01.import android.app.ListActivity;
02.import android.content.ComponentName;
03.import android.content.Context;
04.import android.content.Intent;
05.import android.content.ServiceConnection;
06.import android.os.Bundle;
07.import android.os.IBinder;
08.import android.view.Menu;
09.import android.view.MenuItem;
10.import android.widget.ArrayAdapter;
11.import android.widget.Toast;
12.import org.teleal.cling.android.AndroidUpnpService;
13.import org.teleal.cling.android.AndroidUpnpServiceImpl;
14.import org.teleal.cling.model.meta.Device;
15.import org.teleal.cling.model.meta.LocalDevice;
16.import org.teleal.cling.model.meta.RemoteDevice;
17.import org.teleal.cling.registry.DefaultRegistryListener;
18.import org.teleal.cling.registry.Registry;
19. 
20.public class UpnpBrowser extends ListActivity {
21. 
22.private ArrayAdapter<DeviceDisplay> listAdapter;
23. 
24.private AndroidUpnpService upnpService;
25. 
26.private ServiceConnection serviceConnection = ...
27. 
28.private RegistryListener registryListener = new BrowseRegistryListener();
29. 
30.@Override
31.public void onCreate(Bundle savedInstanceState) {
32.super.onCreate(savedInstanceState);
33. 
34.listAdapter =
35.new ArrayAdapter(
36.this,
37.android.R.layout.simple_list_item_1
38.);
39.setListAdapter(listAdapter);
40. 
41.getApplicationContext().bindService(
42.new Intent(this, AndroidUpnpServiceImpl.class),
43.serviceConnection,
44.Context.BIND_AUTO_CREATE
45.);
46.}
47. 
48.@Override
49.protected void onDestroy() {
50.super.onDestroy();
51.if (upnpService != null) {
52.upnpService.getRegistry().removeListener(registryListener);
53.}
54.getApplicationContext().unbindService(serviceConnection);
55.}
56. 
57....
58. 
59.}
60.```
我们采用Android运行时默认提供的布局和ListActivity父类。注意这个类可以是你应用的主activity,或者进一步上升进任务的堆栈。listAdapter黏合了Cling Registry上设备的增加移除事件与展示在用户界面的列表项目。
 
当没有后台服务绑定到该activity时,upnpService变量为null。绑定和解绑发生在onCreate()和onDestroy()回调,所以activity绑定服务和它的生存周期一样长。
 
```
暂停后台的UPnP服务
当一个activity不再活动时(停止或暂停状态),它仍会绑定着UPnP服务。UPnP服务将会持续运行,即使应用不再可见。由于UPnP服务的注册表一定会定期维护发现的设备、刷新本地设备的通告、删除过期的GENA事件订阅等,将会消耗你设备的CPU和电量。当activity onPause()或onStop()方法被调用时,你可以调用Registry#pause()来通知UPnP服务不再维护注册表。之后,你可以通过Registry#resume()来恢复后台服务,或同时用Registry#isPaused()检查状态。请阅读这些方法的Javadoc了解详细信息,以及暂停注册表维护对于设备、服务和GENA订阅的意义。
```
 
以下是使用ServiceConnection处理绑定和解绑服务:
 

view sourceprint?
01.private ServiceConnection serviceConnection = new ServiceConnection() {
02. 
03.public void onServiceConnected(ComponentName className, IBinder service) {
04.upnpService = (AndroidUpnpService) service;
05. 
06.// Refresh the list with all known devices
07.listAdapter.clear();
08.for (Device device : upnpService.getRegistry().getDevices()) {
09.registryListener.deviceAdded(device);
10.}
11. 
12.// Getting ready for future device advertisements
13.upnpService.getRegistry().addListener(registryListener);
14. 
15.// Search asynchronously for all devices
16.upnpService.getControlPoint().search();
17.}
18. 
19.public void onServiceDisconnected(ComponentName className) {
20.upnpService = null;
21.}
22.};
23.```
首先,所有已知的UPnP设备能够被查询和显示(如果UPnP服务刚开启且到到目前还没有设备通告它的存在)。
 
然后,给UPnP服务的注册表增加一个监听者。该监听者将会处理在你网络上发现的设备的增加和移除,并在更新在用户界面列表内显示的项目。当activity销毁时,BrowseRegistryListener会被移除。
 
最后,通过发送一个搜寻消息给所有UPnP设备,你会开启异步搜索,此时这些设备将通告它们的存在。注意这个搜寻消息不是每次连接服务都需要的。这只需一次,在当主activity和应用启动时,其会将已知设备写入注册表。
以下是BrowseRegistryListener,他的任务就是更新列表项的显示:
 

view sourceprint?
01.class BrowseRegistryListener extends DefaultRegistryListener {
02. 
03.@Override
04.public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice device) {
05.deviceAdded(device);
06.}
07. 
08.@Override
09.public void remoteDeviceDiscoveryFailed(Registry registry, final RemoteDevice device, final Exception ex) {
10.runOnUiThread(new Runnable() {
11.public void run() {
12.Toast.makeText(
13.BrowseActivity.this,
14."Discovery failed of '" + device.getDisplayString() + "': " +
15.(ex != null ? ex.toString() : "Couldn't retrieve device/service descriptors"),
16.Toast.LENGTH_LONG
17.).show();
18.}
19.});
20.deviceRemoved(device);
21.}
22. 
23.@Override
24.public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
25.deviceAdded(device);
26.}
27. 
28.@Override
29.public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
30.deviceRemoved(device);
31.}
32. 
33.@Override
34.public void localDeviceAdded(Registry registry, LocalDevice device) {
35.deviceAdded(device);
36.}
37. 
38.@Override
39.public void localDeviceRemoved(Registry registry, LocalDevice device) {
40.deviceRemoved(device);
41.}
42. 
43.public void deviceAdded(final Device device) {
44.runOnUiThread(new Runnable() {
45.public void run() {
46.DeviceDisplay d = new DeviceDisplay(device);
47.int position = listAdapter.getPosition(d);
48.if (position >= 0) {
49.// Device already in the list, re-set new value at same position
50.listAdapter.remove(d);
51.listAdapter.insert(d, position);
52.} else {
53.listAdapter.add(d);
54.}
55.}
56.});
57.}
58. 
59.public void deviceRemoved(final Device device) {
60.runOnUiThread(new Runnable() {
61.public void run() {
62.listAdapter.remove(new DeviceDisplay(device));
63.}
64.});
65.}
66.}
67.```
鉴于性能的原因,当发现设备时,我们会直到一个完整的hydrated(所有设备被检索和验证)设备元数据模型可用时才执行等待。我们响应尽可能得快,同时只当remoteDeviceAdded()方法被调用时才去等待。甚至当搜索仍在运行时,我们仍旧显示所有设备。在台式电脑上你通常不需要关心这个,不过,Android手持设备效率慢,并且UPnP使用好些臃肿的XML描述符来交换关于设备和服务的元数据。有时,在设备和它的服务完全可用前,这可能会花费数秒钟。而remoteDeviceDiscoveryStarted()和remoteDeviceDiscoveryFailed()方法在搜索处理时会尽快被调用。顺便说一句,如果设备有相同的UDN就表示相等的(a.equal(b)),但它们可能不会完全一致(a==b)。
 
注意注册表将会在分开的线程中调用监听者方法。你必须在UI线程中更新显示列表数据。
 
activity中以下两个方法增加了用来搜寻的菜单,如此用户才能手动的刷新列表:
 

view sourceprint?
01.```
02.@Override
03.public boolean onCreateOptionsMenu(Menu menu) {
04.menu.add(0, 0, 0, R.string.search_lan)
05..setIcon(android.R.drawable.ic_menu_search);
06.return true;
07.}
08. 
09.@Override
10.public boolean onOptionsItemSelected(MenuItem item) {
11.if (item.getItemId() == 0 && upnpService != null) {
12.upnpService.getRegistry().removeAllRemoteDevices();
13.upnpService.getControlPoint().search();
14.}
15.return false;
16.}
17.```
最后,DeviceDisplay类是一个非常简单的JavaBean,只提供一个toString()方法来呈现列表信息。通过修改此方法,你能够显示任何关于UPnP设备的信息:
 

view sourceprint?
01.class DeviceDisplay {
02.Device device;
03. 
04.public DeviceDisplay(Device device) {
05.this.device = device;
06.}
07. 
08.public Device getDevice() {
09.return device;
10.}
11. 
12.@Override
13.public boolean equals(Object o) {
14.if (this == o) return true;
15.if (o == null || getClass() != o.getClass()) return false;
16.DeviceDisplay that = (DeviceDisplay) o;
17.return device.equals(that.device);
18.}
19. 
20.@Override
21.public int hashCode() {
22.return device.hashCode();
23.}
24. 
25.@Override
26.public String toString() {
27.// Display a little star while the device is being loaded
28.return device.isFullyHydrated() ? device.getDisplayString() : device.getDisplayString() + " *";
29.}
30.}
31.```
还有我们必须覆盖相等操作,这样我们才可以用DeviceDisplay实例作为便捷的处理,从列表中手动地移除和增加设备。
 

优化服务行为
UPnP服务运行时会消耗内存和CPU。尽管通常在一个正常的机器上没有什么问题,但在Android手持设备上就可能会有了。如果你禁用Cling UPnP服务的某些功能,或者设置暂停且在合适时恢复它,你可以留有更多的内存和电量。
 
调整注册表维护
当服务运行时,后台有好些东西在执行。首先,有一个服务的注册表和其维护线程。如果你写一个控制端,后台注册表维护者将会定期从远程服务更新你对外的GENA订阅。当没有通知断开网络时,它也会到期并移除任何远程服务。如果你正提供服务,你的设备通告将被注册表维护者刷新,并在GENA订阅没及时更新时移除它。注册表维护者为了有效得防止UPnP网络上的过时状态,所以所有参与者会实时更新其他参与者的视图等等。
 
默认情况下,注册表维护者会每秒运行并检查是否有事要做(当然,大多数情况下没事做)。然而默认的Android配置有5秒的间隔休眠,所以这已经花费了更少的后台CPU占用时间 — 不过你的应用可能会暴露稍微过时的信息。在UpnpServiceConfiguration你可以通过覆盖getRegistryMaintenanceIntervalMillis()进一步的调整设置。在Android上,你必须子类化服务实现来提供一个新的配置。
 

view sourceprint?
01.```
02.public class MyUpnpService extends AndroidUpnpServiceImpl {
03. 
04.@Override
05.protected AndroidUpnpServiceConfiguration createConfiguration(WifiManager wifiManager) {
06.return new AndroidUpnpServiceConfiguration(wifiManager) {
07. 
08.@Override
09.public int getRegistryMaintenanceIntervalMillis() {
10.return 7000;
11.}
12. 
13.};
14.}
15.}
16.```
此时不要忘了在AndroidManifest.xml内配置MyUpnpService,而不是原先的实现。当在你的activities里绑定服务时,也必须使用该类型。
 
暂停和恢复注册表维护
另外一个更有效同时也不是很复杂的优化是,每当你的activites不再需要UPnP服务时,暂停和恢复注册表。这通常发生在当activity不在前台(暂停),甚至不再显示(停止)时。默认情况下,activity状态改变对UPnP服务没有影响,除非你在activities生命周期的回调内绑定和解绑服务。
 
除了绑定和解绑服务,你也可以在activity onPause()或onStop()方法被调用时,通过调用Registry#pause()来暂停注册表。之后,你可以通过Registry#resume()来恢复后台服务,或同时用Registry#isPaused()检查状态。
 
请阅读这些方法的Javadoc了解详细信息,以及暂停注册表维护对于设备、服务和GENA订阅的意义。根据你的应用要做什么,否则这种小的优化可能不值得处理这些效果。另一方面,你的应用应当能够处理失败的GENA订阅续期,或者消失的远程设备。 
 
配置搜索
最有效的优化是UPnP设备有选择性的搜索。尽管UPnP服务的网络传输层在后台会保持运行(线程正等待且socket被绑定),这个特性允许你有选择且快速的丢弃搜索信息。
 
举例来说,如果你正在写一个控制端,且不通告你想要控制的服务(对其他设备没兴趣),那么你可以丢弃所有接收的搜索信息。另一方面,如果你只提供设备和服务,所有搜索信息(除了你自身服务的搜索信息)可能都可以被丢弃,你对其他远程设备和其服务一点都不会有兴趣。
 
一旦UDP数据包内容可用,该搜索信息就会被Cling选择并偷偷的丢弃,所以不需要进一步得解析和处理,同时CPU时间和内存消耗显著得减少,即使当你在Android手持设备上后台持续运行UPnP服务。
 
为了配置你的控制端应用支持哪些服务,需要覆盖前面章节展示的服务接口并提供一组ServiceType实例:
 

view sourceprint?
01.```
02.public class MyUpnpService extends AndroidUpnpServiceImpl {
03. 
04.@Override
05.protected AndroidUpnpServiceConfiguration createConfiguration(WifiManager wifiManager) {
06.return new AndroidUpnpServiceConfiguration(wifiManager) {
07. 
08.@Override
09.public ServiceType[] getExclusiveServiceTypes() {
10.return new ServiceType[] {
11.new UDAServiceType("SwitchPower")
12.};
13.}
14. 
15.};
16.}
17.}
18.```
这个配置将会忽略所有不通告chemas-upnp-org:SwitchPower:1的任何通告。这是我们控制端要处理的,不需要其他任何东西了。如果你返回一个空的数组(默认行为),所有服务和设备将会发现以及没有通告会被丢弃。
 
如果你正在写一个控制端应用而不是服务应用,你可以让getExclusiveServiceTypes()方法返回null。这将会完全禁用搜索,此时所有设备和服务的通告一接收就会被丢弃。


上一篇:CM开发日志
下一篇:Nginx 基本数据结构

 此文从网络中自动搜索生成,不代表本网站赞成被搜索网站的内容或立场    查看原文
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 新闻资讯 小游戏 Chinese Culture 股票 三丰软件 开发 中国文化 网文精选 阅读网 看图 日历 万年历 2018年12日历
2018-12-13 7:16:27
 
  网站联系 软件世界网-www.sjsjw.com ©2014 蜀ICP备06016416号 三峰网旗下网站