📜 ⬆️ ⬇️

Android Studio for NDK for Windows



The other day, I discovered that the version of Android Studio is constantly striving for a unit, and therefore I thought about studying this tool. So that it was not boring, I decided to share my experience and the collected rake in the form of an article-tutorial.

I just want to make a reservation that I am not the guru of Android development, so you will not find any revelations in the text. But there is a step-by-step instruction on installing and configuring Android Studio under Windows and creating the simplest project using Android NDK.
')
I also warn you in advance: the article turned out to be large and very detailed (honestly, I did not expect it myself), even though I almost hid all the screenshots and some code listings under the spoilers.

At the time of writing, the latest version of Android Studio was 0.8.1, for subsequent versions, the necessary actions may differ from those described below (I really hope for the better).

For those who are only interested in NDK
For those who are only interested in the settings Gradle for NDK

Install and configure Android Studio


1. You must install the JDK (Java Development Kit) and JRE (Java Runtime Environment).

Previously, the Android SDK only supported JDK version 6, but now it is in the past. It supports 7 and even 8 (at least, I specified the 8th version as JAVA_HOME and in the settings of Android Studio, and have not experienced any problems).
JRE is needed to run the studio itself. I use it with version 7.
Download JDK and JRE versions greater than 6 from the Oracle site .

The JAVA_HOME variable can now, it would seem, not be set, since in Android Studio we will set the path to the JDK in the settings. But I installed it. For this you need:

If for any reason you need version 6 of the JDK
JDK 6 without registration can be obtained as follows:
  • Download Java EE 6 from Oracle .
  • Install it. Included is a JDK 6.
  • As JAVA_HOME in this case, you need to specify the path to the JDK from Java EE, the default is C: / glassfish3 / jdk .

2. If you have Android SDK installed.

Included with the Android Studio is your Android SDK. And, if you want to use it, then in some cases it may happen strange. For example, when updating the SDK through the SDK Manager, I recorded part of the files in the old folder, and there were problems when I deleted this folder. Most likely this happened due to the fact that the registry key was stored with the path to the old folder. Therefore, it makes sense to clean the registry. To do this, run regedit.exe and find HKEY_LOCAL_MACHINE \ Software \ Android SDK Tools for 32-bit machines or HKEY_LOCAL_MACHINE \ Software \ Wow6432Node \ Android SDK Tools for 64-bit machines and remove Android SDK Tools . If there are no such keys in the registry of your system, then everything is in order.

If you have the ANDROID_SDK_HOME environment variable set and you want it to point to the old installation, then, in theory, this should not be a problem, since when setting up Android Studio we will point it to the SDK. Problems can arise if this variable uses any of the applications included in the Android SDK.

3. Now proceed to install Android Studio.

You need to download Android Studio for your system from the official page and install it. By default, if you select “Install only for me”, it is placed in \ Users \ <user> \ AppData \ Local \ Android \ android-studio \ , otherwise it is placed in \ Program FIles (x86) \ Android \ android-studio \ . You can select another folder.

After installation, run Android Studio.
We see this dialogue

In it we do the following:

Project creation


1. New Project

Click New Project .
There is such a dialogue

In it you can configure:

When everything is entered, click Next .

2. Form Factors

In this dialog, select target platforms and APIs.
The dialogue looks like this

Here I left everything by default: Phone and Tablet and 4.0.3 IceCreamSandwich. If the API for some platforms is not installed, a Not Insatlled message is displayed. For example, as seen in the screenshot, I do not have libraries for Google Glass installed (this can be done via the SDK Manager).

Also pay attention to Help me choose, cool stuff.
If you click Help me choose , then this interesting dialog opens.

It contains information on the coverage of devices when choosing a particular version of the API. The rectangles of the API versions are clickable, a list of the functionality available in them will be displayed on the right. For example, for the default Ice Cream Sandwich:


Click Next .

3. Add Activity

On the next screen, we are asked to select an Activity.

