一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Unity編譯Android的原理解析和apk打包分析

 kiki的號(hào) 2017-04-01

最近由于想在Scene的腳本組件中,調(diào)用Android的Activity的相關(guān)接口,就需要弄明白Scene和Activity的實(shí)際對(duì)應(yīng)關(guān)系,并對(duì)Unity調(diào)用Android的部分原理進(jìn)行了研究。

本文主要探討Scene和Activity之間的關(guān)系,以及Unity打包apk和Android studio打包apk的差別在什么地方?找到這種差別之后,可以怎么運(yùn)用起來(lái)?

本文需要用到的工具:
- Android反編譯工具——apktool
- Android studio自帶的反編譯功能

一、將Unity的Scene編譯成apk,apk的程序入口會(huì)是什么?

  1. 新建一個(gè)Unity項(xiàng)目,創(chuàng)建一個(gè)Scene,將Unity工程編譯打包成apk。
  2. 對(duì)編譯出來(lái)的apk,利用apktool進(jìn)行反編譯:apktool d unityTest.apk
  3. 得到的AndroidManifest文件如下:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas./apk/res/android" android:installLocation="preferExternal" package="com.xfiction.p1" platformBuildVersionCode="25" platformBuildVersionName="7.1.1">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true"/>
    <application android:banner="@drawable/app_banner" android:debuggable="false" android:icon="@drawable/app_icon" android:isGame="true" android:label="@string/app_name" android:theme="@style/UnityThemeSelector">
        <activity android:configChanges="locale|fontScale|keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode" android:label="@string/app_name" android:launchMode="singleTask" android:name="com.unity3d.player.UnityPlayerActivity" android:screenOrientation="fullSensor">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true"/>
        </activity>
    </application>
    <uses-feature android:glEsVersion="0x00020000"/>
    <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
    <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false"/>
    <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false"/>
</manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

由該AndroidManifest文件可知,系統(tǒng)仍然存在主Activity,名字為com.unity3d.player.UnityPlayerActivity。

言下之意,編譯只包含Scene的Unity工程,打包成Android apk,會(huì)以com.unity3d.player.UnityPlayerActivity作為主程序入口,那么問(wèn)題來(lái)了,Scene如何加載顯示到這個(gè)UnityPlayerActivity呢?

二、UnityPlayerActivity如何加載Unity中的Scene?

2.1 UnityPlayerActivity

這個(gè)就要從UnityPlayerActivity源碼入手了,Android工程中使用UnityPlayerActivity需要依賴到Unity的Android插件classes.jar(位于Unity安裝目錄,可以用everything軟件查找查找得到),對(duì)其進(jìn)行反編譯得到UnityPlayerActivity的部分源碼:

public class UnityPlayerActivity extends Activity {
    protected UnityPlayer mUnityPlayer;
    protected void onCreate(Bundle var1) {
        this.requestWindowFeature(1);
        super.onCreate(var1);
        this.getWindow().setFormat(2);
        this.mUnityPlayer = new UnityPlayer(this);
        this.setContentView(this.mUnityPlayer);
        this.mUnityPlayer.requestFocus();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

雖然經(jīng)過(guò)混淆,看起來(lái)比較費(fèi)勁,但從代碼this.setContentView(this.mUnityPlayer)可以看出,最終的界面顯示需要依賴到UnityPlayer的實(shí)例。
另外由于Google也做了一套Unity VR的SDK,與UnityPlayerActivity相對(duì)應(yīng)的類(lèi),就是GoogleUnityActivity,下面也對(duì)它進(jìn)行分析。

2.2 從GoogleUnityActivity.java再入手分析

GoogleUnityActivity是google推出的VR SDK中,用于實(shí)現(xiàn)Unity Activity的類(lèi),通過(guò)google查詢其源碼發(fā)現(xiàn):
1. GoogleUnityActivity.java實(shí)際上的布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/android_view_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" />
</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

布局文件中沒(méi)有具體的內(nèi)容,只包含一個(gè)FrameLayout布局。

2.重點(diǎn)看GoogleUnityActivity的onCreate函數(shù)

public class GoogleUnityActivity   extends Activity
    implements ActivityCompat.OnRequestPermissionsResultCallback {
  protected void onCreate(Bundle savedInstanceState) {

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
setContentView(R.id.activity_main.xml)
        mUnityPlayer = new UnityPlayer(this);
        if (mUnityPlayer.getSettings().getBoolean("hide_status_bar", true)) {
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        ((ViewGroup) findViewById(android.R.id.content)).addView(mUnityPlayer.getView(), 0);
        mUnityPlayer.requestFocus();

  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

mUnityPlayer作為FrameLayoutView加入到view集合中進(jìn)行顯示,注意這里查找的id是android.R.id.content。根據(jù)官方對(duì)這個(gè)id的解釋?zhuān)?
android.R.id.content gives you the root element of a view, without having to know its actual name/type/ID. Check out Get root view from current activity

由此可見(jiàn),GoogleUnityActivity的實(shí)現(xiàn)原理,是創(chuàng)建一個(gè)只包含F(xiàn)rameLayout的空的幀布局,隨后通過(guò)addView將UnityPlayer中的View加載到GoogleUnityActivity中進(jìn)行顯示。

看起來(lái)跟UnityPlayerActivity有異曲同工之妙,兩者牽涉的類(lèi)都是UnityPlayer。

2.3.UnityPlayer究竟是一個(gè)什么類(lèi)呢?

對(duì)classes.jar包進(jìn)行反編譯得到UnityPlayer的部分代碼:

public class UnityPlayer extends FrameLayout implements com.unity3d.player.a.a {
    public static Activity currentActivity = null;
    public UnityPlayer(ContextWrapper var1) {
        super(var1);
        if(var1 instanceof Activity) {
            currentActivity = (Activity)var1;
        }
    }
    public View getView() {
           return this;
    }
    public static native void UnitySendMessage(String var0, String var1, String var2);
    private final native boolean nativeRender();

    public void onCameraFrame(final com.unity3d.player.a var1, final byte[] var2) {
        final int var3 = var1.a();
        final Size var4 = var1.b();
        this.a(new UnityPlayer.c((byte)0) {
            public final void a() {
            UnityPlayer.this.nativeVideoFrameCallback(var3, var2, var4.width, var4.height);
                var1.a(var2);
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

從代碼中可以發(fā)現(xiàn):
1. UnityPlayer實(shí)際上是繼承于FrameLayout
2. 并且自帶一個(gè)currentActivity的成員變量,在構(gòu)造函數(shù)中,直接傳入Activity的相關(guān)參數(shù);
3. 在getView函數(shù)中直接返回該FrameLayout;
4. GoogleUnityActivity通過(guò)UnityPlayer的構(gòu)造函數(shù),將其context傳遞給UnityPlayer,并賦值給其成員變量currentActivity。

由于UnityPlayer類(lèi)做了混淆,關(guān)于渲染的核心功能也封裝在native代碼中,關(guān)于Scene轉(zhuǎn)換到到UnityPlayer作為FrameLayout,只能做一個(gè)簡(jiǎn)單的推測(cè):通過(guò)調(diào)用Android的GL渲染引擎,在native層進(jìn)行渲染,并同步到FrameLayout在UnityPlayerActivity上進(jìn)行顯示

三、 如何將Scene顯示在自定義的Activity當(dāng)中

從以上研究的內(nèi)容可知,假如要從要實(shí)現(xiàn)將Scene顯示在固定的Activity當(dāng)中,則需要對(duì)Activity的oncreate部分的countview和unityplayer進(jìn)行處理。最簡(jiǎn)單的方法是寫(xiě)一個(gè)直接繼承于UnityPlayerActivity或GoogleUnityActivity的類(lèi),并在類(lèi)中寫(xiě)所需要的Unity調(diào)用Android的方法。
這樣Scene就會(huì)加載在特定的Activity當(dāng)中,Unity c#通過(guò)獲取currentActivity變量就可以獲取到該Activity,并調(diào)用其中的函數(shù)。

四、 Unity Android 插件需要注意的問(wèn)題

  1. Android studio工程包含多個(gè)module的依賴,則需要將對(duì)應(yīng)的module編譯的插件一起拷貝Plugins/Android/lib目錄當(dāng)中。
  2. 在第一步驟下,可以直接刪除打包后的aar library目錄,尤其里面假如帶有unity的Android插件classesjar,否則會(huì)編譯報(bào)錯(cuò)。
  3. 多個(gè)module編譯的時(shí)候,注意manifest lablel相關(guān)設(shè)置,另外就是build.gradle的minSDKVersion信息。否則會(huì)出現(xiàn)manifest merger失敗的錯(cuò)誤。
  4. 關(guān)于Unity的Android Manifest文件合并:
    Unity編寫(xiě)一個(gè)Scene,Android studio寫(xiě)一個(gè)包含主Activity的aar包,放在Plugins/Android目錄當(dāng)中。用Unity編譯apk出來(lái)之后,反編譯他的AndroidManifest文件,兩個(gè)主Activity,默認(rèn)顯示包含Scene的Activity。
    解決方法:Unity的Manifest文件合并,把一個(gè)manifest放到Plugins/Android目錄下,就不會(huì)合并manifest了。

五、Unity打包Android apk的結(jié)構(gòu)探究

由于Unity開(kāi)發(fā)Android時(shí),常常設(shè)計(jì)到Unity + Visual和Android studio的環(huán)境切換,Unity的開(kāi)發(fā)往往會(huì)更快一些,更多的是Android java側(cè)的代碼編寫(xiě)和調(diào)試。

這種情況時(shí),有沒(méi)有一種方法,能夠?qū)nity編譯好的Unity Scene和c#相關(guān)文件,放到Android studio中進(jìn)行打包,從而實(shí)現(xiàn)直接在Android studio中進(jìn)行調(diào)試?

方法原理倒是很簡(jiǎn)單,通過(guò)對(duì)比Unity打包的apk,與普通的Android apk的文件差別,找出Unity文件存放的目錄,隨后對(duì)應(yīng)存放到Android studio工程目錄中,最后通過(guò)Android studio完成對(duì)Unity相關(guān)文件的打包。

首先將apk添加zip的后綴,方便用beyond compare進(jìn)行對(duì)比:
1. 發(fā)現(xiàn)只是多了assert/bin目錄,在這個(gè)目錄之下,可以看到unity相關(guān)dll庫(kù)
2. 將該文件,拷貝到Android studio工程的src/main/assert目錄之下;
3. 在Android studio調(diào)試時(shí),可以將aar library工程設(shè)置為app工程,這樣就可以編譯apk運(yùn)行到手機(jī)了。
4. 用Android studio對(duì)該工程進(jìn)行編譯,發(fā)現(xiàn)assert/bin目錄成功被打包進(jìn)去。
5. 直接apk install 運(yùn)行,可以看到跟Unity編譯打包的apk,是相同的效果。

相反,假如Android工程調(diào)試好之后,則直接編譯成app模式修改成library模式,進(jìn)行build之后,就會(huì)生成aar庫(kù),此時(shí)將aar庫(kù)拷貝到Plugins/Android/lib目錄當(dāng)中,注意要?jiǎng)h除aar庫(kù)中的assert/bin,因?yàn)檫@個(gè)目錄是我們先前從Unity拷貝過(guò)去的,假如不刪除,在unity里面會(huì)出現(xiàn)重復(fù)打包導(dǎo)致的文件沖突的情況。

由于當(dāng)將Unity打包之后的bin目錄拷貝到Android studio工程之后,Android studio此時(shí)是一個(gè)library工程,需要轉(zhuǎn)換為app工程。
關(guān)于這其中涉及到的Android studio library和app的轉(zhuǎn)換,通過(guò)設(shè)置build.gradle文件來(lái)實(shí)現(xiàn):

  • app模式:apply plugin: ‘com.android.application’
  • library模式:apply plugin: ‘com.android.library’

不過(guò)在設(shè)置這兩種模式時(shí),需要注意applicationId “com.example.yin.myapplication”的設(shè)置,假如是library模式,則需要直接注釋掉。

假如Android的java部分重新調(diào)試好之后,重新將app模式改成library模式,進(jìn)行build,將生成的aar包,拷貝到Unity Android Plugin目錄中,就可以直接在Unity看運(yùn)行效果了。
不過(guò)一定要記得刪除Android studio打包的aar文件里面的assert/bin目錄,以防止在Unity中重復(fù)打包。

四、結(jié)論:

  1. Unity中的Scene在Android中,其實(shí)對(duì)應(yīng)于Activity的FrameLayout,每個(gè)Scene的運(yùn)行都有其Activity環(huán)境,通過(guò)currentActivity變量可以獲取得到。
  2. 要實(shí)現(xiàn)自定義的Activity能夠具備直接加載Scene的功能,則需要其繼承于UnityPlayerActivity或者GoogleUnityActivity,再或者,直接自定義實(shí)現(xiàn)UnityActivity類(lèi)。
  3. 提升Unity+Android Plugin項(xiàng)目開(kāi)發(fā)效率的方法:
    ● 直接將Unity打包的apk中的assert/bin目錄拷貝到Android studio工程的src/main/assert目錄當(dāng)中,并且將Android工程配置成app模式,就可以直接在Android studio上面,對(duì)整個(gè)Unity+android plugin的工程進(jìn)行調(diào)試。
    ● Android studio部分調(diào)試好之后,需要修改build.gradle文件,重新將app模式修改為library模式,編譯出aar包文件,刪除原來(lái)拷貝過(guò)來(lái)的unity部分,放入到unity的Plugins/Android/lib目錄下進(jìn)行使用即可。

最后套句名言:log打得好,bug解得早

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    高清免费在线不卡视频| 中文字幕中文字幕在线十八区| 国产精品免费自拍视频| 成人日韩在线播放视频| 国产日产欧美精品视频| 年轻女房东2中文字幕| 久久99亚洲小姐精品综合| 亚洲国产成人精品福利| 国产欧美日韩在线精品一二区| 91爽人人爽人人插人人爽| 亚洲人妻av中文字幕| 乱女午夜精品一区二区三区| 国产传媒欧美日韩成人精品| 国产日韩精品激情在线观看| 情一色一区二区三区四| 国产又粗又硬又大又爽的视频| 欧美精品久久一二三区| 太香蕉久久国产精品视频| 久久午夜福利精品日韩| 国产又猛又黄又粗又爽无遮挡| 韩国日本欧美国产三级| 亚洲最新av在线观看| 日本理论片午夜在线观看| 日韩偷拍精品一区二区三区| 久久热在线视频免费观看| 欧美一区二区三区在线播放| 国产精品日韩精品一区| 国产成人亚洲欧美二区综| 午夜精品黄片在线播放| 丝袜av一区二区三区四区五区| 自拍偷拍福利视频在线观看| 成人日韩视频中文字幕| 亚洲熟女诱惑一区二区| 老熟妇乱视频一区二区| 国产一级一片内射视频在线| 日韩欧美第一页在线观看| 丰满人妻少妇精品一区二区三区 | 九九蜜桃视频香蕉视频| 欧美日韩成人在线一区| 中文字幕一区二区久久综合| 儿媳妇的诱惑中文字幕|