这个例子程序将提供一下:
一个带有支持原生代码的Android应用项目。
一个简单的GUI来定义线程的数量,每个工作者迭代的数量,和一个开启线程的按钮和一个文本框来展示来自原生工作者的消息。
一个原生工作则模仿一个持久的工作。
创建Andoid项目:
1.打开IDE,FIle-》new-》other。
2.从向导列表,展开Andoid的目录。
3.旋转Android Application Project。
4.点击下一步来启动新建Android App向导,如下:
5. Set Application Name to Threads.
6. Set Project Name to Threads.
7. Set Package Name to com.apress.threads.
8. Set Build SDK to Android 4.0.
9. Set Minimum Required SDK to API 8.
10. Click the Next button to proceed.
11. Keep the default settings for the launcher icon by clicking the Next button.
12. Select the Create activity.
13. Choose Blank Activity from the template list.
14. Click the Next button to proceed.
15. In the New Blank Activity step, accept the default values by clicking the
Finish button.
增加这原生的支持:
右击这Threads项目,选择Andoid Tools-》add Native Support从上下文菜单。设置类库的名字为Threads和单击完成按钮。本地代码支持被增加到这个项目。
定义字符串资源:
<resources>
<string name="app_name">Threads</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">Threads</string>
<string name="threads_edit">Thread Count</string>
<string name="iterations_edit">Iteration Count</string>
<string name="start_button">Start Threads</string>
</resources>
定义布局文件
<LinearLayout 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:orientation="vertical" >
<EditText
android:id="@+id/threads_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
Download at http://www.pin5i.com/
183 CHAPTER 7: Native Threads
android:hint="@string/threads_edit"
android:inputType="number" >
<requestFocus />
</EditText>
<EditText
android:id="@+id/iterations_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/iterations_edit"
android:inputType="number" />
<Button
android:id="@+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_button" />
<ScrollView
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/log_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
实现Main Activity:
import android.widget.EditText;
import android.widget.TextView;
/**
* Main activity.
*
* @author Onur Cinar
*/
public class MainActivity extends Activity {
/** Threads edit. */
private EditText threadsEdit;
/** Iterations edit. */
private EditText iterationsEdit;
/** Start button. */
private Button startButton;
/** Log view. */
private TextView logView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the native code
nativeInit();
threadsEdit = (EditText) findViewById(R.id.threads_edit);
iterationsEdit = (EditText) findViewById(R.id.iterations_edit);
startButton = (Button) findViewById(R.id.start_button);
logView = (TextView) findViewById(R.id.log_view);
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
int threads = getNumber(threadsEdit, 0);
int iterations = getNumber(iterationsEdit, 0);
if (threads > 0 && iterations > 0) {
startThreads(threads, iterations);
}
}
});
}
@Override
protected void onDestroy() {
// Free the native resources
nativeFree();
super.onDestroy();
}
/**
* On native message callback.
*
* @param message
* native message.
*/
private void onNativeMessage(final String message) {
runOnUiThread(new Runnable() {
public void run() {
logView.append(message);
logView.append("\n");
}
});
}
/**
* Gets the value of edit text as integer. If the value
* is empty or count not be parsed, it returns the
* default value.
*
* @param editText edit text.
* @param defaultValue default value.
* @return numeric value.
*/
private static int getNumber(EditText editText, int defaultValue) {
int value;
try {
value = Integer.parseInt(editText.getText().toString());
} catch (NumberFormatException e) {
value = defaultValue;
}
return value;
}
/**
* Starts the given number of threads for iterations.
*
* @param threads thread count.
* @param iterations iteration count.
*/
private void startThreads(int threads, int iterations) {
// We will be implementing this method as we
// work through the chapter
}
/**
* Initializes the native code.
*/
private native void nativeInit();
Download at http://www.pin5i.com/
186 CHAPTER 7: Native Threads
/**
* Free the native resources.
*/
private native void nativeFree();
/**
* Native worker.
*
* @param id worker id.
* @param iterations iteration count.
*/
private native void nativeWorker(int id, int iterations);
static {
System.loadLibrary("Threads");
}
onNativeMessage是一个回调接口函数能够有原生代码来发送进度消息到这UI。Android并不允许代码运行在一个不同的线程与主要UI线程来获得或管理这UI组件。作为这native工作函数期望执行在一个不同的线,这onNativeMessage方法仅仅调度这实际的update操作通过这runOnUiThread方法。
startThreads方法仅仅开始请求到恰当线程例子。正如你浏览这张,你将经历线程的不同特征。这startThreads函数将这些不同例子的转化。
nativeInit方法由原生代码实现。它操作者原生代码的初始化来执行自身的线程。
nativeFree方法:被实现在原生代码。它是否原生资源当Activity被销毁。
nativeWorker方法:由原生代码实现和一个重复一个长期的任务。它有两个参数,这工作的ID和迭代的数量。
产生着C/C++头文件:
为了产生函数签名对于这两个原生函数,首先选择这MainActivity.java源文件使用Project Explorer,和选择Run-》External Tools-》Generate C and C++ Header File从头部菜单。这javah将产生这个头文件在jni目带有内容,如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_apress_threads_MainActivity */
...
/*
* Class: com_apress_threads_MainActivity
* Method: nativeInit
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_apress_threads_MainActivity_nativeInit
(JNIEnv *, jobject);
/*
* Class: com_apress_threads_MainActivity
* Method: nativeFree
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_apress_threads_MainActivity_nativeFree
(JNIEnv *, jobject);
/*
* Class: com_apress_threads_MainActivity
* Method: nativeWorker
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_com_apress_threads_MainActivity_nativeWorker
(JNIEnv *, jobject, jint, jint);
实现原生函数:
实现原生函数1.右键jni目录
2.new-》other从上下文
3.从向导列表,扩展c/c++目录
4.选择源文件向导
5.点击Next按钮
6.使用这New source File对话框,设置源文件为com_appress_threads_MainActivity.cpp
7.点击完成
#include <stdio.h>
#include <unistd.h>
#include "com_apress_threads_MainActivity.h"
// Method ID can be cached
static jmethodID gOnNativeMessage = NULL;
JNIEnv* env,
jobject obj)
{
// If method ID is not cached
if (NULL == gOnNativeMessage)
{
// Get the class from the object
jclass clazz = env->GetObjectClass(obj);
// Get the method id for the callback
gOnNativeMessage = env->GetMethodID(clazz,
"onNativeMessage",
"(Ljava/lang/String;)V");
// If method could not be found
if (NULL == gOnNativeMessage)
{
// Get the exception class
jclass exceptionClazz = env->FindClass(
"java/lang/RuntimeException");
// Throw exception
env->ThrowNew(exceptionClazz, "Unable to find method");
}
}
}
void Java_com_apress_threads_MainActivity_nativeFree (
JNIEnv* env,
jobject obj)
{
}
void Java_com_apress_threads_MainActivity_nativeWorker (
JNIEnv* env,
jobject obj,
jint id,
jint iterations)
Download at http://www.pin5i.com/
189 CHAPTER 7: Native Threads
{
// Loop for given number of iterations
for (jint i = 0; i < iterations; i++)
{
// Prepare message
char message[26];
sprintf(message, "Worker %d: Iteration %d", id, i);
// Message from the C string
jstring messageString = env->NewStringUTF(message);
// Call the on native message method
env->CallVoidMethod(obj, gOnNativeMessage, messageString);
// Check if an exception occurred
if (NULL != env->ExceptionOccurred())
break;
// Sleep for a second
sleep(1);
}
}
这原生源文件包含三个原生函数:
Java_com_appress_threads_MainActivity_nativeInit函数初始化这原生代码通过指定方法的ID对于onNativeMessage回调函数和缓存它在gOnativeMessage全局变量。
Java_com_appress_threads_MainAcitvity_nativeFree 函数时一个占位符对于释放原生的资源。你将实现这个函数通过。
Java_com_appress_threads_MainActivity_nativeWorker函数执行重复的一个持久工作通过一个for循环。它基于指定迭代数量和在迭代之间休眠1s。它和迭代的状态通信和UI触发onNativeMessage回调函数。
更新着Android.mk建立脚本
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Threads
LOCAL_SRC_FILES := com_apress_threads_MainActivity.cpp
include $(BUILD_SHARED_LIBRARY)