OpenGLES demo - 3. 建立Android工程

原创文章,转载请注明连接 http://blog.csdn.net/hoytgm/article/details/32715655

Android主要都是Java代码,但是我们建立OpenGLES工程,主要还是调用C/C++代码,所以我们需要使用Jni(Java Native Interface)。这里代码的东西比较少,很多都是贴一些Java相关的,本人Java不太熟,所以很多地方就不解释了哈,要是有更好的代码,欢迎指正哈。

根目录下建立AndroidManifest.xml文件,内容照贴

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.hoytgm" >
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name="hoytgm"
                  android:label="@string/app_name"
                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
	<uses-sdk android:minSdkVersion="7" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>


然后就是Android.mk,这个是Android下面的makefile,大家应该比我懂吧

#
# Build 
#

LOCAL_PATH := $(call my-dir)

# Build apk
include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin/
LOCAL_PACKAGE_NAME := hoytgm

LOCAL_SRC_FILES := \
	$(call all-subdir-java-files)

LOCAL_JNI_SHARED_LIBRARIES := \
	libhoytgm

LOCAL_MODULE_TAGS := eng optional
include $(BUILD_PACKAGE)


# Build native shared object
include $(CLEAR_VARS)
LOCAL_MODULE := libhoytgm

LOCAL_SRC_FILES := \
	jni/jniapi.cpp \
	./main.cpp 

LOCAL_C_INCLUDES := \
	./

LOCAL_SHARED_LIBRARIES := \
	liblog \
	libEGL \
	libGLESv2 \
	libandroid 

LOCAL_CFLAGS := \
	-Wall \
	-DANDROID

LOCAL_MODULE_TAGS := eng optional
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

建立三个文件夹和相应的文件,参照我的层次哈,打括号是指目录

(jni) -> jniapi.cpp

(src) -> (com)--> (hoytgm) -> hoytgm.java

(res) -> (drawable)

(res) -> (layout) -> main.xml

(res) -> (values) -> strings.xml


然后是main.xml文件的内容

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<SurfaceView
    android:id="@+id/surfaceview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="#ffffffff"
    android:textSize="60dp"
    />
</RelativeLayout>

和strings.xml的内容

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string name="app_name">hoytgm</string>
</resources>


下面就差hoytgm.java和jniapi.cpp的内容了。这两个文件我们可以稍微讲一下吧。首先是hoytgm.java,需要improt的头
package com.hoytgm;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnClickListener;
import android.util.Log;

然后实现一个SurfaceHolder的类,作为主要的activity

public class hoytgm extends Activity implements SurfaceHolder.Callback {}

在类里面添加一个成员用于打印标志

private static String TAG = "### hoytgm: ";


重载create函数

	@Override
	public void onCreate(Bundle savedInstanceState) {
		
		super.onCreate(savedInstanceState);
		
		Log.i(TAG, "onCreate()");
		
		setContentView(R.layout.main);
		SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surfaceview);
		surfaceView.getHolder().addCallback(this);
		surfaceView.setOnClickListener(new OnClickListener() {
			public void onClick(View view) {
			}
		});
	}

重载几个状态的函数

	@Override
	protected void onStart() {
		
		super.onStart();
		Log.i(TAG, "onStart()");
		nativeOnStart();
	}
	
	@Override
	protected void onResume() {
		
		super.onResume();
		Log.i(TAG, "onResume()");
		nativeOnResume();
	}
	
	@Override
	protected void onPause() {
		
		super.onPause();
		Log.i(TAG, "onPause()");
		nativeOnPause();
	}
	
	@Override
	protected void onStop() {
		
		super.onStop();
		Log.i(TAG, "onStop()");
		nativeOnStop();
	}
	
	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		
        Log.i(TAG, "surfaceChanged()");
		nativeSetSurface(holder.getSurface());
	}
	
	public void surfaceCreated(SurfaceHolder holder) {
	    
        Log.i(TAG, "surfaceCreated()");
	}
	
	public void surfaceDestroyed(SurfaceHolder holder) {
		
		nativeSetSurface(null);
	}

