Unity虽然能直接打包Apk,很多时候我们需要在原生java环境中完成一些功能或者接入第3方库等等。这个时候我们就需要Unity导出java工程并合并,然后利用Eclipce打包APK。另外,Unity直接打包APK包体压缩率很低,同比Eclipce导出大了1倍多(本人测试unity 90M,Eclipce 35M),Unity生成APK利用反编译工具反编译,然后在编译一次就与Eclipce相同,可能Unity从运行效率考虑,默认没有压缩。
言归正传,Android打包其实是对一系打包命令的执行。Unity导出Java工程,手动合并JAVA工程必然工作量提高,还容易出现错误,还需要利用Eclipce等JAVA工具打包,非常繁琐。所以通过Unity打包工具调用BAT一键打包APK非常必要。
环境:Unity5.6.3 JDK8 android-sdk-r24.4.1 (WIN7/Win10下测试可用)
一 导出Java工程
//打包场景
static string[] buildScences = new[] { "Assets/Shiyi/Scenes/Enter.unity",
"Assets/Shiyi/Scenes/GameMain.unity" };
//导出工程临时目录
static string tempProPath = "Android";
BuildPipeline.BuildPlayer(buildScences, tempProPath, BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer);
BuildOptions.AcceptExternalModificationsToPlayer 设置后导出的就是Eclipce工程 tempProPath 生成的临时目录
二.合并工程
///
/// 需要合并的目录
///
static string[] copyPathList = new[] { "assets" ,
"libs" ,
};
string tempPath = tempProPath + "/" + PlayerSettings.productName ;
EditorFunc.DeleteFolder(info.eclipsePath + "/" + copyPathList[0]);//清除掉Assets目录
for (int i=0;i< copyPathList.Length;i++) //替换打包目录Eclipse文件夹
EditorFunc.CopyDirectory(tempPath + "/" + copyPathList[i], info.eclipsePath + "/" + copyPathList[i]);
//=======================================================================================
///
/// 删除文件夹
///
///
public static void DeleteFolder(string dir)
{
if (!Directory.Exists(dir))
return;
foreach (string d in Directory.GetFileSystemEntries(dir))
{
if (File.Exists(d))
{
FileInfo fi = new FileInfo(d);
if (fi.Attributes.ToString().IndexOf("ReadOnly") != -1)
fi.Attributes = FileAttributes.Normal;
File.Delete(d);
}
else
{
DirectoryInfo d1 = new DirectoryInfo(d);
if (d1.GetFiles().Length != 0)
{
DeleteFolder(d1.FullName);////递归删除子文件夹
}
Directory.Delete(d, true);
}
}
}
///
/// 复制文件夹
///
///
///
public static void CopyDirectory(string sourcePath, string destinationPath)
{
DirectoryInfo info = new DirectoryInfo(sourcePath);
Directory.CreateDirectory(destinationPath);
foreach (FileSystemInfo fsi in info.GetFileSystemInfos())
{
string destName = Path.Combine(destinationPath, fsi.Name);
if (fsi is System.IO.FileInfo)
File.Copy(fsi.FullName, destName,true);
else
{
Directory.CreateDirectory(destName);
CopyDirectory(fsi.FullName, destName);
}
}
}
info.eclipsePath 我们需要扩展,合并的工程,也是最终打包的工程
三.bat 调用SDK JDK打包
这一步比较麻烦,网上有人写,但是很多坑没提示不够详细。首先运行CMD,输入javac,appd,java,dx命令时候能正常运行,没有提示“不是命令和关键字”说明你配置对了,如果不能请检查环境变量。 (这是我的 配置成自己电脑目录):
Path E:\Android\android-sdk-r24.4.1\platform-tools;E:\Android\android-sdk-r24.4.1\tools;E:\Android\android-sdk-r24.4.1\build-tools\25.0.2;E:\Android\android-sdk-r24.4.1;%JAVA_HOME%\bin;E:\AndroidSDK\android-sdk-r24.4.1\tools;
JAVA_HOME E:\Java\jdk8
CLASSPATH .;%JAVA_HOME%\lib;E:\Work\TestPJ\Android\TestMajiang\libs\unity-classes.jar;E:\Work\TestPJ\Android\TestMajiang\libs\wechat-sdk-android-with-mta-1.1.7.jar;E:\Work\TestPJ\Android\TestMajiang\libs\android-support-v4.jar;E:\Work\TestPJ\Android\TestMajiang\libs\GCloudVoice.jar;
注意:CLASSPATH 中请配置好 第三方jar包,不然编译不通过,也可以Bat里面调用设置,不过不推荐,最好这里设置好。如果CMD可以运行,但调用BAT报错,这个时候重启电脑就好了
编写Bat文件工程目录
1、生成R.java
aapt package -f -m -J gen -S res -I E:/AndroidSDK/android-sdk-r24.4.1/platforms/android-25/android.jar -M AndroidManifest.xml
生成成功,在gen目录下有R.JAVA
文件参数说明:
-f -m -J gen :以覆盖的形式在gen目录下生成带包路径的R.java
-S res:指定资源文件
-I E:\Android\SDK\android-sdk_r09-windows\platforms\android-7\android.jar:使用指定版本的android jar包
-M AndroidManifest.xml:指定程序的配置文件
2、编译*.java
md bin
javac -encoding UTF-8 -target 1.6 -source 1.6 -bootclasspath E:/AndroidSDK/android-sdk-r24.4.1/platforms/android-25/android.jar -d bin gen/com/yiqiu/mj/jiangsu/*.java src\com\yiqiu\mj\jiangsu\wxapi\*.java src\com\yiqiu\mj\jiangsu\*.java
生成成功,会在bin目录下生成一系列的class文件
md bin 创建bin目录
-encoding UTF-8 使用UTF8编码,不然代码中有中文的地方都会报错编码GBK错误
-target 1.6 生成特定 VM 版本的类文件 ( JavaCompiler版本,推荐设置1.6,)
-source <版本> 提供与指定版本的源兼容性
-bootclasspath <路径> 覆盖引导类文件的位置 android-25对应android 7.1.1 根据项目具体需要选择,部分功能没有,用老版可能编译不过
-d <目录> 指定存放生成的类文件的位置本的类文件(包含第三方的JAVA源文件)
3、生成classes.dex
dx --dex --output=bin\classes.dex bin libs
--output=bin\classes.dex 生成classes.dex 文件目录
bin libs 需要编译的class文件以及jar包,工程引入的JAR包的目录都要包含进来(列:unity的底层函数库 unity-classes.jar)。不然打出的APK无法执行
我的libs 目录
aapt package -f -A assets -S res -I E:/AndroidSDK/android-sdk-r24.4.1/platforms/android-25/android.jar -M AndroidManifest.xml -F bin/TestMajiang
-F bin/TestMajiang 生成成功后,bin目录下有资源文件
5、生成未签名的apk
java -cp E:\AndroidSDK\android-sdk-r24.4.1\tools\lib\sdklib.jar com.android.sdklib.build.ApkBuilderMain bin/majiang_unsigned.apk -v -u -z bin\TestMajiang -f bin/classes.dex -rf src -nf libs
将之前生成的资源文件、classes.dex和src下的文件一起生成APK
-cp E:\AndroidSDK\android-sdk-r24.4.1\tools\lib\sdklib.jar com.android.sdklib.build.ApkBuilderMain 调用打包APK命令,apkbuilder在新版sdk 中已经被移除;
-nf libs 打包so文件
注意:如果第三方jar包里含有图片资源,一定要加上-rj参数,不然jar包里资源文件解不出来,程序会因为无法引用资源而报错!
6、apk签名
arsigner -digestalg SHA1 -sigalg MD5withRSA -keystore keystore\test.keystore -storepass 12345 -keypass 12345 -signedjar bin/majiang.apk bin/majiang_unsigned.apk aliasName
-digestalg SHA1 摘要算法的名称
-sigalg MD5withRSA 签名算法的名称
-keystore keystore\test.keystore keystore位置
-storepass 12345 用于密钥库完整性的口令
-keypass 12345 专用密钥的口令(如果不同)
-signedjar <签名后的包><签名前的包>
时间戳警告 加上-tsa https://timestamp.geotrust.com/tsa 需要VPN 不然连接不上生成失败。不加也可以正常运行。
7._build.bat调用1-6
call 1_genR.bat
call 2_compile.bat
call 3_dex.bat
call 4_package.bat
call 5_unsigned.bat
call 6_signed.bat
四.Unity调用_build
//PakageSettingInfo.Mgr.Instance.androidSDkPath + " " + apkName // SDK路径 apk名字
string apkName = PakageSettingInfo.Mgr.Instance.exportApkPath + "/" + info.pakageName + ".apk";
EditorFunc.ExecuteProgram(info.eclipsePath + "/_build.bat",info.eclipsePath,
PakageSettingInfo.Mgr.Instance.androidSDkPath + " " + apkName);
//---------------------------------------------------------------------------------------------
///
/// 运行Cmd
///
///
///
///
///
public static bool ExecuteProgram(string exeFilename, string workDir, string args)
{
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo();
info.FileName = exeFilename;
info.WorkingDirectory = workDir;
info.CreateNoWindow = false;
info.Arguments = args;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
System.Diagnostics.Process task = null;
bool rt = true;
try
{
Debug.Log("ExecuteProgram:" + args);
task = System.Diagnostics.Process.Start(info);
if (task != null)
{
task.WaitForExit(100000);
}
else
{
return false;
}
}
catch (Exception e)
{
Debug.LogError("ExecuteProgram:" + e.ToString());
return false;
}
finally
{
if (task != null && task.HasExited)
{
rt = (task.ExitCode == 0);
}
}
return rt;
}
我们把sdk目录,以及生成apk名字作为参数传到bat文件中
修改bat,最终如下
call 1_genR.bat %1
call 2_compile.bat %1
call 3_dex.bat
call 4_package.bat %1
call 5_unsigned.bat %1
call 6_signed.bat %1 %2
aapt package -f -m -J gen -S res -I %1/platforms/android-25/android.jar -M AndroidManifest.xml
md bin
javac -encoding UTF-8 -target 1.6 -source 1.6 -bootclasspath %1/platforms/android-25/android.jar -d bin gen/com/yiqiu/mj/jiangsu/*.java src\com\yiqiu\mj\jiangsu\wxapi\*.java src\com\yiqiu\mj\jiangsu\*.java
dx --dex --output=bin\classes.dex bin libs
aapt package -f -A assets -S res -I %1/platforms/android-25/android.jar -M AndroidManifest.xml -F bin/TestMajiang
java -cp %1\tools\lib\sdklib.jar com.android.sdklib.build.ApkBuilderMain bin/majiang_unsigned.apk -v -u -z bin\TestMajiang -f bin/classes.dex -rf src -nf libs
jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore keystore\jklhhh.keystore -storepass jkl0202hhh -keypass jkl0202hhh -signedjar %2 bin/majiang_unsigned.apk android
完工。