Naturally, I could not get past the Fullscreen Activity and chose it. You can choose another Activity, but in this case we will have different source codes in Java, and you will have to add a call to the native method from Java yourself (however, there is nothing complicated about this).
Make a choice, click Next .
The following dialog appears.

Here we are invited to configure the Activity.

Click Finish .
As a result, the project should open

Build and Run


1. Build the project

We launch the build via the menu: Build-> Make Project (icons and hot keys are shown for menu items, so it’s easy to figure out with them).
I had an error during the first build:

It happened in the file MyApplication / app / build.gradle
In order to understand what's the matter, just hover over the underlined line:

Everything is simple, version 19 is indicated here, and we have only 20 installed, and in the project settings we indicated 20 as the target one. Just change the number:

As you can see, the studio has not calmed down on this, and offers to install an even newer version. But now we do not need it.

Run Build-> Make Project again. This time I got everything together. I hope you have too.

2. Configure the virtual device

In order to run the program in the emulator, you need to configure a virtual device. Go to Tools-> Android-> AVD Manager (this, like the SDK Manager, is a utility from the Android SDK). We need the first tab, Android Virtual Device , it is open by default. On the right, we find the Create button ... and click it.
The virtual device setup dialog will appear.


At the bottom of the dialog you can see what error you made when setting up. For example, you cannot enter spaces in the device name, and some fields are required. When there is no label below, everything is entered correctly. Click OK .
The device appears in the list

A couple of words about the Device Definitions tab

It specifies the available devices (those that are then displayed in the Device drop-down list when creating a new virtual device). To create a new device, you need to click on the Create Device ... button and take care of filling in the following dialog:


Close the AVD Manager and return to the main window of Android Studio.

3. Run on emulator

Run Run-> Run 'app' .
We'll have to wait until the project builds to run.
In the end, the device selection window will appear.

Here, the only available option to choose from is our virtual device. He proposed to us immediately, so just click OK .

Emulator runs for quite a while.

You can open Tools-> Android-> Android Device Monitor (utility from Android SDK) to see the logs from the emulator. It should automatically pick up the virtual device and immediately start displaying the log.
Android Device Monitor

But after the emulator started up, the application did not open for me the first time, I had to click Run-> Run 'app' again.
There is a little changed dialogue.
Here the device migrated from the list to run in the list of already running. In this list, by itself, there are also real devices.

After that, it went, and the application quickly installed and started.
Full screen mode
And if you tap the application, it looks like this
This is not the behavior of the system, the processing of a click occurs in the application (FullscreenActivity.java file):
// Set up the user interaction to manually show or hide the system UI. contentView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (TOGGLE_ON_CLICK) { mSystemUiHider.toggle(); } else { mSystemUiHider.show(); } } }); 
After launch, Android Studio suggested that I turn off the Android Device Monitor in order to integrate the output directly into the IDE.
This is what integration looks like.

4. Run in Debug mode

Run-> Debug 'app'
Already familiar to us dialogue
Click OK.

We are waiting for the application to start and connect the debugger. I put a breakpoint when I clicked on the Dummy Button.
Android Studio in debug

A little annoying lack of panels with all sorts of Step Into, Step Out etc.
All this is in the Run menu.

5. Running on 4.0.3

In the process of creating and launching a project, you probably noticed that we created an application compatible with version 4.0.3, but ran it only on the latest version of Android. Now we fix it. To do this, you need to install version 4.0.3 through Tools-> Android-> SDK Manager .
Necessary ticked on a screenshot

These are the SDK Platform , ARM EABI v7a System Image and Intel x86 Atom System Image . In fact, we ran the ARM emulator, and we don’t need to install Intel now. I put it especially because it is installed for Android L.
Now create another device for the new old version of Android (or you can edit the old one).
New device settings

Run ( Run-> Run 'app' ).
In the dialogue, select a new device to run.

And we look how the new emulator looks like - obviously more brutal.
Launch
Full screen mode
After clicking

