请尊重原创,转载请注明出处:http://blog.csdn.net/mabeijianxi/article/details/72904694

本人环境与工具:


  • ndk:r14
  • FFmpeg版本: 3.2.5
  • Android Studio: 2.3.2

一、说明:


本文是经过实战总结出的经验,本文将用两种方式编译可以在Android下执行命令的FFmpeg,一种是传统的ndk-build工具,一种是cmake工具,经过我的项目实战,非常推荐cmake,因为AS 2.2以后对它支持的非常好,你可以非常方便的像debug Java代码一样去debug Native代码。本文以是在假设已经编译好了全架构的 FFmpeg 的基础上进行的,编译步骤: 编译Android下可用的全平台FFmpeg(包含libx264与libfdk-aac)。

二、传统ndk-build命令编译


所谓传统,必然就稍微没那么智能了,我们一步一步的搞。

  1. 打开你养家糊口的Android Studio,娴熟的新建一个项目;
  2. 编写一个 native 函数,如果只是测试我们在MainActivity里面搞就行了:

    public native int ffmpegRun(String[] cmd);
  3. 新建jni目录,在目录下新建文件: jx_ffmpeg_cmd_run.c;

  4. 编码对应的 JNI 接口:

    #include <jni.h><br>
    JNIEXPORT jint JNICALL
    Java_com_mabeijianxi_jianxiffmpegcmd_MainActivity_ffmpegRun(JNIEnv *env, jobject instance, jobjectArray cmd) {// TODO
    }
  5. 找到我们FFmpeg编译后的根目录,然后 copy:
    cmdutils.c cmdutils.h cmdutils_common_opts.h config.h ffmpeg.c ffmpeg.h ffmpeg_filter.c ffmpeg_opt.c(注意需要编译后才会有config.h)到 jni 目录下,再进入到我们的编译后的产物目录,把include文件夹与所有的 .so 动态库也 copy 到jni目录下。完成后你jni目录结构应该如下图:

  6. 文件修改
    修改ffmpeg.c与ffmpeg.h
    找到ffmpeg.c,把int main(int argc, char argv) 改名为 int jxRun(int argc, char argv)
    找到ffmpeg.h, 在文件末尾添加函数申明: int jxRun(int argc, char **argv);

    1)修改cmdutils.c 和 cmdutils.h
    找到cmdutils.c中的exit_program函数
    修改前:

    修改后:

    2)找到cmdutils.h中exit_program的申明,也把返回类型修改为int。
    修改前:

    修改后:

    很多教程都只修改到这里,基本没什么问题,但是你实际运行的时候会发现如果连续多次执行命令会有问题的,通过源码我们可以知道,FFmpeg每次执行完命令后会调用 ffmpeg_cleanup 函数清理内存,并且会调用exit(0)结束当前进程,但是经过我们的修改,exit()的代码已经被删掉,我们在Android中自然不能结束当前进程了,所以有些变量的值还在内存中,这样就会导致下次执行的时候可能会出错。我也尝试过fork一个进程给ffmpeg执行,完事后通过 信号来进程间通信,这样管用但是很麻烦,我们其实只需要简单的重设一些变量即可。
    打开ffmpeg.c找到刚修改的jxRun函数,然后在 return 前加上如下代码即可:

         nb_filtergraphs = 0;progress_avio = NULL;input_streams = NULL;nb_input_streams = 0;input_files = NULL;nb_input_files = 0;output_streams = NULL;nb_output_streams = 0;output_files = NULL;nb_output_files = 0;
  7. 编写调用函数
    我们上面只在jx_ffmpeg_cmd_run.c新建了一个JNI接口函数,还没有实现逻辑,我们实现后的代码如下:

    **
    * Created by jianxi on 2017/6/4.
    * https://github.com/mabeijianxi
    * mabeijianxi@gmail.com
    *
    #include "ffmpeg.h"
    #include <jni.h>
    JNIEXPORT jint JNICALL
    Java_com_mabeijianxi_jianxiffmpegcmd_MainActivity_ffmpegRun(JNIEnv *env, jobject type,jobjectArray commands){int argc = *env)->GetArrayLength(env,commands);char *argv[argc];int i;for (i = 0; i < argc; i++) {jstring js = (jstring) (*env)->GetObjectArrayElement(env,commands, i);argv[i] = (char *) (*env)->GetStringUTFChars(env,js, 0);}return jxRun(argc,argv);
    }
  8. 编写Application.mk与Android.mk
    1)在jni目录下新建Application.mk与Android.mk
    2)编写Application.mk,内容如下:

    APP_ABI := armeabi-v7a
    APP_PLATFORM := android-14

    3)编码Android.mk,其内容如下(不明含义的可看Android下玩JNI的新老三种姿势

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := libavcodec
LOCAL_SRC_FILES := libavcodec.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := libavfilter
LOCAL_SRC_FILES := libavfilter.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := libavformat
LOCAL_SRC_FILES := libavformat.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := libavutil
LOCAL_SRC_FILES := libavutil.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := libswresample
LOCAL_SRC_FILES := libswresample.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := libswscale
LOCAL_SRC_FILES := libswscale.so
include $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_MODULE := jxffmpegrun
LOCAL_SRC_FILES := cmdutils.c ffmpeg.c ffmpeg_filter.c ffmpeg_opt.c jx_ffmpeg_cmd_run.c# 这里的地址改成自己的 FFmpeg 源码目录
LOCAL_C_INCLUDES :=/Users/jianxi/Downloads/code/ffmpeg-3.2.5
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES :=libavcodec libavfilter libavformat libavutil libswresample libswscale
include $(BUILD_SHARED_LIBRARY)

9. 开始编译:
我们打开终端,然后cd 到jni目录下,然后执行ndk-build,如果你没配置其环境变量,你选择写入ndk-build的全路径。
如果顺利你可以在命令结束后看到如下输出:

并且你的lib下会有如下产物:

这里就先不上测试的效果图了,等后面一起搞

三、cMake编译


有好多步骤都是很上面一样的,一会儿我就快乐的copy下来哈

  1. 打开你养家糊口的Android Studio,版本最好大于2.2,很关键。娴熟的新建一个项目,但是与以往不同,你最好勾选上 C++ 支持与 C++ standard选项时选择 C++ 11,如下图:

  2. 新建完成后你会发现帮你生成好了接口,这时我们只需修改native函数即可:
    修改前:

    public native String stringFromJNI();

    修改后

    public native int ffmpegRun(String[] cmd);
  3. 修改native文件与函数:
    你会发现这时多了一个cpp的文件夹,里面多了一个native-lib.cpp的文件,我们修改其名为jx_ffmpeg_cmd_run.c,然后修改里面的函数,修改后的函数应该如下:

    #include <jni.h>
    JNIEXPORT jint JNICALL
    Java_com_mabeijianxi_jianxiffmpegcmd_MainActivity_ffmpegRun(JNIEnv *env, jobject instance, jobjectArray cmd) {// TODO
    }
  4. 找到我们FFmpeg编译后的根目录,然后 copy:
    cmdutils.c cmdutils.h cmdutils_common_opts.h config.h ffmpeg.c ffmpeg.h ffmpeg_filter.c ffmpeg_opt.c(注意需要编译后才会有config.h)到 cpp 目录下,再进入到我们的编译后的产物目录,把include文件夹 copy 到cpp目录下。完成后你cpp目录结构应该如下图:
  1. 文件修改
    修改ffmpeg.c与ffmpeg.h
    找到ffmpeg.c,把int main(int argc, char argv) 改名为 int jxRun(int argc, char argv)
    找到ffmpeg.h, 在文件末尾添加函数申明: int jxRun(int argc, char **argv);

    1)修改cmdutils.c 和 cmdutils.h
    找到cmdutils.c中的exit_program函数
    修改前:

    修改后:

    2)找到cmdutils.h中exit_program的申明,也把返回类型修改为int。
    修改前:

    修改后:

    很多教程都只修改到这里,基本没什么问题,但是你实际运行的时候会发现如果连续多次执行命令会有问题的,通过源码我们可以知道,FFmpeg每次执行完命令后会调用 ffmpeg_cleanup 函数清理内存,并且会调用exit(0)结束当前进程,但是经过我们的修改,exit()的代码已经被删掉,我们在Android中自然不能结束当前进程了,所以有些变量的值还在内存中,这样就会导致下次执行的时候可能会出错。我也尝试过fork一个进程给ffmpeg执行,完事后通过 信号来进程间通信,这样管用但是很麻烦,我们其实只需要简单的重设一些变量即可。
    打开ffmpeg.c找到刚修改的jxRun函数,然后在 return 前加上如下代码即可:

         nb_filtergraphs = 0;progress_avio = NULL;input_streams = NULL;nb_input_streams = 0;input_files = NULL;nb_input_files = 0;output_streams = NULL;nb_output_streams = 0;output_files = NULL;nb_output_files = 0;
  2. 编写调用函数
    我们上面只在jx_ffmpeg_cmd_run.c新建了一个JNI接口函数,还没有实现逻辑,我们实现后的代码如下:

    \**
    \* Created by jianxi on 2017/6/4.
    \* https://github.com/mabeijianxi
    \* mabeijianxi@gmail.com
    */
    #include "ffmpeg.h"
    #include <jni.h>
    JNIEXPORT jint JNICALL
    Java_com_mabeijianxi_jianxiffmpegcmd_MainActivity_ffmpegRun(JNIEnv *env, jobject type,jobjectArray commands){int argc = (*env)->GetArrayLength(env,commands);char *argv[argc];int i;for (i = 0; i < argc; i++) {jstring js = (jstring) (*env)->GetObjectArrayElement(env,commands, i);argv[i] = (char *) (*env)->GetStringUTFChars(env,js, 0);}return jxRun(argc,argv);
    }
  3. 编写cmake编译脚本:
    在没有编写脚本的时候你的代码应该是一片红,没关系,马上我们就干掉它。
    打开你当前Module下的CMakeLists.txt然后填写如下脚本(内容解释可看Android下玩JNI的新老三种姿势):
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.add_library( # Sets the name of the library.jxffmpegrun# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/cmdutils.csrc/main/cpp/ffmpeg.csrc/main/cpp/ffmpeg_filter.csrc/main/cpp/ffmpeg_opt.csrc/main/cpp/jx_ffmpeg_cmd_run.c)
add_library(avcodecSHAREDIMPORTED)add_library(avfilterSHAREDIMPORTED)add_library(avformatSHAREDIMPORTED)add_library(avutilSHAREDIMPORTED)add_library(swresampleSHAREDIMPORTED)add_library(swscaleSHAREDIMPORTED)add_library(fdk-aacSHAREDIMPORTED)if(${ANDROID_ABI} STREQUAL "armeabi")
set_target_properties(avcodecPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libavcodec.so)set_target_properties(avfilterPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libavfilter.so)set_target_properties(avformatPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libavformat.so)set_target_properties(avutilPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libavutil.so)set_target_properties(swresamplePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libswresample.so)set_target_properties(swscalePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libswscale.so)set_target_properties(fdk-aacPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libfdk-aac.so)
endif(${ANDROID_ABI} STREQUAL "armeabi")if(${ANDROID_ABI} STREQUAL "armeabi-v7a")set_target_properties(avcodecPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavcodec.so)set_target_properties(avfilterPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavfilter.so)set_target_properties(avformatPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavformat.so)set_target_properties(avutilPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavutil.so)set_target_properties(swresamplePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libswresample.so)set_target_properties(swscalePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libswscale.so)set_target_properties(fdk-aacPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libfdk-aac.so)endif(${ANDROID_ABI} STREQUAL "armeabi-v7a")if(${ANDROID_ABI} STREQUAL "arm64-v8a")
set_target_properties(avcodecPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libavcodec.so)set_target_properties(avfilterPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libavfilter.so)set_target_properties(avformatPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libavformat.so)set_target_properties(avutilPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libavutil.so)set_target_properties(swresamplePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libswresample.so)set_target_properties(swscalePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libswscale.so)set_target_properties(fdk-aacPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libfdk-aac.so)
endif(${ANDROID_ABI} STREQUAL "arm64-v8a")if(${ANDROID_ABI} STREQUAL "x86")
set_target_properties(avcodecPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavcodec.so)set_target_properties(avfilterPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavfilter.so)set_target_properties(avformatPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavformat.so)set_target_properties(avutilPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavutil.so)set_target_properties(swresamplePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libswresample.so)set_target_properties(swscalePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libswscale.so)set_target_properties(fdk-aacPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libfdk-aac.so)
endif(${ANDROID_ABI} STREQUAL "x86")if(${ANDROID_ABI} STREQUAL "x86_64")
set_target_properties(avcodecPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libavcodec.so)set_target_properties(avfilterPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libavfilter.so)set_target_properties(avformatPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libavformat.so)set_target_properties(avutilPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libavutil.so)set_target_properties(swresamplePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libswresample.so)set_target_properties(swscalePROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libswscale.so)set_target_properties(fdk-aacPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86_64/libfdk-aac.so)
endif(${ANDROID_ABI} STREQUAL "x86_64")include_directories(/Users/jianxi/Downloads/code/ffmpeg-3.2.5/)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log )# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.jxffmpegrunfdk-aacavcodecavfilteravformatavutilswresampleswscale# Links the target library to the log library# included in the NDK.${log-lib} )

