diff options
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | FFI.java | 10 | ||||
-rw-r--r-- | MainActivity.java | 22 | ||||
-rw-r--r-- | README | 9 | ||||
-rw-r--r-- | activity_main.xml | 13 | ||||
-rw-r--r-- | build_apk.sh | 20 | ||||
-rw-r--r-- | build_java.sh | 14 | ||||
-rwxr-xr-x | build_ndk.sh | 8 | ||||
-rw-r--r-- | build_rust.sh | 11 | ||||
-rwxr-xr-x | cargo.config | 7 | ||||
-rwxr-xr-x | install_rust.sh | 4 | ||||
-rw-r--r-- | lib.rs-part0 | 13 | ||||
-rw-r--r-- | lib.rs-part1 | 21 | ||||
-rw-r--r-- | strings.xml | 5 |
14 files changed, 164 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..346a751 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[...] + +[target.'cfg(target_os="android")'.dependencies] +jni = { version = "0.5", default-features = false } + +[lib] +crate-type = ["dylib"] diff --git a/FFI.java b/FFI.java new file mode 100644 index 0000000..8104b2f --- /dev/null +++ b/FFI.java @@ -0,0 +1,10 @@ +package rs.cph.hellorust; + +public class FFI { + + private static native String func(final String pattern); + + public String runFunc(String to) { + return func(to); + } +} diff --git a/MainActivity.java b/MainActivity.java new file mode 100644 index 0000000..5ffaa29 --- /dev/null +++ b/MainActivity.java @@ -0,0 +1,22 @@ +package rs.cph.hellorust; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class MainActivity extends Activity { + + static { + System.loadLibrary("rustcode"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FFI rustcode = new FFI(); + String s = rustcode.runFunc("Sundkaj 7"); + ((TextView)findViewById(R.id.greetingField)).setText(s); + } +} @@ -0,0 +1,9 @@ +Files illustrating a minimal example of how to link Rust code from an android +java application. The pieces should be possible to puzzle together into a hello +world like example. + +These go together with the slides published at: +https://blag.netizen.se/posts/2020-03-22-rust-on-android.html + +As is told on above mentioned blog post, this is the material from a short +presentation I held a year ago on linking to Rust code from java on android. diff --git a/activity_main.xml b/activity_main.xml new file mode 100644 index 0000000..5bbe817 --- /dev/null +++ b/activity_main.xml @@ -0,0 +1,13 @@ +<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" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:text="@string/hello_msg" + tools:context=".MainActivity" /> +</RelativeLayout> diff --git a/build_apk.sh b/build_apk.sh new file mode 100644 index 0000000..ce9707a --- /dev/null +++ b/build_apk.sh @@ -0,0 +1,20 @@ +#!/bin/sh -e + +ANDROID_PATH="/usr/lib/android-sdk/" +BUILD_TOOLS="24.0.2" +AAPT="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/aapt" +DX="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/dx" +ZIPALIGN="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/zipalign" +APKSIGNER="/usr/bin/apksigner" +PLATFORM="${ANDROID_PATH}/platforms/android-24/android.jar" + +echo "Translating in Dalvik bytecode..." +$DX --dex --output=classes.dex obj + +echo "Making APK..." +$AAPT package -f -m -F bin/hello.unaligned.apk -M AndroidManifest.xml -S res -I $PLATFORM +$AAPT add bin/hello.unaligned.apk classes.dex lib/armeabi/librustcode.so + +echo "Aligning and signing APK..." +$APKSIGNER sign --ks ~/.android/keys/release-key.keystore bin/hello.unaligned.apk +$ZIPALIGN -f 4 bin/hello.unaligned.apk bin/hello.apk diff --git a/build_java.sh b/build_java.sh new file mode 100644 index 0000000..6fa5a4a --- /dev/null +++ b/build_java.sh @@ -0,0 +1,14 @@ +#!/bin/sh -e + +ANDROID_PATH="/usr/lib/android-sdk/" +BUILD_TOOLS="24.0.2" +AAPT="${ANDROID_PATH}/build-tools/${BUILD_TOOLS}/aapt" +PLATFORM="${ANDROID_PATH}/platforms/android-24/android.jar" +JAVAOPTS="-d obj -classpath src -bootclasspath $PLATFORM -source 1.7 -target 1.7" + +echo "Generating R.java file..." +$AAPT package -f -m -J src -M AndroidManifest.xml -S res -I $PLATFORM + +echo "Compiling..." +javac $JAVAOPTS src/rs/cph/hellorust/MainActivity.java +javac $JAVAOPTS src/rs/cph/hellorust/R.java diff --git a/build_ndk.sh b/build_ndk.sh new file mode 100755 index 0000000..694f466 --- /dev/null +++ b/build_ndk.sh @@ -0,0 +1,8 @@ +mkdir NDK +NDK_HOME="/usr/lib/android-ndk/" +API_LEVEL=24 +for ARCH in arm64 arm x86 +do + ${NDK_HOME}/build/tools/make_standalone_toolchain.py \ + --api ${API_LEVEL} --arch ${ARCH} --install-dir NDK/${ARCH} +done diff --git a/build_rust.sh b/build_rust.sh new file mode 100644 index 0000000..36cf2a8 --- /dev/null +++ b/build_rust.sh @@ -0,0 +1,11 @@ +#!/bin/sh -e + +for TARGET in aarch64-linux-android armv7-linux-androideabi +do + echo "Building rust lib for ${TARGET}" + (cd rustcode; cargo build --target ${TARGET} --release) +done + +ln -s "`pwd`"/rustcode/target/aarch64-linux-android/release/librustcode.so jniLibs/arm64/ +ln -s "`pwd`"/rustcode/target/armv7-linux-androideabi/release/librustcode.so jniLibs/armeabi/ +ln -s "`pwd`"/rustcode/target/i686-linux-android/release/librustcode.so jniLibs/x86/ diff --git a/cargo.config b/cargo.config new file mode 100755 index 0000000..5f09fe7 --- /dev/null +++ b/cargo.config @@ -0,0 +1,7 @@ +[target.armv7-linux-androideabi] +ar = "`pwd`/NDK/arm/bin/arm-linux-androideabi-ar" +linker = "`pwd`/NDK/arm/bin/arm-linux-androideabi-clang" + +[target.aarch64-linux-android ...] + +[target.i686-linux-android ...] diff --git a/install_rust.sh b/install_rust.sh new file mode 100755 index 0000000..69acd4f --- /dev/null +++ b/install_rust.sh @@ -0,0 +1,4 @@ +for TARGET in aarch64-linux-android armv7-linux-androideabi i686-linux-android +do + rustup target add ${TARGET} +done diff --git a/lib.rs-part0 b/lib.rs-part0 new file mode 100644 index 0000000..d2578cd --- /dev/null +++ b/lib.rs-part0 @@ -0,0 +1,13 @@ +use std::os::raw::{c_char}; +use std::ffi::{CString, CStr}; + +#[no_mangle] +pub extern fn rust_func(to: *const c_char) -> *mut c_char { + let c_str = unsafe { CStr::from_ptr(to) }; + let recipient = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + CString::new("Hello ".to_owned() + recipient).unwrap().into_raw() +} diff --git a/lib.rs-part1 b/lib.rs-part1 new file mode 100644 index 0000000..cec0883 --- /dev/null +++ b/lib.rs-part1 @@ -0,0 +1,21 @@ +#[cfg(target_os="android")] +#[allow(non_snake_case)] +pub mod android { + extern crate jni; + + use super::*; + use self::jni::JNIEnv; + use self::jni::objects::{JClass, JString}; + use self::jni::sys::{jstring}; + + #[no_mangle] + pub unsafe extern fn Java_rs_cph_hellorust_FFI_func(env: JNIEnv, _: JClass, java_pattern: JString) + -> jstring { + let world = rust_func(env.get_string(java_pattern).expect("invalid pattern string").as_ptr()); + // Retake pointer, to use it below and allow memory to be freed when it goes out of scope. + let world_ptr = CString::from_raw(world); + let output = env.new_string(world_ptr.to_str().unwrap()).expect("Couldn't create java string!"); + + output.into_inner() + } +} diff --git a/strings.xml b/strings.xml new file mode 100644 index 0000000..1fa2c41 --- /dev/null +++ b/strings.xml @@ -0,0 +1,5 @@ +<resources> + <string name="app_name">Hello Rust</string> + <string name="hello_msg">Hello Rust!</string> + <string name="title_activity_main">MainActivity</string> +</resources> |