chapter 5 file i/o and jni - forejune.co · chapter 5 file i/o and jni since android uses java as...

15
Chapter 5 File I/O and JNI Since Android uses Java as its programming language, it inherits plenty of the Java classes for accessing files and I/O resources, though many of the Java I/O fields and methods are not needed in practice and might have made the language less attractive. In addition, Android defines other means of specifying and accessing resources. We have seen that Android generates the resource directory res, which contains other files and subdirectories to define layouts, resources, assets, and parameters, often in xml format. The URL http://developer.android.com/reference/android/content/Context.html that describes the functionalities of the class Context, presents a lot of methods for accessing I/O resources. Android also supports JNI (Java Native Interface) for Java programs to interact with native code written in C/C++. JNI is vendor-neutral and has support for loading code from dynamic shared libraries, which can be efficient. 5.1 Read Raw Data From File If we do not want to specify our data in the xml format but to keep them as raw data, we can create our own subdirectories and files under the res directory. Let us consider a very simple example to illustrate the technique. Suppose in our example, we want to read in the data saved in a file, say res/raw/hello, as unformated strings. We call our project and application ReadRaw, and the package, data.readraw. Suppose like what we did before, we have used the defaults of the Eclipse Android settings to create the project. We continue to do the following in Eclipse IDE: 1. Create Directory res/raw: Click File > New > Folder. Select the parent folder to be ReadRaw/res and enter raw for Folder name. Then click Finish, which creates the directory res/raw. 2. Create File res/raw/hello: Click File > New > File. Select the parent folder to be ReadRaw/res/raw and enter hello for File name. Then click Finish, which creates the file res/raw/hello. You may enter any text in hello. For example, we enter Android The Beautiful! in the file and save it. 3. Modify MainActivity.java: Modify the main program MainActivity.java to the following code, which simply reads in the data in the file res/raw/hello and prints it out as a string. --------------------------------------------------------------------- package data.readraw; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.os.Build; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { 1

Upload: others

Post on 05-Jan-2020

27 views

Category:

Documents


0 download

TRANSCRIPT

Chapter 5 File I/O and JNI

Since Android uses Java as its programming language, it inherits plenty of the Java classes foraccessing files and I/O resources, though many of the Java I/O fields and methods are not neededin practice and might have made the language less attractive. In addition, Android defines othermeans of specifying and accessing resources. We have seen that Android generates the resourcedirectory res, which contains other files and subdirectories to define layouts, resources, assets, andparameters, often in xml format. The URL

http://developer.android.com/reference/android/content/Context.html

that describes the functionalities of the class Context, presents a lot of methods for accessing I/Oresources.

Android also supports JNI (Java Native Interface) for Java programs to interact with native codewritten in C/C++. JNI is vendor-neutral and has support for loading code from dynamic sharedlibraries, which can be efficient.

5.1 Read Raw Data From File

If we do not want to specify our data in the xml format but to keep them as raw data, we can createour own subdirectories and files under the res directory. Let us consider a very simple exampleto illustrate the technique. Suppose in our example, we want to read in the data saved in a file,say res/raw/hello, as unformated strings. We call our project and application ReadRaw, and thepackage, data.readraw. Suppose like what we did before, we have used the defaults of the EclipseAndroid settings to create the project. We continue to do the following in Eclipse IDE:

1. Create Directory res/raw: Click File > New > Folder. Select the parent folder to beReadRaw/res and enter raw for Folder name. Then click Finish, which creates the directoryres/raw.

2. Create File res/raw/hello: Click File > New > File. Select the parent folder to beReadRaw/res/raw and enter hello for File name. Then click Finish, which creates thefile res/raw/hello. You may enter any text in hello. For example, we enter Android TheBeautiful! in the file and save it.

3. Modify MainActivity.java: Modify the main program MainActivity.java to the followingcode, which simply reads in the data in the file res/raw/hello and prints it out as a string.

---------------------------------------------------------------------package data.readraw;

import java.io.IOException;import java.io.InputStream;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.os.Build;

public class MainActivity extends Activity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

1