当然你还需要修改脚本里面的一些文件路径。

8 . 修改当前Module下的build.gradle脚本:
我所谓的全架构其实是排除了 mips 的,觉得没什么卵用,所以我们还需要添加过滤:
修改前:

修改后:

做到这步我们就可以直接点击同步按钮了:

完成后就不会再爆红了,这时候是只能直接运行安装的,但是还没有输入命令,所以没什么意义,接下来将开始使用我们做好的工具。

四、测试与使用


我们要用动态库肯定得放入默认目录,或者在gradle.build中修改其路径,这里我就直接放入
jniLibs 里面了,如图:

然后我就是在java里面调用了,搞了一个进度条,一个按钮。没有什么技术含量,直接贴代码了哈:

public class MainActivity extends AppCompatActivity {static {System.loadLibrary("jxffmpegrun");System.loadLibrary("avcodec");System.loadLibrary("avformat");System.loadLibrary("avutil");System.loadLibrary("swscale");System.loadLibrary("fdk-aac");}private ProgressBar pb;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);pb = (ProgressBar) findViewById(R.id.pb);}public void onClick(View v){pb.setVisibility(View.VISIBLE);new Thread(new Runnable() {@Overridepublic void run() {String basePath = Environment.getExternalStorageDirectory().getPath();String cmd_transcoding = String.format("ffmpeg -i %s -c:v libx264 %s  -c:a libfdk_aac %s",basePath+"/"+"girl.mp4","-crf 40",basePath+"/"+"my_girl.mp4");int i = jxFFmpegCMDRun(cmd_transcoding);new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() {pb.setVisibility(View.GONE);Toast.makeText(MainActivity.this,"ok了",Toast.LENGTH_SHORT).show();}});}}).start();}public  int jxFFmpegCMDRun(String cmd){String regulation="[ \\t]+";final String[] split = cmd.split(regulation);return ffmpegRun(split);}public native int ffmpegRun(String[] cmd);
}

