关于Platinum库的MediaRender具体C++代码实现探讨

 
分类: android JNI platinum   1395人阅读  评论(10)  收藏  举报

接上篇博文 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

[cpp]  view plain copy
  1. #include <assert.h>  
  2. #include <jni.h>  
  3. #include <string.h>  
  4. #include <sys/types.h>  
  5.   
  6. #include "platinum-jni.h"  
  7. #include "Platinum.h"  
  8.   
  9. #include <android/log.h>  
  10.   
  11. /*---------------------------------------------------------------------- 
  12. |   logging 
  13. +---------------------------------------------------------------------*/  
  14. NPT_SET_LOCAL_LOGGER("platinum.android.jni")  
  15.   
  16. /*---------------------------------------------------------------------- 
  17. |   functions 
  18. +---------------------------------------------------------------------*/  
  19. __attribute__((constructor)) static void onDlOpen(void)  
  20. {  
  21. }  
  22.   
  23. /*---------------------------------------------------------------------- 
  24. |    JNI_OnLoad 
  25. +---------------------------------------------------------------------*/  
  26. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)  
  27. {  
  28.     NPT_LogManager::GetDefault().Configure("plist:.level=FINE;.handlers=ConsoleHandler;.ConsoleHandler.outputs=2;.ConsoleHandler.colors=false;.ConsoleHandler.filter=59");  
  29.     return JNI_VERSION_1_4;  
  30. }  
  31.   
  32. /* 
  33.  * Class:     com_plutinosoft_platinum_UPnP 
  34.  * Method:    _init 
  35.  * Signature: ()J 
  36.  */  
  37. JNIEXPORT jlong JNICALL Java_com_plutinosoft_platinum_UPnP__1init(JNIEnv *env, jclass)  
  38. {  
  39.     NPT_LOG_INFO("init");  
  40.     PLT_UPnP* self = new PLT_UPnP();  
  41.     return (jlong)self;  
  42. }  
  43.   
  44. /* 
  45.  * Class:     com_plutinosoft_platinum_UPnP 
  46.  * Method:    _start 
  47.  * Signature: (J)I 
  48.  */  
  49. JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1start(JNIEnv *, jclass, jlong _self)  
  50. {  
  51.     NPT_LOG_INFO("start");  
  52.     PLT_UPnP* self = (PLT_UPnP*)_self;  
  53.       
  54.     return self->Start();  
  55. }  
  56.   
  57. /* 
  58.  * Class:     com_plutinosoft_platinum_UPnP 
  59.  * Method:    _stop 
  60.  * Signature: (J)I 
  61.  */  
  62. JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1stop(JNIEnv *, jclass, jlong _self)  
  63. {  
  64.     NPT_LOG_INFO("stop");  
  65.     PLT_UPnP* self = (PLT_UPnP*)_self;  
  66.       
  67.     return self->Stop();  
  68. }  



MediaRendererTest.cpp