2 Read Assets and Display Files

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);String str = null;InputStream inputStream = null;try {

inputStream = getResources().openRawResource(R.raw.hello);byte[] reader = new byte[inputStream.available()];while (inputStream.read(reader) != -1) {}str = new String ( reader );System.out.println ( str );

} catch(IOException e) {Log.e(Tag", e.getMessage());

}}

}---------------------------------------------------------------------

In the code, getResources() is an abstract method declared in the class Context. It returns aResources instance for the application’s package. Note that our class MainActivity extends Ac-tivity, which extends the class ContextThemeWrapper; one of the intermediate classes in theclass hierarchy android.view.ContextThemeWrapper provides an implementation for the methodgetResources. The returned Resources object calls its method OpenRawResources to open adata stream for reading a raw resource, which in the example is specified by the resource nameR.raw.hello and will be translated to an integer indentifier by the Android tools. The code createsa byte array, named reader, large enough to hold all the data of the input data stream using theInputStream class method available(). The raw bytes read into the array reader is converted to astring usng the String constructor and is printed out as a log message.

When we run the application, we will see the message Android The Beautiful! appeared in theText column of the Eclipse IDE LogCat output.

5.2 Read and Write Files

5.2.1 Read Assets and Display Files

For each application, Android tools generate an assets directory for users to put customized re-sources there. In contrast to the res directory, which is accessible from R.class, assets behaves likea file system, In the res directory, each file is assigned an integer identifier, which can be accessedeasily through R.id.res id, providing convenient ways to access images, sounds, icons, xml filesand some commonly used resources. On the other hand, the assets directory provides users morefreedom to put any file there, which can be then accessed like a file in a Java file system. Androiddoes not generate IDs for assets content, and thus we need to specify relative paths and names forfiles inside the directory assets.

The Android public final class AssetManager, which extends class Object provides access to anapplication’s raw asset files. The class presents a lower-level API that allows one to open and readany raw files lying inside the Assets directory or its subdirectories. The APIs could read the rawasset files that have been bundled with the application as a simple stream of bytes.

To illustrate the technique, we again consider a simple example that reads data from an assetfile as raw streams. We use the getAssets() method of public class Resources to obtain an Asset-Manager object, from which we use its open() method to open the asset file as an input stream.Suppose we have used Eclipse IDE defaults to create the the project and application ReadAsset,and we call the package data.readasset, and the asset file hello world.txt. We do the following tofinish building the project that reads data from the asset file and prints out its data as a string:

Chapter 5 File I/O and JNI 3

1. Create File assets/hello world.txt: Click File > New > File. Select the parent folder to beReadAsset/assets and enter hello world.txt for File name. Then click Finish, which createsthe file assets/hello world.txt. You may enter any text in hello world.txt. For example, weenter Hello World, Democracy the Beautiful! in the file and save it.

2. Modify MainActivity.java: Modify the main program MainActivity.java to the followingcode, which simply reads the data from the file assets/hello world.txt as a simple stream andprints it out as a string.

---------------------------------------------------------------------package data.readassest;

import java.io.IOException;import java.io.InputStream;import android.os.Bundle;import android.app.Activity;import android.content.res.AssetManager;

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);

try{InputStream inputStream=this.getAssets().open("hello_world.txt");int nBytes = inputStream.available();byte data[] = new byte[nBytes];String str = "";

//read all data from file "hello_world.txt"while( inputStream.read(data) != -1);

str = new String ( data );System.out.println ( str );inputStream.close();

} catch(IOException e) {e.printStackTrace();

}}

}---------------------------------------------------------------------

In the code, the statementInputStream inputStream = this.getAssets().open(“hello world.txt”);

finds the file location and opens it as an input stream. The code finds the number of bytes thefile has using the method available() of the class InputStream. It then allocates memory foran array, reads the data as bytes to the array, converts the bytes to a string using a constructorof the class String and prints out the string. When we run the application, we should see thetext Hello World, Democracy the Beautiful! appear in the Text column of the entry with TagSystem.out of the Eclipse IDE LogCat output.

We can also make use of the AssetManager class to display files, including subdirectories inthe assets directory. We can use the list() method of AssetManager to find all the files insidethe assets directory. The method takes a string as the input parameter, which specifies a relativepath like docs/home.html within assets; it returns a String array of all the assets at the given path.

