Introduction
Recently we posted the "Windows* 8 Tutorial: Writing a Multithreaded Application for the Windows Store* using Intel® Threading Building Blocks". There we stated that the parallel calculation engine can be ported easily to other mobile or desktop platforms. Android is a good example of such a mobile platform.
In a recently posted stable release of Intel Threading Building Blocks (Intel® TBB), we have added experimental support for Android applications; i.e., building Intel TBB libraries for use in Android applications via the JNI interface. You can download this release from threadingbuildingblocks.org.
To start the process on a Linux* host, unpack the Intel TBB source distribution, source the <unpacked_dir>/build/android_setup.csh script, and build the libraries. Building the libraries is necessary because development releases are distributed in source form only. The <unpacked_dir>/build/index.android.html file contains instructions to configure the environment and build the library on Linux.
Assuming that gnu make 3.81 is available in the %PATH% (on a Microsoft Windows* host platform) and $PATH (on a Linux host) we need to issue the following command in the NDK environment to build the Intel TBB libraries for Android:
gmake tbb tbbmalloc target=android
That’s all that is needed for the library build; we can now move to build the example using Eclipse*. For the example below, I’ll use Android SDK Tools Rev.21 and Android NDK Rev 8C on Windows* to illustrate the process of cross-platform development.
Create a new project using a default template «New Android Application». For simplicity, we name it "app1", the same as in the previous post:
Select FullscreenActivity
as the Activity. That’s it for the template. Please note that com.example* is not an acceptable package naming for Google Play* but it is good enough for our example.
Then add a couple of buttons to the main frame. After adding these, the XML file of the main frame (app1/res/layout/activity_fullscreen.xml) will look like this:
<FrameLayout 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:background="#0099cc"
tools:context=".FullscreenActivity" >
<TextView
android:id="@+id/fullscreen_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:keepScreenOn="true"
android:text="@string/dummy_content"
android:textColor="#33b5e5"
android:textSize="50sp"
android:textStyle="bold" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" >
<LinearLayout
android:id="@+id/fullscreen_content_controls"
style="?buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="74dp"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/black_overlay"
android:orientation="horizontal"
tools:ignore="UselessParent" >
<Button
android:id="@+id/dummy_button1"
style="?buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dummy_button1"
android:onClick="onClickSR" />
<Button
android:id="@+id/dummy_button2"
style="?buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dummy_button2"
android:onClick="onClickDR" />
</LinearLayout>
</FrameLayout>
</FrameLayout>
And the string file (app1/res/values/strings.xml) will look like
="1.0"="utf-8"
<resources>
<string name="app_name">Sample</string>
<string name="dummy_content">Reduce sample</string>
<string name="dummy_button1">Simple Reduce</string>
<string name="dummy_button2">Deterministic Reduce</string>
</resources>
Then add buttons handlers:
private native float onClickDRCall();
private native float onClickSRCall();
public void onClickDR(View myView) {
TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content));
float res=onClickDRCall();
tv.setText("Result DR is n" + res);
}
public void onClickSR(View myView) {
TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content));
float res=onClickSRCall();
tv.setText("Result SR is n" + res);
}
and library loads to the FullscreenActivity.java file:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
…
System.loadLibrary("gnustl_shared");
System.loadLibrary("tbb");
System.loadLibrary("jni-engine");
}
In the case of the "tbb" library everything should be clear, and the "gnustl_shared" library is required to support the C++ language features of TBB. However, for the "jni-engine" library we need to go into more details.
"jni-engine" is a С++ library that implements a calculation engine and exports C-interfaces for JNI calls named onClickSRCall()
and onClickSRCall()
.
According to NDK development rules, create folder "jni" inside the workspace and create 3 files there specific for our "jni-engine" library.
These files are:
Android.mk
(text in <> brackets there is something that should be replaced with actual values)
LOCAL_PATH := $(call my-dir)
TBB_PATH := <path_to_the_package>
include $(CLEAR_VARS)
LOCAL_MODULE := jni-engine
LOCAL_SRC_FILES := jni-engine.cpp
LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -I$(TBB_PATH)/include
LOCAL_LDLIBS := -ltbb -L./ -L$(TBB_PATH)/<path_to_libtbb_so>
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libtbb
LOCAL_SRC_FILES := libtbb.so
include $(PREBUILT_SHARED_LIBRARY)
Application.mk
APP_ABI := x86
APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti
APP_STL := gnustl_shared
jni-engine.cpp
#include <jni.h>
#include "tbb/parallel_reduce.h"
#include "tbb/blocked_range.h"
float SR_Click()
{
int N=10000000;
float fr = 1.0f/(float)N;
float sum = tbb::parallel_reduce(
tbb::blocked_range<int>(0,N), 0.0f,
[=](const tbb::blocked_range<int>& r, float sum)->float
{
for( int i=r.begin(); i!=r.end(); ++i )
sum += fr;
return sum;
},
[]( float x, float y )->float
{
return x+y;
}
);
return sum;
}
float DR_Click()
{
int N=10000000;
float fr = 1.0f/(float)N;
float sum = tbb::parallel_deterministic_reduce(
tbb::blocked_range<int>(0,N), 0.0f,
[=](const tbb::blocked_range<int>& r, float sum)->float
{
for( int i=r.begin(); i!=r.end(); ++i )
sum += fr;
return sum;
},
[]( float x, float y )->float
{
return x+y;
}
);
return sum;
}
extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickDRCall(JNIEnv *env, jobject obj)
{
return DR_Click();
}
extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickSRCall(JNIEnv *env, jobject obj)
{
return SR_Click();
}
We use the same algorithms that we used in the previous blog.
When we use the NDK to build, it compiles libraries to the required folders including our libraries libjni-engine.so, libgnustl_shared.so and libtbb.so.
Next, switch back to Eclipse and build the app1.apk file. Now the application is ready to install on AVD or actual hardware. On AVD it looks like
That’s it! This simple application is ready and should be a good start towards writing a more complex parallel application for Android. And for those who used code from the previous blog, the application was successfully ported to Android.
* Other names and brands may be claimed as the property of others.
Related Articles and Resources
To learn more about Intel tools for the Android developer, visit Intel® Developer Zone for Android.