一、簡介
通常一款游戲開發到后期,一般都會涉及到第三方SDK的接入與集成,對於不熟悉SDK接入的同學來說,接SDK每次都是雲里霧里,而熟悉SDK接入的同學又覺得不斷地重復做接入SDK工作這樣沒有成就感,太尼瑪無聊了(Android渠道一弄就十幾個,直接吐血)。其實通常情況下接入SDK都是很簡單的一個過程,本系列博客就讓馬三和大家從小白開始,從零基礎開始學習如何接入SDK以及一些常見的SDK的接入流程。本系列博客規划為以下幾篇:
- SDK接入與集成——小白入門篇(介紹環境搭建以及Unity和Android的基本交互與調用)
- SDK接入與集成——信鴿SDK篇(介紹消息推送框架--信鴿SDK的接入)
- SDK接入與集成——QQ與微信SDK篇(都是騰訊開放平台的,就放在一起學了)
- SDK接入與集成——科大訊飛SDK篇(游戲中的語音通信和語音識別會用到此SDK)
- SDK接入與集成——百度地圖SDK篇(做LBS游戲必不可少)
- SDK接入與集成——第三方SDK接入解決方案AnySDK篇
- SDK接入與集成——構建自己的Android集成多SDK框架篇
先挖了這么多坑,以后慢慢填吧,放心博主肯定不會太監的。
二、淺談常用的兩種接入方案
1.第三方SDK接入解決方案
其實游戲SDK接入發展到現在,已經有很多成熟的第三方SDK接入解決方案了,比如AnySDK,ShareSDK,U8SDK等等。這些第三方SDK接入解決方案的整個接入過程,不改變任何SDK的功能、特性、參數等,對於最終玩家而言是完全透明無感知的。讓CP商能有更多時間更專注於游戲本身的品質。第三方SDK包括了渠道SDK、用戶系統、支付系統、廣告系統、統計系統、分享系統等等。利用他們可以輕松快速接入第三方SDK。
第三方SDK的統一驗證流程基本如下:

2.手動接入SDK
既然上面說的第三方解決方案那么好,為什么我們還有手動去接入SDK呢?造輪子就這么上癮?其實接入了一些第三方的SDK解決方案以后,我們有的游戲數據是要經過他們的服務器的,對於一些游戲廠商來說,不想讓自己的數據經過別人的服務器,或者需要對驗證服務器有完全自主的控制權,那么必然要手動接入各種SDK了。另外還有一些奇奇怪怪,非常詭異的SDK,我們也是要手動去接入的,不能都指望第三方的集成。而且作為一名合格的猿類來說,知其然更要知其所以然,掌握SDK的接入原理和過程很有必要。
三、開始接入!Unity與Android的交互
前面啰嗦了那麽多,到這里終於可以開始實戰操作了。
1.Android開發環境搭建
關於Android環境的搭建,網上已經有很多博客了,介紹的很詳細,馬三就不在這里水了。這里給大家安利一個關於Android開發工具的好網站:http://www.androiddevtools.cn/ 。上面提供了很多可用的AndroidADK國內鏡像和教程。
2.Android端的開發工作
(1)打開IDE建立一個空的Android庫工程,這里我用Eclispe舉例。注意Min Required SDK最好選擇4.0以上,要不然還需要引入android-support-v7兼容包,比較麻煩,之后我們可以把這個Min Required SDK 再該回來的。需要注意的兩步已經截圖了,剩下一路Next操作即可。注意包名和勾選Mark this project as a library選項。


(2)導入Classes.jar包到Android工程中
Unity和Android做交互,他們兩個之間不認識肯定,沒法直接通信,因此需要一個中間的搭橋牽線的人,Classes.jar就起到了這個作用。Classes.jar是由Unity提供給我們的,我們需要找到它並且引入到我們的Android項目中。Claess.jar的路徑一般如下 X盤:\xxx目錄\Unity\Editor\Data\PlaybackEngines\androidplayer\release\bin\classes.jar(不同的計算機上,這個位置可能會有所不同,大家按照自己的路徑添加即可)。我們找到它直接拖到我們的Android工程的libs目錄下。然后在它上面右鍵,將其添加到Build Path中。

添加到Build Path成功以后,工程是這個樣子的。