[cpp]  view plain copy
  1. #include "PltUPnP.h"  
  2. #include "PltMediaRenderer.h"  
  3.   
  4. #include <stdlib.h>  
  5.   
  6. /*---------------------------------------------------------------------- 
  7. |   globals 
  8. +---------------------------------------------------------------------*/  
  9. struct Options {  
  10.     const char* friendly_name;  
  11. } Options;  
  12.   
  13. /*---------------------------------------------------------------------- 
  14. |   PrintUsageAndExit 
  15. +---------------------------------------------------------------------*/  
  16. static void  
  17. PrintUsageAndExit(char** args)  
  18. {  
  19.     fprintf(stderr, "usage: %s [-f <friendly_name>]\n", args[0]);  
  20.     fprintf(stderr, "-f : optional upnp server friendly name\n");  
  21.     fprintf(stderr, "<path> : local path to serve\n");  
  22.     exit(1);  
  23. }  
  24.   
  25. /*---------------------------------------------------------------------- 
  26. |   ParseCommandLine 
  27. +---------------------------------------------------------------------*/  
  28. static void  
  29. ParseCommandLine(char** args)  
  30. {  
  31.     const char* arg;  
  32.     char**      tmp = args+1;  
  33.   
  34.     /* default values */  
  35.     Options.friendly_name = NULL;  
  36.   
  37.     while ((arg = *tmp++)) {  
  38.         if (!strcmp(arg, "-f")) {  
  39.             Options.friendly_name = *tmp++;  
  40.         } else {  
  41.             fprintf(stderr, "ERROR: too many arguments\n");  
  42.             PrintUsageAndExit(args);  
  43.         }  
  44.     }  
  45. }  
  46.   
  47. /*---------------------------------------------------------------------- 
  48. |   main 
  49. +---------------------------------------------------------------------*/  
  50. int  
  51. main(int /* argc */char** argv)  
  52. {     
  53.     PLT_UPnP upnp;  
  54.   
  55.     /* parse command line */  
  56.     ParseCommandLine(argv);  
  57.   
  58.     PLT_DeviceHostReference device(  
  59.         new PLT_MediaRenderer(Options.friendly_name?Options.friendly_name:"Platinum Media Renderer",  
  60.                               false,  
  61.                               "e6572b54-f3c7-2d91-2fb5-b757f2537e21"));  
  62.     upnp.AddDevice(device);  
  63.     bool added = true;  
  64.   
  65.     upnp.Start();  
  66.   
  67.     char buf[256];  
  68.     while (gets(buf)) {  
  69.         if (*buf == 'q')  
  70.             break;  
  71.   
  72.         if (*buf == 's') {  
  73.             if (added) {  
  74.                 upnp.RemoveDevice(device);  
  75.             } else {  
  76.                 upnp.AddDevice(device);  
  77.             }  
  78.             added = !added;  
  79.         }  
  80.     }  
  81.   
  82.     upnp.Stop();  
  83.     return 0;  
  84. }<span style="color:#ff0000;">  
  85. </span>  

可以看出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);方法就会被调用

[cpp]  view plain copy
  1. NPT_Result  
  2. PLT_MediaRenderer::OnAction(PLT_ActionReference&          action,   
  3.                             const PLT_HttpRequestContext& context)  
  4. {  
  5.     NPT_COMPILER_UNUSED(context);  
  6.   
  7.     /* parse the action name */  
  8.     NPT_String name = action->GetActionDesc().GetName();  
  9.   
  10.     // since all actions take an instance ID and we only support 1 instance  
  11.     // verify that the Instance ID is 0 and return an error here now if not  
  12.     NPT_String serviceType = action->GetActionDesc().GetService()->GetServiceType();  
  13.     if (serviceType.Compare("urn:schemas-upnp-org:service:AVTransport:1"true) == 0) {  
  14.         if (NPT_FAILED(action->VerifyArgumentValue("InstanceID""0"))) {  
  15.             action->SetError(718, "Not valid InstanceID");  
  16.             return NPT_FAILURE;  
  17.         }  
  18.     }  
  19.     serviceType = action->GetActionDesc().GetService()->GetServiceType();  
  20.     if (serviceType.Compare("urn:schemas-upnp-org:service:RenderingControl:1"true) == 0) {  
  21.         if (NPT_FAILED(action->VerifyArgumentValue("InstanceID""0"))) {  
  22.             action->SetError(702, "Not valid InstanceID");  
  23.             return NPT_FAILURE;  
  24.         }  
  25.     }  
  26.   
  27.     /* Is it a ConnectionManager Service Action ? */  
  28.     if (name.Compare("GetCurrentConnectionInfo"true) == 0) {  
  29.         return OnGetCurrentConnectionInfo(action);  
  30.     }    
  31.   
  32.     /* Is it a AVTransport Service Action ? */  
  33.     if (name.Compare("Next"true) == 0) {  
  34.         return OnNext(action);  
  35.     }  
  36.     if (name.Compare("Pause"true) == 0) {  
  37.         return OnPause(action);  
  38.     }  
  39.     if (name.Compare("Play"true) == 0) {  
  40.         return OnPlay(action);  
  41.     }  
  42.     if (name.Compare("Previous"true) == 0) {  
  43.         return OnPrevious(action);  
  44.     }  
  45.     if (name.Compare("Seek"true) == 0) {  
  46.         return OnSeek(action);  
  47.     }  
  48.     if (name.Compare("Stop"true) == 0) {  
  49.         return OnStop(action);  
  50.     }  
  51.     if (name.Compare("SetAVTransportURI"true) == 0) {  
  52.         return OnSetAVTransportURI(action);  
  53.     }  
  54.     if (name.Compare("SetPlayMode"true) == 0) {  
  55.         return OnSetPlayMode(action);  
  56.     }  
  57.   
  58.     /* Is it a RendererControl Service Action ? */  
  59.     if (name.Compare("SetVolume"true) == 0) {  
  60.           return OnSetVolume(action);  
  61.     }  
  62.     if (name.Compare("SetVolumeDB"true) == 0) {  
  63.         return OnSetVolumeDB(action);  
  64.     }  
  65.     if (name.Compare("GetVolumeDBRange"true) == 0) {  
  66.         return OnGetVolumeDBRange(action);  
  67.   
  68.     }  
  69.     if (name.Compare("SetMute"true) == 0) {  
  70.           return OnSetMute(action);  
  71.     }  
  72.   
  73.     // other actions rely on state variables  
  74.     NPT_CHECK_LABEL_WARNING(action->SetArgumentsOutFromStateVariable(), failure);  
  75.     return NPT_SUCCESS;  
  76.   
  77. failure:  
  78.     action->SetError(401,"No Such Action.");  
  79.     return NPT_FAILURE;  
  80. }  