这里大家会发现一些native*()的函数,这些就是需要jniapi.cpp去实现的函数,这里把这些函数的声明也加到我们的这个类里面,并加载这个库

	public static native void nativeOnStart();
	public static native void nativeOnResume();
	public static native void nativeOnPause();
	public static native void nativeOnStop();
	public static native void nativeSetSurface(Surface surface);
	
	static {
		
		System.loadLibrary("hoytgm");
	}

好了,到这里,我们的hoytgm.java就完成了,java相关的代码也结束了,可以开始写jniapi.cpp文件了,先一次性贴出整个文件的代码吧,因为这里就是按照之前的路径和jni的格式把hoytgm.java里面的几个native开头的函数实现了,然后自己创建一个线程用于做Opengl es相关的渲染工作。

/*
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <jni.h>
#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/jni.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>

#undef  LOG_TAG
#define LOG_TAG "### hoytgm:"

#define MAX_SIZE 1024
#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

ANativeWindow* navWindow = 0;
EGLDisplay eglDisplay = 0;
EGLContext eglContext = 0;
EGLSurface eglSurface = 0;

pthread_t mainThreadId = 0;

extern bool Render();
extern void fini();
extern bool init();

enum
{
	KEYCODE_BACK = 4
};

enum RenderMessage 
{
    MSG_NONE = 0,
    MSG_WINDOW_SET,
    MSG_RENDER_LOOP_EXIT,
};

RenderMessage rendermsg;

void destroy()
{
    LOG_I("destroy()");

    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(eglDisplay, eglContext);
    eglDestroySurface(eglDisplay, eglSurface);
    eglTerminate(eglDisplay);

    eglDisplay = EGL_NO_DISPLAY;
    eglSurface = EGL_NO_SURFACE;
    eglContext = EGL_NO_CONTEXT;    
}

void renderLoop() 
{
    bool done = false;
    LOG_I("renderLoop()");
    static unsigned int framecount = 0;

    while(!done)
    {
        if(framecount < 1)
        {
            LOG_I("no render occurs, sleep 500 ms");
            usleep(500);
        }

        switch(rendermsg)
        {
        case MSG_WINDOW_SET:
            init();
            break;

        case MSG_RENDER_LOOP_EXIT:
            done = true;
            fini();
            destroy();
            break;

        default:
            break;
        }

        rendermsg = MSG_NONE;

        if(eglDisplay)
        {
            done = Render();

            framecount++;

            if(!eglSwapBuffers(eglDisplay, eglSurface))
            {
                if(framecount % 60 == 0)
                {
                    LOG_E("eglSwapBuffers() returned error: %d", eglGetError());
                }
            }
        }
    }
}

void* threadCallback(void* )
{
    renderLoop();

    LOG_I("thread exit 0");
    pthread_exit(0);

    return 0;
}

extern "C" {

JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnStart(JNIEnv* jenv, jobject obj)
{
	LOG_I("nativeOnStart()");

    return;
}

JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnResume(JNIEnv* jenv, jobject obj)
{
	LOG_I("nativeOnResume()");

    pthread_create(&mainThreadId, 0, threadCallback, 0);
    LOG_I("thread id: %ld", mainThreadId);

    return;
}

JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnPause(JNIEnv* jenv, jobject obj)
{
	LOG_I("nativeOnPause()");

    rendermsg = MSG_RENDER_LOOP_EXIT;

    return;
}

JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnStop(JNIEnv* jenv, jobject obj)
{
	LOG_I("nativeOnStop()");

    rendermsg = MSG_RENDER_LOOP_EXIT;

    return;
}

JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeSetSurface(JNIEnv* jenv, jobject obj, jobject surface)
{
    if(surface != 0)
    {
        navWindow = ANativeWindow_fromSurface(jenv, surface);
        LOG_I("Got window %p", navWindow);
        rendermsg = MSG_WINDOW_SET;
    }
    else
    {
        LOG_I("Releasing window");
        ANativeWindow_release(navWindow);
    }
}

} // extern "C"

到这里了,差不多就可以工作了吧?当然不行。。。不信你去根目录(有Android.mk)下编译,保证不过~~~因为我们还缺一个main.cpp,这个文件在Android.mk里面已经写好了, 但是我们没有创建并且实现它。而且,我们后面要做的所有工作将在这个文件里面完成,上面写的所有东西,以后几乎都不会碰了。根目录下创建main.cpp文件,这个里面部分渲染工作和第一章讲的都一样,唯一不同的是创建EGL。我觉得Andriod下面创建EGL的方法更有助于我们直接的去了解EGL的参数的创建过程,IOS上面一直觉得怪怪的。。。。。。

首先,我们需要定义一个数组来决定我们需要建立一个怎么样的EGL,也就是定义RGBA的bit大小,需不需要Depth,Stencil,要不要开MSAA等等。同时还需要定义一个额外的数组来告诉显卡我们需要哪个版本。

    const EGLint attribs[] = 
    {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RED_SIZE,           8,
        EGL_GREEN_SIZE,         8,
        EGL_BLUE_SIZE,          8,
        EGL_ALPHA_SIZE,         8,
        EGL_DEPTH_SIZE,         0,
        EGL_STENCIL_SIZE,       0,
        EGL_SAMPLES,            0,
        EGL_NONE,
    };

    EGLint attribListContext[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };


然后我们来得到display的句柄并初始化这个句柄
    egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(egldisplay == EGL_NO_DISPLAY)
    {
        output_log("eglGetDisplay() returned error: 0x%x\n", eglGetError());
        return false;
    }

    if(!eglInitialize(egldisplay, 0, 0))
    {
        output_log("eglInitialize() returned error: 0x%x\n", eglGetError());
        return false;
    }

我们需要知道当前的Display支持多少种配置,不出意外的话,我们刚才列出的那个属性就应该有对应的配置

    /* Get how many configs does the device support */
    if(!eglGetConfigs(egldisplay, NULL, 0, &num_config))
    {
        output_log("eglGetConfigs(NULL) returned error: 0x%x\n", eglGetError());
        return false;
    }

