Windows上のNDK用Android Studio



先日、Android Studioのバージョンが着実に統一を目指していることに気付いたので、このツールを検討することを考えました。 退屈しないように、私は自分の経験と集めたレーキをチュートリアル記事の形で共有することにしました。

私は、Android開発の第一人者ではないことをすぐに予約したいので、テキストには啓示はありません。 ただし、WindowsでのAndroid Studioのインストールと設定、およびAndroid NDKを使用した簡単なプロジェクトの作成については、順を追った説明があります。

また、事前に警告します。ほとんどすべてのスクリーンショットといくつかのコードリストをネタバレの下に隠していましたが、記事は大きくて非常に詳細であることがわかりました(正直、自分では期待していませんでした)。

執筆時点では、Android Studioの最新バージョンは0.8.1でしたが、以降のバージョンでは、必要なアクションが以下で説明するアクションと異なる場合があります(より良いことを願っています)

NDKのみに興味がある人向け
NDKのGradle設定のみに関心がある人向け

Android Studioをインストールして構成する


1. JDK(Java Development Kit)およびJRE(Java Runtime Environment)をインストールする必要があります。

以前は、Android SDKはJDKバージョン6のみをサポートしていましたが、現在は過去のものです。 7と8さえサポートされています (少なくとも、 JAVA_HOMEおよびAndroid Studioの設定で指定した8番目のバージョンであり、問​​題は発生しませんでした)。
スタジオ自体を実行するにはJREが必要です。 彼女はバージョン7を使用します。
Oracle Webサイトから 6を超えるJDKおよびJREバージョンをダウンロードできます。

現在、 JAVA_HOME変数は設定できないようです。AndroidStudioでは、設定でJDKへのパスを規定しているためです。 しかし、私はそれをインストールしました。 これを行うには、次のものが必要です。

何らかの理由でJDKバージョン6が必要な場合
登録なしのJDK 6は、次のようにして取得できます。
  • OracleからJava EE 6をダウンロードします
  • それをインストールします。 JDK 6が含まれています。
  • この場合、Java EEからのJDKパスをJAVA_HOMEとして指定する必要があり、デフォルトではC:/ glassfish3 / jdkです。

2. Android SDKがインストールされている場合。

Android StudioにはAndroid SDKが付属しています。 そして、特にそれを使用したい場合、場合によっては奇妙なことが起こります。 たとえば、SDK Managerを介してSDKを更新すると、ファイルの一部が古いフォルダーに書き込まれ、このフォルダーを削除すると問題が発生しました。 レジストリキーが古いフォルダへのパスと共に保存されていたため、これが発生した可能性が高いです。 したがって、レジストリをクリーンアップすることは理にかなっています。 これを行うには、regedit.exeを実行し、32ビットマシンの場合はHKEY_LOCAL_MACHINE \ Software \ Android SDK Toolsを 、64ビットマシンの場合はHKEY_LOCAL_MACHINE \ Software \ Wow6432Node \ Android SDK Toolsを見つけて、 Android SDK Toolsを削除します 。 システムのレジストリにそのようなキーがない場合、すべてが正常に機能しています。

ANDROID_SDK_HOME環境変数が設定されていて、古いインストールを示すようにしたい場合、Android Studioのセットアップ時にSDKへのパスが表示されるため、理論的にはこれは問題になりません。 この変数をAndroid SDKに含まれているアプリケーションのいずれかで使用すると、問題が発生する可能性があります。

3.ここで、Android Studioのインストールに進みます。

公式ページからシステム用のAndroid Studioをダウンロードしてインストールする必要があります。 デフォルトでは、「私だけにインストール」を選択した場合、 \ Users \ <user> \ AppData \ Local \ Android \ android-studio \に配置されます。 別のフォルダを選択できます。

インストール後、Android Studioを実行します。
私たちはそのような対話を見る

その中で次のことを行います。

プロジェクト作成


1.新しいプロジェクト

新規プロジェクトをクリックします。
そのような対話が表示されます

その中で以下を設定できます:

すべてを入力したら、 「次へ 」をクリックします。

2.フォームファクター

このダイアログで、ターゲットプラットフォームとAPIを選択します。
ダイアログは次のようになります

ここではデフォルトですべてのものを残しました:Phone and Tabletと4.0.3 IceCreamSandwich。 一部のプラットフォームのAPIがインストールされていない場合、「インストールされていません」というメッセージが表示されます。 たとえば、スクリーンショットに見られるように、Google Glassのライブラリはインストールされていません(これはSDKマネージャーを使用して実行できます)。