5. Run on device

Before moving to work with NDK, let's run the project on a real device. I will run on the phone
Huawei Ascend G300 with Android 4.0.3

The first thing to do is install the adb driver. Everything is quite simple with me, the driver is right on the phone, the only thing to do is to plug the cord into the computer, go into the mounted disk and run the executable file, which will install the adb driver. For other devices, things can be more complicated. For example, for the Prestigio tablet I once had to register Vendor ID in a special file (to use the standard Google driver), Samsung needs their own Kies, there was a separate driver for HTC and so on. In general, you will have to figure out how to install a driver for your device.

After the driver has been installed, you need to enable USB debugging on the device. On my phone for this you need to go to Settings-> For Developers-> USB Debugging . But for different devices, as well as assemblies and versions of Android, the location of this menu item may differ.

Now the phone will be Android SDK, and the developed applications will be installed. However, for Huawei, this is not all: there are no logs from the phone, you need to turn them on.
How to enable logs on Huawei
Dial as a phone number: * # * # 2846579 # * #
The service menu appears.
Go to ProjectMenu-> Background Setting-> Log Setting
Open the Log switch and set it to ON .
Open the Log level setting and set the required logging level (I set verbose ).
Reboot the phone.

Now you can run the application on the device: Run-> Run 'app'
The real device appeared in the device selection dialog.

Run on the phone.
The result of the launch.
Application in portrait:
  • Full screen

  • After clicking


Application in the landscape:
  • Full screen
  • After clicking

Install and configure Android NDK


1. Installing NDK

The Android SDK, as we have already found out, is included in the Android Studio package, but the NDK is not. Download the NDK that fits your system from here . We unpack the archive and put it in some folder, for example, in D: \ ndk , so that the ndk-build.cmd file is right in it. Important : it is necessary that there are no gaps in the path to the NDK.

2. Add the variable ANDROID_NDK_HOME

Go to Control Panel \ System and Security \ System , select Additional System Settings on the left, in the opened dialog, press the Environment Variables button. Create a variable with the name ANDROID_NDK_HOME and the value D: \ ndk (the path to your NDK).

Alternatively, instead of specifying a global variable, you can set the path to ndk in the local.properties file of your project (right in the root folder: MyApplication \ local.properties ). The contents of the file will look something like this (note the double backslashes, since this is critical for Windows):
 ## This file is automatically generated by Android Studio. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file should *NOT* be checked into Version Control Systems, # as it contains information specific to your local configuration. # # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. sdk.dir=C:\\Users\\<user>\\AppData\\Local\\Android\\android-studio\\sdk ndk.dir=D:\\ndk 

Do not believe the disclaimer about "your changes will be discarded," in this case it is not. Please note that this file is recommended to be excluded from version control, as it contains only local information for the user. For a change in this we did not lie. And, of course, this change will not affect your other projects. When ANDROID_NDK_HOME is set, it is not necessary to register the path in local.properties.

3. Install the required version of the Android API

Go to the NDK \ platforms (I have this D: \ ndk \ platforms ) and see the maximum version of the available APIs. In my case, the maximum version is 19. But at the same time, we only have versions 20 and 15 installed in the SDK. So, go to the SDK Manager and download the SDK Platform version 19, otherwise nothing will come together.
What to download

4. Configure gradle to work with NDK

I took the information from the samples, you can download them here at the bottom of the page . I downloaded samples for version 0.11. I took ndkSanAngeles as the main example.

How to run ndkSanAngeles
After the samples are downloaded, they need to be unpacked. Then you need to open the ndkSanAngeles project. For Android Studio, the project is a folder, so you have to open it. To do this, run File-> Open , or, if you are in the Welcome dialog, Open Project . We are looking for the ndkSanAngeles folder through the open file dialog.



