はじめに
本記事は以下を参考にAndroidStudioでプロジェクトを作るところから初めて
実際に起動するまでの手順の備忘録です
プロジェクトを新規に作成
Save Locationは phoenix_pj/native/android に設定
Minumum SDKはお好きに自分は10.0にしました
gradleはElixirDesktopに合わせてgrooby DSLにする .ktsにしない
サンプルPJからファイルをコピー
app/libs/erlang.jarを
native/android/app/libs/ にコピー
app/src/main/assets を
native/android/app/src/main/ にコピー
app/src/main/res/xml/provider_paths.xml を
native/android/app/src/main/res/xml にコピー
app/src/main/res/layout/activity_main.xml を
native/android/app/src/main/res/layout/activity_main.xml と差し替える
main/java のファイルをコピー & fix
app/src/main/java/io/elixirdesktop/example/Bridge.ktを
native/android/app/src/main/java/com/example/trareco/ にコピー
packageを差し替え
- package io.elixirdesktop.example
+ package com.example.trareco
import android.annotation.SuppressLint
...
package 以外を差し替え
コピー後
databinding.ActivityMainBindingの前半分を差し替え
- package io.elixirdesktop.example
+ package com.example.trareco
import android.os.Bundle
import android.system.Os
import android.app.Activity
import android.view.KeyEvent
import android.view.View
- import io.elixirdesktop.example.databinding.ActivityMainBinding
+ import com.example.trareco.databinding.ActivityMainBinding
import java.io.*
import java.util.*
import android.webkit.WebView
import android.webkit.WebViewClient
class MainActivity : Activity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.browser.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
if (binding.browser.visibility != View.VISIBLE) {
binding.browser.visibility = View.VISIBLE
binding.splash.visibility = View.GONE
}
}
}
if (bridge != null) {
// This happens on re-creation of the activity e.g. after rotating the screen
bridge!!.setWebView(binding.browser)
} else {
// This happens only on the first time when starting the app
bridge = Bridge(applicationContext, binding.browser)
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
if (binding.browser.canGoBack()) {
binding.browser.goBack()
} else {
finish()
}
return true
}
}
}
return super.onKeyDown(keyCode, event)
}
companion object {
var bridge: Bridge? = null
}
}
manifestファイル変更
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:installLocation="internalOnly">
+ <uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Trareco"
+ android:extractNativeLibs="true"
+ android:usesCleartextTraffic="true"
tools:targetApi="31"
>
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="com.example.trareco.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/provider_paths" />
+ </provider>
<activity
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|density|fontScale|keyboard|layoutDirection|mcc|mnc|navigation|smallestScreenSize|touchscreen|uiMode"
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Trareco">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
run_mixを作成
#!/bin/bash
set -e
BASE=`pwd`
APP_FILE="$BASE/src/main/assets/app.zip"
export MIX_ENV=prod
export MIX_TARGET=android
cd ../../../
if [ ! -d "deps/desktop" ]; then
mix local.hex --force
mix local.rebar
mix deps.get
fi
#if [ ! -d "assets/node_modules" ]; then
# cd assets && npm i && cd ..
#fi
if [ -f "$APP_FILE" ]; then
rm "$APP_FILE"
fi
mix assets.deploy && \
mix release --overwrite && \
cd "_build/${MIX_TARGET}_${MIX_ENV}/rel/trareco" && \
zip -9r "$APP_FILE" lib/ releases/ --exclude "*.so"
proguardに修正
以下を追加
-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5
-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
cpp ファイルの改修
CMakeLists.txtの改修
project("trareco")
# 以下差し替え
add_library(native-lib SHARED native-lib.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library(log-lib log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries(native-lib ${log-lib})
native-lib.cppの改修
一旦全部コピーして環境依存の箇所のみ修正します
extern "C" JNIEXPORT jstring JNICALL
- Java_io_elixirdesktop_example_Bridge_startErlang(
+ Java_com_example_trareco_Bridge_startErlang(
build.gradleの修正
色々足していきます
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.trareco'
compileSdk 33
+ task buildNum(type: Exec, description: 'Update Elixir App') {
+ commandLine './run_mix', 'package.mobile'
+ }
+ tasks.withType(JavaCompile) {
+ compileTask -> compileTask.dependsOn buildNum
+ }
defaultConfig {
applicationId "com.example.trareco"
minSdk 29
targetSdk 33
versionCode 1
versionName "1.0"
+ ndk {
+ abiFilters "armeabi-v7a", "arm64-v8a" , "x86_64"
+ }
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags ''
+ }
+ }
}
buildTypes {
release {
+ minifyEnabled true
+ shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
+ externalNativeBuild {
+ cmake {
+ path file('src/main/cpp/CMakeLists.txt')
+ version '3.22.1'
+ }
+ }
+ buildFeatures {
+ viewBinding true
+ }
+ packagingOptions {
+ jniLibs {
+ useLegacyPackaging = true
+ }
+ }
}
dependencies {
+ implementation fileTree(dir: "libs", include: '*.jar')
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
全部終わったら右上のsync gradleで同期してからビルドを実行します
動いた!
無事Androidも0から構築してElixirDesktopのアプリを起動できました!