また、私が選択するのに役立つ、クールなことに注意を払ってください
Help me chooseをクリックすると 、このような興味深いダイアログが開きます。

APIの特定のバージョンを選択する際のデバイスのカバレッジに関する情報が含まれています。 APIバージョンの長方形はクリック可能です;それらで利用可能な機能のリストは右側に表示されます。 たとえば、デフォルトのアイスクリームサンドイッチの場合:


次へをクリックします。

3.アクティビティを追加する

次の画面で、アクティビティを選択するように求められます

当然、フルスクリーンアクティビティを通過できず、選択しました。 別のアクティビティを選択できますが、この場合、Javaソースは異なり、Javaからのネイティブメソッド呼び出しを自分で追加する必要があります(ただし、複雑なことは何もありません)。
選択して、 「次へ 」をクリックします。
次のダイアログが表示されます

ここでは、アクティビティを構成するように招待されています。

完了をクリックします。
その結果、プロジェクトが開くはずです

組み立てと打ち上げ


1.プロジェクトの組み立て

メニューからビルドを開始します: ビルド->プロジェクトの作成 (メニュー項目にはアイコンとホットキーが表示されるため、それらを簡単に処理できます)。
最初のビルド中にエラーが発生しました:

これはMyApplication / app / build.gradleファイルで発生しました
問題が何であるかを理解するには、下線を引いてください:

簡単です。ここではバージョン19が示されており、インストールされているのは20のみです。プロジェクト設定では、ターゲットとして20を指定しました。 番号を変更するだけです:

ご覧のとおり、スタジオはこれについて落ち着かず、さらに新しいバージョンのインストールを提案しています。 しかし、今は必要ありません。

Build-> Make Projectを再度実行します 。 今回はすべてをまとめました。 私もあなたがそうすることを願っています。

2.仮想デバイスを構成する

エミュレータでプログラムを実行するには、仮想デバイスを構成する必要があります。 [ツール]-> [Android]-> [AVDマネージャー]に移動します (これは、SDKマネージャーと同様、Android SDKのユーティリティです)。 最初のタブであるAndroid Virtual Deviceが必要です。これはデフォルトで開いています。 右側にある[ 作成... ]ボタンを見つけてクリックします。
仮想デバイスのセットアップダイアログボックスが表示されます。


ダイアログの下部には、セットアップ中に行ったエラーが表示されます。 たとえば、デバイス名にスペースを入力することはできず、一部のフィールドは必須です。 下部に碑文がない場合、すべてが正しく入力されています。 OKをクリックします。
デバイスがリストに表示されました。

[デバイスの定義]タブについて一言

使用可能なデバイスが定義されています(新しい仮想デバイスを作成するときに[ デバイス]ドロップダウンリストに表示されるデバイス)。 新しいデバイスを作成するには、[ デバイス作成... ]ボタンをクリックし、次のダイアログに入力するように注意する必要があります。


AVD Managerを閉じて、Android Studioのメインウィンドウに戻ります。

3.エミュレーターで実行する

実行-> 'app'を実行します
プロジェクトの立ち上げが完了するまで待つ必要があります。
最後に、デバイス選択ウィンドウが表示されます。

ここでは、選択できる唯一のオプションは仮想デバイスです。 彼はすぐに私たちに申し出られたので、 OKをクリックしてください。

エミュレーターが起動します

[ツール]-> [Android]-> [Androidデバイスモニター] (Android SDKのユーティリティ)を開いて、エミュレーターからのログを表示できます。 仮想デバイスを自動的に取得し、すぐにログの表示を開始する必要があります。
Androidデバイスモニター

しかし、エミュレーターを起動した後のアプリケーションは最初は開かなかったので、 「実行」->「「アプリ」を再度実行」をクリックする必要がありました。
わずかに変更されたダイアログが表示されました
ここで、デバイスはリストから移行して、すでに実行中のリストに移動しました。 もちろん、このリストには実際のデバイスが含まれています。

その後、事態は進み、アプリケーションはすぐにインストールされて起動しました。
全画面モード
そして、アプリケーションをタップすると、次のようになります
これはシステムの動作ではありません。クリック処理はアプリケーション(FullscreenActivity.javaファイル)で発生します:
// 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(); } } }); 
起動後、Android Studioは、出力をIDEに直接統合するためにAndroid Device Monitorをオフにすることを提案しました。
統合は次のようになります

4.デバッグモードで実行する

実行->「アプリ」をデバッグ
すでに知っている対話
OKをクリックします。

アプリケーションが起動し、デバッガーが接続するまで待機します。 ダミーボタンをクリックしたときにブレークポイントを設定します。
デバッグ中のAndroid Studio