4 Read Assets and Display Files

Suppose in our project, in addition to the file hello world.txt, we have created a subdirectory, dir1,inside assets and another subdirectory, dir2 under dir1. We create another file, called demo.txtinside assets/dir1/dir2. The following code shows using this method to display all assets files anddirectories:

---------------------------------------------------------------------package data.displayasset;

import java.io.File;import android.util.Log;import android.os.Bundle;import java.io.IOException;import android.app.Activity;import android.content.res.AssetManager;

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final AssetManager mgr = getAssets();displayFiles(mgr, "");

}

void displayFiles (AssetManager mgr, String path) {try {

String list[] = mgr.list(path);if (list != null)for (int i=0; i<list.length; ++i) {

if ( path.length() > 0 ) {Log.v("Assests:", path + "/" + list[i]);displayFiles(mgr, path + "/" + list[i] );

}else{Log.v("Assests:", path + list[i]);displayFiles(mgr, path + list[i] );

}}

elseLog.v("List:", "empty!");

} catch (IOException e) {Log.v("List error:", "can’t list" + path);

}}

}---------------------------------------------------------------------

In the code, the method displayFiles() is called recursively to display all the assets files and direc-tories. Initially, the empty string is passed in as the path parameter for the list() method of the classAssetManager, which returns the files and directories in the root directory of the asset file system.A directory name is then appended to path to search for the next level recursively. At deeper levelsthe symbol ‘/’ is added to construct a path in the hierarchical form dir1/dir2. Figure 5-1 shows ascreen shot of a portion of the Eclipse IDE, showing the project of the outputs at LogCat when theprogram is executed. As one can see from the outputs, besides the files in the assets directory, thesystem also consists of other assets such as images, and sounds.

Chapter 5 File I/O and JNI 5

Figure 5-1 Assets

5.2.2 Write to Files

We can easily use a Java class of file I/O to save data in a file. The interested question is: where isthe written file located? To answer this question, let us again consider a simple example, in whichwe write some text to a file named sampleFile.txt, and read it back, printing the text on the LogCatoutput. Suppose we call the project of this example WriteFile. The following is the complete codeof the MainActivity of WriteFile:

---------------------------------------------------------------------package data.writefile;

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.IOException;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.Menu;

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);try {

final String helloString = new String("Hello, Friend!");FileOutputStream fos = openFileOutput("sampleFile.txt",

MODE_WORLD_READABLE);OutputStreamWriter osw = new OutputStreamWriter( fos );

// Write the string to the fileosw.write ( helloString );

// Flush out anything in bufferosw.flush();osw.close();

// Read the file back...

6 Write to Files

FileInputStream fis = openFileInput( "sampleFile.txt" );InputStreamReader isr = new InputStreamReader( fis );

// Prepare a char-Array that to read data backchar[] inputBuffer = new char[fis.available()];

// Fill the Buffer with data from the fileisr.read(inputBuffer);

// Transform the chars to a StringString readString = new String(inputBuffer);

// Check if we read back the same chars that we had written outboolean writeReadEqual = helloString.equals ( readString );Log.i( "String read:", readString );Log.i( "File Reading:", "success = " + writeReadEqual );

} catch (IOException ioe) { ioe.printStackTrace(); }}

}---------------------------------------------------------------------

In the code, we use the methods openFileOutput() and openFileInput() of the class Context,which are implemented in ContextWrapper, to access files. For openFileOutput, the first param-eter is file name and the second is access mode, which specifies who has the right to access thefile. In the example, the mode is MODE WORLD READABLE implying that everyone can accessthe file.

When we run this code in Eclipse IDE, we will see in the LogCat a portion of the output similarto following:

Application Tag Textdata.writefile String read: Hello, Friend!data.writefile File Reading: success = true

The log indicates that we have successfully written the data to the file sampleFile.txt and read thedata back. However, if we examine the directory tree of the project WriteFile, we will not find inany of the subdirectories. This is because the file has been saved somewhere else, in an emulatedfile system, which is compressed and is in the path of our home directory. We can find this out byissuing a listing command such as

$ ls ∼/.android/

