精华内容
参与话题
问答
  • Android 平台的Python——基础篇(一)

    万次阅读 多人点赞 2017-11-18 22:26:08
    要想在Android平台运行起Python,也有方案实现,其实质就是在Android系统上搭建Python环境。对此Google已经提供了SL4A(Scripting Layer for Android )方案,支持多种脚本语言,除此之外,还可以使用一个

    Android 平台的Python——基础篇(一)
    Android 平台的Python——JNI方案(二)
    Android 平台的Python——CLE方案实现(三)
    Android 平台的Python——第三方库移植
    Android 平台的Python——编译Python解释器
    新篇——Android与Python混合编程

    前言
    (本文以Python3为例,Python3是未来,大家都懂的)
    Python作为一个功能强大又语法简洁的语言,其应用已无需多言。要想在Android平台运行起Python,也有方案实现,其实质就是在Android系统上搭建Python环境。对此Google已经提供了SL4A(Scripting Layer for Android )方案,支持多种脚本语言,除此之外,还可以使用一个叫QPython的app,可以直接在Android上编写以及运行Python代码。但其实意义不大,写好的Python代码并不是以一个独立的app进程运行的,只不过是在QPython这个应用中运行而已。这两者都不符合我现在要讨论的东西,如题,笔者想要讨论的是如何在Android平台使用Java与Python代码相互调用,换言之,就是如何在Android工程中嵌入一个Python解释器。

    首先谈一点,为什么要在Android平台使用Python?Python拥有众多强大的第三方库和框架,在机器学习、大数据处理等诸多方面都有不俗的应用。另外,就语法而言,Python比Java更加简洁,同时又功能强大,既可面向过程亦可面向对象,而不像Java一样,是一种纯粹的面向对象语言,哪怕打印一句话也需要先创建类。Python作为一种脚本语言,可以边解释边执行,而不需编译,另外Python中存在的元类,可以使我们动态的创建类,如此可以在不需要重新编译安装apk的情况下,动态的由远程服务端为Android项目添加功能。我们还可以将Python已有的一些东西移植到Android平台,例如tornado、django等,总之玩法多多。

    在Android平台,官方并不支持直接使用Python开发app,基于虚拟机的Java(或kotlin)才是更好的选择,其他语言是无法自如的使用官方Framework提供的api的,尤其是在程序界面的表现上,典型的反例就是kivy。什么是kivy,可自行了解,但要解决Android平台上Java与Python的交互,kivy确实是一个方向,而且是一个醍醐灌顶的方向。kivy实际上已经解决我们需要实现的目的,模仿Android平台上的kivy实现机制即可。但是,kivy使用了大量的Cython技术,而非CPython API接口,需要学习Cython语法,并且在其他一些方面存在一些限制。kivy给我们提供的思路就是借助Java的jni机制,实现Python与Java的交互。即在一个安卓apk工程中包含一个cython.so解释器,通过jni机制调用解释器去解释执行Python代码,通过Java调C,C调Python实现交互。有一点需要说明,Python作为一门胶水语言,Python与C的交互是非常方便的,因此才能实现这一系列调用。

    关于该种方案,已有国外网友实践,原理如下

    这里写图片描述

    链接地址

    除此之外,本博客将通过另外两种方案实现。其中第一种类似上述方案,但集成CPython解释器,非Cython,因此需要掌握如何实现Python与C的交互。

    Python与C交互基础

    C调用Python

    • 简单使用
      流程:

      • 初始化Python解析器
      • 执行Python代码,字符串,对象或模块。
      • 关闭Python解析器。

      创建一个.c源文件,代码如下,创建一个pytest.py文件,实现一个printTime函数

    	#include<Python.h>
    	int main()
    	{
    	    Py_Initialize();//初始化Python解析器
    	    if (!Py_IsInitialized())
    	    {
    		    printf("Initialize failed");
    		      return -1;
    	    }
    	    PyRun_SimpleString("print('hello C !')");
    	    PyRun_SimpleString("import pytest");
    	    PyRun_SimpleString("pytest.printTime()");
    	    Py_Finalize();/关闭Python解析器
    	    return 0;
    	}		
    

    注意:除了用PyRun_SimpleString函数直接运行代码,还可以使用PyRun_SimpleFile函数运行一个Python脚本
    原型:PyRun_SimpleFile(FILE *fp, const char *filename) ,由于版本差异,使用该方式可能会造成崩溃,推荐另一种替代方式
    PyRun_SimpleString(“execfile(“test.py”)”)

    • 调用Python函数
      pytest.py

      import time
      
      def printTime():
          print('invoke printTime:'+str(time.time()))
          return (1,)#元组只有一个元素时,需在末尾加逗号
      

      C 代码

      int main()
      {
      	PyObject * module_name,*module,*func,*dic;
      	char * fun_name = "printTime";//需调用的Python函数名
      	PyObject *resultValue;
      
      	Py_Initialize();
      	if (!Py_IsInitialized())
      	{
      		printf("Initialize failed");
      		return -1;
      	}
      
      	//导入Python 模块并检验
      	module_name = Py_BuildValue("s", "pytest");
      	module = PyImport_Import(module_name);
      
      
      	if (!module)
      	{
      		printf("import test failed!");
      		return -1;
      	}
      	
          //获取模块中的函数列表,是一个函数名和函数地址对应的字典结构
          dic = PyModule_GetDict(module);
      	if (!dic)
      	{
      		printf("failed !\n");
      		return -1;
      	}
      
      	func = PyDict_GetItemString(dic, fun_name);
      	if (!PyCallable_Check(func))
      	{
      		printf("not find %s\n", fun_name);
      		return -1;
      	}
      
      	int r;
          //获取Python函数返回值,是一个元组对象
      	resultValue = PyObject_CallObject(func, NULL);
      	PyArg_ParseTuple(resultValue, "i", &r);
      	printf("result :%d\n", r);
      
      	Py_DECREF(module);
          Py_DECREF(dic);
      	Py_Finalize();
      	return 0;
      }
      

    基础API

    C API 调用 Python 对应
    PyImport_ImportModel import module
    PyImport_ReloadModule reload(module)
    PyImport_GetModuleDict module._dict_
    PyDict_GetItemString dict[key]
    PyDict_SetItemString dict[key] = value
    PyDict_New dict = {}
    PyObject_GetAttrString getattr(obj, attr)
    PyObject_SetAttrString setattr(obj, attr, val)
    PyObject_CallObject funcobj(*argstuple)
    PyEval_CallObject funcobj(*argstuple)
    PyRun_String eval(exprstr) , exec(stmtstr)
    PyRun_File exec(open(filename().read())
    • Py_BuildValue()函数
      作用:将C/C++类型类型的数据转变成PyObject*对象。
      原型:PyAPI_FUNC(PyObject*) Py_BuildValue(const char *format, ...);

      参数解释:
      format及转换格式,类似与C语言中%d,%f,后面的不定参数对应前面的格式,具体格式如下:

      “s”(string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。

      “s#”(string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。

      “z”(string or None) [char *] :作用同"s"。

      “z#” (stringor None) [char *, int] :作用同"s#"。

      “i”(integer) [int] :将一个C类型的int转换成Python int对象。

      “b”(integer) [char] :作用同"i"。

      “h”(integer) [short int] :作用同"i"。

      “l”(integer) [long int] :将C类型的long转换成Pyhon中的int对象。

      “c”(string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。

      “d”(float) [double] :将C类型的double转换成python中的浮点型对象。

      “f”(float) [float] :作用同"d"。

      “O&”(object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数被调用并且返回一个新的Python对象,如果发生错误返回NULL。

      “(items)”(tuple) [matching-items] :将一系列的C值转换成Python元组。

      “[items]”(list) [matching-items] :将一系列的C值转换成Python列表。

      “{items}”(dictionary) [matching-items] :将一系类的C值转换成Python的字典,每一对连续的C值将转换成一个键值对。

      例:
      后面为PyObject的返回值

            Py_BuildValue("")None
      
            Py_BuildValue("i",123) 123
      
            Py_BuildValue("iii",123, 456, 789) (123, 456, 789)
      
            Py_BuildValue("s","hello") 'hello'
      
            Py_BuildValue("ss","hello", "world") ('hello', 'world')
      
            Py_BuildValue("s#","hello", 4) 'hell'
      
            Py_BuildValue("()")()
      
            Py_BuildValue("(i)",123) (123,)     
      
            Py_BuildValue("(ii)",123, 456) (123, 456)
      
            Py_BuildValue("(i,i)",123, 456) (123, 456)
      
            Py_BuildValue("[i,i]",123, 456) [123, 456]      Py_BuildValue("{s:i,s:i}", "abc",123, "def", 456) {'abc': 123, 'def': 456}
      
            Py_BuildValue("((ii)(ii))(ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
      
    • PyArg_ParseTuple函数
      作用:此函数其实相当于sscanf(str,format,…),是Py_BuildValue的逆过程,这个函数将PyObject参数转换成C/C++数据类型,传递的是指针,但这个函数与Py_BuildValue有点不同,这个函数只能解析Tuple元组,而Py_BuildValue函数可以生成元组,列表,字典等。
      原型:PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *args, const char *format,...)

        Args:一般为Python程序返回的元组。
         
        Foramt:与Py_BulidValue类型,就不在累述咯。
         
        元组操作函数:
        因为程序之间传递的参数,大多数为Tuple类型,所以有专门的函数来操作元组:
         
        PyAPI_FUNC(PyObject *)PyTuple_New(Py_ssize_t size);
        解释:新建一个参数列表(调试了下,发现其实是用链表实现的),size列表为长度的宽度
         
        PyAPI_FUNC(Py_ssize_t)PyTuple_Size(PyObject *);
        解释:获取该列表的大小
         
        PyAPI_FUNC(PyObject *)PyTuple_GetItem(PyObject *, Py_ssize_t);
        解释:获取该列表某位置的值
         
        PyAPI_FUNC(int) PyTuple_SetItem(PyObject *,Py_ssize_t, PyObject *);
        解释:设置该列表此位置的值。如PyTuple_SetItem(pyParams,1,Py_BuildValue("i",2));设置第2个位置的值为2的整数。	
      

      备注:对应的列表和字典也有对应的操作

    更多的接口调用以及数据类型转化,参照Python文档
    这里写图片描述

    Python 调用C

    Python调用C有两种方式

    • 使用ctypes模块,Python文档有详细示例
      这里写图片描述

    • 使用C为Python编写拓展模块
      Python之所以如此强大,正是由于可以使用C\C++为其编写拓展模块,手动编写拓展模块的方式稍微有些繁琐,可借用SWIG自动实现,简洁快速。更多详细的SWIG用法,见其官方文档
      官网下载 windows包并解压

      使用vs创建空项目,并配置vs。右键当前项目,选择属性
      这里写图片描述
      现在使用C为Python创建一个叫user的拓展模块,该模块包含一个showHello函数:
      分别创建三个文件
      user.i
      user.c
      user_wrap.c

    在user.i中添加如下代码

    	%module user
    	
    	%inline %{
    	extern void showHello();
    	%}
    

    user.c中添加

    	#include <stdio.h>
    	void showHello()
    	{
    		printf("hello Python!\n");
    	}
    

    右键user.i 文件并选择属性
    这里写图片描述
    点击应用后如下图,完成配置
    这里写图片描述
    右键当前项目,选择属性,完成如下配置,确定
    这里写图片描述
    最后生成即可(选择工具栏 生成 --> 批生成)

    创建测试代码调用C验证

    import user
    user.show()
    

    在Linux下则无需如此麻烦的配置,可直接使用命令

    On Unix the compilation of examples is done using the file Example/Makefile. This makefile performs a manual module compilation which is platform specific. Typically, the steps look like this (Linux):
    
    
    % swig -python interface.i
    % gcc -fpic -c interface_wrap.c -I/usr/local/include/python1.5
    % gcc -shared interface_wrap.o $(OBJS) -o interfacemodule.so 
    % python
    Python 1.5.2 (#3, Oct  9 1999, 22:09:34)  [GCC 2.95.1 19990816 (release)] on linux2
    Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
    >>> import interface
    >>> interface.blah(...)
    

    此处.i文件为SWIG的接口文件,其中%module后面定义模块名,用%inline定义方法列表

    %inline %{
    包含导出的函数
    %}
    

    有了Python与C的交互基础,则还需要Android中的NDK开发基础,关于Android平台的jni调用,本文不在此处详解,可看看我的JNI方面博客,而此处我们需要使用Crystax NDK开发工具链,非官方NDK工具链,需自行下载。下一篇正式涉及Python for Android。

    关注个人公众号:编程之路从0到1

    编程之路从0到1

    展开全文
  • Android平台漏洞挖掘与利用

    千人学习 2015-10-30 15:33:25
    Android平台漏洞挖掘与利用视频教程,该课程将介绍几类漏洞的特征以及利用方法。本次课程将以Qualcomm平台上的CVE-2013-6123漏洞为例,介绍如何找出危险的数据交换代码,以及如何稳定利用任意地址写漏洞。也将介绍...
  • Android平台的崩溃捕获机制及实现

    千人学习 2015-12-31 10:20:10
    Android平台的崩溃捕获机制视频教程,该课程主要对Java崩溃和Native崩溃的捕获机制进行分析,并结合简单的实例演示实现方法。 讲师介绍:贾志凯,Testin崩溃分析项目研发工程师,负责客户端SDK相关技术研发工作。5...
  • FFmpeg的Android平台移植—编译篇

    万次阅读 多人点赞 2014-04-01 19:11:20
    FFmpeg的Android平台移植—编译篇 Dennis Hu 2014年3月28日 摘要:本文主要介绍将FFmpeg音视频编解码库移植到Android平台上的编译和基本测试过程。 环境准备: Ubuntu12.04 TLS android-ndk-r9d-linux-x86_64.tar.bz...

    FFmpeg的Android平台移植—编译篇

    Dennis Hu 2014年3月28日

    摘要:本文主要介绍将FFmpeg音视频编解码库移植到Android平台上的编译和基本测试过程。

    环境准备:

    Ubuntu12.04 TLS

    android-ndk-r9d-linux-x86_64.tar.bz2

    adt-bundle-windows-x86_64-20131030.zip


    第一步:源代码下载

    到FFmpeg官方网站http://www.ffmpeg.org/上去下载源代码,这里下载的源代码是最权威的。进入官网之后,选择”Download”进入下载页面,截止2014年3月28日止,最新的发布的稳定版本为FFmpeg2.2,代号”Muybridge”。选择该下方的”Downloadgzip tarball”进行下载,下载后的文件名为ffmpeg-2.2.tar.gz,大约8.3M。

    第二步:在Linux环境下编译FFmpeg

    在Windows平台可以采用VMplayer虚拟机上安装ubuntu的方式,本人也是采用这种方式。

    本文以/home/dennis为根目录进行操作和说明:

    将ffmpeg-2.2.tar.gz拷贝至根目录,然后执行如下解压命令将其解压:

    $tar zxf ffmpeg-2.2.tar.gz

    解压后将得到/home/dennis/ffmpeg-2.2目录。

     

    修改ffmpeg-2.2/configure文件

    如果直接按照未修改的配置进行编译,结果编译出来的so文件类似libavcodec.so.55.39.101,版本号位于so之后,Android上似乎无法加载。因此需要按如下修改:

    将该文件中的如下四行:

    SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'

    LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'

    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'

    SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'

    替换为:

    SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'

    LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'

    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'

    SLIB_INSTALL_LINKS='$(SLIBNAME)'

     

    编写build_android.sh脚本文件

    FFmpeg可以说是一个包络音视频编解码及格式的超级霸。因此在编译前通常都需要进行配置,设置相应的环境变量等。

    所有的配置选项都在ffmpeg-2.2/configure这个脚本文件中,可以通过执行如下命令来查看所有的配置选项:

    $ ./configure –help

    配置选项很多,也较为复杂,这里先把我需要的搞出来,然后有时间再慢慢看。

    我们将需要的配置项和环境变量设置写成一个sh脚本文件来运行以便编译出Android平台需要的so文件出来。

    build_android.sh的内容如下:

    #!/bin/bash
    NDK=/home/dennis/android-ndk-r9d
    SYSROOT=$NDK/platforms/android-9/arch-arm/
    TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
    
    function build_one
    {
    ./configure \
        --prefix=$PREFIX \
        --enable-shared \
        --disable-static \
        --disable-doc \
        --disable-ffserver \
        --enable-cross-compile \
        --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
        --target-os=linux \
        --arch=arm \
        --sysroot=$SYSROOT \
        --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
        --extra-ldflags="$ADDI_LDFLAGS" \
        $ADDITIONAL_CONFIGURE_FLAG
    }
    CPU=arm
    PREFIX=$(pwd)/android/$CPU
    ADDI_CFLAGS="-marm"
    build_one

    这个脚本文件有几个地方需要注意:

    (1)    NDK,SYSROOT和TOOLCHAIN这三个环境变量一定要换成你自己机器里的。

    (2)    确保cross-prefix变量所指向的路径是存在的。

    给build_android.sh增加可执行权限:

    $chmod+x build_android.sh

    执行build_android.sh

    $./build_android.sh

    配置该脚本完成对ffmpeg的配置,会生成config.h等配置文件,后面的编译会用到。如果未经过配置直接进行编译会提示无法找到config.h文件等错误。

    $make
    $make install

    至此,会在/home/dennis/ffmpeg-2.2目录下生成一个android目录,其中/home/dennis/ffmpeg-2.2/android/arm/lib目录下的so库文件如下:

    -rwxr-xr-x 1 dennisdennis   55208 Mar 29 16:26libavdevice-55.so
    -rwxr-xr-x 1 dennisdennis  632476 Mar 29 16:26 libavfilter-4.so
    -rwxr-xr-x 1 dennisdennis 1442948 Mar 29 16:26 libavformat-55.so
    -rwxr-xr-x 1 dennisdennis 7985396 Mar 29 16:26 libavcodec-55.so
    -rwxr-xr-x 1 dennisdennis   83356 Mar 29 16:26libswresample-0.so
    -rwxr-xr-x 1 dennisdennis  308636 Mar 29 16:26 libswscale-2.so
    -rwxr-xr-x 1 dennisdennis  300580 Mar 29 16:26libavutil-52.so

    注:以上列表去掉了符号链接文件和pkgconfig目录。

    第三步:创建一个普通的Android工程

    1. 创建一个新的Android工程FFmpeg4Android
    2. 在工程根目录下创建jni文件夹
    3. 在jni下创建prebuilt目录,然后:

    (1)     将上面编译成功的7个so文件放入到该目录下;

    (2)     将/home/dennis/ffmpeg-2.2/android/arm/include下的所有头文件夹拷贝到该目录下.

    1. 创建包含native方法的类,先在src下创建cn.dennishucd包,然后创建FFmpegNative.java类文件。主要包括加载so库文件和一个native测试方法两部分,其内容如下:

    package cn.dennishucd;
    public class FFmpegNative {
                    static{
                                    System.loadLibrary("avutil-52");
                                    System.loadLibrary("avcodec-55");
                                    System.loadLibrary("swresample-0");
                                    System.loadLibrary("avformat-55");
                                    System.loadLibrary("swscale-2");
                                    System.loadLibrary("avfilter-3");
                                    System.loadLibrary("ffmpeg_codec");
                    }
                    publicnative int avcodec_find_decoder(int codecID);
    }

    1. 用javah创建.头文件:

    进入bin/classes目录,执行:javah-jni cn.dennishucd.FFmpegNative                 

     会在当前目录产生cn_dennishucd_FFmpegNative.h的C头文件;

    1. 根据头文件名,建立相同名字才C源文件cn_dennishucd_FFmpegNative.c

    在这个源文件中实现头文件中定义的方法,核心部分代码如下:

    JNIEXPORT jint JNICALLJava_cn_dennishucd_FFmpegNative_avcodec_1find_1decoder
     (JNIEnv *env, jobject obj, jint codecID)
    {
                    AVCodec*codec = NULL;
     
                    /*register all formats and codecs */
                    av_register_all();
     
                    codec= avcodec_find_decoder(codecID);
     
                    if(codec != NULL)
                    {
                                    return0;
                    }
                    else
                    {
                                    return-1;
                    }
    } 
    1. 编写Android.mk,内容如下:

    LOCAL_PATH := $(callmy-dir)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=avcodec-55-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libavcodec-55.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=avdevice-55-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libavdevice-55.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=avfilter-4-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libavfilter-4.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=avformat-55-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libavformat-55.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=  avutil-52-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libavutil-52.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=  avswresample-0-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libswresample-0.so
    include $(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
    LOCAL_MODULE :=  swscale-2-prebuilt
    LOCAL_SRC_FILES :=prebuilt/libswscale-2.so
    include$(PREBUILT_SHARED_LIBRARY)
     
    include $(CLEAR_VARS)
     
    LOCAL_MODULE :=ffmpeg_codec
    LOCAL_SRC_FILES :=cn_dennishucd_FFmpegNative.c
     
    LOCAL_LDLIBS := -llog-ljnigraphics -lz -landroid
    LOCAL_SHARED_LIBRARIES:= avcodec-55-prebuilt avdevice-55-prebuilt avfilter-4-prebuiltavformat-55-prebuilt avutil-52-prebuilt
     
    include$(BUILD_SHARED_LIBRARY)
    1. 编写Application.mk[可省略]
    2. 编译so文件

    打开cmd命令行,进入FFmpeg4Android\jni目录下,执行如下命令:

    $ndk-build

    截止本步骤完成,将在FFmpeg4Android根目录下生成libs\armeabi目录,该目录除了包含上面的7个so之外,另外还生成了libffmpeg_codec.so文件。

    1. 添加库的加载方法

    在FFmpegNative类中增加如下加载so库的代码:

    static {
                                    System.loadLibrary("avutil-52");
                                    System.loadLibrary("avcodec-55");
                                    System.loadLibrary("swresample-0");
                                    System.loadLibrary("avformat-55");
                                    System.loadLibrary("swscale-2");
                                    System.loadLibrary("avfilter-3");
                                    System.loadLibrary("avdevice-55");
                                    System.loadLibrary("ffmpeg_codec");
    }
    1. 修改layout/main.xml,给TextView增加id,以便在代码中操作它。
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="horizontal"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          >
        <TextView
          android:id="@+id/textview_hello"
          android:text="@string/hello"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="center"
          />
      </LinearLayout>

    2. 增加一个Activity实现类FFmpeg4AndroidActivity,在OnCreate方法中调用native函数将值传给TextView控件,打包运行即可。FFmpeg4AndroidActivity代码如下:

    package cn.dennishucd;
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;
    public class FFmpeg4AndroidActivity extends Activity {
                    @Override
                    protectedvoid onCreate(Bundle savedInstanceState) {
                                    super.onCreate(savedInstanceState);
                                   
                                    setContentView(R.layout.main);
                                   
                                    TextViewtv = (TextView)this.findViewById(R.id.textview_hello);
                                   
                                    FFmpegNativeffmpeg = new FFmpegNative();
                                    intcodecID = 28; //28 is the H264 Codec ID
                                   
                                    intres = ffmpeg.avcodec_find_decoder(codecID);
                                   
                                    if(res ==0) {
                                                    tv.setText("Success!");
                                    }
                                    else{
                                                    tv.setText("Failed!");
                                    }
                    }
    }

            代码中的28是H264的编解码ID,可以在ffmpeg的源代码中找到,它是枚举类型定义的。在C语言中,可以换算为整型值。这里测试能否找到H264编解码,如果能找到,说明调用ffmpeg的库函数是成功的,这也表明我们编译的so文件是基本可用。


    作者注:

    [1] 本文编译的方法主要参考了参考资料 [1] 中的思路,这里要感谢作者的贡献;

    [2] 后面的测试过程是参考了ffmpeg-2.1.4中的decoding_encoding.c例子;

    [3] 关于如何使用pre-built参考了参考资料 [2] 中的思路;

    [4] 这只是移植过程第一步,后面还会进一步分析ffmpeg的接口来调用其编解码库.

    [5]Android.mk文件应该还可以优化,这是后面的工作.

    [6] 整个过程完成还是耗费了我不少精力,如有转载请注明出处,多谢。本文的完整源代码可以在我的CSDN资源(http://download.csdn.net/detail/gobitan/7132037)下载,最新版本可跟踪我的github(https://github.com/dennishucd/FFmpeg4Android)。

     

    参考资料:

    1. http://www.roman10.net/how-to-build-ffmpeg-with-ndk-r9/
    2. http://www.ciaranmccormack.com/creating-a-prebuilt-shared-library-on-android-using-the-ndk/

     

    展开全文
  • Android平台App进程优先级

    千次阅读 2016-08-29 11:02:56
    我们都知道Android手机上是可以安装很多App的,每一个App至少是会有一个进程的。创建进程是件麻烦而且耗资源的事情,Android为了让App启动的时候能更快,会把那么暂时不使用的App的进程缓存起来,但是内存是有限的啊...

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/52351047 文章出自:薛瑄的博客
    你也可以查看我的其他同类文章,也会让你有一定的收货

    我们都知道Android手机上是可以安装很多App的,每一个App至少是会有一个进程的。创建进程是件麻烦而且耗资源的事情,Android为了让App启动的时候能更快,会把那么暂时不使用的App的进程缓存起来,但是内存是有限的啊,总不能让所有的进程都放在内存里边吧,所以Android有一个淘汰机制,会根据App的运行状态设置一个进程的优先级(oom_adj),然后根据内存的紧张程度,把那些优先级低(oom_adj值大)的进程kill掉,以保证其他的进程能够有足够的内存使用。

    注意:这个oom_adj值,是根据当前APP的运行状态(前台,后台等等)动态改变的。

    进程

    1. Zygote进程
      这个是Android框架的主要进程,所有的App进程以及系统服务进程SystemServer都是由Zygote进程Fork出来的

    2. App的主进程
      每一个App的运行都是在一个独立的进程,进程的名字就是App的packagename,这些进程都是从Zygote进程Fork出来的,并受AMS(ActivityManagerService)管理

    3. App的辅助进程
      可以允许App有多个进程,在AndroidManifest.xml里边配置android:process属性,就可以开启多进程,这些进程名字都是packagename:name这种,以区分是属于哪个App,我一般称之为辅助进程。但这些进程也都跟主进程一样,也是从Zygote进程Fork出来的,并受AMS管理

    4. Native进程
      Android除了使用Java,还有NDK,可以使用C/C++去开发,然后这里也是可以Fork出进程的,我一般称之为Native进程,Native进程可以不受AMS管理,自由度很大,本文暂且不讲

    如何查看App进程优先级

    对于App进程,因为受AMS管理,都会有一个oom_adj的文件记录着oom_adj的值,但是对于Native进程,那AMS就管不着了。

    //oom_adj的值就是进程的优先级,
    //查看oom_adj值
    cat /proc/${pid}/oom_adj

    下面以查看微信的oom_adj为例,先通过ps | grep com.tencent.mm得到进程名包含“com.tencent.mm”的微信进程pid。ps命令使用详解可参考这里

    这里写图片描述

    优先级

    App进程的优先级是在com.android.server.am.ProcessList类里边定义的,这个类在Android的API里边是找不到的,要想看看里边的实现可以去Android SDK里边去找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java

    主要有这么几个优先级(oom_adj值):

    • UNKNOWN_ADJ = 16
      预留的最低级别,一般对于缓存的进程才有可能设置成这个级别

      Adjustment used in certain places where we don’t know it yet. (Generally this is something that is going to be cached, but we don’t know the exact value in the cached range to assign yet.)

    • CACHED_APP_MAX_ADJ = 15
      缓存进程,空进程,在内存不足的情况下就会优先被kill

      This is a process only hosting activities that are not visible, so it can be killed without any disruption.

    • CACHED_APP_MIN_ADJ = 9
      缓存进程,也就是空进程

    • SERVICE_B_ADJ = 8
      不活跃的进程

      The B list of SERVICE_ADJ – these are the old and decrepit services that aren’t as shiny and interesting as the ones in the A list.

    • PREVIOUS_APP_ADJ = 7
      切换进程

      This is the process of the previous application that the user was in. This process is kept above other things, because it is very common to switch back to the previous app. This is important both for recent task switch (toggling between the two top recent apps) as well as normal UI flow such as clicking on a URI in the e-mail app to view in the browser, and then pressing back to return to e-mail.

    • HOME_APP_ADJ = 6
      与Home交互的进程

      This is a process holding the home application – we want to try avoiding killing it, even if it would normally be in the background, because the user interacts with it so much.

    • SERVICE_ADJ = 5
      有Service的进程

      This is a process holding an application service – killing it will not have much of an impact as far as the user is concerned.

    • HEAVY_WEIGHT_APP_ADJ = 4
      高权重进程

      This is a process with a heavy-weight application. It is in the background, but we want to try to avoid killing it. Value set in system/rootdir/init.rc on startup.

    • BACKUP_APP_ADJ = 3
      正在备份的进程

      This is a process currently hosting a backup operation. Killing it is not entirely fatal but is generally a bad idea.

    • PERCEPTIBLE_APP_ADJ = 2
      可感知的进程,比如那种播放音乐

      This is a process only hosting components that are perceptible to the user, and we really want to avoid killing them, but they are not immediately visible. An example is background music playback.

    • VISIBLE_APP_ADJ = 1
      可见进程

      This is a process only hosting activities that are visible to the user, so we’d prefer they don’t disappear.

    • FOREGROUND_APP_ADJ = 0
      前台进程
      This is the process running the current foreground app. We’d really rather not kill it!

    • PERSISTENT_SERVICE_ADJ = -11
      重要进程

      This is a process that the system or a persistent process has bound to, and indicated it is important.

    • PERSISTENT_PROC_ADJ = -12
      核心进程

      This is a system persistent process, such as telephony. Definitely don’t want to kill it, but doing so is not completely fatal.

    • SYSTEM_ADJ = -16
      系统进程

      The system process runs at the default adjustment.

    • NATIVE_ADJ = -17
      系统起的Native进程

      Special code for native processes that are not being managed by the system (so don’t have an oom adj assigned by the system).

    在Android-18及以下空进程不叫CACHED_APP_MIN_ADJ ,叫HIDDEN_APP_MIN_ADJ,有这么点不一样,但是值都一样。

    lowmemorykiller 机制

    这个机制是在goldfish层面实现的,如果要想具体了解是怎么选择杀进程的话,需要去看Android drivers下的lowmemorykiller.c的代码。

    编译过goldfish的人,应该知道它其实就是Linux kernel。因为Android是基于Linux的操作系统嘛,kernel其实也是跟着Linux kernel走的,只不过在里边需要开发(或者精简)一些专门针对Android的东西,lowmemorykiller 就是其中的一部分,是个驱动。至于为啥会被放在drivers/staging目录下,可以参考下这篇文章《小议Linux staging tree》

    有这么几个关键点:

    1. 向系统注册lowmem_shrinker回调,当系统空闲内存不足时调用,去杀进程

    2. 把空闲内存低于多少,就去杀那个级别的进程,我称之为策略

    3. 杀的策略由application层根据内存状况指定,写入文件传递给驱动

    4. 根据策略会计算出min_score_adj

    5. 每个App进程都有oom_adj值,根据oom_adj值计算出oom_score_adj(得分值),oom_adj越大,oom_score_adj越高,高于min_score_adj的被列入死亡名单

    6. 在死亡名单里面选一个占内存最大的进程kill掉

    所以当内存不足的时候,进程优先级低的(oom_adj越大的),占内存大的App进程将会被优先kill掉。

    至于这个策略嘛,各个ROM可能不一致的,根据内存的大小,系统的严格程度来制定,但都只能分6个级别。

    查看传给lowmemorykiller驱动的策略文件:
    查看传给lowmemorykiller驱动的策略文件

    • minfree:列举6个内存阀值。
      比如:32768,61440,73728,129024,147456,184320

    • adj:列举6个oom_adj的级别。
      比如:0,1,2,3,9,15

    这两个值相互对应,空闲内存低于多少的时候就kill那个级别的进程。

    这里写图片描述

    当然有些ROM也会把这个参数定义在init.rc里边,开机后再写入minfree和adj文件,传递给驱动

    trimApplications机制

    仅仅只有低内存的情况下才去kill进程吗?很明显不是,对于App的运行是有很多状况的,比如Crash、App退出、系统清理、卸载。

    trimApplications是一个方法,定义在ActivityManagerService里边,Android API里边也是没有这个类的,得去SDK里边找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ActivityManagerService.java

    这个机制大概也有几个关键点:

    1. package已被卸载的无用进程会被Kill
    2. persistent的App会被优先照顾,进程优先级设置为
      PERSISTENT_PROC_ADJ=-12
    3. Activity仅在主进程跑的App会被认为是高权重进程,HEAVY_WEIGHT_APP_ADJ=4
    4. 只有前台进程才是FOREGROUND_APP_ADJ=0,前台进程不会被杀
    5. 每当Activity、Service等生命周期发生变化的时候都会引起进程的oo_adj的调整
    6. 进程里边没有任何的Activity的存在优先被杀
    7. 空进程最容易被杀

    如何提高后台App进程的优先级

    其实Android框架的思想是很好的,对于空的进程,没事干的进程直接kill掉,对于用户体验来讲是不会有影响的,但是往往我们的App都会有推送这个功能,恰巧GCM(Google Cloud Messaging)在国内又不能用,所以很多情况下我们也会希望App在后台的时候也尽量不要被杀。

    可以考虑把后台App进程的优先级提高,下面有几个方法:

    • AndroidManifest.xml中配置persistent属性
    <application android:name="App"
    android:persistent="true"
    android:label="@string/dialerIconLabel"
    android:icon="@drawable/ic_launcher_phone">
    • 重载back按键事件,让activity在后台运行,不要Destory
    • 开Service,并设置前台运行方式
    • 与NotificationManager交互,让进程变成可感知进程
    • 发送/接收广播,别让自己变成空进程

    很明显这会消耗更多的电量,也有点流氓,有些需求也许本就不该做。

    参考:

    Android平台App进程优先级

    微信Android客户端后台保活经验分享

    展开全文
  • Android_Page Curl:Android平台上的翻书翻页 我在之前介绍了一种Android平台上的翻书翻页第三方开源实现,参见附录文章1。现在再介绍一篇第三方开源的翻书翻页开源实现Android_Page Curl,其在github上的项目主页...
    

    Android_Page Curl:Android平台上的翻书翻页

    我在之前介绍了一种Android平台上的翻书翻页第三方开源实现,参见附录文章1。现在再介绍一篇第三方开源的翻书翻页开源实现Android_Page Curl,其在github上的项目主页是:https://github.com/harism/android_page_curl
    Android_Page Curl与附录文章1介绍的android-flip相比较,各有千秋,我个人感觉Android_Page Curl在设计模拟翻书翻页效果实现有些“敏感”,逼真。根据个人情况选择使用。


    附录:
    1,《Android书页翻页设计:android-flip》链接地址:http://blog.csdn.net/zhangphil/article/details/50969892


    展开全文
  • android平台api帮助文档---android版的MSDN

    千次阅读 多人点赞 2012-12-21 18:52:35
    android平台api帮助文档---android版的MSDN    做android应用开发,现在网上的资料大把大把的,但是都是零零碎碎的,有时候去查一个接口得去android官方网站http://developer.android.com/develop/index.html,...
  • Android平台技术:JNI开发初步

    万人学习 2015-09-23 00:14:32
    Android的高度开放性,非常有利于软硬整合,人人都能自由使用Java & C/C++撰写上、中、下层服务,紧密结合硬件,呈现其差异化,创造增值效果。这是一条产业发展的美好之路。Android应用软件几乎都需要Java与C/C++...
  • Android平台特性

    千次阅读 2016-11-17 16:47:48
    随着科技的发展,移动电话(Mobile Phone)正朝着智能化的方向发展,逐步成为多种工具的功能载体,而Android就是这样一个智能...除了传统的语音通话功能外,Android平台还具有短消息功能,以及通常移动电话都具有的个
  • Android平台上OCR识别应用技术解密

    千人学习 2016-12-19 15:36:31
    本节课介绍在Android系统平台基于OpenCV与Tesseract-OCR框架实现对身份证号码识别技术为例,探讨移动平台上实际项目中OCR识别的常规处理流程与相关算法介绍
  • Android 平台的Python——CLE方案实现(三)

    万次阅读 热门讨论 2017-11-18 23:01:24
    CLE(Common Language Extension)公共语言拓展 ... 来自官网的简介: CLE中间件支持多脚本语言混合编程。c/c++, lua, python, c#, ruby, java等语言可以通过CLE中间件直接使用其它语言中的类,函数,变量或者...
  • Ogre3d Android平台编译教程

    千次阅读 热门讨论 2015-03-27 23:24:08
    我们介绍 Ogre3d 编译到Android 平台的方法。可以和官方英文教程对照学习。 首先下载Ogre3d 依赖库源代码 和Ogre3d 源代码,下载方法在上一篇中介绍了,这里不再重复
  • Android 平台搭建

    千次阅读 2010-07-02 13:07:00
    Android 开发平台搭建:1.安装环境:OS :windows 7旗舰版JDK :jdk 6.0Eclipse :eclipse-java-helios-win32 ADT :ADT-0.9.7,Eclipse IDE 进行 Android 开发的一种插件Android SDK : android-...
  • 第四章、Android编译系统与定制Android平台系统 4.1Android编译系统Android的源码由几十万个文件构成,这些文件之间有的相互依赖,有的又相互独立,它们按功能或类型又被放到不同目录下,对于这个大的一个工程,...
  • [019] Android平台调用WebService详解

    万次阅读 多人点赞 2011-05-18 00:06:00
    上篇文章已经对Web Service及其相关知识进行了介绍(Android...本文将通过一个简单的示例讲解和演示Android平台的Web Service开发。 Ksoap2-android简介  在Android平台调用Web Service需要依赖于第三方类库ksoap
  • Android 平台的Python——JNI方案(二)

    万次阅读 热门讨论 2017-11-18 22:42:29
    关于在Android项目中嵌入解释器,实现Java与Python相互调用,以有好的项目提供了思路,地址:https://github.com/joaoventura/pybridge我这里说一下简单实现1、环境准备配置好crystax ndk环境,并创建一个NDK项目,...
  • Android平台上的Native内存分析

    千次阅读 2018-09-28 09:39:03
    UE4游戏在Android上的进程内存占用(PSS)很让人困惑, 没有一个清晰直观的方式可以统计到每一部分的内存占用. 所以在做内存分析的过程中顺手做了一个统计工具, 可以从系统底层统计UE4在Android的所有内存分配(包括...
  • 如今,大部分的AR都是基于移动设备的,所以这里我们尝试在Android中调用OpenCV的功能来做一些相关的测试。由于OpenCV是基于C语言和部分C++语言开发的,而Android开发使用的则是Java,那么如何才能在Java中调用OpenCV...
  • Android平台移植FFmpeg和x264

    千次阅读 2016-07-20 23:24:05
    之前记录过一篇编译FFmpeg的文章,没写完整 最近在做ijkplayer的二次开发,重新拾起FFmpeg,记录完。 记得之前的移植很头疼,网上很多帖子,都是抄来抄去,作者也没有去验证 笔者记录的目的,是完整的记录下...
  • **本人刚学Android平台下的OpenCV开发,但是现在遇到一个问题:在Android模拟器下调试程序时出现“OpenCV manager package was not found,请安装它,”请问去下载什么文件,怎么在模拟器下安装它??先谢谢各位的...
  • 什么是Android——Android平台简介

    万次阅读 2011-03-10 17:52:00
    什么是Android ——Android平台简介 <br /> <br />Keyword: Google Android,Linux内核,开源,应用领域,开放手机联盟(Open Handset Alliance) <br />   在网上看到有“Android平台”和...
  • Android 平台的Python——第三方库移植

    万次阅读 多人点赞 2018-09-20 16:11:00
    前面写了三篇关于将Python3嵌入Android项目的博客,后来一直有人留言问怎么移植Python的第三方库,包括说调用标准库报错等等问题,我之前一直以为这些是Python的基本常识,没想到很多人都不知道。之前的三篇博客,...
  • 要想将Python解释器移植到Android平台,首先要做的就是将Python源码用NDK工具交叉编译为Android平台的二进制库。目前官方是没有提供对Android平台的支持的,但新的版本已经在考虑对Android提供支持,参考文档 API 24...
  • Android平台利用ZXING生成二维码图片

    千次阅读 2015-01-13 16:20:32
    zxing是google的一个开源二维码项目,目前基本上和二维码打交道的东西,都会用到它. 最近项目中用到了android手机需要根据提供的字符串生成二维码图片,之前用zxing做过二维码...然后根据android平台的提供的api来生成图片
  • ubuntu移植libcurl到Android平台

    万次阅读 2019-03-20 11:34:00
    最近移植了很多C++平台的库,很多都是后台开发的库,因为NDK开发,以后很...最近在跟后台的人聊天过程中了解了很多C++开发的相关库,这是其中一个,Android中网络操作的库有很多种,从基本的Httpurlconnection到h...

空空如也

1 2 3 4 5 ... 20
收藏数 77,226
精华内容 30,890
关键字:

android平台