再上面代码中我们指定编码器压缩了一个mp4的文件,压缩前叫girl.mp4,压缩后叫my_girl.mp4,如图所示,我们发现其缩小了近80%。

五、总结


这里输入路径不能每次是同一个,不然会报错,也就是说,运行了一次上面的命令后,输出的名字不能再叫my_girl.mp4了,你可以叫your_girl.mp4。然后还是那句话推荐用cMake,你看我们通过cMake编译可以直接debug native

是不是很hi,然后我们在开发中可能会发现有些命令执行不了,这时候你首先需要检查命令,然后确保没问题后,你需要确定你在编译FFmpeg的时候是否开启了此功能,很关键,确定办法很简单,一是检查FFmpeg的编译脚本,二是通过FFmpeg的函数获取编译信息,我工程里面已经内含这个jni接口,有兴趣的基友可以下载来try一try。如果有兴趣进一步探索FFmpeg在Android上的实战运用,可以看我的开源项目和其讲解博客,分别是:https://github.com/mabeijianxi/small-video-record、利用FFmpeg玩转Android视频录制与压缩(一)、利用FFmpeg玩转Android视频录制与压缩(二)。

本文所有源码地址:https://github.com/mabeijianxi/FFmpeg4Android

编译Android下可执行命令的FFmpeg相关推荐

  1. 编译Android下可用的全平台FFmpeg(包含libx264与libfdk-aac)

    请尊重原创,转载请注明出处:http://blog.csdn.net/mabeijianxi/article/details/74544879 源码或工具版本: ndk :r14 FFmpeg 版本: ...

  2. python编译exe文件太大了_python编译windows下可执行的exe文件

    python编译windows下可执行的exe文件的操作流程: 1.在安装python环境的windows下,安装 pyinstaller,请在dos下执行如下命令: pip install pyin ...

  3. java test abc_DOS命令行下成功执行命令:java Test abc 21,则以下描述正确的是()

    [单选题]假设有两个整数x和y,表达式x>=y?x:y的含义是() [单选题]下列哪项不是Java语言的关键字?() [判断题]每项经济业务发生后,都会引起企业的资产和权益总额发生增减变动,但会 ...

  4. 安卓和Linux动态库一样吗,在Linux环境下编译Android下的最新版ffmpeg+x264单个动态库(.so)...

    最近在mac下用ndk交叉编译最ffmpeg出问题,总是显示用系统的gcc而不是ndk的toolchain的交叉编译gcc来编译的.之前明明没问题的,可能是由于最近升级macOS导致的.由于对这方面实 ...

  5. android 命令kill进程号,一定是我打开的姿势不对——Android下使用kill命令杀死指定进程...

    环境:Android 5.1.1(已root) 最近有一个需求,需要在Android下杀死指定pid的进程.Android原生API提供了几个接口: (1)android.os.Process.kil ...

  6. mac下编译android下libaac,Mac系统下编译FFmpeg(支持libx264)for iOS

    前言 iOS平台的FFmpeg(支持libx264)已经编译完成.经过之前Android平台上的编译过程,iOS平台的编译总体来说少走了很多弯路,但还是遇到了一些问题,现在做一下总结. 一.编译x26 ...

  7. 在linux下编译android下的opencv,使用cmake的方法

    #前一篇帖子实验了build_sdk.py来编译opencv,失败了. #本篇尝试使用cmake来编译 #感谢这篇帖子提供的指导:https://www.cnblogs.com/jojodru/p/1 ...

  8. Android下的 iptables命令的用法

    netfilter/iptables(简称为iptables)组成Linux平台下的包过滤防火墙,与大多数的Linux软件一样,这个包过滤防火墙是免费的,它可以代替昂贵的商业防火墙解决方案,完成封包过 ...

  9. android studio 退出编译,Android studio用什么命令可以退出当前应用

    最佳答案 Android Studio 启动速度优化1.开启 gradle 单独的守护进程当 Android Studio 遇到错误的时候,往往会导致 Android Studio 挂掉,为了防止退出 ...