少し面倒なのは、あらゆる種類のステップイン、ステップアウトなどのパネルがないことです。
これはすべて[実行]メニューにあります。

5. 4.0.3で起動

プロジェクトを作成して起動する過程で、バージョン4.0.3と互換性のあるアプリケーションを作成したことに気づいたでしょうが、Androidの最新バージョンでのみ起動しました。 今それを修正します。 これを行うには、 ツール-> Android-> SDK Managerからバージョン4.0.3をインストールします
スクリーンショットに必要なチェックマークが付いています

これらは、 SDKプラットフォームARM EABI v7aシステムイメージ 、およびIntel x86 Atomシステムイメージです。 実際、ARMエミュレータを起動しました。Intelをインストールする必要はありません。 Android L用にインストールされているため、純粋にインストールしました。
次に、新しい古いバージョンのAndroid用に別のデバイスを作成します(または、古いデバイスを編集できます)。
新しいデバイス設定

実行( Run-> Run 'app' )。
ダイアログで、開始する新しいデバイスを選択します

そして、新しいエミュレータがどのように見えるかを見てください-明らかにもっと残忍です。
打ち上げ
全画面モード
を押した後

5.デバイスで起動する

NDKを使い始める前に、実際のデバイスでプロジェクトを実行しましょう。 電話で走ります
Huawei Ascend G300 with Android 4.0.3

最初に行うことは、adbドライバーをインストールすることです。 私はこれでかなり簡単です、ドライバーは電話に直接配置されています、唯一のことはケーブルをコンピューターに接続し、マウントされたドライブに行き、実行可能ファイルを実行し、adb-driverをインストールします。 他のデバイスの場合、事態はより複雑になる可能性があります。 たとえば、Prestigioタブレットの場合、特別なファイルにベンダーIDを書き込む必要がありました(Googleの標準ドライバーを使用するため)、Samsungの場合は独自のKiesが必要で、HTCの別のドライバーなどがありました。 一般に、デバイスのドライバーを自分でインストールする方法を理解する必要があります。

ドライバーをインストールしたら、デバイスでUSBデバッグを有効にする必要があります。 私の電話では、このために設定->開発者向け-> USBデバッグに移動する必要があります。 ただし、Androidのビルドとバージョンだけでなく、さまざまなデバイスでは、このメニュー項目の場所は異なる場合があります。

これで、電話機はAndroid SDKになり、開発されたアプリケーションがインストールされます。 ただし、これはHuaweiだけではありません。ログは電話から送られるのではなく、有効にする必要があります。
Huaweiでログを有効にする方法
電話番号としてダイヤル:*#*#2846579#*#*
サービスメニューが表示されます。
ProjectMenu->バックグラウンド設定->ログ設定に移動します
ログスイッチを開き、そこでONに設定します。
[ ログレベル]設定を開き、必要なログレベルを設定します( 詳細を設定します)。
電話を再起動します。

これで、デバイスでアプリケーションを実行できます: Run-> Run 'app'
デバイス選択ダイアログに実際のデバイスが表示されました

電話で始めます。
打ち上げの結果。
縦長のアプリケーション:
  • 全画面

  • を押した後


ランドスケープでのアプリケーション:
  • 全画面
  • を押した後

Android NDKをインストールして構成する


1. NDKをインストールします

Android SDKは、すでにわかっているように、Android Studioに含まれていますが、NDKは含まれていません。 ご使用のシステムに適したNDK をここからダウンロードします 。 アーカイブを解凍して、たとえばD:\ ndkのようなフォルダーに配置し、 ndk-build.cmdファイル直接その中にあるようにします。 重要 :NDKへのパスにスペースがないことが不可欠です。

2.変数ANDROID_NDK_HOMEを追加します

[コントロールパネル] \ [システムとセキュリティ] \ [システム]に移動し、左側の[ 高度なシステムパラメーター ]を選択して、開いたダイアログで[ 環境変数 ]ボタンをクリックします。 ANDROID_NDK_HOMEという名前と値D:\ ndk (NDKへのパス)の変数を作成します。