In this command, the symbol ‘∼’ means home directory and the dot ‘.’ preceding a directory namemeans that the directory is hidden. This command displays the files, including directories under.android, similar to the following:

adbkey androidwin.cfg debug.keystore repositories.cfgadbkey.pub avd default.keyset sites-settings.cfgadb_usb.ini cache modem-nv-ram-5554androidtool.cfg ddms.cfg modem-nv-ram-5556

Suppose we examine deeper directories:

$ ls /.android/avd/avd name/

This command would list files with names like the following:

Chapter 5 File I/O and JNI 7

cache.img emulator-user.ini sdcard.img userdata-qemu.imgcache.img.lock hardware-qemu.ini sdcard.img.lock userdata-qemu.img.lockconfig.ini hardware-qemu.ini.lock userdata.img

Our data file sampleFile.txt is saved in the compressed file image userdata-qemu.img. (We canverify this by a command like strings userdata-qemu.img | grep sampleFile, which would dis-play sampleFile.txt.) The compressed image can be decompressed by the Android extra utilitysimg2img. Alternatively, we can extract the data files using the Android adb command with thepull option. (The command adb –help lists all options of the utility adb.) We can first make adirectory, say uncompressed inside our project:

$ mkdir uncompressed$ cd uncompressed

While extracting the data, the emulator should be running. To check the device status, we canissue the command,

$ adb devices

which lists the attached devices, displaying a message similar to the following:

List of devices attachedemulator-5554 device

Now we can extract the data using the command,

$ adb pull data

If we use ls to list the files in uncompressed, we will see the following list:

anr backup data nativebenchmark property tombstonesapp dalvik-cache misc nativetest system

Our file sampleFile.txt is in the path data, which can be displayed by:

$ ls data/data.writefile/files/

We can use the cat command to see its content:

$ cat data/data.writefile/files/sampleFile.txt

which displays the message

Hello, Friend!

which is indeed the text we have written to sampleFile.txt in our applicaiton.For more information about internal storage, one can refer to the site:

http://developer.android.com/guide/topics/data/data-storage.html#filesInternal

5.2.3 External Storage

Normally, an Android device supports a shared external storage that we can store data. It canbe a removable storage media such as an SD card. Files saved in the external storage are world-readable. They can be accessed and modified by the user if the USB mass storage has been enabledto transfer files on a computer.

In order to read or write files on the external storage, the application must acquire systempermissions READ EXTERNAL STORAGE or WRITE EXTERNAL STORAGE, which can be doneby adding the permission statements in the manifest file AndroidManifest.xml like the following:

8 A Simple Example Of JNI

<manifest ...><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />...

</manifest>

External storage will become unavailable if the user removes the media. There is no enforcingsecurity on files that we save in the external storage; any application can read or write the files andthe user can remove them.

We can setup external storage in the emulator by clicking in the Eclipse IDE:

Window > Android Virtual Device Manager > Android Virtual Devices

Then we choose our AVD and click Edit, which displays an I/O panel showing the information ofour AVD. In the entry SD Card, we may check Size and enter 200 for 200 MB storage.

5.3 Android JNI

5.3.1 Installing Android NDK

The Java Native Interface (JNI) is a programming framework that enables Java code running in aJava Virtual Machine (JVM) to interface with native applications that may be specific to a hardwareand operating system platform. Through JNI, Java programs can call libraries written in otherlanguages such as C/C++ and assembly.

In Android, two key data structures, JavaVM and JNIEnv are defined by JNI. They are essen-tially pointers to pointers to function tables. The JavaVM structure provides invocation interfacefunctions, which allow us to create and destroy a JavaVM object. Though in principle a processcan have multiple JavaVM objects, Android only allows one per process. The JNIEnv structureprovides most of the JNI functions and our native functions all receive a JNIEnv object as the firstargument. It is used for thread-local storage and thus we cannot share a JNIEnv object threads.

To use the JNI, we must first install the Android NDK, which can be downloaded from the site:https://developer.android.com/tools/sdk/ndk/index.html

We have to unzip and unpack the package. Suppose we unpack it into the directory/apps/android/android-ndk-r10