最新文章

  1. 算法工程师当前选哪个方向好?
  2. iOS10 UI教程管理层次结构
  3. 图像处理之ROI区域裁剪
  4. Spark之GraphX
  5. 为什么坐飞机时会耳痛?
  6. Azure Cosmos DB从零到10分钟
  7. html css 圆形按钮 仿uc,10款基于jquery的web前端动画特效
  8. java 手势识别_【人体分析-手势识别】-Java示例代码
  9. LSTM 文本分类模型的实现
  10. 详细安装WIN 10纯净版(虚拟机安装、真机笔记本安装)
  11. c3300 京瓷km5050_京瓷复印机通用维修代码2
  12. C语言打印输出图形(初版)
  13. 不踩雷不将就 京东智能产品30天无忧退
  14. gps串口通信程序c语言,stm32单片机串口接收GPS数据并解析NMEA之GPRMC
  15. 科技大停滞--过去已逝,未来未来
  16. Win7系统电脑调节屏幕亮度的几种方法。
  17. 学硕上几年学计算机,研究生一般要读几年毕业
  18. spring配置与监听mysql_spring boot (8)mybatis配置监听,
  19. w7电脑蓝屏怎么解决_详解win7电脑蓝屏怎么办
  20. 2020年下半年教育热点事件舆情分析研判报告整合

热门文章

  1. Java实现简单的个人网络嗅探器
  2. EPSON 4750 打印机清理
  3. 面试太诚实居然被淘汰?真的不公平!
  4. 服务器mtbf性能测试,云服务器 MTBF指标
  5. 如何实现消息通知系统
  6. GET http://localhost:8080/api/getArticle 504 (Gateway Timeout) 或者404错误
  7. 终于有大佬把计算机底层原理全部总结出来了
  8. java(中国邮政储蓄银行个人网上银行系统项目实训)
  9. 面试阿里,HashMap 这一篇就够了
  10. A*算法(五):在三维地图的可行性