【Unity】 Unity内嵌网页采坑记录 - ULiteWebView
前言
项目需要要求Unity项目能与H5交互,这可真的是难倒我这个弱鸡了。不过好在功夫不负有心人,在不停的摸索之后摸索出了解决方案。在这里要感谢ULiteWebView插件的作者,很热心的帮助我解决一些疑惑,还有身边的朋友,多多少少都有帮到我一些。附上插件下载链接
首先
关于ULiteWebView的使用方法官方Demo应该够容易入门了,有一点需要注意的是,只有LoadHtml只有在手机上运行才能加载出来
重点
关于ULiteWebView播放视频只有声音没有画面的原因,是因为没有开启硬件加速,硬件加速需要在AndroidMinifest文件里的activity添加
android:hardwareAccelerated=“true” 。
重点来了! 当你尝试在Unity里面放入自定义的AndroidMinifest后,并开启了硬件加速,打包完成的apk
在手机上运行照样无法渲染视频。是什么原因呢?
首先我将项目打包成安卓项目[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TewcZ2BU-1592554286811)(/images/ULiteWebView/builtsetting.png)],并查看打包出来的Minifest文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pieces.jing.ulitewebview"
android:versionCode="1" android:versionName="1.0"
android:installLocation="preferExternal">
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true" />
<application
android:theme="@style/UnityThemeSelector"
android:label="@string/app_name"
android:isGame="true"
android:banner="@drawable/app_banner"
android:icon="@mipmap/app_icon">
<activity
android:label="@string/app_name"
android:screenOrientation="sensorLandscape"
android:launchMode="singleTask"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
android:hardwareAccelerated="false"
android:name="pieces.jing.ulitewebview.UnityPlayerActivity">
<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>
<meta-data android:name="unity.build-id" android:value="9757b814-10c8-4646-8b69-94d7049a5648" />
<meta-data android:name="unity.splash-mode" android:value="0" />
<meta-data android:name="unity.splash-enable" android:value="True" />
</application>
<uses-feature android:glEsVersion="0x00020000" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
<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>
可以看到第 23 行 android:hardwareAccelerated=“false” 。纳尼!?我不是设置为true了么。虽然我不太明白安卓,但我猜测是我自定义的Minifest在打包合并的时候被覆盖掉了。那怎么办呢?在国内各种网站都找不到解决方法之后我决定派出调查军团出去查找原因。果然墙外的老哥个个都是人才233,很快就找到了解决方法。
附上链接
根据我工地英语的翻译过后,我想就是只要在我的minifest被覆盖之后再修改hardwareAccelerated并覆盖已经被覆盖了的minifest就行了(雾)。顺便一提,这个方法只有通过Gradle打包才能通过,记得取消Export Project选项。当然是用Gradle打包的时候估计会遇到一对问题就是了233,这个等会我会粗略说一下。
好了我们将需要用到的脚本拷贝完放到Unity的Editor目录下,代码如下
using System.Collections;
using System.IO;
using System.Text;
using System.Xml;
using UnityEditor.Android;
using UnityEditor.Callbacks;
//using UnityEditor.iOS.Xcode;
using UnityEditor;
using UnityEngine;
#if UNITY_2018_1_OR_NEWER
public class UnityWebViewPostprocessBuild : IPostGenerateGradleAndroidProject
#else
public class UnityWebViewPostprocessBuild
#endif
{
for android/unity 2018.1 or newer
cf. https://forum.unity.com/threads/android-hardwareaccelerated-is-forced-false-in-all-activities.532786/
cf. https://github.com/Over17/UnityAndroidManifestCallback
public void OnPostGenerateGradleAndroidProject(string basePath) {
Debug.Log("adjusted AndroidManifest.xml.");
var androidManifest = new AndroidManifest(GetManifestPath(basePath));
androidManifest.SetHardwareAccelerated(true);
androidManifest.Save();
}
public int callbackOrder {
get {
return 1;
}
}
private string GetManifestPath(string basePath) {
var pathBuilder = new StringBuilder(basePath);
pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
return pathBuilder.ToString();
}
for others
#if UNITYWEBVIEW_ANDROID_USE_ACTIVITY_NAME
// please modify ACTIVITY_NAME if you set UNITYWEBVIEW_ANDROID_USE_ACTIVITY_NAME and utilize any custom activty.
private const string ACTIVITY_NAME = "com.unity3d.player.UnityPlayerActivity";
#endif
[PostProcessBuild(100)]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path) {
#if !UNITY_2018_1_OR_NEWER
if (buildTarget == BuildTarget.Android) {
string manifest = Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml");
if (!File.Exists(manifest)) {
string manifest0 = Path.Combine(Application.dataPath, "../Temp/StagingArea/AndroidManifest-main.xml");
if (!File.Exists(manifest0)) {
Debug.LogError("cannot find both Assets/Plugins/Android/AndroidManifest.xml and Temp/StagingArea/AndroidManifest-main.xml. please build the app to generate Assets/Plugins/Android/AndroidManifest.xml and then rebuild it again.");
return;
} else {
File.Copy(manifest0, manifest);
}
}
XmlDocument doc = new XmlDocument();
doc.Load(manifest);
XmlElement activity = SearchActivity(doc);
if (activity != null
&& string.IsNullOrEmpty(activity.GetAttribute("android:hardwareAccelerated"))) {
activity.SetAttribute("hardwareAccelerated", "http://schemas.android.com/apk/res/android", "true");
doc.Save(manifest);
Debug.LogError("adjusted AndroidManifest.xml about android:hardwareAccelerated. Please rebuild the app.");
}
#if UNITY_5_6_0 || UNITY_5_6_1
if (activity != null
&& activity.GetAttribute("android:name") == "com.unity3d.player.UnityPlayerActivity") {
activity.SetAttribute("name", "http://schemas.android.com/apk/res/android", "net.gree.unitywebview.CUnityPlayerActivity");
doc.Save(manifest);
Debug.LogError("adjusted AndroidManifest.xml about android:name. Please rebuild the app.");
}
#endif
}
#endif
/* if (buildTarget == BuildTarget.iOS) {
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.TargetGuidByName("Unity-iPhone");
proj.AddFrameworkToProject(target, "WebKit.framework", false);
File.WriteAllText(projPath, proj.WriteToString());
}*/
}
private static XmlElement SearchActivity(XmlDocument doc) {
foreach (XmlNode node0 in doc.DocumentElement.ChildNodes) {
if (node0.Name == "application") {
foreach (XmlNode node1 in node0.ChildNodes) {
#if UNITYWEBVIEW_ANDROID_USE_ACTIVITY_NAME
if (node1.Name == "activity"
&& ((XmlElement)node1).GetAttribute("android:name") == ACTIVITY_NAME) {
return (XmlElement)node1;
}
#else
if (node1.Name == "activity") {
foreach (XmlNode node2 in node1.ChildNodes) {
if (node2.Name == "intent-filter") {
bool hasActionMain = false;
bool hasCategoryLauncher = false;
foreach (XmlNode node3 in node2.ChildNodes) {
if (node3.Name == "action"
&& ((XmlElement)node3).GetAttribute("android:name") == "android.intent.action.MAIN") {
hasActionMain = true;
} else if (node3.Name == "category"
&& ((XmlElement)node3).GetAttribute("android:name") == "android.intent.category.LAUNCHER") {
hasCategoryLauncher = true;
}
}
if (hasActionMain && hasCategoryLauncher) {
return (XmlElement)node1;
}
}
}
#endif
}
}
}
}
return null;
}
}
internal class AndroidXmlDocument : XmlDocument {
private string m_Path;
protected XmlNamespaceManager nsMgr;
public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
public AndroidXmlDocument(string path) {
m_Path = path;
using (var reader = new XmlTextReader(m_Path)) {
reader.Read();
Load(reader);
}
nsMgr = new XmlNamespaceManager(NameTable);
nsMgr.AddNamespace("android", AndroidXmlNamespace);
}
public string Save() {
return SaveAs(m_Path);
}
public string SaveAs(string path) {
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false))) {
writer.Formatting = Formatting.Indented;
Save(writer);
}
return path;
}
}
internal class AndroidManifest : AndroidXmlDocument {
private readonly XmlElement ApplicationElement;
public AndroidManifest(string path) : base(path) {
ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
Debug.Log ("ApplicationElement: " + ApplicationElement);
}
private XmlAttribute CreateAndroidAttribute(string key, string value) {
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
attr.Value = value;
return attr;
}
internal XmlNode GetActivityWithLaunchIntent() {
return
SelectSingleNode(
"/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and "
+ "intent-filter/category/@android:name='android.intent.category.LAUNCHER']",
nsMgr);
}
internal void SetHardwareAccelerated(bool enabled) {
var activity = GetActivityWithLaunchIntent() as XmlElement;
activity.SetAttribute("android:hardwareAccelerated", (enabled) ? "true" : "false");
}
}
ok! 快去打包试试看吧!
Gradle打包失败的坑
- 如果你看到这里你应该被Gradle搞到崩溃了吧,反正我是抓狂了一天了!具体问题其实我也不太清楚,但是大致方向我还是知道的,首先Gradle打包需要连接到外网,如果你打包卡在最后一步很久很久很久~那么八成是网络问题,你可以断网试一下,估计就会打包失败了(划重点:翻墙工具)。
- 检查一下Unity使用的gradle的版本,路径为项目Assets文件夹同级下的Temp文件夹下Temp/gradleOut/.gradle。我这里打包的时候自动下载的是5.1.1版本,如图对应的是
然后我们打开 PlayerSetting 勾选 Custom Gradle Template!
对应会生成一个文件
修改mainTemplate文件
注意google()要放在jcenter()上面,别问我为什么,我也不清楚。这里gradle的版本对应上面那张图,我Unity使用的是 5.1.1 所以应该对应 3.4.0。
当然还有一些琐碎的坑就需要自己努力了~