部分事件会由m_Delegate成员变量来处理,也就是 virtual void SetDelegate(PLT_MediaRendererDelegate* delegate) { m_Delegate = delegate; }传进来的

所以我们只需要从PLT_MediaRendererDelegate类派生一个子类,然后将它设进PLT_MediaRenderer并实现相应的action方法并把一些需要的值反射出来即可

[cpp]  view plain copy
  1. class PLT_MediaRendererDelegate  
  2. {  
  3. public:  
  4.     virtual ~PLT_MediaRendererDelegate() {}  
  5.   
  6.     // ConnectionManager  
  7.     virtual NPT_Result OnGetCurrentConnectionInfo(PLT_ActionReference& action) = 0;  
  8.   
  9.     // AVTransport  
  10.     virtual NPT_Result OnNext(PLT_ActionReference& action) = 0;  
  11.     virtual NPT_Result OnPause(PLT_ActionReference& action) = 0;  
  12.     virtual NPT_Result OnPlay(PLT_ActionReference& action) = 0;  
  13.     virtual NPT_Result OnPrevious(PLT_ActionReference& action) = 0;  
  14.     virtual NPT_Result OnSeek(PLT_ActionReference& action) = 0;  
  15.     virtual NPT_Result OnStop(PLT_ActionReference& action) = 0;  
  16.     virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action) = 0;  
  17.     virtual NPT_Result OnSetPlayMode(PLT_ActionReference& action) = 0;  
  18.   
  19.     // RenderingControl  
  20.     virtual NPT_Result OnSetVolume(PLT_ActionReference& action) = 0;  
  21.     virtual NPT_Result OnSetVolumeDB(PLT_ActionReference& action) = 0;  
  22.     virtual NPT_Result OnGetVolumeDBRange(PLT_ActionReference& action) = 0;  
  23.     virtual NPT_Result OnSetMute(PLT_ActionReference& action) = 0;  
  24. };  


