作者博客
http://www.jianshu.com/u/1da72f6f0c2f
文章目录
- 前言
- Js——WebView
- Android端调用Js端(下图示例有参数和无参两种调用)
- Js端调用Android端
- 题外话
- C#——Unity3D
- 调用方式
- 实现
- C、C ——JNI
- 准备工作
- 新项目处理方法
- 已有项目处理方法
- 调用
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系列方法。