または、グローバル変数を指定する代わりに、プロジェクトのlocal.propertiesファイル(直接ルートフォルダー: MyApplication \ local.properties )にndkへのパスを書き込むことができます。 ファイルの内容は次のようになります(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 

「変更は破棄される」という免責事項を信じないでください。この場合はそうではありません。 このファイルにはユーザーのローカル情報のみが含まれているため、バージョン管理から除外することをお勧めします。 変更のために、私たちは嘘をつきませんでした。 もちろん、この変更は他のプロジェクトには影響しません。 ANDROID_NDK_HOMEが設定されている場合、 local.propertiesでパスを指定する必要はありません。

3.必要なバージョンのAndroid APIをインストールします

NDK \プラットフォームD:\ ndk \プラットフォーム )に移動し、利用可能なAPIの最大バージョンを確認します。 私の場合、最大バージョンは19ですが、同時にSDKにインストールされるのはバージョン20と15のみですので、SDK ManagerにアクセスしてSDK Platformバージョン19をダウンロードしてください。
ダウンロードするもの

4. NDKで動作するようにgradleを設定します

サンプルから情報を取得しましたページの一番下からダウンロードできます 。 バージョン0.11のサンプルをダウンロードしました。 基本的な例として、ndkSanAngelesを取り上げました。

ndkSanAngelesの実行方法
サンプルをダウンロードしたら、解凍する必要があります。 次に、ndkSanAngelesプロジェクトを開く必要があります。 Android Studioの場合、プロジェクトはフォルダーなので、開く必要があります。 これを行うには、「 ファイル」->「開く 」を実行します。または、「ようこそ」ダイアログを開いている場合は、 「プロジェクトを開く」 ファイルを開くダイアログでndkSanAngelesフォルダーを探しています。



プロジェクトを開いた後、 build.gradleファイルを確認する必要があります。 オリジナルは次のとおりです。
 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 } } 

そして、プロジェクトが私からビルドされるように修正されたバージョンがあります:
 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 } } 

変更点は次のとおりです。
  • gradleプラグインのバージョンの不一致( classpath 'com.android.tools.build:gradlemail.11.0' )は、ビルドしようとしたときに表示され、正しいバージョン番号が提案されます。 0.12あります。
  • 現在のNDKの最大バージョンであるため、 compileSdkVersion 19は残ります。
  • buildToolsVersionが20に変更されました。インストールされているバージョンはSDKマネージャーで表示でき、他のバージョンもそこにインストールできます。

その後、ndkSanAngelesが一緒になります。 注意して、インストールされているバージョンを確認してください。

プロジェクトでは、2つのファイルがあるため、正しいbuild.gradleファイルを選択する必要があります。 実際、MyApplicationはプロジェクトであり、appはプロジェクトモジュール(またはサブプロジェクト)であり、それぞれにbuild.gradleがあります。 まず、プロジェクトファイルを検討します
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() } } 

モジュールファイルが必要であるというコード内のコメントから明らかに続きます。
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.+' } 


NDKで動作するように構成し、ndkSanAngelesのbuild.gradleを「ドナー」として使用します。

手始めに、交換
compileSdkVersion 20

compileSdkVersion 19
NDKはバージョン19に制限されているためです。

defaultConfigでは、 ndk設定を追加し、 targetSdkVersionを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設定には以下が含まれます

buildTypesで、デバッグ用のJNIのデバッグビルドを有効にします。
  buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug.jniDebugBuild true } 

ここでproductFlavorsを追加します 。 ここでは、特定のアーキテクチャのアセンブリに含めるためにコンパイルされたライブラリ* .soを示します。 したがって、arm用にコンパイルされた* .apkには、arm用、x86用、x86用などのライブラリバージョンのみが含まれます。 このピースはndkSanAngelesから完全にコピーされます。 コメントからのversionCode値の説明:x86ではversionCodeの最大値を設定します。これは、デバイスがx86とarmの両方をサポートする場合、x86の組み立てが望ましい(明らかに、大きなバージョンがあるためインストールされる)ためであり、最小のversionCodeは fatに規定されています (理論上、これはライブラリのすべての可能なバージョンを一度に含む「厚い」* .apkである必要があります)。
  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 } } 

各アセンブリオプションのversionCodeの値を「 アセンブル 」します。
  // 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 } 

最後に、最後のセクション依存関係 。 覚えている場合は、ここでコンパイルした「com.android.support:support-v4:19.+」「com.android.support:support-v4:20.+」に変更し、インストールしたライブラリの唯一のバージョンでコンパイルします。 ここで、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.+' } 

変更されたbuild.gradleファイルの完全なリスト
 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. jniフォルダーを作成する

jniフォルダーにC / C ++ファイルを保存します。 NDKはこれを期待しています。 MyApplication / app / src / mainにフォルダーを作成する必要があります 。 これは、Android Studioから直接、2つの方法で実行できます。
まず、メインを右クリックして、フォルダを作成します
新規->ディレクトリ

第二に、特別なメニュー項目を使用できます
新規->フォルダ-> JNIフォルダ
フォルダー作成ウィザードを起動します


