关于Platinum库的MediaRender具体C++代码实现探讨
接上篇博文 NDK下 将Platinum SDK 编译成so库 (android - upnp)
讲述了如何利用该代码库编译给android程序调用的so库,其中也提到了,在使用sample-upnp工程来测试生成的so库是无效的
大家比对一下Platinum开发库的Platinum\Source\Platform\Android\module\platinum\jni\platinum-jni.cpp和
Platinum\Source\Tests\MediaRenderer\MediaRendererTest.cpp
platinum-jni.cpp
- #include <assert.h>
- #include <jni.h>
- #include <string.h>
- #include <sys/types.h>
- #include "platinum-jni.h"
- #include "Platinum.h"
- #include <android/log.h>
- /*----------------------------------------------------------------------
- | logging
- +---------------------------------------------------------------------*/
- NPT_SET_LOCAL_LOGGER("platinum.android.jni")
- /*----------------------------------------------------------------------
- | functions
- +---------------------------------------------------------------------*/
- __attribute__((constructor)) static void onDlOpen(void)
- {
- }
- /*----------------------------------------------------------------------
- | JNI_OnLoad
- +---------------------------------------------------------------------*/
- JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
- {
- NPT_LogManager::GetDefault().Configure("plist:.level=FINE;.handlers=ConsoleHandler;.ConsoleHandler.outputs=2;.ConsoleHandler.colors=false;.ConsoleHandler.filter=59");
- return JNI_VERSION_1_4;
- }
- /*
- * Class: com_plutinosoft_platinum_UPnP
- * Method: _init
- * Signature: ()J
- */
- JNIEXPORT jlong JNICALL Java_com_plutinosoft_platinum_UPnP__1init(JNIEnv *env, jclass)
- {
- NPT_LOG_INFO("init");
- PLT_UPnP* self = new PLT_UPnP();
- return (jlong)self;
- }
- /*
- * Class: com_plutinosoft_platinum_UPnP
- * Method: _start
- * Signature: (J)I
- */
- JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1start(JNIEnv *, jclass, jlong _self)
- {
- NPT_LOG_INFO("start");
- PLT_UPnP* self = (PLT_UPnP*)_self;
- return self->Start();
- }
- /*
- * Class: com_plutinosoft_platinum_UPnP
- * Method: _stop
- * Signature: (J)I
- */
- JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1stop(JNIEnv *, jclass, jlong _self)
- {
- NPT_LOG_INFO("stop");
- PLT_UPnP* self = (PLT_UPnP*)_self;
- return self->Stop();
- }
MediaRendererTest.cpp
- #include "PltUPnP.h"
- #include "PltMediaRenderer.h"
- #include <stdlib.h>
- /*----------------------------------------------------------------------
- | globals
- +---------------------------------------------------------------------*/
- struct Options {
- const char* friendly_name;
- } Options;
- /*----------------------------------------------------------------------
- | PrintUsageAndExit
- +---------------------------------------------------------------------*/
- static void
- PrintUsageAndExit(char** args)
- {
- fprintf(stderr, "usage: %s [-f <friendly_name>]\n", args[0]);
- fprintf(stderr, "-f : optional upnp server friendly name\n");
- fprintf(stderr, "<path> : local path to serve\n");
- exit(1);
- }
- /*----------------------------------------------------------------------
- | ParseCommandLine
- +---------------------------------------------------------------------*/
- static void
- ParseCommandLine(char** args)
- {
- const char* arg;
- char** tmp = args+1;
- /* default values */
- Options.friendly_name = NULL;
- while ((arg = *tmp++)) {
- if (!strcmp(arg, "-f")) {
- Options.friendly_name = *tmp++;
- } else {
- fprintf(stderr, "ERROR: too many arguments\n");
- PrintUsageAndExit(args);
- }
- }
- }
- /*----------------------------------------------------------------------
- | main
- +---------------------------------------------------------------------*/
- int
- main(int /* argc */, char** argv)
- {
- PLT_UPnP upnp;
- /* parse command line */
- ParseCommandLine(argv);
- PLT_DeviceHostReference device(
- new PLT_MediaRenderer(Options.friendly_name?Options.friendly_name:"Platinum Media Renderer",
- false,
- "e6572b54-f3c7-2d91-2fb5-b757f2537e21"));
- upnp.AddDevice(device);
- bool added = true;
- upnp.Start();
- char buf[256];
- while (gets(buf)) {
- if (*buf == 'q')
- break;
- if (*buf == 's') {
- if (added) {
- upnp.RemoveDevice(device);
- } else {
- upnp.AddDevice(device);
- }
- added = !added;
- }
- }
- upnp.Stop();
- return 0;
- }
可以看出JNI接口中的PLT_UPnP对象并没有加入PLT_DeviceHostReference对象,所以无法启动设备,只要参照MediaRenderTest.cpp的做法我们就可以实现一个dmr设备的开启与关闭了,童鞋们可以仿照博文 基于Platinum库的DMR实现(android)当中的jni类来尝试实现一下这两个接口
public static native int startMediaRender(byte[] friendname ,byte[] uuid);
public static native int stopMediaRender();
完成这一步,DMR的实现就已经成功一半了
接下来我们要做的两件事就是
1.将外部的action事件通过反射机制抛给java层处理
2.通过jni将一些事件状态值更新至所在服务列表
先看第一点,每当有action事件到来时,PLT_MediaRenderer的
virtual NPT_Result OnAction(PLT_ActionReference&action,const PLT_HttpRequestContext& context);方法就会被调用
- NPT_Result
- PLT_MediaRenderer::OnAction(PLT_ActionReference& action,
- const PLT_HttpRequestContext& context)
- {
- NPT_COMPILER_UNUSED(context);
- /* parse the action name */
- NPT_String name = action->GetActionDesc().GetName();
- // since all actions take an instance ID and we only support 1 instance
- // verify that the Instance ID is 0 and return an error here now if not
- NPT_String serviceType = action->GetActionDesc().GetService()->GetServiceType();
- if (serviceType.Compare("urn:schemas-upnp-org:service:AVTransport:1", true) == 0) {
- if (NPT_FAILED(action->VerifyArgumentValue("InstanceID", "0"))) {
- action->SetError(718, "Not valid InstanceID");
- return NPT_FAILURE;
- }
- }
- serviceType = action->GetActionDesc().GetService()->GetServiceType();
- if (serviceType.Compare("urn:schemas-upnp-org:service:RenderingControl:1", true) == 0) {
- if (NPT_FAILED(action->VerifyArgumentValue("InstanceID", "0"))) {
- action->SetError(702, "Not valid InstanceID");
- return NPT_FAILURE;
- }
- }
- /* Is it a ConnectionManager Service Action ? */
- if (name.Compare("GetCurrentConnectionInfo", true) == 0) {
- return OnGetCurrentConnectionInfo(action);
- }
- /* Is it a AVTransport Service Action ? */
- if (name.Compare("Next", true) == 0) {
- return OnNext(action);
- }
- if (name.Compare("Pause", true) == 0) {
- return OnPause(action);
- }
- if (name.Compare("Play", true) == 0) {
- return OnPlay(action);
- }
- if (name.Compare("Previous", true) == 0) {
- return OnPrevious(action);
- }
- if (name.Compare("Seek", true) == 0) {
- return OnSeek(action);
- }
- if (name.Compare("Stop", true) == 0) {
- return OnStop(action);
- }
- if (name.Compare("SetAVTransportURI", true) == 0) {
- return OnSetAVTransportURI(action);
- }
- if (name.Compare("SetPlayMode", true) == 0) {
- return OnSetPlayMode(action);
- }
- /* Is it a RendererControl Service Action ? */
- if (name.Compare("SetVolume", true) == 0) {
- return OnSetVolume(action);
- }
- if (name.Compare("SetVolumeDB", true) == 0) {
- return OnSetVolumeDB(action);
- }
- if (name.Compare("GetVolumeDBRange", true) == 0) {
- return OnGetVolumeDBRange(action);
- }
- if (name.Compare("SetMute", true) == 0) {
- return OnSetMute(action);
- }
- // other actions rely on state variables
- NPT_CHECK_LABEL_WARNING(action->SetArgumentsOutFromStateVariable(), failure);
- return NPT_SUCCESS;
- failure:
- action->SetError(401,"No Such Action.");
- return NPT_FAILURE;
- }
部分事件会由m_Delegate成员变量来处理,也就是 virtual void SetDelegate(PLT_MediaRendererDelegate* delegate) { m_Delegate = delegate; }传进来的
所以我们只需要从PLT_MediaRendererDelegate类派生一个子类,然后将它设进PLT_MediaRenderer并实现相应的action方法并把一些需要的值反射出来即可
- class PLT_MediaRendererDelegate
- {
- public:
- virtual ~PLT_MediaRendererDelegate() {}
- // ConnectionManager
- virtual NPT_Result OnGetCurrentConnectionInfo(PLT_ActionReference& action) = 0;
- // AVTransport
- virtual NPT_Result OnNext(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnPause(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnPlay(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnPrevious(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnSeek(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnStop(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnSetPlayMode(PLT_ActionReference& action) = 0;
- // RenderingControl
- virtual NPT_Result OnSetVolume(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnSetVolumeDB(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnGetVolumeDBRange(PLT_ActionReference& action) = 0;
- virtual NPT_Result OnSetMute(PLT_ActionReference& action) = 0;
- };
- void ActionInflect(int cmd, const char* value, const char* data)
- {
- if (g_vm == NULL)
- {
- UpnpPrintInfo("g_vm = NULL!!!");
- return ;
- }
- int status;
- JNIEnv *env = NULL;
- bool isAttach = false;
- status = g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
- if(status != JNI_OK)
- {
- status = g_vm->AttachCurrentThread(&env, NULL);
- if(status < 0) {
- UpnpPrintInfo("callback_handler: failed to attach , current thread, status = %d", status);
- return;
- }
- isAttach = true;
- }
- jstring valueString = NULL;
- jstring dataString = NULL;
- jclass inflectClass = g_inflectClass;
- jmethodID inflectMethod = g_methodID;
- if (inflectClass == NULL || inflectMethod == NULL)
- {
- goto end;
- }
- // UpnpPrintInfo("CMD = %d\nVALUE = %s\nDATA = %s",cmd, value, data);
- valueString = env->NewStringUTF(value);
- dataString = env->NewStringUTF(data);
- env->CallStaticVoidMethod(inflectClass, inflectMethod, cmd, valueString, dataString);
- env->DeleteLocalRef(valueString);
- env->DeleteLocalRef(dataString);
- end:
- if (env->ExceptionOccurred())
- {
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
- if (isAttach)
- {
- g_vm->DetachCurrentThread();
- }
- }
再看第二点,如何将事件变量值更新至服务列表
首先通过FindServiceByType方法找到urn:schemas-upnp-org:service:AVTransport:1服务,
然后通过SetStateVariable更新值即可
变量名与值对应关系大家参照dlna文档开发即可
http://download.csdn.net/detail/geniuseoe2012/4969961
重点看standardizeddcps\MediaServer_4 and MediaRenderer_3下的UPnP-av-AVTransport-v3-Service-20101231.pdf文档
代码实现可以参照PLT_MediaRenderer::SetupServices()方法
- NPT_Result
- PLT_MediaRenderer::SetupServices()
- {
- PLT_Service* service;
- {
- /* AVTransport */
- service = new PLT_Service(
- this,
- "urn:schemas-upnp-org:service:AVTransport:1",
- "urn:upnp-org:serviceId:AVTransport",
- "AVTransport",
- "urn:schemas-upnp-org:metadata-1-0/AVT/");
- NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_AVTransportSCPD));
- NPT_CHECK_FATAL(AddService(service));
- service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));
- service->SetStateVariable("A_ARG_TYPE_InstanceID", "0");
- // GetCurrentTransportActions
- service->SetStateVariable("CurrentTransportActions", "Play,Pause,Stop,Seek,Next,Previous");
- // GetDeviceCapabilities
- service->SetStateVariable("PossiblePlaybackStorageMedia", "NONE,NETWORK,HDD,CD-DA,UNKNOWN");
- service->SetStateVariable("PossibleRecordStorageMedia", "NOT_IMPLEMENTED");
- service->SetStateVariable("PossibleRecordQualityModes", "NOT_IMPLEMENTED");
- // GetMediaInfo
- service->SetStateVariable("NumberOfTracks", "0");
- service->SetStateVariable("CurrentMediaDuration", "00:00:00");
- service->SetStateVariable("AVTransportURI", "");
- service->SetStateVariable("AVTransportURIMetadata", "");;
- service->SetStateVariable("NextAVTransportURI", "NOT_IMPLEMENTED");
- service->SetStateVariable("NextAVTransportURIMetadata", "NOT_IMPLEMENTED");
- service->SetStateVariable("PlaybackStorageMedium", "NONE");
- service->SetStateVariable("RecordStorageMedium", "NOT_IMPLEMENTED");
- service->SetStateVariable("RecordMediumWriteStatus", "NOT_IMPLEMENTED");
- // GetPositionInfo
- service->SetStateVariable("CurrentTrack", "0");
- NPT_Result durResult = service->SetStateVariable("CurrentTrackDuration", "00:00:00");
- service->SetStateVariable("CurrentTrackMetadata", "");
- service->SetStateVariable("CurrentTrackURI", "");
- NPT_Result relTimeResult = service->SetStateVariable("RelativeTimePosition", "00:00:00");
- service->SetStateVariable("AbsoluteTimePosition", "00:50:00");
- service->SetStateVariable("RelativeCounterPosition", "2147483647"); // means NOT_IMPLEMENTED
- service->SetStateVariable("AbsoluteCounterPosition", "2147483647"); // means NOT_IMPLEMENTED
- // disable indirect eventing for certain state variables
- PLT_StateVariable* var;
- var = service->FindStateVariable("RelativeTimePosition");
- if (var) var->DisableIndirectEventing();
- var = service->FindStateVariable("AbsoluteTimePosition");
- if (var) var->DisableIndirectEventing();
- var = service->FindStateVariable("RelativeCounterPosition");
- if (var) var->DisableIndirectEventing();
- var = service->FindStateVariable("AbsoluteCounterPosition");
- if (var) var->DisableIndirectEventing();
- // GetTransportInfo
- service->SetStateVariable("TransportState", "NO_MEDIA_PRESENT");
- service->SetStateVariable("TransportStatus", "OK");
- service->SetStateVariable("TransportPlaySpeed", "1");
- // GetTransportSettings
- service->SetStateVariable("CurrentPlayMode", "NORMAL");
- service->SetStateVariable("CurrentRecordQualityMode", "NOT_IMPLEMENTED");
- }
- {
- /* ConnectionManager */
- service = new PLT_Service(
- this,
- "urn:schemas-upnp-org:service:ConnectionManager:1",
- "urn:upnp-org:serviceId:ConnectionManager",
- "ConnectionManager");
- NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_ConnectionManagerSCPD));
- NPT_CHECK_FATAL(AddService(service));
- service->SetStateVariable("CurrentConnectionIDs", "0");
- // put all supported mime types here instead
- service->SetStateVariable("SinkProtocolInfo", "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_SP_G726,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMDRM_WMABASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPLL_BASE,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC_XAC3,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMDRM_WMVSPLL_BASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_BASE,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L5_SO_G726,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL_XAC3,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L4_SO_G726,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3X,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_MP3,http-get:*:video/x-ms-wmv:*,http-get:*:video/mp4:*");
- service->SetStateVariable("SourceProtocolInfo", "");
- }
- {
- /* RenderingControl */
- service = new PLT_Service(
- this,
- "urn:schemas-upnp-org:service:RenderingControl:1",
- "urn:upnp-org:serviceId:RenderingControl",
- "RenderingControl",
- "urn:schemas-upnp-org:metadata-1-0/RCS/");
- NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_RenderingControlSCPD));
- NPT_CHECK_FATAL(AddService(service));
- service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));
- service->SetStateVariable("Mute", "0");
- service->SetStateVariableExtraAttribute("Mute", "Channel", "Master");
- service->SetStateVariable("Volume", "100");
- service->SetStateVariableExtraAttribute("Volume", "Channel", "Master");
- service->SetStateVariable("VolumeDB", "0");
- service->SetStateVariableExtraAttribute("VolumeDB", "Channel", "Master");
- service->SetStateVariable("PresetNameList", "FactoryDefaults");
- }
- return NPT_SUCCESS;
- }
至此代码实现步骤已全部讲解完毕,当然在具体实现过程中多多少少会遇到些问题
根据log信息多问问谷哥度娘,一般都能找到解决方案的
同时在jni层转换C++类型与java类型时一定要注意及时释放资源,不要造成内存泄漏了
另外不要再向楼主索要源码了,编程最重要的是思维
只有自己动手实践了,摸索了,思考了,解决了,自身水平才会提高
否则你永远都只能是code-farmer而无法成为code-designer
授之以鱼不如授之予渔也是蓝老师一贯的教学宗旨,伸手党请自觉绕道
最后感谢大家的支持,我们下节课再见!

