검색결과 리스트
글
안드로이드 Framework 단계에서 Surface 생성과정
안드로이드 기기에 있어 가장 중요한 부분이라 할 수 있는 것 중 하나가 바로 기기의 화면에 나오는 View들을 제대로 처리하는 것이라 생각합니다. 특히 역동적인 안드로이드 화면을 출력하기 위해서는 SurfaceView를 사용하게 됩니다. 이번 포스팅에서는 안드로이드 Framework 단계에서 Surface가 생성돠는 과정에 대해 살펴보도록 하겠습니다.
※본 예제는 Android의 Camera 동작 과정을 토대로 Surface가 동작하는 과정에 대해 다루었습니다.
/frameworks/base/core/java/android/hardware/Camera.java
|
1
2
3
4
5
6
7 |
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
if (holder != null) {
setPreviewDisplay(holder.getSurface());
} else {
setPreviewDisplay((Surface)null);
}
} |
cs |
안드로이드의 카메라 Preview를 설정하는 과정에서 setPreviewDisplay() 함수를 통해 SurfaceView 클래스가 적용되고 있는 상황을 나타내고 있습니다. holder 내의 SurfaceView 클래스는 다음과 같습니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 |
public class SurfaceView extends View {
static private final String TAG = "SurfaceView";
static private final boolean DEBUG = false;
final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
final int[] mLocation = new int[2];
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface(); // Current surface in use
final Surface mNewSurface = new Surface(); // New surface we are switching to
....
/**
* Return the SurfaceHolder providing access and control over this
* SurfaceView's underlying surface.
*
* @return SurfaceHolder The holder of the surface.
*/
public SurfaceHolder getHolder() {
return mSurfaceHolder;
}
....
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
private static final String LOG_TAG = "SurfaceHolder";
....
@Override
public Surface getSurface() {
return mSurface;
}
@Override
public Rect getSurfaceFrame() {
return mSurfaceFrame;
}
};
} |
cs |
위의 소스코드를 통해 holder가 mSurface를 return 하고 있고 mSurface는 new Surface()를 통해 클래스를 생성하고 있는 것을 확인하실 수 있습니다. 그렇다면 이번에는 Surface 클래스가 생성되는 과정을 살펴보도록 합시다.
/frameworks/base/core/java/android/view/Surface.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public class Surface implements Parcelable { private static final String TAG = "Surface"; .... // Guarded state. final Object mLock = new Object(); // protects the native state private String mName; int mNativeObject; // package scope only for SurfaceControl access .... /** * Create an empty surface, which will later be filled in by readFromParcel(). * @hide */ public Surface() { } .... public void readFromParcel(Parcel source) { if (source == null) { throw new IllegalArgumentException("source must not be null"); } synchronized (mLock) { // nativeReadFromParcel() will either return mNativeObject, or // create a new native Surface and return it after reducing // the reference count on mNativeObject. Either way, it is // not necessary to call nativeRelease() here. mName = source.readString(); setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); } } .... private void setNativeObjectLocked(int ptr) { if (mNativeObject != ptr) { if (mNativeObject == 0 && ptr != 0) { mCloseGuard.open("release"); } else if (mNativeObject != 0 && ptr == 0) { mCloseGuard.close(); } mNativeObject = ptr; mGenerationId += 1; } } .... } | cs |
맨 처음에 surface가 생성될 때에는 빈 Class 하나가 생성됩니다. 이후 readFromParcel()을 통하여 Surface 클래스 내에 mNativeObject의 값이 채워지게 되며 nativeReadFromParcel() 함수는 다음과 같다.
/frameworks/base/core/jni/android_view_Surface.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | static jint nativeReadFromParcel(JNIEnv* env, jclass clazz, jint nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { doThrowNPE(env); return 0; } sp<Surface> self(reinterpret_cast<Surface *>(nativeObject)); sp<IBinder> binder(parcel->readStrongBinder()); // update the Surface only if the underlying IGraphicBufferProducer // has changed. if (self != NULL && (self->getIGraphicBufferProducer()->asBinder() == binder)) { // same IGraphicBufferProducer, return ourselves return int(self.get()); } sp<Surface> sur; sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder)); if (gbp != NULL) { // we have a new IGraphicBufferProducer, create a new Surface for it sur = new Surface(gbp, true); // and keep a reference before passing to java sur->incStrong(&sRefBaseOwner); } if (self != NULL) { // and loose the java reference to ourselves self->decStrong(&sRefBaseOwner); } return int(sur.get()); } | cs |
일단 지금은 이러한 방식으로 Surface가 생성되고 있구나 라고 감을 잡으시고 이해를 하고 넘어가셨으면 합니다. 위 코드를 확인해보고 예상컨데 Surface로 Parcel을 보내는 주체는 IGraphicBufferProduce를 넘겨주고 있는 듯 하다는 생각으로 진행하면 될 것으로 보입니다.
'안드로이드 > 프레임워크' 카테고리의 다른 글
| SurfaceView가 다른 View 클래스와의 차이(updateWindow() method in SurfaceView class) (0) | 2015.08.17 |
|---|---|
| [Android Developers] SurfaceHolder (0) | 2015.08.13 |
| Java JNI 코드 분석 : GetObjectClass() (0) | 2015.04.29 |
| 안드로이드 프레임워크 프로그래밍(20) [JNI를 통해 Native에서 JAVA 변수 및 클래스 객체 다루기] (0) | 2015.04.28 |
| 안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리] (0) | 2015.04.10 |
설정
트랙백
댓글
글
Java JNI 코드 분석 : GetObjectClass()
안드로이드 프레임워크 프로그래밍을 진행하시다 보면 JNI 부분이 생각보다 많은 지식이 필요함을 느끼고 있습니다. 오늘 분석하게 되는 코드 또한 안드로이드는 물론이고 Java 에서 JNI를 사용할 때 자주 사용하는 함수들인데요. 오늘부터 각 코드들에 대한 설명을 적어내려가볼까 생각합니다.
jclass (*env)->GetObjectClass(env, jobject);
jclass env->GetObjectClass(jobject);
윗부분운 C로 JNI를 코딩하실 때, 아래부분은 C++로 JNI를 코딩하실 때 사용하는 함수입니다. 이 함수는 jobject 변수로부터 해당 변수의 jclass 값을 얻기 위해 사용합니다.
이게 무슨 말이냐 하면, jobject는 java 클래스 객체 자체라고 이해하시면 되겠습니다. 그리고 jclass는 해당 java 클래스의 경로를 저장한다는 역할을 하고 있다는 식으로 이해하시면 감이 오실 것이라 생각합니다. 위의 함수의 결과값인 return은 아래의 함수를 실행할 때에도 같은 값을 나타냅니다.
jclass (*env)->FindClass(env, "Java 클래스의 경로명");
위와 같이 해당 Java 클래스의 경로명을 직접 적어주는 방식으로로도 jclass 값을 return 받으실 수 있습니다.
아래는 위의 소스코드를 적용한 예제입니다. 해당 예제에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.
hello.c
1 2 3 4 5 6 7 8 9 10 11 | #include <string.h> #include <jni.h> #include <stdio.h> void Java_com_example_calljava_MainActivity_callJava(JNIEnv* env, jobject thiz){ //jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity"); jclass jCallJava = (*env)->GetObjectClass(env, thiz); jmethodID testToast = (*env)->GetStaticMethodID(env, jCallJava, "testToast", "()V"); (*env)->CallStaticVoidMethod(env, jCallJava, testToast); } | cs |
결론적으로 말해서
jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
jclass jCallJava = (*env)->GetObjectClass(env, thiz);
이 두 함수의 return 값이 같다는 의미로 기억해 주시면 되겠습니다!!
'안드로이드 > 프레임워크' 카테고리의 다른 글
| [Android Developers] SurfaceHolder (0) | 2015.08.13 |
|---|---|
| 안드로이드 Framework 단계에서 Surface 생성과정 (0) | 2015.04.30 |
| 안드로이드 프레임워크 프로그래밍(20) [JNI를 통해 Native에서 JAVA 변수 및 클래스 객체 다루기] (0) | 2015.04.28 |
| 안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리] (0) | 2015.04.10 |
| 안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기] (0) | 2015.04.02 |
설정
트랙백
댓글
글
안드로이드 프레임워크 프로그래밍(20) [JNI를 통해 Native에서 JAVA 변수 및 클래스 객체 다루기]
안드로이드 Native 단계의 Framework에서는 종종 Java 단계에서 사용되는 Framework의 값들을 사용해야 할 때가 종종 있습니다. 이를 위해 Java 단계에서 native 함수 호출을 통해 Native에 값들을 인자값을 통해 전달하는 방법이 있습니다만 Native 단계에서 실행중인 프로세스가 Java로부터 데이터를 받아올 수 있는 방법은 없을까요?
이러한 의문은 아래의 Native 소스코드를 통해 어떤 방식으로 진행되는지 대략적인 감을 잡으실 수 있을겁니다.
/frameworks/base/core/jni/android_view_Surface.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int register_android_view_Surface(JNIEnv* env) { int err = AndroidRuntime::registerNativeMethods(env, "android/view/Surface", gSurfaceMethods, NELEM(gSurfaceMethods)); jclass clazz = env->FindClass("android/view/Surface"); gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz)); gSurfaceClassInfo.mNativeObject = env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject", "I"); gSurfaceClassInfo.mLock = env->GetFieldID(gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;"); gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(I)V"); clazz = env->FindClass("android/graphics/Canvas"); gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;"); gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I"); gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I"); clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer"); gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I"); clazz = env->FindClass("android/graphics/Rect"); gRectClassInfo.left = env->GetFieldID(clazz, "left", "I"); gRectClassInfo.top = env->GetFieldID(clazz, "top", "I"); gRectClassInfo.right = env->GetFieldID(clazz, "right", "I"); gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom", "I"); return err; } | cs |
위의 소스코드는 우리들이 앞으로 예제를 통해 사용하게 될 소스코드의 일부입니다. 이 코드를 처음 보시는 분이시라면 상당히 복잡한 구조의 코드를 단번에 이해하시기 힘드실 것이라 생각합니다. 진행하기에 앞서 Android 환경에서 JNI의 활용에 대해 자세히 알고 싶으신 분은 이전에 작성하였던 포스팅을 참조해 주시기 바랍니다.
안드로이드 프레임워크 프로그래밍(7) [NDK 활용을 위한 JNI로 JAVA와 C 상호 호출]
안드로이드 프레임워크 프로그래밍(8) [JNI에서 작성된 C++ 코드에서 C 코드 함수 호출하기]
안드로이드 프레임워크 프로그래밍(10) [Native C/C++ 코드에서 Java 호출]
※본 포스팅의 예제는 이전에 작성하였던 예제를 토대로 진행할 예정입니다. 예제가 필요하신 분께서는 아래의 포스팅을 통해 예제를 다운로드 받으시길 바랍니다.
이번 예제에서 사용되는 소스코드들중 일부를 미리 설명을 하고 넘어가도록 하겠습니다. 예제는 C언어를 기본으로 작성되었으며 C++에서는 약간의 수정이 필요함을 밝힙니다.
jclass (*env)->FindClass(env, "Java/Class/Path"); //C
jclass env->FindClass("Java/Class/Path"); //C++
jclass (*env)->FindClass(env, "Java/Class/Path")
Native 소스코드에서 Java의 클래스의 경로를 저장하는 함수입니다.
jfieldID (*env)->GetFieldID(env, jclass, "변수명", "Signature값")
FindClass를 통해 jclass에서 설정한 Class 내에 존재하는 변수의 이름과 해당 변수의 Signature를 설정하는 함수입니다.
Signature에 대해 자세한 내용은 아래 포스팅을 참조해 주시길 바랍니다.
int (*env)->GetIntField(env, thiz, jfieldID);
char (*env)->GetCharField(env, thiz, jfieldID);
float (*env)->GetFloatField(env, thiz, jfieldID);
이전 위에서 설정한 jclass와 jfieldID를 이용하여 Class 내에 있는 변수값을 가져옵니다. 이를 실행히면 Class에 저장된 현재값이 JNI를 통해 Native로 넘어오게 됩니다.
int (*env)->SetIntField(env, thiz, jfieldID, Class에 설정하고자 하는 값);
char (*env)->SetCharField(env, thiz, jfieldID, Class에 설정하고자 하는 값);
float (*env)->SetFloatField(env, thiz, jfieldID, Class에 설정하고자 하는 값);
이를 통해 Java Class 내에 있는 변수의 값은 Native 단계에서 변경할 수 있습니다.
jmethodID(*env)->GetMethodID(env, jclass, "Method명", "Signature값");
jmethodID(*env)->GetStaticMethodID(env, jclass, "Method명", "Signature값");
jclass에 설정된 Class 내에 있는 Method(함수)를 설정해 줍니다. 이를 통해 Native 단계에서 Java 내의 Method를 실행할 수 있는 준비가 완료됩니다.
(*env)->CallVoidMethod(env, jclass, jmethodID, ... );
(*env)->CallIntMethod(env, jclass, jmethodID, ... );
(*env)->CallCharMethod(env, jclass, jmethodID, ... );
(*env)->CallFloatMethod(env, jclass, jmethodID, ... );
(*env)->CallStaticVoidMethod(env, jCallJava, jmethodID, ...);
(*env)->CallStaticIntMethod(env, jCallJava, jmethodID, ...);
(*env)->CallStaticCharMethod(env, jCallJava, jmethodID, ...);
(*env)->CallStaticFloatMethod(env, jCallJava, jmethodID, ...);
위에서 설정한 jclass와 jmethodID 값을 통해 Java Class 내의 Method를 실행합니다. 위의 ... 표시로 되어 있는 부분에 해당 호출하고자 하는 함수의 인자값을 넣어 실행하며 인자값이 없을 경우 빈칸으로 두시면 되겠습니다.
jmethodID (*env)->GetMethodID(env, jclass, "<init>", "Signature값");
jobject (*env)->NewObject(env, jclass, jmethodID);
Native 단계에서 특정 Class를 생성하고자 할 때 사용하는 함수입니다. Native 단계 내에서 jmethodID를 통해 해당 클래스의 Constructor(생성자)를 함수명으로 "<init>"를 생성하며 NewObject() 함수를 통해 객체를 생성해 냅니다.
아래는 이전 소스코드를 응용한 예제입니다.
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | package com.example.calljava; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private TextView tv; private Button bt; private static Context mContext; public int a = 11; public int b = 23; static { System.loadLibrary("hello"); } public native String stringFromJNI(); public native void callJava(); public native int nativeSum(); public native TestClass nativeConst(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.textview); tv.setText(this.stringFromJNI()); mContext = this; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } public void OnClick(View v){ switch(v.getId()){ case R.id.button1: this.callJava(); break; case R.id.button2: Toast.makeText(this, this.nativeSum()+"", Toast.LENGTH_SHORT).show(); break; case R.id.button3: Toast.makeText(this, this.nativeConst().toString(), Toast.LENGTH_SHORT).show(); } } public static void testToast(){ Toast.makeText(mContext, "Hello, Toast!", Toast.LENGTH_SHORT).show(); } } class TestClass{ public TestClass(){ } public String toString(){ return "JNI를 통해 생성된 클래스입니다!"; } } | cs |
Activity_Main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.calljava.MainActivity" > <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textview" android:layout_below="@+id/textview" android:layout_marginTop="14dp" android:onClick="OnClick" android:text="Button1" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button1" android:layout_alignBottom="@+id/button1" android:layout_marginLeft="14dp" android:layout_toRightOf="@+id/button1" android:onClick="OnClick" android:text="Button2" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button2" android:layout_alignBottom="@+id/button2" android:layout_marginLeft="20dp" android:layout_toRightOf="@+id/button2" android:onClick="OnClick" android:text="Button3" /> </RelativeLayout> | cs |
hello.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <string.h> #include <jni.h> #include <stdio.h> void Java_com_example_calljava_MainActivity_callJava(JNIEnv* env, jobject thiz){ jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity"); //jclass jCallJava = (*env)->GetObjectClass(env, thiz); jmethodID testToast = (*env)->GetStaticMethodID(env, jCallJava, "testToast", "()V"); (*env)->CallStaticVoidMethod(env, jCallJava, testToast); } jstring Java_com_example_calljava_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz){ return (*env)->NewStringUTF(env, "Hello, JNI World!"); } jint Java_com_example_calljava_MainActivity_nativeSum(JNIEnv* env, jobject thiz){ jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity"); jfieldID ja = (*env)->GetFieldID(env, jCallJava, "a", "I"); int a = (*env)->GetIntField(env, thiz, ja); jfieldID jb = (*env)->GetFieldID(env, jCallJava, "b", "I"); int b = (*env)->GetIntField(env, thiz, jb); int c = a+b; return c; } jobject Java_com_example_calljava_MainActivity_nativeConst(JNIEnv* env, jobject thiz){ jclass jTestClass = (*env)->FindClass(env, "com/example/calljava/TestClass"); jmethodID jmid = (*env)->GetMethodID(env, jTestClass, "<init>", "()V"); jobject obj = (*env)->NewObject(env, jTestClass, jmid); return obj; } | cs |
'안드로이드 > 프레임워크' 카테고리의 다른 글
| 안드로이드 Framework 단계에서 Surface 생성과정 (0) | 2015.04.30 |
|---|---|
| Java JNI 코드 분석 : GetObjectClass() (0) | 2015.04.29 |
| 안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리] (0) | 2015.04.10 |
| 안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기] (0) | 2015.04.02 |
| 안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정] (0) | 2015.04.01 |
