Bruce Zhang
Recently, because I wanted to invoke the interface of Android's activity in the script component of scene, we needed to figure out the actual correspondence between scene and activity, and studied some of the principles that unity calls Android.
This article focuses on the relationship between scene and activity, and where is the difference between Unity Pack apk and Android Studio pack apk? When you find this difference, how can you use it?
tools to use in this article:
-Android Anti-compilation tool--apktool
-Android Studio's own anti-compilation feature
What is the entrance to the program that compiles unity's scene into apk,apk?
- Build a new Unity project, create a scene, and package the Unity Project compilation as an APK.
- Against the compiled apk, use Apktool to decompile: Apktool D unitytest.apk
- The resulting androidmanifest files are as follows:
<?xml version= "1.0" encoding= "Utf-8" standalone= "no"?><manifest xmlns:android = "http://schemas.android.com/apk/res/android" android:installlocation = "preferexternal" Span class= "Hljs-attribute" >package = "COM.XFICTION.P1" platformbuildversioncode = platformbuildversionname = "7.1.1" > <supports-screens android:anydensity = "true" android: Largescreens = "true" android:normalscreens = android:smallscreens = "true" android:xlargescreens = /> <application android: Banner = "@drawable/app_banner" android: debuggable = android:icon =< Span class= "Hljs-value" > "@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>
By the Androidmanifest file, the system still exists the main activity, the name is com.unity3d.player.UnityPlayerActivity.
The implication is that compiling only the scene Unity project, packaged into Android APK, will be com.unity3d.player.UnityPlayerActivity as the main program entrance, then the problem comes, How does scene load display to this unityplayeractivity?
Second, unityplayeractivity How to load scene in unity?
2.1 unityplayeractivity
This is going to start with the unityplayeractivity source, Android Engineering uses unityplayeractivity to rely on Unity's Android plug-in Classes.jar (located in the Unity installation directory, can be found with everything software lookup), to decompile it to get unityplayeract Ivity part of the source code:
Public class unityplayeractivity extends Activity { protectedUnityplayer Munityplayer;protected void onCreate(Bundle var1) { This. Requestwindowfeature (1);Super. OnCreate (VAR1); This. GetWindow (). SetFormat (2); This. Munityplayer =NewUnityplayer ( This); This. Setcontentview ( This. Munityplayer); This. Munityplayer.requestfocus (); }}
Although confusing and seemingly laborious, it can be seen from Code This.setcontentview (This.munityplayer) that the final interface display needs to be dependent on unityplayer instances.
In addition, because Google also made a Unity VR SDK, and unityplayeractivity corresponding to the class, is googleunityactivity, which is also analyzed below.
2.2 Starting from Googleunityactivity.java and analyzing
Googleunityactivity is a Google-launched VR SDK that is used to implement unity activity classes, and Google queries its source discovery:
1. Googleunityactivity.java the actual layout file Activity_main.xml
<?xml version= "1.0" encoding= "Utf-8"?><framelayout xmlns:android="Http://schemas.android.com/apk/res/android" Android:layout_width="Match_parent"android:layout_height="Match_parent" > <framelayoutandroid:id="@+id/android_view_container"android:layout_width ="Match_parent"android:layout_height="Match_parent"android:background ="@android: Color/transparent" /> </framelayout>
There is no specific content in the layout file, only one framelayout layout is included.
2. Focus on the OnCreate function of googleunityactivity :
Public class googleunityactivityextends 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 =NewUnityplayer ( This);if(Munityplayer.getsettings (). Getboolean ("Hide_status_bar",true) {GetWindow (). SetFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, WINDOWMANAGER.LAYOUTPA Rams. Flag_fullscreen); } ((ViewGroup) Findviewbyid (Android. r.id.content). AddView (Munityplayer.getview (),0); Munityplayer.requestfocus (); }}
Munityplayer as Framelayoutview is added to the view collection for display, note that the ID found here is android. R.id.content. According to the official explanation of this ID:
Android. R.id.content gives you the root element of a view, without has to know its actual name/type/id. Check out Get root view from current activity
Thus,Googleunityactivity's implementation principle is to create an empty frame layout that contains only framelayout, The view in the Unityplayer is then loaded into the googleunityactivity via AddView to display.
Looks similar to unityplayeractivity, both of which are involved in the Unityplayer.
What kind of a 2.3.UnityPlayer is it?
To decompile the Classes.jar package, get unityplayer part of the code:
Public class unityplayer extends framelayout implements com. Unity3d. player. a. a { Public StaticActivity currentactivity =NULL; Public Unityplayer(Contextwrapper var1) {Super(VAR1);if(var1instanceofActivity) {currentactivity = (activity) var1; } } PublicViewGetView() {return This; } Public Static native void Unitysendmessage(String var0, String var1, string var2);Private Final native Boolean Nativerender(); Public void Oncameraframe(FinalCom.unity3d.player.a var1,Final byte[] var2) {Final intVAR3 = var1.a ();FinalSize VAR4 = var1.b (); This. A (NewUNITYPLAYER.C ((byte)0) { Public Final void a() {Unityplayer. This. Nativevideoframecallback (Var3, Var2, Var4.width, var4.height); var1.a (VAR2); } }); }}
From the code you can find:
1. Unityplayer is actually inherited from Framelayout;
2. and comes with a currentactivity member variable, in the constructor, directly into the activity of the relevant parameters ;
3. Return the framelayout directly in the GetView function;
4. Googleunityactivity passes its context to unityplayer through the Unityplayer constructor, and assigns it to its member variable currentactivity.
Because of the confusion of the Unityplayer class, the core function of the rendering is also encapsulated in the native code, about scene transitions to Unityplayer as Framelayout, Can only be a simple speculation: by calling the Android GL rendering engine, the native layer is rendered, and synchronized to framelayout on the unityplayeractivity display .
Iii. How to display scene in custom activity
From the above research, we can see that if we want to show the scene in fixed activity, we need to deal with the Countview and Unityplayer of the oncreate part of activity. The simplest approach is to write a class that inherits directly from the unityplayeractivity or googleunityactivity and writes the required unity call to Android in the class.
The scene will be loaded in a particular activity, and Unity C # can get to the activity and invoke the function in it by acquiring the currentactivity variable.
Iv. issues needing attention in Unity Android plugin
- Android Studio project contains multiple module dependencies, you need to copy the corresponding module compiled plug-in Plugins/android/lib directory.
- In the first step, you can directly delete the packaged AAR library directory, especially if the Android plugin with Unity Classesjar, otherwise it will compile the error.
- When more than one module compiles, note the manifest Lablel related settings, the other is Build.gradle minsdkversion information. Otherwise, an error manifest merger failure will occur.
- About Unity's Android manifest file merge:
Unity writes a scene,android studio to write an AAR package containing the main activity, which is placed in the Plugins/android directory. After compiling the apk out with unity, decompile his androidmanifest file, two main activity, which shows the activity with the scene by default.
workaround : Unity's manifest file merge, put a manifest in the Plugins/android directory, will not merge manifest.
V. Structure of Unity Pack Android APK
As Unity develops Android, it is often designed to switch between Unity + visual and Android studio environments, and unity is often developed faster and more of the Android Java side of code writing and debugging.
In this case, is there a way to put Unity's compiled unity scene and C # related files into Android studio for packaging and debugging directly in Android Studio?
The principle of the method is very simple, by comparing the unity packaged apk, with the common Android apk file differences, find the Unity file storage directory, then corresponding to the Android Studio project directory, and finally through the Android Studio completes the packaging of the unity-related files.
first add the apk to the zip suffix, easy to compare with beyond Compare:
1. Found just assert/bin directory, under this directory, you can see the unity-related DLL library
2. Copy the file to the Src/main/assert directory of the Android studio project;
3. When you are debugging in Android Studio, you can set up the AAR Library project as an app project so that you can compile the APK to run to your phone.
4. Compile the project with Android Studio and discover that the Assert/bin directory has been successfully packaged in.
5. Direct APK install run, you can see with unity compiled packaged APK, is the same effect.
Conversely, if the Android project has been debugged, it will be compiled into the library mode directly, and after the build, the AAR libraries are generated and the AAR library is copied to the Plugins/android/lib directory. Note To delete the Assert/bin in the AAR library, because this directory is the one we previously copied from unity, and if not deleted, there will be a case of file conflicts in unity that results in duplicate packaging.
Android Studio is a library project that needs to be converted to app engineering, since it was copied to Android studio after the bin directory was packaged in unity.
This involves the conversion of the Android Studio library and the app by setting up the Build.gradle file to achieve:
- App mode: Apply plugin: ' Com.android.application '
- Library mode: Apply plugin: ' Com.android.library '
However, in setting these two modes, you need to pay attention to the ApplicationID "com.example.yin.myapplication" setting, if it is the library mode, you need to comment out directly.
If the Java part of Android is re-debugged, re-changing the app mode to library mode, build, and copy the generated AAR package to the Unity Android plugin directory, you can see the effect directly in unity.
Be sure to remember to delete the Assert/bin directory inside the AAR files packaged by Android Studio to prevent repeated packaging in unity.
Iv. Conclusion:
- Scene in Unity in Android, in fact, corresponds to the activity of the framelayout, each scene of the operation has its activity environment, through the currentactivity variable can be obtained.
- To implement a custom activity with the ability to load the scene directly, you need to inherit from unityplayeractivity or googleunityactivity, or directly customize the implementation of the Unityactivity class.
- Ways to improve the efficiency of Unity+android plugin project development:
Copy the Assert/bin directory from the Unity packaged apk directly into the Src/main/assert directory of the Android Studio project and configure the Android project as app mode, directly on Android studio , the whole Unity+android plugin project is debugged.
After debugging the Android studio, you need to modify the Build.gradle file, re-modify the app mode to library mode, compile the AAR package file, delete the original copy of the unity part, and put it into Unity's plugins/ Android/lib directory to use.
The last set of famous sayings: Log good, bug solved early
Read the original text, this article by the Tengyun authorized release, after the community permission can be reproduced. For more technical articles, please visit Tengyun.
Unity compiled Android's principle parsing and apk packaging analysis