[cpp]  view plain copy
  1. void ActionInflect(int cmd, const char* value, const char* data)  
  2. {  
  3.   
  4.     if (g_vm == NULL)  
  5.     {  
  6.         UpnpPrintInfo("g_vm = NULL!!!");  
  7.         return ;  
  8.     }  
  9.   
  10.   
  11.     int status;  
  12.     JNIEnv *env = NULL;  
  13.     bool isAttach = false;  
  14.     status = g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);  
  15.     if(status != JNI_OK)   
  16.     {  
  17.         status = g_vm->AttachCurrentThread(&env, NULL);  
  18.         if(status < 0) {  
  19.             UpnpPrintInfo("callback_handler: failed to attach , current thread, status = %d", status);  
  20.             return;  
  21.         }  
  22.         isAttach = true;  
  23.     }  
  24.   
  25.     jstring valueString = NULL;  
  26.     jstring dataString = NULL;  
  27.     jclass inflectClass = g_inflectClass;  
  28.     jmethodID inflectMethod = g_methodID;  
  29.   
  30.     if (inflectClass == NULL || inflectMethod == NULL)  
  31.     {  
  32.         goto end;  
  33.     }  
  34.   
  35.   
  36. //  UpnpPrintInfo("CMD = %d\nVALUE = %s\nDATA = %s",cmd, value, data);  
  37.       
  38.     valueString = env->NewStringUTF(value);  
  39.     dataString = env->NewStringUTF(data);  
  40.   
  41.     env->CallStaticVoidMethod(inflectClass, inflectMethod, cmd, valueString, dataString);  
  42.   
  43.     env->DeleteLocalRef(valueString);  
  44.     env->DeleteLocalRef(dataString);  
  45.   
  46. end:  
  47.     if (env->ExceptionOccurred())  
  48.     {  
  49.         env->ExceptionDescribe();  
  50.         env->ExceptionClear();  
  51.     }  
  52.     if (isAttach)  
  53.     {  
  54.         g_vm->DetachCurrentThread();  
  55.     }  
  56.   
  57.   
  58.       
  59.   
  60. }  

再看第二点,如何将事件变量值更新至服务列表

首先通过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()方法

