打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
在android应用中使用luasocket / 蓝讯

最近接手一个移动端应用,要为其android版本扩展支持调用lua脚本解析,而且最好同时能支持luasocket。如果只是希望在android下支持lua标准库的使用,那么androLua这个开源项目就可以解决这个问题。然而在为其扩展支持三方库,如luasocket时,遇到了一些问题,经过一翻折腾,最终解决了这个问题,把折腾的过程记录下来,方便有其他相同需求的人少走弯路。


    基础知识

    Lua是一门用标准C编写的动态脚本语言,如果希望在android上使用,则需要解决两个问题。第一,需要用JNI为Lua的C库进行封装,这样才可能在Java中使用。第二,由于Android系统开发所特有的系统环境限制,Lua三方库的动态加载机制和lua脚本模块的导入机制将不能正常运行,需要进行特殊处理。

    对于第一个问题,LuaJava这个开源项目提供了Java调用Lua C代码的JNI封装接口。

    对于第二个问题,分为两个方面。一方面是lua脚本模块的加载,由于android系统和普通的Linux环境并不相同,如果按照Lua标准的模块查找机制,将无法找到相关的模块文件。AndroLua解决了这个问题,它通过把lua脚本模块放到android应用的asserts目录下,在运行时通过资源类打开这些文件,用dostring来执行lua代码。另一方面是lua三方库如luasocket /luaJson等共享库的路径查找机制,在android系统下无法正常使用。本文主要解决第二个问题的第二个方面。


     让lua能支持三方库的调用思考

    第一,使用Java调用共享库的方式,把三方库编译成so文件,然后再封装一层JNI接口,通过java代码使用。但这种方式还不如直接用Java实现相关功能,在Lua中调用来的简单。

    第二,利用lua自己的so模块加载机制,直接加载三方库。最初,我是希望走这条路的,因为这样不需要修改lua的源代码,但一直未能成功。其方法思想是把三方库编译的单独so文件打到apk包里,这样安装应用后,so文件会有一个路径。这样只需要修改luaconf.h文件,把CPATH的配置修改为so文件的路径,然后用ndk重新编译luaJava。这种方法确实有一定的效果,当我用这种方式把luasocket加载进去后,可以成功调用一次操作,操作返回后应用就崩溃了,查看logcat输出的日志,在调用共享库方法后出现段错误。一度以为是luasocket的实现有缺陷,希望能解决源码的问题,花费了不少无用功。后来,随着我对lua C API了解的增加,这条捷径被我放弃了。决定采用下面的方法。

   第三,直接把luasocket代码静态编译到lua中,也就是说socket将成为lua的标准库,无须动态加载,这样luajava最终编译出来只有一个so文件,其中包含了lua/luasocket的功能。


   具体实现步骤如下

  1  目录结构整理

     在jni目录下创建3个子目录,分别存放lua / luasocket / luajava 的源码

  2  修改jni/lua/linit.c源码

     将luasocket导出加载函数注册到标准库加载函数中,此外增加两个头文件的包含和模块名宏定义 

#define linit_c#define LUA_LIB#include "lua.h"#include "lualib.h"#include "lauxlib.h"#include "luasocket.h"#include "mime.h"#define LUA_SOCKETLIBNAME "socket"#define LUA_MIMELIBNAME "mime"static const luaL_Reg lualibs[] = {  {"", luaopen_base},  {LUA_LOADLIBNAME, luaopen_package},  {LUA_TABLIBNAME, luaopen_table},  {LUA_IOLIBNAME, luaopen_io},  {LUA_OSLIBNAME, luaopen_os},  {LUA_STRLIBNAME, luaopen_string},  {LUA_MATHLIBNAME, luaopen_math},  {LUA_DBLIBNAME, luaopen_debug},  {LUA_MIMELIBNAME,luaopen_mime_core},  {LUA_SOCKETLIBNAME,luaopen_socket_core},  {NULL, NULL}};

  3  修改ndk的构建文件

 创建luasocket/Android.mk将luasocket先编译为静态库,ndk的项目文件和Make的语法一样,只是增加了一些宏和函数,不再此做介绍

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := socketLOCAL_C_INCLUDES += $(LOCAL_PATH)/../lua/LOCAL_SRC_FILES := luasocket.c timeout.c buffer.c io.c auxiliar.c options.c inet.c tcp.c udp.c except.c select.c usocket.c mime.cinclude $(BUILD_STATIC_LIBRARY)
修改luajava/Android.mk将编译的lua和luasocket静态库一起编译为共享库
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_C_INCLUDES += $(LOCAL_PATH)/../luaLOCAL_C_INCLUDES += $(LOCAL_PATH)/../luasocketLOCAL_MODULE     := luajavaLOCAL_SRC_FILES  := luajava.cLOCAL_STATIC_LIBRARIES := liblua libsocketLOCAL_LDLIBS    := -ldl -lminclude $(BUILD_SHARED_LIBRARY)

  4 修改socket.lua mime.lua  http.lua源代码

