#概要
Android向けに開発したSDKをUnityアプリに対応させる場合、プラグインを作ってSDKとUnityアプリの橋渡しをしてやる必要があります。Android向けネイティブプラグインの実装方法は複数存在しますが、本記事ではUnityPlayerActivityを継承する方式を紹介します。iOS向けプラグインとの共通部分の実装については共通編を参考にして下さい。
#プラグイン側
##BaseNativeActivity
BaseNativeActivityはUnityPlayerActivityを継承したクラスです。SampleやSampleLifecycleといったSDKで定義されたクラスのメソッドを代理で呼び出すことで、Wrapperとしての役割を果たしています。
public class BaseNativeActivity extends UnityPlayerActivity {
public static final void setCallbackGameObjectName(String name) {
SampleUnityListener.getInstance().setCallbackGameObjectName(name);
}
@Override
protected void onStart() {
super.onStart();
SampleLifecycle.onStart(this);
}
@Override
protected void onResume() {
super.onResume();
SampleLifecycle.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
SampleLifecycle.onPause(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
SampleLifecycle.onDestroy();
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
return super.onPrepareOptionsMenu(menu);
}
public void start(String sampleCode) {
Sample.getInstance().start(this);
}
public void doSomething() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Sample.getInstance().doSomething()
}
});
}
public int getScore() {
return Sample.getInstance().getScore();
}
public String getSomeText() {
return Sample.getInstance().getSomeText();
}
public boolean isAvailable() {
return Sample.getInstance().isAvailable();
}
}
##SampleUnityListener
SampleUnityListenerはSDK側のコールバックをUnity側へ伝えるためのクラスです。SDK側で定義されたリスナークラス、SampleListenerを実装しています。
public class SampleUnityListener implements SampleListener {
private static final Sample sample = Sample.getInstance();
private static SampleUnityListener instance;
private String callbackGameObjectName;
public synchronized static SampleUnityListener getInstance() {
if (instance == null) {
instance = new SampleUnityListener();
sample.setListener(instance);
}
return instance;
}
public final void setCallbackGameObjectName(String name) {
callbackGameObjectName = name;
}
@Override
public void onStatusChanged(SampleStatus status) {
if (callbackGameObjectName != null) {
String statusIndex = String.valueOf(status.ordinal());
UnityPlayer.UnitySendMessage(callbackGameObjectName, "_Sample_didStatusChange", statusIndex);
}
}
}
##build.grade
build.gradleの一例です。 SDK本体(SampleSDK-*.jar)に加え、UnityPlayerActivity等のクラスをimportするためclasses.jarをlibsディレクトリに含めていますが、ビルド時は不要のためexcludeしています。またUnity側から名前解決する必要があるため、minifyもfalseにしています。
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:26.+'
}
android.libraryVariants.all { variant ->
variant.outputs.each { output ->
output.packageLibrary.exclude('libs/classes.jar')
output.packageLibrary.exclude('libs/SampleSDK-*.jar')
}
}
##ビルドについて
AndroidStudioの場合、Build -> Make Moduleからaarを作成することができます。ビルドしたaarはUnityプロジェクトのAssets/Plugins/Android配下に置くと良いでしょう。また、コンパイル時に使用しているclasses.jarの取得方法等についてはこちらを参照して下さい。
#Unity側
##Sample_Android.cs
Sample_Android.csはISampleを実装したクラスで、プラグイン側で実装したクラス(BaseNativeActivity)にアクセスすることでUnityアプリとSDKの橋渡しを行います。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class Sample_Android : ISample {
private Sample sampleGameObject;
private SampleEventListener listener;
private static AndroidJavaObject androidInstance;
public Sample_Android(Sample sampleParent) {
sampleGameObject = sampleParent;
InitAndroidInstance();
CreateListenerObject();
if (sampleParent.androidSampleCode != null) {
Start(null);
}
}
private void CreateListenerObject() {
listener = sampleGameObject.gameObject.AddComponent<SampleEventListener>();
using (AndroidJavaObject activityObject = GetCurrentActivity()) {
activityObject.CallStatic("setCallbackGameObjectName", sampleGameObject.gameObject.name);
}
listener.SetNativeParent(this);
}
public void Start(string sampleCode) {
using (AndroidJavaObject activityObject = GetCurrentActivity()) {
if (sampleGameObject.androidSampleCode != null) {
activityObject.Call("start", sampleGameObject.androidSampleCode);
} else if (sampleCode != null) {
activityObject.Call("start", sampleCode);
}
}
}
public void DoSomething() {
using (AndroidJavaObject activityObject = GetCurrentActivity()) {
activityObject.Call("doSomething");
}
}
public int GetScore() {
using (AndroidJavaObject activityObject = GetCurrentActivity()) {
return activityObject.Call<int>("getScore");
}
}
public SampleStatus GetStatus() {
SampleStatus status = SampleStatus.Unavailable;
using (AndroidJavaObject statusObject = androidInstance.Call<AndroidJavaObject>("getStatus")) {
string statusName = statusObject.Call<string>("name");
if (statusName.Equals("Unavailable")) {
status = SampleStatus.Unavailable;
} else if(statusName.Equals("Available")) {
status = SampleStatus.Available;
}
}
return status;
}
public string GetSomeText() {
return androidInstance.Call<string>("getSomeText");
}
public bool IsAvailable() {
using (AndroidJavaObject activityObject = GetCurrentActivity()) {
return activityObject.Call<bool>("isAvailable");
}
}
public AndroidJavaObject GetCurrentActivity() {
using (AndroidJavaClass playerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
return playerClass.GetStatic<AndroidJavaObject>("currentActivity");
}
}
protected static void InitAndroidInstance() {
using (AndroidJavaClass sampleClass = new AndroidJavaClass("com.sdk.Sample")) {
androidInstance = sampleClass.CallStatic<AndroidJavaObject>("getInstance");
}
}
}
##AndroidManifest.xml
UnityプロジェクトのAssets/Plugins/Android配下に置くAndroidManifestの一例です。前述のBaseNativeActivityを記載しています。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player"
android:installLocation="preferExternal"
android:theme="@android:style/Theme.NoTitleBar"
android:versionCode="1" android:versionName="1.0">
<supports-screens android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true" />
<application android:label="@string/app_name" android:debuggable="false">
<activity android:name="com.sample.plugin.BaseNativeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
Proguard
一部のクラスに対するMinifyの回避等の設定を行いたい場合、proguard-user.txtを作成してUnityプロジェクトのAssets/Plugins/Android配下に置いておくと、AndroidプロジェクトとしてExportした際に自動的に反映してくれます。
-keepattributes *Annotation*
-keepattributes Signature
-keep class com.sample.sdk.** {
public <fields>;
public <methods>;
}
-keep class com.sample.plugin.** {
public <fields>;
public <methods>;
}
-dontwarn com.sample.sdk.**
##ディレクトリ構成
下記を必要に応じてUnityプロジェクトのAssets/Plugins/Android配下に配置して下さい。
- SDK本体(jar/aar)
- プラグイン(jar/aar)
- AndroidManifest.xml
- proguard-user.txt
#参考
https://docs.unity3d.com/ja/540/Manual/PluginsForAndroid.html