• NEON在Android中的使用举例【转】


    1、  打开Eclipse,File-->New-->AndroidApplication Project-->Application Name:Hello-Neon, Project Name: Hello-Neon,Package Name:com.hello_neon.Android, Minimum Required SDK:API 9:Android 2.3(Gingerbread),Next-->去掉Create custom launcher icon的勾选,Next-->Next-->ActivityName:Hello_NeonProjectActivity,Finish.

    2、  打开Hello-Neon工程下的src-->com.hello_neon.android-->Hello_NeonProjectActivity.Java,将其内容改为:

    [java] view plain copy
     
    1. package com.hello_neon.android;  
    2.   
    3. import android.os.Bundle;  
    4. import android.app.Activity;  
    5. import android.widget.TextView;  
    6.   
    7. public class Hello_NeonProjectActivity extends Activity {  
    8.   
    9.     /** Called when the activity is first created. */  
    10.     @Override  
    11.     public void onCreate(Bundle savedInstanceState)  
    12.     {  
    13.         super.onCreate(savedInstanceState);  
    14.         /* Create a TextView and set its content. 
    15.          * the text is retrieved by calling a native function. 
    16.          */  
    17.         TextView  tv = new TextView(this);  
    18.         tv.setText( stringFromJNI() );  
    19.         setContentView(tv);  
    20.     }  
    21.   
    22.     /* A native method that is implemented by the 
    23.      * 'helloneon' native library, which is packaged with this application. 
    24.      */  
    25.     public native String  stringFromJNI();  
    26.   
    27.     /* this is used to load the 'helloneon' library on application 
    28.      * startup. The library has already been unpacked into 
    29.      * /data/data/com.example.neon/lib/libhelloneon.so at 
    30.      * installation time by the package manager. 
    31.      */  
    32.     static {  
    33.         System.loadLibrary("helloneon");  
    34.     }  
    35.   
    36. }  

    3、 保存Hello_NeonProjectActivity.java文件,打开命令行窗口,将其定位到inclasses目录下,输入命令:javah –classpath D:ProgramFilesAndroidandroid-sdkplatformsandroid-9android.jar;com.hello_neon.android.Hello_NeonProjectActivity ,会在inclasses目录下生成com_hello_neon_android_Hello_NeonProjectActivity.h文件(说明:*.jar也可以是其它版本);

    4、  选中Hello-Neon工程,点击右键-->New-->Folder新建一个jni文件夹,在此文件夹下添加Android.mk、Application.mk、helloneon.c、helloneon-intrinsics.c、helloneon-intrinsics.h五个文件,其中内容分别为:

    Android.mk:

    [html] view plain copy
     
    1. LOCAL_PATH := $(call my-dir)  
    2. include $(CLEAR_VARS)  
    3. LOCAL_MODULE    := helloneon  
    4.   
    5. #填写要编译的源文件路径  
    6. LOCAL_SRC_FILES := helloneon.c helloneon-intrinsics.c  
    7.   
    8. #默认包含的头文件路径  
    9. LOCAL_C_INCLUDES :=   
    10. $(LOCAL_PATH)   
    11. $(LOCAL_PATH)/..  
    12.   
    13. #-g 后面的一系列项目添加了才能使用arm_neon-h头文件, -mfloat-abi=softfp -mfpu=neon 使用arm_neon.h必须  
    14. LOCAL_CFLAGS := -g -mfloat-abi=softfp -mfpu=neon -march=armv7-a -mtune=cortex-a8  
    15.   
    16. LOCAL_LDLIBS := -lz -llog  
    17. TARGET_ARCH_ABI := armeabi-v7a   
    18. LOCAL_ARM_MODE := arm  
    19.   
    20. ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)  
    21. #采用NEON优化技术  
    22.     LOCAL_ARM_NEON := true  
    23.     #LOCAL_CFLAGS := -DHAVE_NEON=1  
    24. endif  
    25.   
    26. LOCAL_STATIC_LIBRARIES := cpufeatures  
    27.   
    28. #生成动态调用库  
    29. include $(BUILD_SHARED_LIBRARY)  
    30.   
    31. $(call import-module,cpufeatures)  

    Application.mk:
    [html] view plain copy
     
    1. APP_PROJECT_PATH := $(call my-dir)/..  
    2. APP_PLATFORM := android-10  
    3. #choose which library to compile against in your Makefile  
    4. APP_STL := stlport_static  
    5. #APP_ABI这句指定了编译的目标平台类型,可以针对不同平台进行优化,x86 or armeabi-v7a  
    6. # Build both ARMv5TE and ARMv7-A machine code.  
    7. APP_ABI := armeabi armeabi-v7a  
    8. APP_CPPFLAGS += -fexceptions  
    9. #for using c++ features,you need to enable these in your Makefile  
    10. APP_CPP_FEATURES += exceptions rtti  

    helloneon.c:
    [cpp] view plain copy
     
    1. #include <jni.h>  
    2. #include <time.h>  
    3. #include <stdio.h>  
    4. #include <stdlib.h>  
    5. #include <cpu-features.h>  
    6. #include "helloneon-intrinsics.h"  
    7.   
    8. #define DEBUG 0  
    9. #define HAVE_NEON  
    10.   
    11. #if DEBUG  
    12. #include <android/log.h>  
    13. #  define  D(x...)  __android_log_print(ANDROID_LOG_INFO,"helloneon",x)  
    14. #else  
    15. #  define  D(...)  do {} while (0)  
    16. #endif  
    17.   
    18. /* return current time in milliseconds */  
    19. static double  
    20. now_ms(void)  
    21. {  
    22.     struct timespec res;  
    23.     clock_gettime(CLOCK_REALTIME, &res);  
    24.     return 1000.0*res.tv_sec + (double)res.tv_nsec/1e6;  
    25. }  
    26.   
    27.   
    28. /* this is a FIR filter implemented in C */  
    29. static void  
    30. fir_filter_c(short *output, const short* input, const short* kernel, int width, int kernelSize)  
    31. {  
    32.     int  offset = -kernelSize/2;  
    33.     int  nn;  
    34.     for (nn = 0; nn < width; nn++) {  
    35.         int sum = 0;  
    36.         int mm;  
    37.         for (mm = 0; mm < kernelSize; mm++) {  
    38.             sum += kernel[mm]*input[nn+offset+mm];  
    39.         }  
    40.         output[nn] = (short)((sum + 0x8000) >> 16);  
    41.     }  
    42. }  
    43.   
    44. #define  FIR_KERNEL_SIZE   32  
    45. #define  FIR_OUTPUT_SIZE   2560  
    46. #define  FIR_INPUT_SIZE    (FIR_OUTPUT_SIZE + FIR_KERNEL_SIZE)  
    47. #define  FIR_ITERATIONS    600  
    48.   
    49. static const short  fir_kernel[FIR_KERNEL_SIZE] = {  
    50.     0x10, 0x20, 0x40, 0x70, 0x8c, 0xa2, 0xce, 0xf0, 0xe9, 0xce, 0xa2, 0x8c, 070, 0x40, 0x20, 0x10,  
    51.     0x10, 0x20, 0x40, 0x70, 0x8c, 0xa2, 0xce, 0xf0, 0xe9, 0xce, 0xa2, 0x8c, 070, 0x40, 0x20, 0x10 };  
    52.   
    53. static short        fir_output[FIR_OUTPUT_SIZE];  
    54. static short        fir_input_0[FIR_INPUT_SIZE];  
    55. static const short* fir_input = fir_input_0 + (FIR_KERNEL_SIZE/2);  
    56. static short        fir_output_expected[FIR_OUTPUT_SIZE];  
    57.   
    58. /* This is a trivial JNI example where we use a native method 
    59.  * to return a new VM String. See the corresponding Java source 
    60.  * file located at: 
    61.  * 
    62.  *   apps/samples/hello-neon/project/src/com/example/neon/HelloNeon.java 
    63.  */  
    64. JNIEXPORT jstring JNICALL Java_com_hello_1neon_android_Hello_1NeonProjectActivity_stringFromJNI(JNIEnv *env, jobject thiz)  
    65. {  
    66.     char*  str;  
    67.     uint64_t features;  
    68.     char buffer[512];  
    69.     char tryNeon = 0;  
    70.     double  t0, t1, time_c, time_neon;  
    71.   
    72.     /* setup FIR input - whatever */  
    73.     {  
    74.         int  nn;  
    75.         for (nn = 0; nn < FIR_INPUT_SIZE; nn++) {  
    76.             fir_input_0[nn] = (5*nn) & 255;  
    77.         }  
    78.         fir_filter_c(fir_output_expected, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    79.     }  
    80.   
    81.     /* Benchmark small FIR filter loop - C version */  
    82.     t0 = now_ms();  
    83.     {  
    84.         int  count = FIR_ITERATIONS;  
    85.         for (; count > 0; count--) {  
    86.             fir_filter_c(fir_output, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    87.         }  
    88.     }  
    89.     t1 = now_ms();  
    90.     time_c = t1 - t0;  
    91.   
    92.     asprintf(&str, "FIR Filter benchmark: C version          : %g ms ", time_c);  
    93.     strlcpy(buffer, str, sizeof buffer);  
    94.     free(str);  
    95.   
    96.     strlcat(buffer, "Neon version   : ", sizeof buffer);  
    97.   
    98.     if (android_getCpuFamily() != ANDROID_CPU_FAMILY_ARM) {  
    99.         strlcat(buffer, "Not an ARM CPU ! ", sizeof buffer);  
    100.         goto EXIT;  
    101.     }  
    102.   
    103.     features = android_getCpuFeatures();  
    104.     if ((features & ANDROID_CPU_ARM_FEATURE_ARMv7) == 0) {  
    105.         strlcat(buffer, "Not an ARMv7 CPU ! ", sizeof buffer);  
    106.         goto EXIT;  
    107.     }  
    108.   
    109.     /* HAVE_NEON is defined in Android.mk ! */  
    110. #ifdef HAVE_NEON  
    111.     if ((features & ANDROID_CPU_ARM_FEATURE_NEON) == 0) {  
    112.         strlcat(buffer, "CPU doesn't support NEON ! ", sizeof buffer);  
    113.         goto EXIT;  
    114.     }  
    115.   
    116.     /* Benchmark small FIR filter loop - Neon version */  
    117.     t0 = now_ms();  
    118.     {  
    119.         int  count = FIR_ITERATIONS;  
    120.         for (; count > 0; count--) {  
    121.             fir_filter_neon_intrinsics(fir_output, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    122.         }  
    123.     }  
    124.     t1 = now_ms();  
    125.     time_neon = t1 - t0;  
    126.     asprintf(&str, "%g ms (x%g faster) ", time_neon, time_c / (time_neon < 1e-6 ? 1. : time_neon));  
    127.     strlcat(buffer, str, sizeof buffer);  
    128.     free(str);  
    129.   
    130.     /* check the result, just in case */  
    131.     {  
    132.         int  nn, fails = 0;  
    133.         for (nn = 0; nn < FIR_OUTPUT_SIZE; nn++) {  
    134.             if (fir_output[nn] != fir_output_expected[nn]) {  
    135.                 if (++fails < 16)  
    136.                     D("neon[%d] = %d expected %d", nn, fir_output[nn], fir_output_expected[nn]);  
    137.             }  
    138.         }  
    139.         D("%d fails ", fails);  
    140.     }  
    141. #else /* !HAVE_NEON */  
    142.     strlcat(buffer, "Program not compiled with ARMv7 support ! ", sizeof buffer);  
    143. #endif /* !HAVE_NEON */  
    144. EXIT:  
    145.     return (*env)->NewStringUTF(env, buffer);  
    146. }  

    helloneon-intrinsics.h:

    [cpp] view plain copy
     
    1. #ifndef HELLONEON_INTRINSICS_H  
    2. #define HELLONEON_INTRINSICS_H  
    3.   
    4. void fir_filter_neon_intrinsics(short *output, const short* input, const short* kernel, int width, int kernelSize);  
    5.   
    6. #endif /* HELLONEON_INTRINSICS_H */  

    helloneon-intrinsics.c:
    [cpp] view plain copy
     
    1. #include "helloneon-intrinsics.h"  
    2. #include <arm_neon.h>  
    3.   
    4. /* this source file should only be compiled by Android.mk when targeting 
    5.  * the armeabi-v7a ABI, and should be built in NEON mode 
    6.  */  
    7. void  
    8. fir_filter_neon_intrinsics(short *output, const short* input, const short* kernel, int width, int kernelSize)  
    9. {  
    10. #if 1  
    11.    int nn, offset = -kernelSize/2;  
    12.   
    13.    for (nn = 0; nn < width; nn++)  
    14.    {  
    15.         int mm, sum = 0;  
    16.         int32x4_t sum_vec = vdupq_n_s32(0);  
    17.         for(mm = 0; mm < kernelSize/4; mm++)  
    18.         {  
    19.             int16x4_t  kernel_vec = vld1_s16(kernel + mm*4);  
    20.             int16x4_t  input_vec = vld1_s16(input + (nn+offset+mm*4));  
    21.             sum_vec = vmlal_s16(sum_vec, kernel_vec, input_vec);  
    22.         }  
    23.   
    24.         sum += vgetq_lane_s32(sum_vec, 0);  
    25.         sum += vgetq_lane_s32(sum_vec, 1);  
    26.         sum += vgetq_lane_s32(sum_vec, 2);  
    27.         sum += vgetq_lane_s32(sum_vec, 3);  
    28.   
    29.         if(kernelSize & 3)  
    30.         {  
    31.             for(mm = kernelSize - (kernelSize & 3); mm < kernelSize; mm++)  
    32.                 sum += kernel[mm] * input[nn+offset+mm];  
    33.         }  
    34.   
    35.         output[nn] = (short)((sum + 0x8000) >> 16);  
    36.     }  
    37. #else /* for comparison purposes only */  
    38.     int nn, offset = -kernelSize/2;  
    39.     for (nn = 0; nn < width; nn++) {  
    40.         int sum = 0;  
    41.         int mm;  
    42.         for (mm = 0; mm < kernelSize; mm++) {  
    43.             sum += kernel[mm]*input[nn+offset+mm];  
    44.         }  
    45.         output[nn] = (short)((sum + 0x8000) >> 16);  
    46.     }  
    47. #endif  
    48. }  

    5、 利用NDK生成.so文件:选中工程-->Properties-->Builders-->New-->选中Program-->OK,Name:Hello_Neon_Builder,Location: D:ProgramFilesAndroidandroid-sdkandroid-ndk-r9 dk-build.cmd,Working Directory: E:NEONEclipseHello-Neon -->Apply,选中Refresh,勾选Refreshresources upon completion, 勾选Specific resources,点击Specify Resources…,勾选Hello-Neon工程下的libs文件夹,Finish-->Apply,选中BuildOptions,勾选Allocate Console(necessary for input), After a “Clean”, During manualbuilds, During auto builds, Specify working set of relevant resources,点击SpecifyResoures…,勾选Hello-Neon工程下的jni文件夹,Finish-->Apply-->OK-->OK,会在libs文件夹下生成libhelloneon.so文件;

    6、  选中Hello-Neon,-->Run As-->AndroidApplication,运行结果为:

    FIRFilter benchmark:

    C version       :282.84 ms

    Neon version    :135985 ms(x2.07994 faster)

    以上是.c文件的操作步骤,若将.c文件该为.cpp文件,则需改动两个文件:

    1、将Android.mk改为:
    [html] view plain copy
     
    1. LOCAL_PATH := $(call my-dir)  
    2. include $(CLEAR_VARS)  
    3. LOCAL_MODULE    := helloneon  
    4.   
    5. #填写要编译的源文件路径  
    6. LOCAL_SRC_FILES := helloneon.cpp helloneon-intrinsics.cpp  
    7.   
    8. #默认包含的头文件路径  
    9. LOCAL_C_INCLUDES :=   
    10. $(LOCAL_PATH)   
    11. $(LOCAL_PATH)/..  
    12.   
    13. #-g 后面的一系列项目添加了才能使用arm_neon-h头文件, -mfloat-abi=softfp -mfpu=neon 使用arm_neon.h必须  
    14. LOCAL_CFLAGS := -g -mfloat-abi=softfp -mfpu=neon -march=armv7-a -mtune=cortex-a8  
    15.   
    16. LOCAL_LDLIBS := -lz -llog  
    17. TARGET_ARCH_ABI := armeabi-v7a   
    18. LOCAL_ARM_MODE := arm  
    19.   
    20. ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)  
    21. #采用NEON优化技术  
    22.     LOCAL_ARM_NEON := true  
    23.     #LOCAL_CFLAGS := -DHAVE_NEON=1  
    24. endif  
    25.   
    26. LOCAL_STATIC_LIBRARIES := cpufeatures  
    27.   
    28. #生成动态调用库  
    29. include $(BUILD_SHARED_LIBRARY)  
    30.   
    31. $(call import-module,cpufeatures)  

    2、helloneon.c改为:
    [cpp] view plain copy
     
    1. #include <jni.h>  
    2. #include <time.h>  
    3. #include <stdio.h>  
    4. #include <stdlib.h>  
    5. #include <cpu-features.h>  
    6. #include "helloneon-intrinsics.h"  
    7.   
    8. #define DEBUG 0  
    9. #define HAVE_NEON  
    10.   
    11. #ifdef __cplusplus  
    12. extern "C" {  
    13. #endif  
    14.   
    15. #if DEBUG  
    16. #include <android/log.h>  
    17. #  define  D(x...)  __android_log_print(ANDROID_LOG_INFO,"helloneon",x)  
    18. #else  
    19. #  define  D(...)  do {} while (0)  
    20. #endif  
    21.   
    22. /* return current time in milliseconds */  
    23. static double  
    24. now_ms(void)  
    25. {  
    26.     struct timespec res;  
    27.     clock_gettime(CLOCK_REALTIME, &res);  
    28.     return 1000.0*res.tv_sec + (double)res.tv_nsec/1e6;  
    29. }  
    30.   
    31.   
    32. /* this is a FIR filter implemented in C */  
    33. static void  
    34. fir_filter_c(short *output, const short* input, const short* kernel, int width, int kernelSize)  
    35. {  
    36.     int  offset = -kernelSize/2;  
    37.     int  nn;  
    38.     for (nn = 0; nn < width; nn++) {  
    39.         int sum = 0;  
    40.         int mm;  
    41.         for (mm = 0; mm < kernelSize; mm++) {  
    42.             sum += kernel[mm]*input[nn+offset+mm];  
    43.         }  
    44.         output[nn] = (short)((sum + 0x8000) >> 16);  
    45.     }  
    46. }  
    47.   
    48. #define  FIR_KERNEL_SIZE   32  
    49. #define  FIR_OUTPUT_SIZE   2560  
    50. #define  FIR_INPUT_SIZE    (FIR_OUTPUT_SIZE + FIR_KERNEL_SIZE)  
    51. #define  FIR_ITERATIONS    600  
    52.   
    53. static const short  fir_kernel[FIR_KERNEL_SIZE] = {  
    54.     0x10, 0x20, 0x40, 0x70, 0x8c, 0xa2, 0xce, 0xf0, 0xe9, 0xce, 0xa2, 0x8c, 070, 0x40, 0x20, 0x10,  
    55.     0x10, 0x20, 0x40, 0x70, 0x8c, 0xa2, 0xce, 0xf0, 0xe9, 0xce, 0xa2, 0x8c, 070, 0x40, 0x20, 0x10 };  
    56.   
    57. static short        fir_output[FIR_OUTPUT_SIZE];  
    58. static short        fir_input_0[FIR_INPUT_SIZE];  
    59. static const short* fir_input = fir_input_0 + (FIR_KERNEL_SIZE/2);  
    60. static short        fir_output_expected[FIR_OUTPUT_SIZE];  
    61.   
    62. /* This is a trivial JNI example where we use a native method 
    63.  * to return a new VM String. See the corresponding Java source 
    64.  * file located at: 
    65.  * 
    66.  *   apps/samples/hello-neon/project/src/com/example/neon/HelloNeon.java 
    67.  */  
    68. JNIEXPORT jstring JNICALL Java_com_hello_1neon_android_Hello_1NeonProjectActivity_stringFromJNI(JNIEnv *env, jobject thiz)  
    69. {  
    70.     char str[512] = {0};  
    71.     uint64_t features;  
    72.     char buffer[512];  
    73.     char tryNeon = 0;  
    74.     double  t0, t1, time_c, time_neon;  
    75.   
    76.     /* setup FIR input - whatever */  
    77.     {  
    78.         int  nn;  
    79.         for (nn = 0; nn < FIR_INPUT_SIZE; nn++) {  
    80.             fir_input_0[nn] = (5*nn) & 255;  
    81.         }  
    82.         fir_filter_c(fir_output_expected, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    83.     }  
    84.   
    85.     /* Benchmark small FIR filter loop - C version */  
    86.     t0 = now_ms();  
    87.     {  
    88.         int  count = FIR_ITERATIONS;  
    89.         for (; count > 0; count--) {  
    90.             fir_filter_c(fir_output, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    91.         }  
    92.     }  
    93.     t1 = now_ms();  
    94.     time_c = t1 - t0;  
    95.   
    96.     sprintf(str, "FIR Filter benchmark: C version          : %g ms ", time_c);  
    97.     strlcpy(buffer, str, sizeof buffer);  
    98.   
    99.     strlcat(buffer, "Neon version   : ", sizeof buffer);  
    100.   
    101.     if (android_getCpuFamily() != ANDROID_CPU_FAMILY_ARM) {  
    102.         strlcat(buffer, "Not an ARM CPU ! ", sizeof buffer);  
    103.         goto EXIT;  
    104.     }  
    105.   
    106.     features = android_getCpuFeatures();  
    107.     if ((features & ANDROID_CPU_ARM_FEATURE_ARMv7) == 0) {  
    108.         strlcat(buffer, "Not an ARMv7 CPU ! ", sizeof buffer);  
    109.         goto EXIT;  
    110.     }  
    111.   
    112.     /* HAVE_NEON is defined in Android.mk ! */  
    113. #ifdef HAVE_NEON  
    114.     if ((features & ANDROID_CPU_ARM_FEATURE_NEON) == 0) {  
    115.         strlcat(buffer, "CPU doesn't support NEON ! ", sizeof buffer);  
    116.         goto EXIT;  
    117.     }  
    118.   
    119.     /* Benchmark small FIR filter loop - Neon version */  
    120.     t0 = now_ms();  
    121.     {  
    122.         int  count = FIR_ITERATIONS;  
    123.         for (; count > 0; count--) {  
    124.             fir_filter_neon_intrinsics(fir_output, fir_input, fir_kernel, FIR_OUTPUT_SIZE, FIR_KERNEL_SIZE);  
    125.         }  
    126.     }  
    127.     t1 = now_ms();  
    128.     time_neon = t1 - t0;  
    129.     sprintf(str, "%g ms (x%g faster) ", time_neon, time_c / (time_neon < 1e-6 ? 1. : time_neon));  
    130.     strlcat(buffer, str, sizeof buffer);  
    131.   
    132.     /* check the result, just in case */  
    133.     {  
    134.         int  nn, fails = 0;  
    135.         for (nn = 0; nn < FIR_OUTPUT_SIZE; nn++) {  
    136.             if (fir_output[nn] != fir_output_expected[nn]) {  
    137.                 if (++fails < 16)  
    138.                     D("neon[%d] = %d expected %d", nn, fir_output[nn], fir_output_expected[nn]);  
    139.             }  
    140.         }  
    141.         D("%d fails ", fails);  
    142.     }  
    143. #else /* !HAVE_NEON */  
    144.     strlcat(buffer, "Program not compiled with ARMv7 support ! ", sizeof buffer);  
    145. #endif /* !HAVE_NEON */  
    146. EXIT:  
    147.     return env->NewStringUTF(buffer);  
    148. }  
    149.   
    150. #ifdef __cplusplus  
    151. }  
    152. #endif  


    参考文献:

    1、  http://blog.csdn.net/fengbingchun/article/details/11580983

    2、  android-ndk-r9-windows-x86_64中的hello-neon例子代码

  • 相关阅读:
    子类继承方法的重写
    操作系统的用户模式和内核模式
    Java中的CAS
    FaceBook SDK登录功能实现(Eclipse)
    eclipse集成ijkplayer项目
    android handler传递数据
    android发送短信
    hadoop中的job.setOutputKeyClass与job.setMapOutputKeyClass
    mysql对事务的支持
    使用jd-gui+javassist修改已编译好的class文件
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5694598.html
Copyright © 2020-2023  润新知