more brilliant,Please pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012
关于Platinum库的MediaRender具体C++代码实现探讨的更多相关文章
- 基于Platinum库的DMS实现(android)
接上篇博文:基于Platinum库的DMR实现(android) 文章讲述了如何使用Platinum库实现DMR 今天同样使用该库,来讲解一下DMS的实现 关于该库如何编译,请参考这篇博文:NDK下 ...
- [转]如何利用ndk-stack工具查看so库的调用堆栈【代码示例】?
如何利用ndk-stack工具查看so库的调用堆栈[代码示例]? http://hi.baidu.com/subo4110/item/d00395b3bf63e4432bebe36d Step1:An ...
- 在Team Foundation Server (TFS)的代码库或配置库中查找文件或代码
[update 2017.2.11] 最新版本的TFS 2017已经增加了代码搜索功能,可以参考这个链接 https://blogs.msdn.microsoft.com/visualstudioal ...
- php spl标准库简介(SPL是Standard PHP Library(PHP标准库)(直接看代码实例,特别方便)
php spl标准库简介(SPL是Standard PHP Library(PHP标准库)(直接看代码实例,特别方便) 一.总结 直接看代码实例,特别方便易懂 thinkphp控制器利眠宁不支持(说明 ...
- JavaScript公共库event-stream被植入恶意代码
[安全预警]JavaScript公共库event-stream被植入恶意代码 2018年11月27日,阿里云云盾应急响应中心监测到JavaScript公共库event-stream被植入恶意代码,该恶 ...
- AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码
目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告 AIR32F103(二) Linux环境和LibOpenCM3项目模板 AIR32F103(三) Linux环境基于标准外 ...
- jstl标签库基础教程及其使用代码
概述 在 JSP 页面中,使用标签库代替传统的 Java 片段语言来实现页面的显示逻辑已经不是新技术了,然而,由自定义标签很容易造成重复定义和非标准的实现.鉴于此,出现了 JSTL ( JSP Sta ...
- MVP模式, 开源库mosby的使用及代码分析
Android中的构架模式一直是一个很hot的topic, 近年来Architecture components推出之后, MVVM异军突起, 风头正在逐渐盖过之前的MVP. 其实我觉得MVP还是有好 ...
- x64共享库中的位置无关代码(PIC)
原作者:Eli Bendersky http://eli.thegreenplace.net/2011/11/11/position-independent-code-pic-in-shared-li ...
随机推荐
- 单击Android设备后退键,主屏幕键以及旋转屏幕如何影响Activity的生命周期
单击设备的后退键,相当于通知Android系统“我已完成activity的使用,现在不需要它了.”接到指令后,系统立即销毁了activity.即调用onPause()->onStop()-> ...
- Linux 静态库&动态库调用
1.什么是库在windows平台和linux平台下都大量存在着库.本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.由于windows和linux的本质不同,因此二者库的二进制是不 ...
- 1.0.x-学习Opencv与MFC混合编程之---视频运动检测
源代码地址: http://download.csdn.net/detail/nuptboyzhb/3961668 版本1.0.x新增内容 视频运动检测 Ø 新建菜单项,Learning OpenCV ...
- View实现涂鸦、撤销以及重做功能
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import j ...
- .net面试题大全(有答案)
在网上找来的,希望对大家有所帮助. 1 (1)面向对象的语言具有__继承性_性._封装性_性._多态性 性. (2)能用foreach遍历访问的对象需要实现 _ IEnumerable 接口或 ...
- 有没有安全的工作?(99条评论)——结论是没有一劳永逸的工作,要终身学习,IT业刚出道和老手还是有区别的(同样对于新技术,薪资可能是个问题)
作者: 阮一峰 日期: 2015年12月15日 如果你经常使用互联网,可能知道有一种东西叫做Flash. 它是一种软件,用来制作网页游戏.动画,以及视频播放器.只要观看网络视频,基本都会用到它. 七八 ...
- C2B未来:大数据定制
昨天看到微信SuperSofter写了一篇文章,有感而发.以便备记. 这是一种典型的C2B模式.阿里不仅仅是在与腾讯拼移动.它的电商本土业务也在稳步推进.近期一个里程碑事件是.阿里包下了美的.九阳.苏 ...
- 函数alv下的颜色设置
ABAP中的颜色代码是由4位字都组成的 cxyz c:color的简写,颜色代码均以C开头 x:标准色代码,SAP中一共有7个标准色 y:反转颜色启用/关闭 1/0 z:增强颜色启用/关闭 ...
- 【Demo 0006】iOS常用控件
本章学习要点 1. 了解iOS中控件继承关系: 2. 掌握UIControl基础知识; 3. 掌握UIButton基本用法: 4. 掌握UILa ...
- UVA 116 Unidirectional TSP(dp + 数塔问题)
Unidirectional TSP Background Problems that require minimum paths through some domain appear in ma ...