美文网首页Android的数据处理方案TECH_ANDROID我爱编程
Android_其他语言交互篇——Js、C#、C、C++

Android_其他语言交互篇——Js、C#、C、C++

作者: 鱼满楼 | 来源:发表于2017-04-29 08:20 被阅读877次

    本文已被公众号 码个蛋 收录。

    码个蛋——优质干货公众号

    在Android开发中我们有很多时候要与其他语言进行交互,然而对于小白来说学习安卓就够头疼的了更不用说其他的语言了,很多教程的实现过程繁杂简直是天书,本篇就用最易懂最简单的方式教小白们掌握Anroid如何与Js、C#、C和C++进行交互,让大家克服对其他语言的恐惧!


    第一部分:Js——WebView

    Js交互可能是我们开发中涉及到的最多的(也有第三方有名的像腾讯X5内核),很多开发者应该很熟悉了,我们就稍微复习下:

    1、Android端调用Js端(下图示例有参数和无参两种调用):

    这个非常的简单,就是我们加载网页的方法loadUrl,但是传入的字符串不再是url,而是以 " javascript:" 开头后面跟所调用的js方法名;

    js是脚本语言,如果是该方法还没加载你就调用当然什么都不会发生。

    android调用js有参无参

    2、Js端调用Android端:

    首先,在android代码中定义可供js端调用的方法,一定不要忘记添加@JavascriptInterface注解;

    在android中定义可供js调用的方法

    然后,设置webview参数(1、打开js开关;2、设置webviewClient的如下方法返回值为true,否则loadUrl的时候会打开系统的浏览器而不是我们的WebView),最重要的是最后使用WebView的addJavascriptInterface(Object,String)方法(第一个参数是你上面定义的方法所在的类的对象,第二个参数是这个类的对象在js中的名字)。

    配置webview

    随后在js中就可以使用你设置的那个String类型的名字来调用这个方法了:

    js中调用android的方法

    很多朋友搞不懂addJavascriptInterface后为什么js就可以调用android的方法了呢,我们贴一下这个方法的部分注释,其意思是说调用这个方法会把第一个参数java对象挂载到webview的当前页面,挂载的名字就是第二个String类型的参数,然后java类的方法就可以被js调用了。

    addJavascriptInterface 的源码注释

    有朋友在思考中纠结到,我在js中调用了android的方法,但是这个方法是异步操作,该怎么回调js呢?其实兄台你想多了,不同的语言,哪来的回调呢,我们只能说在这样的情境下如何实现回调的效果:其实很简单吖,在android的异步回调中,使用loadUrl调用js的相关方法就行了嘛,哈哈......

    3、题外话:

    关于安卓的WebView,一直是诟病所在;实际开发中人家IOS的页面玩到飞起,咱这边一直是卡卡卡,奶奶个腿儿的领导还以为都是我们没写好有没有,都是泪!

    WebView在4.4之前,一直是WebKit内核,4.4之后谷歌也看不下去WebKit的性能了就将其内核换成了Chromium。之前公司正好有一步测试机4.4系统(魅蓝),我打印过它的WebView版本号大概是23,再看看我的电脑的Chrome的内核版本58,抛开移动版本身就经过阉割不说这性能也是甩了不只一条街啊......

    桌面版Chrome最新版本

    于是很多开发者将目光移到了腾讯X5浏览器内核上,我在其中一个项目中也用过,但是真的没有感觉到它快反而觉得很坑,logcat报各种奇葩错误(其实导入X5后只是在低版本系统的手机上使用了X5内核,高版本依旧是系统的Chromium内核,所以我一直觉得腾讯开放的这个东东是自己使用的好东东的阉割版......这个没办法,谁让人家免费而且是老大呢)。

    于是我又开始移动目光到了CrossWalk上,这是intel所开源的一款浏览器组件,也是使用Chromium内核,但是最新版本已经是Chromium-53了,实测中流畅到飞起啊,简直不要太快!然而它也有它的缺点:lib包神奇的庞大,动辄几十兆,额......不过我猜想是否可以用热更新的方式来引导用户载入这个组件(目前还未实际测试),这里贴一下CrossWalk的官网及仓库地址,有兴趣的朋友可以研究下:

    官网:https://crosswalk-project.org/index_zh.html

    仓库:https://download.01.org/crosswalk/releases/crosswalk/android/


    第二部分:C#——Unity3D

    这个其实并不难,但是网上大多教程都写的神麻烦且不清晰,我这里就用最简单的方法教大家如何使用。与Unity交互可能在游戏开发领域涉及较多,毕竟Unity是做游戏用的,然而上个奇葩公司用它做了一款应用(还是在我推荐下使用的哈哈):

    我们先来分析一下需求(开发unity项目需要android提供支持):


    第一种,我们使用unity新建项目,在其中写好逻辑并定义好所要调用的android方法等;然后将unity项目导出成android项目,直接用eclipse打开这个项目然后编写在unity中定义好的所需要的android方法;实际测试这种方法非常简单可行,然而难道每次unity开发中都要把unity项目导成android项目去重复编辑么,这岂不是太浪费精力了,所以这种方法抛弃之。


    第二种,我们使用eclipse建安卓项目,编写完成后将项目导到unity中,就像android导入其他android Library一般将这个android项目做成插件;这样每次unity版本更新时,android Library是不需要修改的,如果不涉及到功能修改或增加,就是一劳永逸吖,我们就采取这个方案。

    1、调用方式

    Unity端调用Android端:

    前两行都是固定的(也有其他写法,但这个既常用又简单),最后两个分别是有返回值方法和无返回值方法的调用(第一个参数是安卓所定义的方法的名字,第二个参数是方法所传入的参数<类型是Object数组>,方法带的泛型就是返回值类型);

    除了Call、当然还有CallStatic,各位自己试验吧。

    unity调用android

    Android端调用Unity端:

    编写时需要导入classes.jar,位于unity安装目录下(...\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Development\Classes\classes.jar):

    android调用unity

    2、实现

    我们用eclipse(项目设为Library)或android studio新建安卓项目,导入classes.jar,使你的启动Activity继承自UnityPlayerActivity,

    编写activity

    编译无错误后,打包jar(打包jar的时候名字无所谓,但是特别注意只打包项目中的java代码文件,其他的一概不要),

    eclipse:菜单export----jar----此时只选择src目录;

    android studio:命令打包,自己百度吧。

    然后我们在unity项目中的Assets目录下新建Plugins----Android文件夹,

    unity目录

    在此Android文件夹下,放入打包好的jar包、AndroidManifest文件,然后根据需要放入assets、libs、res等文件夹(不必全部放入)。

    然后在Unity的C#代码中就可以愉快的调用了:

    编写unity代码

    是不是挺简单的,但是一定要注意:

    1、打包jar的时候,只要.java文件,不能有任何其他的,不然各种报错你也查不出来;

    2、异步操作实现回调的话,思路也是跟js回调一样的,即在android异步操作的回掉方法中调用UnityPlayer.UnitySendMessage;

    3、Unity貌似只能读取固定文件夹下的文件,比如我配合队友开发的时候,只能把图片放在这个路径下:getExternalFilesDir("").getAbsolutePath()+"/"+file.getName(),然后只把这个文件名给他就行了UnityPlayer.UnitySendMessage("goUpload", "UpLoadTex", file.getName());

    4、以上处理思路适用于开发Unity项目;反过来在android项目中插入Unity是件非常简单的事,只需要你理解Unity虽然页面很多但是只有一个Activity也就是UnityPlayerActivity,其中的变量mUnityPlayer就是Unity的内容,只需要在合适的地方viewGroup.addView(mUnityPlayer)即可!



    第三部分:C、C++——JNI

    很多小白提起NDK都直摇头,我想告诉你的是它一点也不难,只是你对它一无所知所以表现出恐惧;而且网上许多教程中都使用各种命令行也另小白们望而却步,这篇软文拒绝命令行,现在我们就揭开JNI的神秘面纱吧:

    看到C和C++与Java交互,我们脑海里第一时间想起的就是JNI。很小白以为JNI是安卓搞出来的,其实这玩意跟安卓没毛线关系,人家是在Java1.1就引入的东东,JNI全称Java Native Interface(Java原生接口),它提供了若干的Api实现了Java和其他语言的通信(说明JNI能用于很多语言与Java进行交互,但平时我们提起JNI,主要指的是C和C++);话说年初的时候换工作面试,有个面试官问我会不会NDK开发,我虽然没在项目中用过,但是流程给他讲的明明白白,从他的眼神中我还是看出他对我一点都不相信,呵呵......

    0、准备工作:

    JNI开发需要NDK及CMake(也可以不使用CMake而是用其他方法,但是CMake用起来最简单易懂,且在安卓Sdk中即可下载说明它比较先进是有很大优势的所以谷歌推荐使用,于是本篇就使用CMake进行编写),使用Android Sdk Manager或如图的Settings界面下载:

    准备CMake及NDK

    1、新项目处理方法:

    只需这一步操作,即在建立新项目的时候,如图所示的选项打上对勾就行了,无需其他任何操作:

    新项目增加JNI支持

    2、已有项目处理方法:

    如果我们已经存在的项目还没有引入JNI的支持,操作就稍微有些繁琐了(本篇示例是在Activity中):

    在项目的某个类中定义一个native方法,alt + enter 提示发现并没有生成JNI方法的选项,说明当前并未配置好JNI的支持;

    未添加JNI支持的项目内容

    首先,在app或module上右键如图的选项,新建JNI文件夹(在随后出现的对话框点Finish即可,也可以new Directory然后起自己想要的名字):

    创建JNI文件夹

    然后在建好的JNI文件夹上右键如图的选项,新建需要的JNI文件(.c代表C文件,.cpp代表C++文件,可以建立多个):

    创建JNI文件

    这时候,在打开的JNI文件编辑区会有如图的提示,说明缺少编译可用的CMakeLists.txt(如果用的不是CMake,则可能缺少的是Android.mk):

    提示缺少CMakeLists.txt文件

    于是,我们在app或module上右键如图的选项,新建CMakeLists.txt文件:

    新建CMakeLists.txt文件

    然后,编辑CMakeLists.txt文件,有两项不可缺少的配置(图中每行中 # 后面的都是注释,可忽略):

    1、cmake_minimum_required:最低的cmake版本号;

    2、add_library:配置刚才新建的JNI文件路径及根据这个JNI文件将要生成的so库的名字,可添加多个add_library块。

    编辑CMakeLists.txt文件

    在app或module的build.gradle文件的android块中加入如图的externalNativeBuild块:

    编辑app或module的build.gradle文件

    点击sync,然后make project,这时候在java代码中定义的native方法上alt+enter就会提示自动实现方法了,这就说明JNI的支持已经配置好了:

    提示实现native

    3、调用:

    JNI的支持配置好了,交互只需进行下面的 ab 或 ac 即可:

    a、首先LoadLibrary:

    Android的虚拟机想要使用so库就要先进行这一步,android studio自动生成支持JNI的项目时就是这样处理的;

    先加载编译好的so库

    b、Android调用JNI:

    在.java文件中定义native方法,并调用:

    Android调用JNI

    native方法在JNI文件中的代码实现(C++和C的代码还是略微不同的,下图分别贴出做下比较,其实.cpp文件中是既可以编写C++代码又可以编写C代码的):

    native代码实现(左:C++,右:C)

    很多人第一次看到JNI里的代码就懵圈了,其实特别简单(对照你所定义的native方法):

    0、实现的方法的格式是JNIEXPORT+返回值类型+JNICALL+Java开头后面拼写包名、类名、方法名并用下划线进行连接,

    1、方法的第一个参数是指针,说白了你不用知道它是啥,只需要知道有了它就可以使用JNI提供的一系列的方法;

    2、方法的第二个参数是当前调用这个方法的java对象,

    3、如果你定义的native方法是带有参数的,这些参数会依次排在第二个参数之后。

    c、JNI调用Android:

    首先我们定义两个java方法,分别有参数和无参数:

    Android调用JNI

    在JNI文件中调用.java文件中的方法,一般分为3部:

    1、首先反射拿到我们需要调用的类,注意包名中的 . 变成 / 否则报错;

    2、然后得到所要调用方法的id,第一个参数是第一步中得到的类,第二个参数是方法名,第三个参数是Signature(签名、签署)【它又分为括号内和括号后:括号内依次是参数列表的类型(具体对照下面的那张Signature的type类型对照图);括号后是方法的返回值类型】

    3、Call系列调用方法,第一个参数是类对象(我们当前使用的是Activity,不能new,就是参数中的instance),第二个参数是第二步得到的方法的id,第三个参数是...也就是可变长参数(也可以不传就是无参)。

    native方法实现中,又调用了java文件中的方法

    Signature的type类型对照图:

    Signature的type类型对照图

    细心的同学发现有些方法后面会带A或V,这是指的需要传入的第三个参数的类型:

    V:代表Vector,可以理解为java中的集合;

    A:代表Union,又名结构体,可以理解为java中的自定义类型。

    带A、V结束的方法,及CallStatic的方法

    当然除了调用普通方法,也不会少了调用static类型的方法CallStatic系列方法。

    其他:

    1、其实JNI交互时,Android调用JNI较多,反过来用会很少,本篇这样研究只是为了与以上的Js、Unity交互相对应,三种语言的交互都有正反调用的讲解,可实现特殊使用时候的回调效果;

    2、JNI的内容非常多,本篇只是以最简单的实现方式教小白们用最快的方法理解并上手,如果需要更多探索请自行研究(百度+google);

    3、如果其他朋友有更多的Android与其他语言交互的经验,希望加群交流赐教!


    安卓开发交流:

    有兴趣一起学习的朋友,请加Q群:76161364

    相关文章

      网友评论

        本文标题:Android_其他语言交互篇——Js、C#、C、C++

        本文链接:https://www.haomeiwen.com/subject/vinbzttx.html