(3)編寫Android端的代碼
我們在Android端編寫一些代碼,提供一些接口來供Unity一會的調用。打開我們的MainActivity.java,然后添加代碼。需要注意的是,讓我們的MainActivity繼承Jar包中的UnityPlayerActivity類,這樣,Unity才能調的到哦,缺什么包,直接讓Eclipe自動導下包即可,快捷鍵ctrl+shift+o。對了,還需要把 setContentView(R.layout.activity_main); 這段代碼注釋掉,要不然會顯示Android的默認布局文件,上面就一個 Hello World。


簡單的寫了幾個普通方法和一個靜態方法,供一會的測試調用。(無論是靜態方法還是普通方法,在Unity中都是可以調用的到的)MainActicity.java的代碼內容如下:
1 package com.mx.sdkbase;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.view.Menu;
6 import android.view.MenuItem;
7 import android.widget.Toast;
8
9 import com.unity3d.player.UnityPlayer;
10 import com.unity3d.player.UnityPlayerActivity;
11
12 public class MainActivity extends UnityPlayerActivity {
13
14 private static MainActivity instance;
15
16 @Override
17 protected void onCreate(Bundle savedInstanceState) {
18 super.onCreate(savedInstanceState);
19 // setContentView(R.layout.activity_main);
20
21 instance = this;
22 }
23
24 @Override
25 public boolean onCreateOptionsMenu(Menu menu) {
26 // Inflate the menu; this adds items to the action bar if it is present.
27 getMenuInflater().inflate(R.menu.main, menu);
28 return true;
29 }
30
31 @Override
32 public boolean onOptionsItemSelected(MenuItem item) {
33 // Handle action bar item clicks here. The action bar will
34 // automatically handle clicks on the Home/Up button, so long
35 // as you specify a parent activity in AndroidManifest.xml.
36 int id = item.getItemId();
37 if (id == R.id.action_settings) {
38 return true;
39 }
40 return super.onOptionsItemSelected(item);
41 }
42
43
44 /**
45 * 供Unity調用的求和函數
46 * @param x
47 * @param y
48 * @return
49 */
50 public int Sum(int x, int y) {
51 return x + y;
52 }
53
54 /**供Unity調用的比較最大值函數
55 * @param x
56 * @param y
57 * @return
58 */
59 public int Max(int x, int y) {
60 return Math.max(x, y);
61 }
62
63
64 /**供Unity調用的顯示吐司的函數
65 * @param str
66 */
67 public void MakeToast(String str) {
68 Toast.makeText(this, str, Toast.LENGTH_LONG).show();
69 }
70
71
72 /**供Unity調用的自加一函數
73 * @param x
74 * @return
75 */
76 public int AddOne(int x) {
77 return x + 1;
78 }
79
80
81 /**供Unity調用的靜態方法,單例類,返回當前的Activity對象
82 * @return
83 */
84 public static MainActivity GetInstance() {
85 return instance;
86 }
87
88
89 /**供Unity調用的函數,此函數會回調指定的一個Unity中的方法,完成數據的雙向交互
90 * @param str
91 */
92 public void CallUnityFunc(String str){
93 str=str+"Android Call Unity.";
94 String ReceiveObject="MessageHandler";
95 String ReceiverMethod="Receive";
96 UnityPlayer.UnitySendMessage(ReceiveObject, ReceiverMethod, str);
97 }
98 }
(4)導出我們的Android項目為Jar包供Unity調用
在我們的項目上面右鍵,然后選擇Export,選擇Java目錄下的 Jar file。因為沒有用到第三方的jar包或者lib庫,因此只要勾選src/和res/目錄導出為jar包即可。


(5)Unity端工程的開發
建立一個新的空Unity工程,然后在Asset/目錄下建立如下路徑的文件夾:Plugins/Android。從名字就可以看出來,這個文件夾是用來存放安卓的插件的。然后將我們上面剛剛導出的SDKBase.jar 包導入到這個目錄下,並且將Andoird工程目錄下的,libs/ 、res/ 、AndroidMainFest.xml 都復制到該路徑下。
需要特別注意的是要將Unity 項目中 libs下的classes.jar文件刪除掉,這個就是上面提到的那個起到中介作用的jar包,一定要刪掉!一定要刪掉!一定要刪掉!(重要的事情說三遍,網上不少教程都是針對Unity老版的教程,沒有提到要刪除這個classes.jar包,結果在Unity 5.x中打包肯定會出錯)。出錯截圖如下所示。