To associate ths package with the Eclipse IDE, click Window > Preferences > Android > NDK.Then enter the root directory of the NDK for NDK Location (e.g. /apps/android/android-ndk-r10), and click Apply > OK. (You may need to restart Eclipse.)

5.3.2 A Simple Example of JNI

We present a simple example here to illustrate how an Android Java program can call a routinewritten in C/C++ through JNI. Suppose we call our project and application JniDemo and the pack-age data.jnidemo. As usual, we have created the project using Eclipse and the default main pro-gram is MainActivity.java. The steps of writing a C program and calling its function are exaplainedbelow. In our example, our C program ( sum-jni.c ) has a function named sum( int n ), which addsthe integers from 1 to n and returns the sum of them.

1. Add folder jni: Click File > New > Folder. Enter JniDemo for the parent folder name andjni for the folder name. Then click Finish to create the folder named jni.

2. Add Android.mk inside folder jni: Click File > New > File. Enter JniDemo/jni for theparent folder name and Android.mk for the file name and click Finish to create the file.

Chapter 5 File I/O and JNI 9

This file acts as a Makefile for linking Java and C programs. We write it with the followngstatements:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := sum-jniLOCAL_SRC_FILES := sum-jni.cinclude $(BUILD_SHARED_LIBRARY)

This Makefile says that our C file, which contains the C routines is named sum-jni.c.3. Create a Java source wrapper, SumWrapper.java: Click File > Class. Enter JniDemo/src

for Source Foler and SumWrapper for Name. Click Finish to create the class. Modify thefile to the following:

package data.jnidemo;

public class SumWrapper{

// Declare native method public to expose itpublic static native int sum ( int n );

public static int getSum ( int n) {int s = sum ( n ); //call native methodreturn s;

}// Load librarystatic {

System.loadLibrary( "sum-jni"); // C-file is sum-jni.c}

}

This wrapper‘s job is to load the library, and expose any functions in the C program we willuse in other Java programs.

4. Create the C header: We compile the source SumWrapper.java to obtain its class file and useit to create a C header file, which contains the function prototypes of the native methods.We do this in a terminal via the javac command, assuming our workspace is in the directory/workspace:

$ cd /workspace/JniDemo/src #change into the source directory$ javac -d /tmp data/jnidemo/SumWrapper.java

The switch -d specifies an output directory, and here we simply put the class in the directory/tmp. We can list the class with the command ls /tmp/data/jnidemo/, which will display thefile name SumWrapper.class. The C header file can be created by the commands:

$ cd /tmp$ javah -jni data.jnidemo.SumWrapper

This creates the C header file data jnidemo SumWrapper.h in the directory /tmp, whichlooks like the following:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class data_jnidemo_SumWrapper */

10 A Simple Example Of JNI

#ifndef _Included_data_jnidemo_SumWrapper#define _Included_data_jnidemo_SumWrapper#ifdef __cplusplusextern "C" {#endif/** Class: data_jnidemo_SumWrapper

* Method: sum

* Signature: (I)I

*/JNIEXPORT jint JNICALL Java_data_jnidemo_SumWrapper_sum

(JNIEnv *, jclass, jint);#ifdef __cplusplus}#endif#endif

5. Create C source code: Using the prototype generated by javah, we can implement our Csource code. We first create the file sum-jni.c inside the directory jni by clicking File >New > File and entering file name sum-jni.c. Modify this file to the following:

#include <jni.h>JNIEXPORT jint JNICALL Java_data_jnidemo_SumWrapper_sum

(JNIEnv * je, jclass jc, jint n){

int i, sum = 0;

for ( i = 1; i <= n; i++ )sum += i;

return sum;}

6. Build a shared library: We use the ndk-build script to build a shared library from sum-jni.c.Suppose we have installed the Android NDK in the directory /apps/android/android-nkk-r10. To compile, we first go to the project directory /workspace/JniDemo/jni/, which wecreated earlier, then run the ndk-build script provided by the Android NDK:

$ cd /workspace/JniDemo/jni/$ /apps/android/android-ndk-r10/ndk-build

The script makes use of the information in jni/Android.mk to create the dynamic-linked li-brary libsum-jni.so from sum-jni.c and install it in the directory libs/armeabi of the project.(i.e. /workspace/JniDemo/libs/armeabi ) The script generates the following messages, re-flecting what it has done:

[armeabi] Compile thumb :sum-jni <= sum-jni.c[armeabi] SharedLibrary :libsum-jni.so[armeabi] Install :libsum-jni.so=>libs/armeabi/libsum-jni.so

7. Modify MainActivity.java: Modify the default main program to the following:

package data.jnidemo;

Chapter 5 File I/O and JNI 11

import android.app.Activity;import android.os.Bundle;import android.util.Log;

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);int n = 10;int s = SumWrapper.getSum( n );Log.v ( "Sum of", String.format ("1 to %d = %d",n,s));

}}

This main program deos not do much. It simply calls the SumWrapper’s getSum( n )method, which calls the native method sum( n ) to calculate the sum 1 + 2 + ... + n. Itsends the result to the LogCat.

8. Run the program: We run the progam as usual, clicking Run > Run. The emulator willnot show any output but the Eclipse LogCat will show the sum output. Figure 5-2 shows acaputred image of the Eclipse environment when running the program; we can see from itthe file struture and log output of the project.

Figure 5-2 A Simple JNI Example

5.3.3 A Simple JNI Example with UI

We can easily add a UI to the above example. Suppose we call this new project JniDemo1, andhave gone through the above steps. To create a UI we first modify res/layout/activity main.xml to:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><LinearLayout

12 A Simple JNI Example with UI

android:id="@+id/linearLayout0"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="12pt"android:layout_marginRight="12pt"android:layout_marginTop="4pt" >

<TextViewandroid:id="@+id/info"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="6pt"android:layout_marginRight="6pt"android:layout_marginTop="4pt"android:gravity="center_horizontal"android:text="Enter a postive number:"android:textSize="12pt" >

</TextView></LinearLayout>

<LinearLayoutandroid:id="@+id/linearLayout1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="12pt"android:layout_marginRight="12pt"android:layout_marginTop="4pt" >

<EditTextandroid:id="@+id/n"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginRight="6pt"android:layout_weight="1"android:inputType="numberDecimal" >

</EditText>

<Button android:id="@+id/getsum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Get Sum"android:textSize="10pt"android:onClick="onClick"/>

</LinearLayout>

<TextViewandroid:id="@+id/sum"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="6pt"android:layout_marginRight="6pt"android:layout_marginTop="4pt"android:gravity="center_horizontal"android:textSize="12pt" >

</TextView>

Chapter 5 File I/O and JNI 13

</LinearLayout>

In this layout, we define an EditText for the user to enter an integer. We define a Button namedgetsum for the user to click upon that calls the summing routine. The result is didsplayed inTextView sum.

We also need to modify MainActivity.java to the following:

package data.jnidemo1;

import android.os.Bundle;import android.view.View;import android.widget.*;import android.app.Activity;import android.text.TextUtils;

public class MainActivity extends Activity{

private EditText n;private TextView sum;private Button getsum;

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);n = (EditText) findViewById(R.id.n);getsum = (Button) findViewById(R.id.getsum);sum = (TextView) findViewById(R.id.sum);TextView info = (TextView) findViewById(R.id.info);

}

// @Overridepublic void onClick(View view) {

// check if the fields are emptyif (TextUtils.isEmpty(n.getText().toString()) )

return;

// read numbers from EditTextString str = n.getText().toString();

int n1 = Integer.parseInt( str );int s = SumWrapper.getSum( n1 );

// display resultstr = "Sum of 1 to " + str + " is ";str += Integer.toString ( s );;sum.setText ( str );

}}

The method onClick() is called when the user clicks on the Button getsum. It reads the integerentered and saves it in EditText n. The EditText n is converted to a String, which in turn is convertedto an integer. The method then calls getSum() of the class SumWrapper, which in turn calls thenative routine sum to calculate the sum of the integers from 1 to n. The returned result is displayedby TextView sum.

14 A Simple JNI Example with UI

When we run the program we will see a UI like that of Figure 5-3(a). Figure 5-3(b) shows theUI after we have entered the number 100 and clicked the button.

(a) (b)Figure 5-3 UI of Project JniDemo1

Chapter 5 File I/O and JNI 15