After opening the project, you should look at the file build.gradle . Here is its original:
 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.11.0' } } apply plugin: 'android' android { compileSdkVersion 19 buildToolsVersion '19.1.0' defaultConfig { ndk { moduleName "sanangeles" cFlags "-DANDROID_NDK -DDISABLE_IMPORTGL" ldLibs "GLESv1_CM", "dl", "log" stl "stlport_static" } // This actual the app version code. Giving ourselves 1,000,000 values versionCode = 123 } buildTypes.debug.jniDebugBuild true productFlavors { x86 { ndk { abiFilter "x86" } // this is the flavor part of the version code. // It must be higher than the arm one for devices supporting // both, as x86 is preferred. versionCode = 3 } arm { ndk { abiFilter "armeabi-v7a" } versionCode = 2 } mips { ndk { abiFilter "mips" } versionCode = 1 } fat { // fat binary, lowest version code to be // the last option versionCode = 0 } } // make per-variant version code applicationVariants.all { variant -> // get the single flavor def flavorVersion = variant.productFlavors.get(0).versionCode // set the composite code variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode } } 

But the modified version, so that the project was collected from me:
 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } } apply plugin: 'android' android { compileSdkVersion 19 buildToolsVersion '20.0.0' defaultConfig { ndk { moduleName "sanangeles" cFlags "-DANDROID_NDK -DDISABLE_IMPORTGL" ldLibs "GLESv1_CM", "dl", "log" stl "stlport_static" } // This actual the app version code. Giving ourselves 1,000,000 values versionCode = 123 } buildTypes.debug.jniDebugBuild true productFlavors { x86 { ndk { abiFilter "x86" } // this is the flavor part of the version code. // It must be higher than the arm one for devices supporting // both, as x86 is preferred. versionCode = 3 } arm { ndk { abiFilter "armeabi-v7a" } versionCode = 2 } mips { ndk { abiFilter "mips" } versionCode = 1 } fat { // fat binary, lowest version code to be // the last option versionCode = 0 } } // make per-variant version code applicationVariants.all { variant -> // get the single flavor def flavorVersion = variant.productFlavors.get(0).versionCode // set the composite code variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode } } 

The changes are as follows:
  • The missed version of the gradle plugin ( classpath 'com.android.tools.build:gradle 11.11' ) will be discussed when attempting to build and the correct version number is suggested. I have this 0.12. +.
  • compileSdkVersion 19 remains as it is the maximum version for the current NDK.
  • buildToolsVersion has changed to 20. Installed versions can be viewed in the SDK Manager, others can also be installed there.

After that, ndkSanAngeles should pack up. Be careful, check the versions you have installed.

In our project, you need to select the correct build.gradle file, since there are two of them here. The fact is that MyApplication is a project, and the app is a project module (or subproject), and they have their own build.gradle. First consider the project file
MyApplication-> build.gradle
 // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } } 

From the comments in the code it is clear that we need a module file.
MyApplication-> app-> build.gradle
 apply plugin: 'com.android.application' android { compileSdkVersion 20 buildToolsVersion "20.0.0" defaultConfig { applicationId "com.example.markedone.myapp" minSdkVersion 15 targetSdkVersion 20 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // You must install or update the Support Repository through the SDK manager to use this dependency. //compile 'com.android.support:support-v4:19.+' compile 'com.android.support:support-v4:20.+' } 


We configure it to work with NDK using build.gradle from ndkSanAngeles as a “donor”.

For a start, replace
compileSdkVersion 20
on
compileSdkVersion 19
since the NDK is limited to version 19.

We ’ll add the ndk settings to defaultConfig and also replace targetSdkVersion with 19:
 defaultConfig { applicationId "com.example.markedone.myapp" minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" ndk { moduleName "myapp" cFlags "-DANDROID_NDK" ldLibs "log" stl "stlport_static" } } 
NDK settings include

In buildTypes, enable debug JNI build for debug:
  buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug.jniDebugBuild true } 

Now add the productFlavors . Here we indicate which collected * .so libraries to include in the assembly for a specific architecture. Thus, * .apk compiled for arm will contain the library version only for arm, under x86 - for x86 and so on. This piece is completely copied from ndkSanAngeles. Explanation of versionCode values ​​from comments: for x86 we set the maximum value to versionCode , since if the device supports both x86 and arm, then the x86 build is preferable (apparently, since it has a larger version, then it will be installed), and the minimum versionCode is written for fat (in theory, it should be “thick” * .apk, containing all possible versions of libraries at once).
  productFlavors { x86 { ndk { abiFilter "x86" } // this is the flavor part of the version code. // It must be higher than the arm one for devices supporting // both, as x86 is preferred. versionCode = 3 } arm { ndk { abiFilter "armeabi-v7a" } versionCode = 2 } mips { ndk { abiFilter "mips" } versionCode = 1 } fat { // fat binary, lowest version code to be // the last option versionCode = 0 } } 

“ Build ” the versionCode value for each of the build options:
  // make per-variant version code applicationVariants.all { variant -> // get the single flavor def flavorVersion = variant.productFlavors.get(0).versionCode // set the composite code variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode } 

Finally, the last section, dependencies . If you remember, here we changed compile 'com.android.support:support-v4:19.+' to compile 'com.android.support:support-v4:20.+' to build with the only library version we have installed. Now you have to change back to 19.
 dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // You must install or update the Support Repository through the SDK manager to use this dependency. compile 'com.android.support:support-v4:19.+' } 

Full listing of the modified build.gradle file
 apply plugin: 'com.android.application' android { compileSdkVersion 19 buildToolsVersion "20.0.0" defaultConfig { applicationId "com.example.markedone.myapp" minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" ndk { moduleName "myapp" cFlags "-DANDROID_NDK" ldLibs "log" stl "stlport_static" } } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug.jniDebugBuild true } productFlavors { x86 { ndk { abiFilter "x86" } // this is the flavor part of the version code. // It must be higher than the arm one for devices supporting // both, as x86 is preferred. versionCode = 3 } arm { ndk { abiFilter "armeabi-v7a" } versionCode = 2 } mips { ndk { abiFilter "mips" } versionCode = 1 } fat { // fat binary, lowest version code to be // the last option versionCode = 0 } } // make per-variant version code applicationVariants.all { variant -> // get the single flavor def flavorVersion = variant.productFlavors.get(0).versionCode // set the composite code variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode } sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // You must install or update the Support Repository through the SDK manager to use this dependency. compile 'com.android.support:support-v4:19.+' } 

5. Create jni folder

In the jni folder, we will save C / C ++ files. This is what NDK expects of us. We need to create a folder in MyApplication / app / src / main . This can be done directly from Android Studio, and in two ways.
First, you can right-click on main and simply create a folder through
New-> Directory

Secondly, you can use a special menu item.
New-> Folder-> JNI Folder
It starts the folder creation wizard.


In the first dialog, we select for which part of the module the jni folder will be created, and in the second, you can change its location.

6. Add C ++ files

There is no wizard for C ++ files, so to create the file we right-click on the jni folder and select
New-> File

First, create the header file myapp.h :
 #pragma once #include <jni.h> #ifdef __cplusplus extern "C" { #endif JNIEXPORT jstring JNICALL Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI(JNIEnv* env, jclass clazz); #ifdef __cplusplus } #endif 
Description
  • #pragma once - instead of the standard (# ifndef / # define / # endif) protection against re-activation. Now #pragma once is understood by most C ++ compilers.
  • #include <jni.h> - #include <jni.h> JNI header so that you can use the types declared there.
  • #ifdef __cplusplus ... #endif - the code inside will be compiled only in C ++ (but not in C).
  • extern "C" { ... } - we get rid of name mangling (what it is and why, it is well described here ).
  • JNIEXPORT jstring JNICALL Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI(JNIEnv* env, jclass clazz); - declaration of the function that we will implement.
    Let us examine this announcement in more detail.
    • JNIEXPORT is necessary for proper linking.
    • JNICALL for the correct calling agreement.
    • jstring is the return type of the function, in this case it is a string compatible with the Java string.
    • Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI - the name of the function, consisting of:
      Java is the language that calls.
      com_example_markedone_myapp is the application id ( com.example.markedone.myapp ).
      FullscreenActivity is the name of the Java class that contains the declaration of the method representing the native function.
      stringFromJNI is actually the name of the function (it will be like this in Java).
    • JNIEnv* env, jclass clazz - required parameters passed from Java.
      JNIEnv* env - a pointer to an object, which is a JNI environment.
      jclass clazz is the class to which the native method declaration in Java belongs. Here we need to make a reservation that jclass clazz is for a static native ( static native ) method. For a non-static method, you will need to write a jobject obj .

Now create an implementation file, myapp.cpp. In it we will write the following code:
 #include <android/log.h> #include <string> #include "myapp.h" JNIEXPORT jstring JNICALL Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI(JNIEnv* env, jclass clazz) { std::string tag("GREETING"); std::string message("Hello from C++!"); __android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%s", message.c_str()); std::string jniMessage("Hello from JNI!"); return env->NewStringUTF(jniMessage.c_str()); } 
Description
  • #include <android/log.h> - #include <android/log.h> log, for which we even added the library ( ldLibs "log" )
  • #include - std::string, STL.
    #include "myapp.h" - connect our header file.
    JNIEXPORT jstring JNICALL Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI(JNIEnv* env, jclass clazz) { ... } - , "myapp.h".
    std::string tag("GREETING"); std::string message("Hello from C++!"); - create lines for output to the log.
    __android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%s", message.c_str()); - output to log. Please note that you need to specify 4 parameters: the type of message in the log, the tag, the format of the string, and, finally, the message itself.
    std::string jniMessage("Hello from JNI!"); - a string that we will pass in java.
    return env->NewStringUTF(jniMessage.c_str()); - return value, using JNIEnv, create a jstring from a C-string. Please note that we do not need the construction of the form (*env)-> , since we write in C ++, and not in C.

And now we will create another file: stub.cpp, and leave it empty. The fact is that if you leave only one * .cpp file in the jni folder, then ndk-build will generate an error "no rule to make target".

7. Add a call to the native function from Java

Open the file MyApplication / app / src / main / java / com.example.markedone.myapp.FullscreenActivity . In fact, it has a java extension, and com, example, markedone and myapp are folders, but Android Studio hides it.
File contents
 package com.example.markedone.myapp; import com.example.markedone.myapp.util.SystemUiHider; import android.annotation.TargetApi; import android.app.Activity; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.view.MotionEvent; import android.view.View; /** * An example full-screen activity that shows and hides the system UI (ie * status bar and navigation/system bar) with user interaction. * * @see SystemUiHider */ public class FullscreenActivity extends Activity { /** * Whether or not the system UI should be auto-hidden after * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. */ private static final boolean AUTO_HIDE = true; /** * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after * user interaction before hiding the system UI. */ private static final int AUTO_HIDE_DELAY_MILLIS = 3000; /** * If set, will toggle the system UI visibility upon interaction. Otherwise, * will show the system UI visibility upon interaction. */ private static final boolean TOGGLE_ON_CLICK = true; /** * The flags to pass to {@link SystemUiHider#getInstance}. */ private static final int HIDER_FLAGS = SystemUiHider.FLAG_HIDE_NAVIGATION; /** * The instance of the {@link SystemUiHider} for this activity. */ private SystemUiHider mSystemUiHider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fullscreen); final View controlsView = findViewById(R.id.fullscreen_content_controls); final View contentView = findViewById(R.id.fullscreen_content); // Set up an instance of SystemUiHider to control the system UI for // this activity. mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS); mSystemUiHider.setup(); mSystemUiHider .setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() { // Cached values. int mControlsHeight; int mShortAnimTime; @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) public void onVisibilityChange(boolean visible) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { // If the ViewPropertyAnimator API is available // (Honeycomb MR2 and later), use it to animate the // in-layout UI controls at the bottom of the // screen. if (mControlsHeight == 0) { mControlsHeight = controlsView.getHeight(); } if (mShortAnimTime == 0) { mShortAnimTime = getResources().getInteger( android.R.integer.config_shortAnimTime); } controlsView.animate() .translationY(visible ? 0 : mControlsHeight) .setDuration(mShortAnimTime); } else { // If the ViewPropertyAnimator APIs aren't // available, simply show or hide the in-layout UI // controls. controlsView.setVisibility(visible ? View.VISIBLE : View.GONE); } if (visible && AUTO_HIDE) { // Schedule a hide(). delayedHide(AUTO_HIDE_DELAY_MILLIS); } } }); // Set up the user interaction to manually show or hide the system UI. contentView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (TOGGLE_ON_CLICK) { mSystemUiHider.toggle(); } else { mSystemUiHider.show(); } } }); // Upon interacting with UI controls, delay any scheduled hide() // operations to prevent the jarring behavior of controls going away // while interacting with the UI. findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Trigger the initial hide() shortly after the activity has been // created, to briefly hint to the user that UI controls // are available. delayedHide(100); } /** * Touch listener to use for in-layout UI controls to delay hiding the * system UI. This is to prevent the jarring behavior of controls going away * while interacting with activity UI. */ View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (AUTO_HIDE) { delayedHide(AUTO_HIDE_DELAY_MILLIS); } return false; } }; Handler mHideHandler = new Handler(); Runnable mHideRunnable = new Runnable() { @Override public void run() { mSystemUiHider.hide(); } }; /** * Schedules a call to hide() in [delay] milliseconds, canceling any * previously scheduled calls. */ private void delayedHide(int delayMillis) { mHideHandler.removeCallbacks(mHideRunnable); mHideHandler.postDelayed(mHideRunnable, delayMillis); } } 


Add the following code to the FullscreenActivity class:
  static { System.loadLibrary("myapp"); } private static native String stringFromJNI(); 
Here, the library is loaded first and then the declaration of the stringFromJNI method, which corresponds to our function in C ++. Please note that it is declared as static (this affects the (jclass or jobject) as the second parameter of the C ++ function) and native. You do not need to implement the native method, we have already done this in C ++, and the rest is JNI will do for us.

Now we, in general, can already call our function. If you, like me, have chosen FullscreenActivity, then we have a Dummy Button, which, in fact, does nothing. And even there is already a touch listener, though not the best (it will be called many times while the finger is on the screen), but in order not to produce extra code, we use it.

First, add to the import list:
import android.widget.Button;
so that you can work normally with the button.

Find the following code:
  View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (AUTO_HIDE) { delayedHide(AUTO_HIDE_DELAY_MILLIS); } return false; } }; 

and add a few lines before return false .
  View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (AUTO_HIDE) { delayedHide(AUTO_HIDE_DELAY_MILLIS); } final String message = stringFromJNI(); final Button button = (Button)findViewById(R.id.dummy_button); final String actualText = button.getText().toString(); if(message.equals(actualText)) { button.setText("Dummy Button"); } else { button.setText(message); } return false; } }; 

Description of the code added
  • final String message = stringFromJNI(); - get a string from C ++. The call to the native method is what it was all about.
  • final Button button = (Button)findViewById(R.id.dummy_button); - find the button object.
  • final String actualText = button.getText().toString(); — .
  • if(message.equals(actualText)) — , C++, .
    • button.setText("Dummy Button"); — , Dummy Button.
    • button.setText(message); — , , C++.


 package com.example.markedone.myapp; import com.example.markedone.myapp.util.SystemUiHider; import android.annotation.TargetApi; import android.app.Activity; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.view.MotionEvent; import android.view.View; import android.widget.Button; /** * An example full-screen activity that shows and hides the system UI (ie * status bar and navigation/system bar) with user interaction. * * @see SystemUiHider */ public class FullscreenActivity extends Activity { static { System.loadLibrary("myapp"); } private static native String stringFromJNI(); /** * Whether or not the system UI should be auto-hidden after * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. */ private static final boolean AUTO_HIDE = true; /** * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after * user interaction before hiding the system UI. */ private static final int AUTO_HIDE_DELAY_MILLIS = 3000; /** * If set, will toggle the system UI visibility upon interaction. Otherwise, * will show the system UI visibility upon interaction. */ private static final boolean TOGGLE_ON_CLICK = true; /** * The flags to pass to {@link SystemUiHider#getInstance}. */ private static final int HIDER_FLAGS = SystemUiHider.FLAG_HIDE_NAVIGATION; /** * The instance of the {@link SystemUiHider} for this activity. */ private SystemUiHider mSystemUiHider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fullscreen); final View controlsView = findViewById(R.id.fullscreen_content_controls); final View contentView = findViewById(R.id.fullscreen_content); // Set up an instance of SystemUiHider to control the system UI for // this activity. mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS); mSystemUiHider.setup(); mSystemUiHider .setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() { // Cached values. int mControlsHeight; int mShortAnimTime; @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) public void onVisibilityChange(boolean visible) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { // If the ViewPropertyAnimator API is available // (Honeycomb MR2 and later), use it to animate the // in-layout UI controls at the bottom of the // screen. if (mControlsHeight == 0) { mControlsHeight = controlsView.getHeight(); } if (mShortAnimTime == 0) { mShortAnimTime = getResources().getInteger( android.R.integer.config_shortAnimTime); } controlsView.animate() .translationY(visible ? 0 : mControlsHeight) .setDuration(mShortAnimTime); } else { // If the ViewPropertyAnimator APIs aren't // available, simply show or hide the in-layout UI // controls. controlsView.setVisibility(visible ? View.VISIBLE : View.GONE); } if (visible && AUTO_HIDE) { // Schedule a hide(). delayedHide(AUTO_HIDE_DELAY_MILLIS); } } }); // Set up the user interaction to manually show or hide the system UI. contentView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (TOGGLE_ON_CLICK) { mSystemUiHider.toggle(); } else { mSystemUiHider.show(); } } }); // Upon interacting with UI controls, delay any scheduled hide() // operations to prevent the jarring behavior of controls going away // while interacting with the UI. findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Trigger the initial hide() shortly after the activity has been // created, to briefly hint to the user that UI controls // are available. delayedHide(100); } /** * Touch listener to use for in-layout UI controls to delay hiding the * system UI. This is to prevent the jarring behavior of controls going away * while interacting with activity UI. */ View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (AUTO_HIDE) { delayedHide(AUTO_HIDE_DELAY_MILLIS); } final String message = stringFromJNI(); final Button button = (Button)findViewById(R.id.dummy_button); final String actualText = button.getText().toString(); if(message.equals(actualText)) { button.setText("Dummy Button"); } else { button.setText(message); } return false; } }; Handler mHideHandler = new Handler(); Runnable mHideRunnable = new Runnable() { @Override public void run() { mSystemUiHider.hide(); } }; /** * Schedules a call to hide() in [delay] milliseconds, canceling any * previously scheduled calls. */ private void delayedHide(int delayMillis) { mHideHandler.removeCallbacks(mHideRunnable); mHideHandler.postDelayed(mHideRunnable, delayMillis); } } 


17.

Build->Make Project. Java-.
Run->Run 'app'. , , C++ . - , . , , , OK .

Conclusion


, Android Studio . Android SDK , , . , , Help me choose. — Gradle, — : .

, , NDK , . , - C++-.

PS C++ .

Source: https://habr.com/ru/post/228365/


All Articles