在原始的luasocket中,共享库这一层生成了socket/core.so  mime/core.so,而在lua模块这一层导出了socket.http socket.url  socket.lua mime.lua这4个重要的模块。其中socket.lua中加载socket/core.so,导出了socket模块,mime.lua加载mime/core.so导出了mime模块。

由于android中只能把所有.lua文件存放在assert目录下,且不能有层次结构。因而创建模块时,要把http/url模块从socket中提出来,形成单独的模块。另外luasocket内部注册模块时,把模块名定义为socket和mime,为了不修改luasocket的代码,因而依旧保留了共享库的模块名为socket/mime。这样就和上层lua脚本模块重名,造成冲突。需要修改lua脚本的模块名,分别修改为socketlua.lua,mimelua.lua。这样只需要修改各lua文件的头部模块变量初始化部分的代码即可,各修改部分的代码如下:

socketlua.lua(原socket.lua)文件头部

local base = _Glocal string = require("string")local math = require("math")local socket = require("socket")module("socket")
mimelua.lua(原mime.lua)文件头部
local base = _Glocal ltn12 = require("ltn12")local mime = require("mime")local io = require("io")local string = require("string")module("mime")
http.lua(原socket/http.lua)文件头部
require("socketlua")require("mimelua")local socket = _G.socketlocal mime = _G.mimelocal base = _Glocal url = require("url")local ltn12 = require("ltn12")local string = require("string")local table = require("table")module("http")
url.lua(原socket/url)文件头部
local string = require("string")local base = _Glocal table = require("table")module("url")

5 应用

修改后,在lua中需要使用哪个模块就可以直接引用哪个模块了。下面是个小示例,它用flvxz网站提供的视频解析功能来解析一些主流网站的视频下载地址:

local http=require('http')local url =require('url')local mime=require('mime') local json=require('json')local base = _Gmodule("flvxz")local flvxz_parse_json = function(jobj)    for k,item in base.pairs(jobj) do        files = item['files']        if #files == 1 then           if  files[1]['ftype']=="mp4" then               return files[1]['furl']            end        end    endend        flvxz_parse_url = function(url)        local api="http://api.flvxz.com/jsonp/purejson/url/";        local surl = base.string.gsub(url,"://",":##");        local eurl = mime.b64(surl);        local b,c,h = http.request(api..eurl);        local succ,data = base.pcall(json.decode,b)        if not succ then            return nil;        end        return flvxz_parse_json(data)endlocal test = function()  local urls= {        "//v.youku.com/v_show/id_XNTUzMDQzODky.html",        "http://www.tudou.com/programs/view/YDn_zTq_8gI/",        "http://www.iqiyi.com/v_19rrh3v2vw.html",        "http://www.letv.com/ptv/vplay/20047225.html"       }  for k,url in base.pairs(urls) do     base.print(flvxz_parse_url(url),url)     base.print("-------------\n")  endend--test();

相关链接

Luajava  http://keplerproject.org/luajava/

androLua  https://github.com/mkottman/AndroLua/

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
小试 LuaSocket
lua中统计毫秒精度的时间(转)
使用Lua的扩展库LuaSocket用例
在winXP+Eclipse中搭建quick
[Cocos2d-x]Lua 资源热更新
用C语言扩展lua模块(入门)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服