Android动态加载源码库

很多跨平台实现都避免不了需要加载一个so库,而so库针对不同的芯片还需要不同的编辑版本(ARM和X86).这间接得导致android APP包越来越大.例如目前cocos2d-x的6-8M,Unity5的16-18M,而这都是一张图片 一句具体代码都没写的情况下.做过游戏的都知道:

  1. 资源可以脱离APK包 采用在线更新的方式download到手机上.
  2. 脚本(Lua or Js or C#)也可以采用热更新的方式download到手机上.
  3. 甚至玩家安装了一次APK,以后的新版本APK都可以采用后台download静默安装的方式更新自身.

那现在就剩下最后两个问题:

  1. 我嫌弃更新APK麻烦,只想更新so库,那so库是否可以动态更新?
  2. 或者更变态一点,即使非常必要的时候,我依然不想更新APK,dex是否可以动态更新?

so库:android项目的C++原生拓展编译库,可被加载后执行.
dex文件:android项目中的java代码生成的可执行文件.

下面我们探讨下这第一个问题.

Android中so库动态加载

首先是静态加载,静态加载相信大家都知道:

// 静态加载.so,假设so文件名为 librectdymanic.so
static 
{
    System.loadLibrary("rectdymanic");
}

静态加载需要so文件名为 lib+Name+.so 的格式,我一直很奇怪为什么Google要限定这个格式,直接传文件全名多简单的事情?在Java中调用C++代码也非常简单,加载了so库 并在java代码中使用关键字native 声明了函数名之后 就可以调用了。

其实System还有一个函数叫load,他也可以直接加载so文件。我们正是用它来实现我们的动态加载C++so库并执行。贴一点代码:

从APK中的Assets文件夹中加载SO文件

下面是把assets文件夹中的文件librectdymanicassets.so(文件名随意) 加载到lib目录中

private void __LoadFormAssets() 
{
    
    if(m_bLoadAssetsSuccess)
    {
        return;
    }
    
    File dir = getDir("lib", Context.MODE_PRIVATE);
    File soFile = new File(dir, "librectdymanicassets.so");
    FileUtils.AssetToFile(this, "librectdymanicassets.so", soFile);
    try 
    {
        System.load(soFile.getAbsolutePath());
        m_bLoadAssetsSuccess = true;
    } 
    catch (Exception e) 
    {
    }
        
}

从网络上下载SO文件,并加载(运用这个可以实现动态更新SO库!!)

下面是从网络上下载一个SO文件(注意 我例子中的SO文件有时效性,如果要测试这个请自己把assets文件夹中的librectdymanichttp.so上传到自己的服务器上,并修改demo里的URL)

private void __LoadFormURL()
{
         
    if(m_bLoadHTTPSuccess)
    {
        return;
    }
        
    File dir = getDir("lib", Context.MODE_PRIVATE);
    File soFile = new File(dir, "librectdymanichttp.so");
    // !Warnning!
    // 这个URL上的so文件只保存30天,若这个实效了?
    // 0.请到 assets文件夹取 librectdymanichttp.so
    // 1.自行上传到随便一个服务器
    // 2.修改一下URL为你的URL即可
    FileUtils.URLToFile( "http://box.zjuqsc.com/-66469911", soFile);
    try 
    {
        System.load(soFile.getAbsolutePath());
        m_bLoadHTTPSuccess = true;
    } 
    catch (Exception e) 
    {
    }
        
}

加载之后运行效果如图:

动态加载SO库的例子代码:https://github.com/recter/RectDymanicSO

Android设备中如何在cocos2d-x 或者Untiy中使用?

对于cocos2d-x

  1. java层新建一个原生Activity,作为第一个启动Activity。 把so更新逻辑写到java层
  2. 利用两种动态加载方式:游戏首发的时候可考虑直接打包到assets文件夹,往后的更新则采用网络的方式
  3. 使用system.load so源码
  4. 启动cocos2d-x的Activity
  5. done

对于Unity

对于unity可能稍微有点技术难度。但并非是不可行。Unity下有三个so分别是:libmain.so,libmomo.so,libunity.so.经过我反编译查看可知 libmain.so是唯一一个被java层load的,也就是说其他两个so文件被linmain.so加载入内存,也就是说 动态加载出了本文说的使用java层加载的方式 还可以使用C++直接加载。知道了它的加载原理我们就可以修改它实现我们的动态更新原生库。

  1. java层新建一个原生Activity 作为第一个启动Activity。 把so更新逻辑写到java层
  2. 利用两种动态加载方式:游戏首发的时候可考虑直接打包到assets文件夹,往后的更新则采用网络的方式
  3. 反编译libmain.so 修改加载libmomo.so,libunity.so的路径为 从assets加载 或者 网络下载后的路径加载
  4. 启动Untiy的Activity
  5. done

使用这种方式可以把so库与APK脱离开来,达到了让APK足够小的市场需求。最后其实连dex都可以动态更新的。。。

-EOF-

1 评论

  1. `反编译libmain.so 修改加载libmomo.so,libunity.so的路径为 从assets加载 或者 网络下载后的路径加载“
    简单一句话。。但是就这个反编译并修改so好像就很难了。。。

发表评论

电子邮件地址不会被公开。 必填项已用*标注