由于Java是Android的官方语言,大量原生调用和库都是基于Java语言的。而Unity3D的开发语言是C#,导致很多原生功能需要借用Jar库。
使用U3D的很多程序员对Java以及Android Studio之类的开发工具并不熟悉,一个很简单的功能也可能要耗费掉许多精力,需要大量学习以帮助熟悉Java和他的开发框架。
自从Unity3D 2018以后,这种烦恼减少了。Unity3D 2018可以直接使用Java(.java)和Kotlin(.kt)源码文件了。只要把.java或者.kt的源代码文件直接放置在Plugins目录下,Android Gradle编译系统会自动编译Jar,无需用户自己做设置。
下图为Gradle编译器自动生成的类似于Android Studio开发功能目录结构
以下提供一个最简单的Android Service框架,可以在自己的Unity3D Android工程中方便使用,扩展Android原生功能。
创建一个最简单的Android Service类
新建一个MainService.java文件,放置于Plugins目录下。Java类的文件命名方式和C#类类似,文件名和类名要一致。
package com.VRFab.VRXPlayer; // 调用服务的package包名前缀,此处包名全程是com.VRFab.VRXPlayer.MainService
import android.app.Service;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.ComponentName;
import android.app.ActivityOptions;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.provider.Settings;
import android.graphics.Rect;
import com.unity3d.player.UnityPlayer;
import android.widget.Toast;
import android.os.Build;
public class MainService extends Service {
// 从Service继承后台服务类(另一个类型是Activity实体窗口类)
//Log用的TAG
private static final String TAG = "MainService";
protected UnityPlayer mUnityPlayer;
// 静态成员
public static String message = "";
public static Activity activity = null;
// 静态函数
public static void StartService(Activity act) {
activity = act;
act.startService(new Intent(act, MainService.class));
}
public static void Launch(String pkgName) {
message = "Launch";
Intent LaunchIntent = new Intent();
LaunchIntent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName componentName = new ComponentName(pkgName, "com.unity3d.player.UnityPlayerActivity");
LaunchIntent.setComponent(componentName);
LaunchIntent.setAction("android.intent.action.MAIN");
ActivityOptions ao = ActivityOptions.makeBasic();
Rect rect = new Rect(0, 0, 200, 100);
ActivityOptions bounds = ao.setLaunchBounds(rect);
activity.startActivity(LaunchIntent, bounds.toBundle());
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "MainService Created");
message = "MainService Created";
mUnityPlayer = new UnityPlayer(this.getApplicationContext());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
// Quit Unity
@Override public void onDestroy ()
{
super.onDestroy();
}
}
注册Android服务的package
打开\Assets\Plugins\Android\AndroidManifest.xml文件,添加服务注册代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal">
<application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name" android:isGame="true">
<!--activity注册-->
<activity>
<!--...-->
</activity>
<!--service注册-->
<service android:name="com.VRFab.VRXPlayer.MainService" android:enabled="true" android:exported="true">
<!--android:name和Service的package全名一致-->
</service>
</application>
</manifest>
通过C#代码启动Android Service
由于C#不能直接使用Java的类型,Unity3D C#通过AndroidJavaClass和AndroidJavaObject对Java类进行包装,AndroidJavaClass是对Java类类型的引用,AndroidJavaObject是对Java类实例的引用。
通常通过AndroidJavaClass调用静态函数*(CallStatic和CallStatic<>,注意,没有返回值的函数不要用CallStatic<>调用,会报没有匹配函数错误)和静态成员(GetStatic<>/SetStatic<>)*
通过AndroidJavaObject调用成员函数*(Call和Call<>,注意,没有返回值的函数不要用Call<>调用,会报没有匹配函数错误)和属性(Get<>/Set<>)*
实际上AndroidJavaClass和AndroidJavaObject都有Call和CallStatic函数,也都有GetStatic/SetStatic和Get/Set函数,规则上有一定混乱的,最好还是按照字面意思分别用作静态函数调用和成员函数调用
AndroidJavaClass clsMainService = new AndroidJavaClass("com.VRFab.VRXBle.MainService"); // 创建MainService类型引用
AndroidJavaClass clsActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); // 创建UnityPlayer类型引用
AndroidJavaObject objActivity = clsActivity.GetStatic<AndroidJavaObject>("currentActivity"); // UnityPlayer类型调用静态成员获得UnityPlayerActivity实例引用,这是Unity3D生成App的主Activity,也是唯一Activity
clsMainService.CallStatic("StartService", objActivity); // 通过MainService类型调用Java静态成员,启动com.VRFab.VRXBle.MainService后台服务,具体启动代码见java代码
之后,我们可以在MainService中添加Java函数和代码,通过Call和CallStatic调用,帮助Unity3D C#实现Android原生功能。
比如我们可以调用MainService.Launch函数帮我们启动apk,通过java代码可以设置更多启动参数
clsMainService.CallStatic("Launch", "com.VRFab.GameApp"); // Launch代码见MainService.java
如果通过C#直接调用启动apk包,则只能通过AndroidJavaClass和AndroidJavaObject调用Java库,实现的代码比较复杂和别扭
public static XmlRequestResult OpenPackage(string pkgName)
{
XmlRequestResult r = new XmlRequestResult();
using (AndroidJavaClass jcPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (AndroidJavaObject joActivity = jcPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
try
{
AndroidJavaObject Intent = new AndroidJavaObject("android.content.Intent");
AndroidJavaObject ComponentName = new AndroidJavaObject("android.content.ComponentName", pkgName, "com.unity3d.player.UnityPlayerActivity");
Intent.Call<AndroidJavaObject>("setComponent", ComponentName);
Intent.Call<AndroidJavaObject>("setAction", "android.intent.action.MAIN");
joActivity.Call("startActivity", Intent);
r.Code = XmlRequestResult.ExceptionCode.noerror;
r.Message = "OK";
}
catch (System.Exception ex)
{
r.Code = XmlRequestResult.ExceptionCode.unknown_exception;
r.Message = "调用程序包失败: " + ex.Message;
}
}
}
return r;
}