然后我們建立一個場景,簡單地在里面放上一些Label和輸入框、按鈕,供我們驗證交互操作。並且編寫一個腳本(MessageHandler.cs即是我創建的腳本), 在其中編寫用來調用Jar包的C#方法,然后將按鈕和這些函數綁定(Unity基本操作,不贅述了)。

MessageHandler.cs 腳本的內容如下,函數的功能看注釋就行了,寫得很全。
1 using System;
2 using UnityEngine;
3 using UnityEngine.UI;
4
5 public class MessageHandler : MonoBehaviour
6 {
7 private AndroidJavaClass _jc;
8 private AndroidJavaObject _jo;
9
10 public InputField inputFieldA;
11 public InputField inputFiledB;
12 public Text resultLabel;
13
14 // Use this for initialization
15 void Start()
16 {
17 //初始化
18 //"com.unity3d.player.UnityPlayer"和"currentActivity"這兩個參數都是固定的
19 //UnityPlayerActivity里面對其進行了處理
20 _jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
21 _jo = _jc.GetStatic<AndroidJavaObject>("currentActivity");
22 }
23
24 public void AddOne()
25 {
26 int a = Convert.ToInt32(inputFieldA.text);
27
28 //注意,這里使用的就不是之前默認的com.unity3d.player.UnityPlayer,而是需要傳入自己的類(實現了需要調用相應方法的類)
29 //因為默認的UnityPlayer中是沒有我們所需要的方法的,所以需要加載自己的類
30 AndroidJavaClass jc = new AndroidJavaClass("com.mx.sdkbase.MainActivity");
31 //調用Java中的靜態方法,單例模式,返回當前Activity實例
32 AndroidJavaObject jo = jc.CallStatic<AndroidJavaObject>("GetInstance");
33 resultLabel.text = "AddOne" + jo.Call<int>("AddOne",a);
34 }
35
36 public void Sum()
37 {
38 int a = Convert.ToInt32(inputFieldA.text);
39 int b = Convert.ToInt32(inputFiledB.text);
40 //調用Java類中的普通方法,返回值為int型
41 resultLabel.text = "Sum: " + _jo.Call<int>("Sum", a, b);
42 }
43
44 public void Max()
45 {
46 int a = Convert.ToInt32(inputFieldA.text);
47 int b = Convert.ToInt32(inputFiledB.text);
48 resultLabel.text = "Max: " + _jo.Call<int>("Max", a, b);
49 }
50
51 public void CallUnityFunc()
52 {
53 //調用Java中的一個方法,該方法會回調Unity中的指定的一個方法,這里會回調Receive( )
54 _jo.Call("CallUnityFunc","Unity Call Android.\n");
55 }
56
57 public void Receive(string str)
58 {
59 resultLabel.text = str;
60 }
61
62 public void Toast()
63 {
64 _jo.Call("MakeToast","Unity 調用Toast");
65 }
66 }
通過上面的代碼,我們就可以看出來,想在Unity中調用Android的代碼,主要涉及到了兩個類。AndroidJavaClass 和 AndroidJavaObject 。這兩個類在Unity API手冊里面有詳細的解釋。
下面的代碼是獲取到對應包名的java.lang.Class實例,這里獲取到的是com.unity3d.player.UnityPlayer類。
_jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
下面的代碼是獲取到UnityPlayer類中的靜態字段,它的返回值類型是AndroidJavaObject對象。
_jo = _jc.GetStatic<AndroidJavaObject>("currentActivity");
通過以上兩行代碼,我們可以獲取到這個AndroidJavaObject 對象,然后用 AndroidJavaObject 對象就可以任意地調用Android中的靜態和非靜態函數了。其中兩個函數中的字符串參數 "com.unity3d.player.UnityPlayer" 和 "currentActivity" 都是固定的寫法,我們不用去改變。
AndroidJavaObject 類的一些常用方法及功能如下表所示:
| AndroidJavaObject | 構造函數,根據類名返回AndroidJavaObject對象 |
| Call | 調用Android代碼中的非靜態方法 |
| CallStatic | 調用Android代碼中的靜態方法 |
| Dispose | IDisposable 回調 |
| Get | 獲取Android代碼中的非靜態字段 |
| GetRawClass | 獲取一個指向Java class的原始引用 |
| GetRawObject | 獲取一個指向Java object的原始引用 |
| GetStatic | 獲取Android代碼中的靜態字段 |
| Set | 設置Android代碼中的非靜態字段 |
| SetStatic | 設置Android代碼中的靜態字段 |
另外,我們還有第二種方法去訪問Java的代碼,那就是利用我們之前在Java代碼中寫的 GetInstance() 靜態方法,它會返回一個MainActivity的實例,我們拿到這個實例以后,就能訪問里面的方法和字段了。需要注意的是此時的AndroidJavaClass構造函數中傳遞的字符串就不是 "com.unity3d.player.UnityPlayer" 了。而是要傳入自己的包名,比如代碼中的 “com.mx.sdkbase.MainActivity” 。代碼如下:
int a = Convert.ToInt32(inputFieldA.text);
//注意,這里使用的就不是之前默認的com.unity3d.player.UnityPlayer,而是需要傳入自己的類(實現了需要調用相應方法的類)
//因為默認的UnityPlayer中是沒有我們所需要的方法的,所以需要加載自己的類
AndroidJavaClass jc = new AndroidJavaClass("com.mx.sdkbase.MainActivity");
//調用Java中的靜態方法,單例模式,返回當前Activity實例
AndroidJavaObject jo = jc.CallStatic<AndroidJavaObject>("GetInstance");
resultLabel.text = "AddOne" + jo.Call<int>("AddOne",a);
不止Unity可以調用Android的代碼,Android也可以反過來回調Unity的代碼。下面這段代碼就是用來回調Unity函數的:
/**供Unity調用的函數,此函數會回調指定的一個Unity中的方法,完成數據的雙向交互
* @param str
*/
public void CallUnityFunc(String str){
str=str+"Android Call Unity.";
String ReceiveObject="MessageHandler";
String ReceiverMethod="Receive";
UnityPlayer.UnitySendMessage(ReceiveObject, ReceiverMethod, str);
}
利用UnityPlayer.UnitySendMessage(ReceiveObject, ReceiverMethod, str); 就可以返回過來回調一個Unity中的方法,完成Unity和Android的雙向通信。其中第一個參數是接受該回調的gameobject名稱,第二個參數是掛載在該gameobject上面的一個腳本中接受該消息的方法,最后一個參數是本條消息發送的字符串信息。比如上面例子中的代碼就會調用名稱為MessageHandler的gameobject上面掛載的腳本中的Receive方法。
(6)打包發布Android平台的APK
代碼寫好以后,我們會習慣性地在Unity Editor 里面運行查看一下效果,但是如果要調用 Android 代碼的話,是不可以這樣做的,一定要在真機上運行(模擬器上也行),在Editor中運行會報錯的。所以我們還是打包發布到Android端查看效果吧。
在Unity中按快捷鍵 ctrl +b ,打開Build Setting界面,然后把平台切換為 Android 平台並將我們的測試場景加到Build Setting隊列中。點擊PlayerSetting,對工程的信息進行配置。注意要把里面的Company Name和Product Name修改成和包名一致。如下圖所示:

然后 ,Bundle Identifier的值也要修改成和包名一樣,並且調整下Minimum API Level。如下圖:

最后,還記得我們在最一開始建立Android庫工程的時候,將最小安裝需求的API調成了4.0嗎,這就意味着,打出來的APK包安裝運行的最低系統要求是Android 4.0。這樣肯定是不可以的,要考慮到低版本的Android系統。因此還需要做最后一步的修改,才能打包。
找到我們Unity項目中的 AndroidManifest.xml 文件,用文本編輯器打開它,將android:minSdkVersion的值修改成上一步在面板中設置的 Minimum API Level 對應的版本號,比如我這里面的 Minimum API Level 為2.3.3,其版本號為 10。

之后,我們就可以放心地打包了,打包成功后安裝到手機上測試下效果,下面是我在模擬器上測試的幾張效果圖:


可以看到Unity成功地調用到了Android中的方法,並返回正確的結果,而且Android反過來也回調了Unity中的方法。
四、結語
關於“SDK接入與集成的小白入門篇”就寫到這里了,通過本篇博客,我們一起初步地了解和學習了一下Unity和Android是如何交互的。下篇博客,我們將會實戰地練習一下“消息推送框架”信鴿SDK的接入與使用,敬請期待!
最后放上本篇博客中演示的項目源碼:
Github地址:https://github.com/XINCGer/Unity3DTraining/tree/master/SDK/SDKBase 歡迎fork!

