本文参照了VincentChou的个人博客文章:cocos2d-x在iOS/Android双平台上嵌入WebView和Cocos2d-x中通过JNI进行C++调用Java代码
1.基本说明
要通过jni从cocos2d-x的c++代码调用java函数,要使用到cocos2d-x中有一个JniHelper类。
头文件:#include"platform/android/jni/JniHelper.h"
需要使用的接口如下:
每个参数的意义和使用方法:const char *className,const char *methodName,const char *paramCode); static bool getMethodInfo(JniMethodInfo &methodinfo,const char *className,const char *paramCode);
//函数信息结构体 JniMethodInfo minfo; bool isHave = JniHelper::getStaticMethodInfo(minfo,/*JniMethodInfo的引用*/ "com/omega/MyApp",/*类的路径*/ "getJavaActivity",/*函数名*/ "()Ljava/lang/Object;");/*函数类型简写*/ jobject activityObj; if (isHave) { //CallStaticObjectMethod调用java函数,并把返回值赋值给activityObj activityObj = minfo.env->CallStaticObjectMethod(minfo.classID,minfo.methodID); }
这里重点要说明一下("com/omega/MyApp",/*类的路径*/),这个类的路径,我弄了很久才搞明白,它其实是你在创建android时使用的包名,比如com.xxx.xxx,如果你不知道或者忘记了是什么可以打开proj.android目录下的AndroidManifest.xml文件查看,package="com.cocos2dx.example"中的"com.cocos2dx.example"就是你的包名。下面继续
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.cocos2dx.example"
android:versionCode="1"
android:versionName="1.0">
.....
源文件是放在:youProject\proj.android\src\org\cocos2dx\cpp\AppActivity.java下,比如我的JAVA函数是写在AppActivity.java文件当,那么最终这里类的路径就是:"proj.android\src\org\cocos2dx\cpp\AppActivity.java".
AppActivity.java 代码如下:
/**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2010-2012 cocos2d-x.org Copyright (c) 2011 Zynga Inc. Copyright (c) 2013-2014 Chukong Technologies Inc. http://www.cocos2d-x.org Permission is hereby granted,free of charge,to any person obtaining a copy of this software and associated documentation files (the "Software"),to deal in the Software without restriction,including without limitation the rights to use,copy,modify,merge,publish,distribute,sublicense,and/or sell copies of the Software,and to permit persons to whom the Software is furnished to do so,subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS",WITHOUT WARRANTY OF ANY KIND,EXPRESS OR IMPLIED,INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE,ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ package org.cocos2dx.cpp; import org.cocos2dx.lib.Cocos2dxActivity; import org.cocos2dx.lib.Cocos2dxGLSurfaceView; import org.cocos2dx.hellocpp.R; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; public class AppActivity extends Cocos2dxActivity{ static AppActivity test = null; WebView m_webView; ImageView m_imageView; FrameLayout m_webLayout; LinearLayout m_topLayout; Button m_backButton; protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); test=this; Log.v("cccccc","onCreate"); //初始化一个空布局 m_webLayout = new FrameLayout(this); FrameLayout.LayoutParams lytp = new FrameLayout.LayoutParams(800,640); lytp .gravity = Gravity.CENTER; addContentView(m_webLayout,lytp); } //返回实例 public static AppActivity getInstance() { Log.v("aaaaa","getInstance"); return test; } public void openWebview() { this.runOnUiThread(new Runnable() {//在主线程里添加别的控件 public void run() { //初始化webView m_webView = new WebView(test); //设置webView能够执行javascript脚本 m_webView.getSettings().setJavaScriptEnabled(true); //设置可以支持缩放 m_webView.getSettings().setSupportZoom(true);//设置出现缩放工具 m_webView.getSettings().setBuiltInZoomControls(true); //载入URL m_webView.loadUrl("http://www.baidu.com"); //使页面获得焦点 m_webView.requestFocus(); //如果页面中链接,如果希望点击链接继续在当前browser中响应 m_webView.setWebViewClient(new WebViewClient(){ public boolean shouldOverrideUrlLoading(WebView view,String url) { if(url.indexOf("tel:")<0){ view.loadUrl(url); } return true; } }); //背景图 m_imageView = new ImageView(test); m_imageView.setImageResource(R.drawable.bkgnd); m_imageView.setScaleType(ImageView.ScaleType.FIT_XY); //初始化线性布局 里面加按钮和webView m_topLayout = new LinearLayout(test); m_topLayout.setOrientation(LinearLayout.VERTICAL); //初始化返回按钮 m_backButton = new Button(test); Log.v("zzzz2","openWebView"); // m_backButton.setBackgroundResource(R.drawable.btn); Log.v("zzzz3","openWebView"); LinearLayout.LayoutParams lypt=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT); lypt.gravity=Gravity.RIGHT; m_backButton.setLayoutParams(lypt); m_backButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { removeWebView(); } }); //把image加到主布局里 m_webLayout.addView(m_imageView); //把webView加入到线性布局 m_topLayout.addView(m_backButton); m_topLayout.addView(m_webView); //再把线性布局加入到主布局 m_webLayout.addView(m_topLayout); } }); } //移除webView public void removeWebView() { m_webLayout.removeView(m_imageView); m_imageView.destroyDrawingCache(); m_webLayout.removeView(m_topLayout); m_topLayout.destroyDrawingCache(); m_topLayout.removeView(m_webView); m_webView.destroy(); m_topLayout.removeView(m_backButton); m_backButton.destroyDrawingCache(); } //重写return键 public boolean onKeyDown(int keyCoder,KeyEvent event) { //如果网页能回退则后退,如果不能后退移除WebView if(m_webView.canGoBack() && keyCoder == KeyEvent.KEYCODE_BACK){ m_webView.goBack(); }else{ removeWebView(); } return false; } public Cocos2dxGLSurfaceView onCreateView() { Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this); // Test should create stencil buffer glSurfaceView.setEGLConfigChooser(5,6,5,16,8); return glSurfaceView; } static { System.loadLibrary("cocos2dcpp"); } }
HelloWord.cpp 代码如下:
#include "HelloWorldScene.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include <jni.h> #include "platform/android/jni/JniHelper.h" #endif USING_NS_CC; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); ///////////////////////////// // 2. add a menu item with "X" image,which is clicked to quit the program // you may modify it. // add a "close" icon to exit the progress. it's an autorelease object auto closeItem = MenuItemImage::create( "CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this)); closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2,origin.y + closeItem->getContentSize().height/2)); // create menu,it's an autorelease object auto menu = Menu::create(closeItem,NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu,1); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label auto label = Label::createWithTTF("Hello World","fonts/Marker Felt.ttf",24); // position the label on the center of the screen label->setPosition(Vec2(origin.x + visibleSize.width/2,origin.y + visibleSize.height - label->getContentSize().height)); // add the label as a child to this layer this->addChild(label,1); // add "HelloWorld" splash screen" auto sprite = Sprite::create("HelloWorld.png"); // position the sprite on the center of the screen sprite->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y)); // add the sprite as a child to this layer this->addChild(sprite,0); return true; } void HelloWorld::menuCloseCallback(Ref* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) JniMethodInfo minfo; //getStaticMethodInfo,判断Java静态函数是否存在,并且把信息保存到minfo里 //参数1:JniMethodInfo //参数2:Java类包名+类名 //参数3:Java函数名称 //参数4:函数参数类型和返回值类型 bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/cpp/AppActivity","getInstance","()Lorg/cocos2dx/cpp/AppActivity;"); jobject jobj;//存对象 // log("aaa=%s,bbb=%s",minfo.classID,minfo.methodID); if (isHave) { //这里的调用getInstance,返回Test类的对象。 jobj = minfo.env->CallStaticObjectMethod(minfo.classID,minfo.methodID); if(jobj!=NULL){ isHave = JniHelper::getMethodInfo(minfo,"openWebview","()V"); if (isHave) { //调用openWebview,参数1:Test对象 参数2:方法ID minfo.env->CallVoidMethod(jobj,minfo.methodID); } } } #else CCDirector::sharedDirector()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif #endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) // MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); // return; //#endif // // Director::getInstance()->end(); // //#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) // exit(0); //#endif }