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

2020-12-15 14:42:29 浏览数 (1)

作者博客

http://www.jianshu.com/u/1da72f6f0c2f

文章目录

  1. 前言
  2. Js——WebView
    1. Android端调用Js端(下图示例有参数和无参两种调用)
    2. Js端调用Android端
    3. 题外话
  3. C#——Unity3D
    1. 调用方式
    2. 实现
  4. C、C ——JNI
    1. 准备工作
    2. 新项目处理方法
    3. 已有项目处理方法
    4. 调用

1

前言

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

2

Js——WebView

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

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

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

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

android调用js有参无参

②、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的相关方法就行了嘛,哈哈......

③、题外话:

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

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

于是很多开发者将目光移到了腾讯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/

3

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是不需要修改的,如果不涉及到功能修改或增加,就是一劳永逸吖,我们就采取这个方案。

①、调用方式

Unity端调用Android端:

前两行都是固定的(也有其他写法,但这个既常用又简单),最后两个分别是有返回值方法和无返回值方法的调用(第一个参数是安卓所定义的方法的名字,第二个参数是方法所传入的参数<类型是Object数组>,方法带的泛型就是返回值类型); (除了Call、当然还有CallStatic,各位自己试验吧。)

unity调用android

Android端调用Unity端:

编写时需要导入classes.jar,位于unity安装目录下(...UnityEditorDataPlaybackEnginesAndroidPlayerVariationsmonoDevelopmentClassesclasses.jar):

android调用unity

②、实现

我们用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)即可!

4

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开发,我虽然没在项目中用过,但是流程给他讲的明明白白,从他的眼神中我还是看出他对我一点都不相信,呵呵......

①、准备工作:

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

准备CMake及NDK

②、新项目处理方法:

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

新项目增加JNI支持

③、已有项目处理方法:

如果我们已经存在的项目还没有引入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

④、调用:

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系列方法。

0 人点赞