[cpp]  view plain copy
  1. NPT_Result  
  2. PLT_MediaRenderer::SetupServices()  
  3. {  
  4.     PLT_Service* service;  
  5.   
  6.     {  
  7.         /* AVTransport */  
  8.         service = new PLT_Service(  
  9.             this,  
  10.             "urn:schemas-upnp-org:service:AVTransport:1",   
  11.             "urn:upnp-org:serviceId:AVTransport",  
  12.             "AVTransport",  
  13.             "urn:schemas-upnp-org:metadata-1-0/AVT/");  
  14.         NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_AVTransportSCPD));  
  15.         NPT_CHECK_FATAL(AddService(service));  
  16.   
  17.         service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));  
  18.         service->SetStateVariable("A_ARG_TYPE_InstanceID""0");   
  19.   
  20.         // GetCurrentTransportActions  
  21.         service->SetStateVariable("CurrentTransportActions""Play,Pause,Stop,Seek,Next,Previous");  
  22.   
  23.         // GetDeviceCapabilities  
  24.         service->SetStateVariable("PossiblePlaybackStorageMedia""NONE,NETWORK,HDD,CD-DA,UNKNOWN");  
  25.         service->SetStateVariable("PossibleRecordStorageMedia""NOT_IMPLEMENTED");  
  26.         service->SetStateVariable("PossibleRecordQualityModes""NOT_IMPLEMENTED");  
  27.   
  28.         // GetMediaInfo  
  29.         service->SetStateVariable("NumberOfTracks""0");  
  30.         service->SetStateVariable("CurrentMediaDuration""00:00:00");  
  31.         service->SetStateVariable("AVTransportURI""");  
  32.         service->SetStateVariable("AVTransportURIMetadata""");;  
  33.         service->SetStateVariable("NextAVTransportURI""NOT_IMPLEMENTED");  
  34.         service->SetStateVariable("NextAVTransportURIMetadata""NOT_IMPLEMENTED");  
  35.         service->SetStateVariable("PlaybackStorageMedium""NONE");  
  36.         service->SetStateVariable("RecordStorageMedium""NOT_IMPLEMENTED");  
  37.         service->SetStateVariable("RecordMediumWriteStatus""NOT_IMPLEMENTED");  
  38.   
  39.         // GetPositionInfo  
  40.         service->SetStateVariable("CurrentTrack""0");  
  41.         NPT_Result durResult = service->SetStateVariable("CurrentTrackDuration""00:00:00");  
  42.         service->SetStateVariable("CurrentTrackMetadata""");  
  43.         service->SetStateVariable("CurrentTrackURI""");  
  44.         NPT_Result relTimeResult = service->SetStateVariable("RelativeTimePosition""00:00:00");   
  45.         service->SetStateVariable("AbsoluteTimePosition""00:50:00");  
  46.         service->SetStateVariable("RelativeCounterPosition""2147483647"); // means NOT_IMPLEMENTED  
  47.         service->SetStateVariable("AbsoluteCounterPosition""2147483647"); // means NOT_IMPLEMENTED  
  48.   
  49.         // disable indirect eventing for certain state variables  
  50.         PLT_StateVariable* var;  
  51.         var = service->FindStateVariable("RelativeTimePosition");  
  52.         if (var) var->DisableIndirectEventing();  
  53.         var = service->FindStateVariable("AbsoluteTimePosition");  
  54.         if (var) var->DisableIndirectEventing();  
  55.         var = service->FindStateVariable("RelativeCounterPosition");  
  56.         if (var) var->DisableIndirectEventing();  
  57.         var = service->FindStateVariable("AbsoluteCounterPosition");  
  58.         if (var) var->DisableIndirectEventing();  
  59.   
  60.         // GetTransportInfo  
  61.         service->SetStateVariable("TransportState""NO_MEDIA_PRESENT");  
  62.         service->SetStateVariable("TransportStatus""OK");  
  63.         service->SetStateVariable("TransportPlaySpeed""1");  
  64.   
  65.         // GetTransportSettings  
  66.         service->SetStateVariable("CurrentPlayMode""NORMAL");  
  67.         service->SetStateVariable("CurrentRecordQualityMode""NOT_IMPLEMENTED");  
  68.     }  
  69.   
  70.     {  
  71.         /* ConnectionManager */  
  72.         service = new PLT_Service(  
  73.             this,  
  74.             "urn:schemas-upnp-org:service:ConnectionManager:1",   
  75.             "urn:upnp-org:serviceId:ConnectionManager",  
  76.             "ConnectionManager");  
  77.         NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_ConnectionManagerSCPD));  
  78.         NPT_CHECK_FATAL(AddService(service));  
  79.   
  80.         service->SetStateVariable("CurrentConnectionIDs""0");  
  81.   
  82.         // put all supported mime types here instead  
  83.         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:*");  
  84.         service->SetStateVariable("SourceProtocolInfo""");  
  85.     }  
  86.   
  87.     {  
  88.         /* RenderingControl */  
  89.         service = new PLT_Service(  
  90.             this,  
  91.             "urn:schemas-upnp-org:service:RenderingControl:1",   
  92.             "urn:upnp-org:serviceId:RenderingControl",  
  93.             "RenderingControl",  
  94.             "urn:schemas-upnp-org:metadata-1-0/RCS/");  
  95.         NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_RenderingControlSCPD));  
  96.         NPT_CHECK_FATAL(AddService(service));  
  97.   
  98.         service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));  
  99.   
  100.         service->SetStateVariable("Mute""0");  
  101.         service->SetStateVariableExtraAttribute("Mute""Channel""Master");  
  102.         service->SetStateVariable("Volume""100");  
  103.         service->SetStateVariableExtraAttribute("Volume""Channel""Master");  
  104.         service->SetStateVariable("VolumeDB""0");  
  105.         service->SetStateVariableExtraAttribute("VolumeDB""Channel""Master");  
  106.   
  107.         service->SetStateVariable("PresetNameList""FactoryDefaults");  
  108.     }  
  109.   
  110.     return NPT_SUCCESS;  
  111. }  

至此代码实现步骤已全部讲解完毕,当然在具体实现过程中多多少少会遇到些问题

根据log信息多问问谷哥度娘,一般都能找到解决方案的

同时在jni层转换C++类型与java类型时一定要注意及时释放资源,不要造成内存泄漏了

另外不要再向楼主索要源码了,编程最重要的是思维

只有自己动手实践了,摸索了,思考了,解决了,自身水平才会提高

否则你永远都只能是code-farmer而无法成为code-designer

授之以鱼不如授之予渔也是蓝老师一贯的教学宗旨,伸手党请自觉绕道

最后感谢大家的支持,我们下节课再见!


more brilliantPlease pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012 







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值