最初のダイアログでは、モジュールのどの部分にjniフォルダーを作成するかを選択し、2番目では、その場所を変更できます。

6. C ++ファイルを追加する

C ++ファイル用のウィザードはありません。したがって、ファイルを作成するには、 jniフォルダーを右クリックして選択します。
新規->ファイル

まず、ヘッダーファイル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 
説明
  • #pragma once once-再包含に対する標準(#ifndef /#define /#endif)保護の代わり。 #pragma onceは、ほとんどのC ++コンパイラで理解されるようになりました。
  • #include <jni.h>ヘッダーを有効にして、そこで宣言された型を使用できるようにします。
  • #ifdef __cplusplus ... #endif内部のコードはC ++でのみコンパイルされます(Cではコンパイルされません)。
  • extern "C" { ... } -名前のマングリングを取り除きます(それが何であり、なぜここで詳しく説明されているか )。
  • JNIEXPORT jstring JNICALL Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI(JNIEnv* env, jclass clazz); -実装する関数の宣言。
    この広告をさらに詳しく分析しましょう。
    • JNIEXPORTは、適切なリンクに必要です。
    • 正しい呼び出し規約についてはJNICALL
    • jstringは、関数の戻り値の型です。この場合、Java文字列と互換性のある文字列です。
    • Java_com_example_markedone_myapp_FullscreenActivity_stringFromJNI-関数の名前は、次のもので構成されます。
      Javaは呼び出す言語です。
      com_example_markedone_myappはアプリケーションID( com.example.markedone.myapp )です。
      FullscreenActivityは、ネイティブ関数を表すメソッドの宣言を含むJavaクラスの名前です。
      stringFromJNI-実際には、関数の名前(これはJavaでの方法です)。
    • JNIEnv* env, jclass clazzは、Javaから渡される必須パラメーターです。
      JNIEnv* env -JNI環境を表すオブジェクトへのポインター。
      jclass clazzは、Javaのネイティブメソッド宣言を所有するクラスです。 ここでは、jclass clazzが静的ネイティブメソッド用であることを予約する必要があります。 非静的メソッドの場合、 jobject objを記述する必要があります。

次に、実装ファイルmyapp.cppを作成します。 その中に次のコードを書きます:
 #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()); } 
説明
  • #include <android/log.h> -ログを接続します。そのためにライブラリ( ldLibs "log" )を追加しました。
  • #include - std::string, STL.
    #include "myapp.h" -ヘッダーファイルを含めます。
    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++!"); -ログに出力するための行を作成します。
    __android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%s", message.c_str()); -ログへの出力。 4つのパラメーターを指定する必要があることに注意してください:ログ内のメッセージのタイプ、タグ、行のフォーマット、最後にメッセージ自体。
    std::string jniMessage("Hello from JNI!"); Javaで渡す文字列です。
    return env->NewStringUTF(jniMessage.c_str()); -戻り値。JNIEnvを使用して、C文字列からjstringを作成します。 CではなくC ++で記述しているため、フォーム(*env)->構築は必要ないことに注意してください。

次に、別のファイルstub.cppを作成し、空のままにします。 実際、jniフォルダーに* .cppファイルを1つだけ残した場合、ndk-buildは「ターゲットを作成するルールがありません」というエラーをスローします。

7. Javaからネイティブ関数呼び出しを追加します

ファイルMyApplication / app / src / main / java / com.example.markedone.myapp.FullscreenActivityを開きます。 実際、java拡張機能があり、com、たとえば、markedoneとmyappはフォルダーですが、Android Studioはそれを隠します。
ファイルの内容
 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); } } 


次のコードをFullscreenActivityクラスに追加します。
  static { System.loadLibrary("myapp"); } private static native String stringFromJNI(); 
ここでは、最初にライブラリが読み込まれ、次にC ++の関数に対応するstringFromJNIメソッドの宣言が読み込まれます。 静的(これはC ++関数の2番目のパラメーターとして(jclassまたはjobject)に影響します)およびネイティブとして宣言されていることに注意してください。 ネイティブメソッドを実装する必要はありません。すでにC ++でこれを行っており、残りはJNIによって行われます。

これで、一般に、すでに関数を呼び出すことができます。 私のように、FullscreenActivityを選択した場合、実際には何もしないダミーボタンがあります。 touch listener, ( , ), , , .

:
import android.widget.Button;
.

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

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; } }; 

  • final String message = stringFromJNI(); — C++. — , .
  • final Button button = (Button)findViewById(R.id.dummy_button); — .
  • 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 .

おわりに


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

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

PS C++ .

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


All Articles