好了,我们现在传入刚才的属性数组,看下能得到多少个满足这个条件的配置,也就是很重要的eglChooseConfig

    if(!eglChooseConfig(egldisplay, attribs, eglConfig_array, num_config, &supportConfigCount))
    {
        output_log("eglChooseConfig() returned error: 0x%x\n", eglGetError());
        return false;
    }

通过eglChooseConfig,我们就能得到我们支持的一个config id,用这个id,我们就能够创建一个eglcontext。eglcontext是一个非常重要的句柄,我们所需要画的任何东西,任何OpenGLES相关的操作,都需要在一个context下面才能工作。

    if(!(eglcontext = eglCreateContext(egldisplay, eglConfig, 0, attribListContext)))
    {
        output_log("eglCreateContext() returned error: 0x%x\n", eglGetError());
        return false;
    }

有了context,我们就差一个surface了,这个surface就相当于我们的backbuffer,所有画的操作和结果都是默认放在这个surface上面的

    if(!(eglsurface = eglCreateWindowSurface(egldisplay, eglConfig, navWindow, 0)))
    {
        output_log("eglCreateWindowSurface() returned error: 0x%x\n", eglGetError());
        return false;
    }

有了eglcontext和eglsurface了,我们再调用eglMakeCurrent将eglcontext设置为当前,我们就可以开始做GLES的工作了。

    if(!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext))
    {
        output_log("eglMakeCurrent() returned error: 0x%x\n", eglGetError());
        return false;
    }

到这里为止,我们在Android上面的工作基本就结束了。剩下的三个函数,init(), Render()和fini()都和IOS上面差不多了。有兴趣的同学可以去我的资源上面下载Android工程的源码并编译,会生成一个apk,就可以安装到虚拟机或者Android手机上面看结果。贴下代码的资源地址 http://download.csdn.net/detail/hoytgm/7532901

好了,Android工程的建立就到这里了。后面demo的代码主要都是在IOS上面实现的,用Android开发的同学们照着代码搬到Android应该没问题吧???调用都是相同的话,如果搬代码过程有什么问题,欢迎大家交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值