• JNI笔记


      由于要做一个能够加红字体的dialog,而cocos2d中的CCMessageBox是系统内带的,我无法修改其字体颜色。事实上是可以修改的,通过观察发现CCMessageBox被调用后,在安卓平台中会调用org.cocos2dx.lib.Cocos2dxHandler类中的showDialog方法,结果发现Cocos2dx使用AlertDialog来实现的,贴上代码:

    	private void showDialog(Message msg) {
    		Cocos2dxActivity theActivity = this.mActivity.get();
    		DialogMessage dialogMessage = (DialogMessage)msg.obj;
    		
    		new AlertDialog.Builder(theActivity)
    		.setTitle(dialogMessage.titile)
    		.setMessage(dialogMessage.message)
    		//.setView(view) 有一个setView成员方法允许我们定义自己的View
    		.setPositiveButton("ok", 
    				new DialogInterface.OnClickListener() {
    					
    					@Override
    					public void onClick(DialogInterface dialog, int which) {
    						// TODO Auto-generated method stub
    						
    					}
    				}).create().show();
    	}
    

     然后里面可以通过setView来实现字体加红,但是这个org.cocos2dx.lib是公共库,每个游戏都需要依赖它,修改它可能会带来不知道的隐患,因此我觉得提供一个方法给游戏调用,能降低耦合。这样就需要在Cocos2dx中调用java的AlertDialog,所以便引出了想记录的东西JNI。


     利用create_project命令创建cocos2dx游戏工程,在eclipse中import进来,之后在src文件夹中创建一个.java文件,记录代码如下:

    package com.My.Dialog;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.res.Resources;
    import android.os.Handler;
    import android.os.Message;
    import android.text.Html;
    import android.text.Spanned;
    import android.text.method.ScrollingMovementMethod;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    public class ShowRedDialog {
    	private static Handler mHandler;
    	private Activity activity;
    	
    	public void init(Activity act){
    		Log.i("%s", "show dialog init");
    		activity = act;
    		mHandler = new Handler()
    		    {
    				@Override
    				public void handleMessage(Message msg) {
    					Log.i("%s", "we are in handler");
    					Resources res = activity.getResources();
    					//通过在strings.xml中配置layout的位置的方法来添加View
    					String layoutResStr = res.getString(com.My.Dialog.R.string.view_layout);
    					if(layoutResStr == null){
    						Log.e("%s", "layoutResStr can not find !!!");
    						new AlertDialog.Builder(activity).setTitle("Error").setMessage("layoutResStr can not find !!!")
    															  .setPositiveButton("ok", new DialogInterface.OnClickListener() {
    																
    																@Override
    																public void onClick(DialogInterface dialog, int which) {
    																	// TODO Auto-generated method stub
    																	
    																}
    															}).create().show();
    						
    					}
    					int layoutId = res.getIdentifier(layoutResStr, "", "");
    					Log.i("%s", "the resource is found!!");
    					//反射出layout中的textView
    					LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    					LinearLayout view = (LinearLayout)inflater.inflate(layoutId, null);
    					TextView textview = (TextView) view.getChildAt(0);
    					//Spanned prasedText = Html.fromHtml("<font color="FF0000"> MESSAGE </font> " + Integer.toString(layoutId));
    					//设置textView的文本显示,利用html来修改文本样式
    					Spanned prasedText = Html.fromHtml("<font color="FF0000"> MESSAGE </font>");
    					textview.setText(prasedText);
    					textview.setMovementMethod(ScrollingMovementMethod.getInstance());
    					Log.i("%s", "ready to show our messagebox");
    					//messageBox弹出
    					new AlertDialog.Builder(activity)
    					.setTitle("Warning")
    					.setView(view)
    					.setPositiveButton("ok", 
    							new DialogInterface.OnClickListener() {
    								
    								@Override
    								public void onClick(DialogInterface dialog, int which) {
    									// TODO Auto-generated method stub
    									
    								}
    							}).create().show();
    				}
    		    };
    	}
    	
        public static void showMyDialog(String data)
        {
        	Log.i("in java show my dialog %s", data);
        	Message msg = mHandler.obtainMessage();
        	msg.sendToTarget();
        }
    
    }
    

     这里还有很多要改进的地方,可是不是现在的重点,例如我想把View分离出来,可以让别人去实现;还有方法到底该怎么设计更好,变量是静态还是非静态等等。在这里通过Handler实现C++调用静态函数,然后静态函数调用非静态函数,这只是其中一个实现C++调用非静态函数的办法,网上还有好多。我在strings.xml中加入了如下配置:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">JniDialog</string>
        <string name="hello_world">Hello</string>
        <string name="view_layout">com.My.Dialog:layout/godmsgdialog</string>
    </resources>
    

    在jni/hellocpp下新建test.h和test.cpp,代码:

    #ifndef TEST_H
    #define TEST_H
    
    void showMyDialog(const char *tmp);
    
    #endif
    

    test.cpp中的代码:

    #include <jni.h>
    #include "test.h"
    #include "platform/android/jni/JniHelper.h"
    #include "cocos2d.h"
    #include <android/log.h>
    
    
    #define TAG "myDemo-jni" // 这个是自定义的LOG的标识
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
    #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
    #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
    
    using namespace cocos2d;
    
    void showMyDialog(const char *tmp){
    	LOGI("########## i = %d", "call in JNI");
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    	JniMethodInfo t;
    	if(JniHelper::getStaticMethodInfo(t, "com/My/Dialog/ShowRedDialog", "showMyDialog", "(Ljava/lang/String;)V")){
    		jstring jMsg = t.env->NewStringUTF("do not touch me !!");
    		t.env->CallStaticVoidMethod(t.classID, t.methodID, jMsg);
    		t.env->DeleteLocalRef(jMsg);
    	}
    #endif
    }
    

     在这里遇到了各种问题:

    1、第一个是JniHelper::getStaticMethodInfo静态方法在Eclipse中报错说找不到,解决办法是加上编译头#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)和#endif。

    2、t.env->CallStaticVoidMethod(t.classID, t.methodID, "do not touch me !!");之前是这样调用这个方法的,结果出错需要将C++的字符串转为jstring,jstring jMsg = t.env->NewStringUTF("do not touch me !!");

    3、如何打印日志,通过引入<android/log.h>并添加宏定义然后就可以在LOGCAT中打印日志了,并且参考:http://blog.csdn.net/zengraoli/article/details/11644815来修改Android.mk文件。

    4、jni/hellocpp/main.cpp报错,找不到方法,重启eclipse即可。。。

    5、第四个参数是方法签名例如:"(Ljava/lang/String;)V",可以通过在.class文件目录下打开命令行窗口,输入命令 javap -s -p ShowRedDialog (-s表示打印签名信息 -p表示打印所有函数和成员的签名信息,默认只打印public的签名信息)。


     修改Android.mk文件,加入要编译的test.cpp文件:

    LOCAL_SRC_FILES := hellocpp/main.cpp 
    		   hellocpp/test.cpp 
                       ../../Classes/AppDelegate.cpp 
                       ../../Classes/HelloWorldScene.cpp 
    

    在游戏中如下修改,添加头文件:

    #if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    #include "../proj.android/jni/hellocpp/test.h"
    #endif
    

     添加菜单事件:

    void HelloWorld::menuCloseCallback(CCObject* pSender)
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    	CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    	CCLog("in cocos2d show dialog");
    	showMyDialog("dont touch me !!");
    #else
        CCDirector::sharedDirector()->end();
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
    #endif
    #endif
    }
    

     以上说的是C++调用JAVA,之后有时间再写JAVA调用C++。

  • 相关阅读:
    POJ1741
    聪聪可可
    [USACO07NOV]Cow Relays
    Android 程序的反破解技术
    在Android上实现SSL握手(客户端需要密钥和证书),实现服务器和客户端之间Socket交互
    vmware中的bridge、nat、host-only的区别
    史上最易懂的Android jni开发资料--NDK环境搭建
    使用javah生成.h文件, 出现无法访问android.app,Activity的错误的解决
    Android apk反编译基础(apktoos)图文教程
    Smali文件语法解析
  • 原文地址:https://www.cnblogs.com/Key-Ky/p/4644709.html
Copyright © 2020-2023  润新知