2010年5月24日 星期一

【分享】- 如何整合 Eclipse、Ant、Android SDK、Proguard、Zipalign,以輸出有程式保護且可上載與發佈到 Android Market 的 APK 檔? PART-2

註:如您已更新到 Android SDK 2.3,或不想用 Ant,請可直接參考另一篇 (PART3)的說明

近日因使用 Android SDK 的更新程式下載了 Android SDK 1.5 revision 4 (SDK 2.2 亦同),突然發覺沒法使用 Ant 編譯可上載 Android Market 的程式,檢查後才知原來在 Android SDK 中的 Eclipse 與 Ant 的設定全給蓋掉了,且 xml 內的語法也不一樣了,請參考原整合設定的步驟,與以下參數變更 SDK 1.5 新版之 ant_rules_r2.xml 中的內容: 

(1) 原舊版 SDK 1.5 內定引入的樣板 xml 是 <$SDK>/platforms/android-1.5/templates/android_rules.xml,但新版 SDK 1.5 卻改為內定引入 <$SDK>/platforms/android-1.5/ant/ant_rules_r2.xml

(2) 新版的 ant_rules_r2.xml 修改內容參考:

<target name="compile" depends="-resource-src, -aidl"
                description="Compiles project's .java files into .class files">

..................... 略 ...........................

<!-- 將 encoding="ascii" 改為 encoding="utf-8" -->
<!-- 將 debug="ture" 改為 debug="false" -->
<!-- 加入 optimize="true" , 如造成問題, 請改為 false -->
  <javac encoding="utf-8" target="1.5" debug="false" extdirs=""
    optimize="true"
    destdir="${out.classes.absolute.dir}"
    bootclasspathref="android.target.classpath"
    verbose="${verbose}" classpath="${extensible.classpath}"
    classpathref="android.libraries.jars">
    <src path="${source.absolute.dir}" />
    <src path="${gen.absolute.dir}" />
    <src refid="android.libraries.src" />
    <classpath>
        <fileset dir="${external.libs.absolute.dir}" includes="*.jar" />
        <fileset dir="${extensible.libs.classpath}" includes="*.jar" />
    </classpath>
  </javac>
</target>

<!-- 將 dex 的 depends 由 compile 改為 proguard , Cori -->
<!-- Converts this project's .class files into .dex files -->
<target name="-dex" depends="proguard">
    <dex-helper />
</target>

.................... 略 .............................


<!-- Add a Target "proguard" Cori -->
<target name="proguard" depends="compile">
<taskdef resource="proguard/ant/task.properties"
classpath="${android.tools.dir}/lib/proguard.jar" />
<proguard>
-injars ${out.classes.absolute.dir}
-outjars ${out.dir}/obfuscate.jar



   <!-- If use Android 2.2 APIs , -->
   <!-- 紅色部份記得改成您 Android SDK 實際路徑 -->
   -libraryjars D:\AndroidSDK\platforms\android-8\android.jar
<!-- -libraryjars ${android-jar} -->


   -libraryjars ${external.libs.absolute.dir}


<!-- use proguard 4.5.1 , 20100918 -->
<!-- keep all class name for your src/com.e68club.android.* -->
-keep public class com.e68club.android.Class01
-keep public class com.e68club.android.Class02
-keep public class com.e68club.android.Class03
-keep public class com.e68club.android.Class04

-dontwarn
-dontpreverify
-optimizationpasses 7
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
</proguard>
<delete dir="${out.classes.absolute.dir}"/>
 <mkdir dir="${out.classes.absolute.dir}" />
<unzip src="${out.dir}/obfuscate.jar" dest="${out.classes.absolute.dir}"/>
<delete file="${out.dir}/obfuscate.jar"/>
<echo>Obfuscated classes are put to ${out.classes.absolute.dir}.</echo>
</target>

<!-- jarsigner --><!-- Cori -->
<target name="jarsigner" depends="release">
<echo>Jarsignning ${out.dir}/${ant.project.name}-unsigned.apk to ${out.dir}/${ant.project.name}-signed.apk ...</echo>
<exec executable="jarsigner${exe}" failonerror="true">
<arg value="-verbose" />
<arg value="-keystore" />
<!-- 個別專案特別目錄中的 android.keystore -->
<arg path="${basedir}/../android.keystore/android.keystore" />
<arg value="-storepass" />
<arg value="YourKeyStorePassword" />
<arg value="-signedjar" />
<arg path="${out.dir}/${ant.project.name}-signed.apk" />
<arg path="${out.dir}/${ant.project.name}-unsigned.apk" />
<arg value="android.keystore" />
</exec>
</target>


<!-- zipalign --><!-- Cori -->
<target name="zipalign" depends="jarsigner">
<echo>Zipalignning ${out.dir}/${ant.project.name}-unsigned.apk to ${out.dir}/${ant.project.name}-zipalign.apk ...</echo>
<exec executable="${android.tools.dir}/zipalign${exe}" failonerror="true">
<arg value="-f" />
<arg value="4" />
<arg path="${out.dir}/${ant.project.name}-signed.apk" />
<arg path="${out.dir}/${ant.project.name}-zipalign.apk" />
</exec>
</target>

29 則留言:

  1. 您好,我按照您的教學操作一下,發現確實會輸入三個apk,但我發現那些apk裡面的classes.dex檔案,跟我沒使用ProGuard之前是完全依樣的,將之反組後,code的內容也看得一清二楚,不知道是哪裡設定有問題了?

    感覺proguard出來的class檔案跟本沒有包到dex中的說

    [echo] Obfuscated classes are put to "${out-classes-location}".
    -dex:
    [echo] Converting compiled files and external libraries into C:\WorkSpace\Project\bin\classes.dex...
    -package-resources:
    [echo] Packaging resources
    [aaptexec] Creating full resource package...

    回覆刪除
  2. 您好, 我有遇過這種情況, 請試著將 bin 下面的所有東西都殺掉, 再重新編譯看看, 我遇到的情況是 Ant 會比較是否有須重新編譯的 class , 如沒有, 它不會重新編譯, 因此造成您遇到的情況.

    回覆刪除
  3. 您好,問題發現了

    主要是在rule_r2.xml裡面的這邊



    因為make dex file時候,會從${out.dir}/classes把裡面的class file壓成dex,但混亂過的class卻解壓縮到${out-classes-location},這邊抓不到路徑,要改成${out.dir}/classes就可以了,但包成dex後會變大,因為混亂後跟混亂前的class會一起被包進去

    有點怪怪的,不知道有沒有解決方法?

    我猜可能要再解壓縮進去前先把裡面的class檔案殺掉

    但不知道要怎麼在rule_r2.xml裡面寫

    另外如果在SDK V8,用這樣的rule會build不起來,有些語法還是路徑好像又改了?

    回覆刪除
  4. 不好意思, 是我的疏失, 請參照原文的內容, 已經更新並測試沒問題了, 主要是修改路徑參數, 還有加入了刪目錄與重建目錄的語法, 所以, 只有混亂後的 class 會被包進 dex 中. 謝謝您發覺與協助解決問題 ! 結果正不正確, 請要告訴我喔 ^__^ .

    回覆刪除
  5. Hi!Gpc,不知您的問題解決了沒?漁郎還能幫上什麼忙嗎?

    回覆刪除
  6. 順便請問一下,有沒有試過混亂內有JNI(.so)的程式,我混亂了一個,結果一直啟動不了LoadLibray就爆掉啦?@?

    so檔案好像也不需要放class宣告的說

    回覆刪除
  7. 我按照上面的新版,在Eclipe3.5.2+SDK2.2+rule_r2.xml,結果要跑proguard的時候就出錯了,把SDK調降到1.5就OK,非常的怪異,好像是proguard根本沒LOAD到就爆掉了.

    紀錄如下,但我看不出問題的說QQ

    464
    466

    BUILD FAILED
    D:\android-sdk-windows-1.6_r1\platforms\android-8\ant\ant_rules_r2.xml:466: Expecting class path separator ';' before '{' in argument number 1

    Total time: 13 seconds

    回覆刪除
  8. -outjars ${out.dir}\obfuscate.jar
    -injars ${out.classes.absolute.dir}
    -libraryjars ${android-jar}

    挺嚴重的問題,到限在都還沒辦法在2.2成功build起來 不知道是什麼問題的說

    回覆刪除
  9. 乎解決囉~問題是 -libraryjars ${android-jar}
    在sdk2.2裡面的取不到${android-jar}會爆掉,要改成絕對路徑,如D:\sdk\planxxxxx\android-8\android.jar

    另外LoadLibrary好像要加些甚麼吧@?

    回覆刪除
  10. 好樣的 ^__^ , 真有你的! 記得有空咱們多切磋切磋喔 ^_^ , Google 也真是的, 怎麼語法一直改來改去 >_< ...

    回覆刪除
  11. 對了, 像 ${android-jar} 這類的參數, 在 Ant 中類似 Java 中的 final a_b_c , 通常會在最前面定義, 剛看了 2.2 的 ant_rules_r2.xml , 我猜將 android-jar 改成 external.libs.dir 還是 external.libs.absolute.dir 什麼的, 搞不好就行了 ?! @@

    回覆刪除
  12. Activity的部分已經搞好了,但關於layout xml裡面的自訂元件 如: com.android.gpc.ListViewPlus 這個class,要怎麼寫到keep阿 試了好幾種寫法都沒用的說

    回覆刪除
  13. 應加入
    -keep public class com.android.gpc.ListViewPlus

    我的是這樣加的 ^_^

    回覆刪除
  14. 我也是這麼做的說,但是沒用,一直說 NoSuchMethodException 耶

    對了 我的元件是 extens ImageView

    不知道這有沒有差

    另外您有用過LoadLibrary("xxx")嗎?

    我proguard後,一LoadLibray就FC,說找不到xxx(progaurd前可以的說)

    回覆刪除
  15. 我寫了一個很簡單的程式,裡面就一個自訂元件的class

    檔案名稱 DontPressWithParentImageView.java
    package com.android.TestB;
    public class DontPressWithParentImageView extends ImageView {

    }

    另外一個就是 TestB.java
    package com.android.TestB;
    public class TestB extends Activity {
    }

    裡面就去讀 main.xml,裡面有一個


    不Proguard跑,OK沒問題
    Proguard裡面設定
    -keep public class com.android.TestB.TestB
    -keep public class com.android.TestB.DontPressWithParentImageView

    一跑,就FC囉~


    出錯原因

    06-09 17:49:20.650: ERROR/AndroidRuntime(17751): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.TestB/com.android.TestB.TestB}: android.view.InflateException: Binary XML file line #15: Error inflating class com.android.TestB.DontPressWithParentImageView
    06-09 17:49:20.650: ERROR/AndroidRuntime(17751): Caused by: android.view.InflateException: Binary XML file line #15: Error inflating class com.android.TestB.DontPressWithParentImageView
    06-09 17:49:20.650: ERROR/AndroidRuntime(17751): Caused by: java.lang.NoSuchMethodException: DontPressWithParentImageView(Context,AttributeSet)

    不知道有沒有什麼想法啊?
    還是我哪裡搞錯了@@

    回覆刪除
  16. 您好,我把原始碼傳到這邊了,謝謝

    http://www.megaupload.com/?d=AZ9H1KAP

    回覆刪除
  17. -keep public class com.android.TestB.DPWPIV
    { public *;
    }

    經過千辛萬苦的測試,終於弄出來啦!
    除了單一行還不行,後面要加上,讓該class中的這public function可以被摳到,但不知道怎麼指定某個function的說QQ

    { public *; }

    回覆刪除
  18. 哈哈, 漁郎也來研究看看是怎回事 ? 目前倒是還可順利使用, 如還是有問題, 建議可以將 proguard 程序先取消, 尤其是要上載 Android Market 的程式, 畢竟別讓使用者有不好的使用經驗才是最重要的.

    回覆刪除
  19. Hi! Gpc,
    近日下載了新版的 Proguard 4.5.1 , 並使用 Android 2.2 來編譯, 經在漁郎所有的 App 上測試, 只要將所有在 src/ 下面的 class 全部 keep 就不會有問題了, 且除了 class name 沒被混淆, 其編譯後的內容都有混淆過, 給您參考參考.

    回覆刪除
  20. 小弟用 android update 出來的檔案跟版主的都不一樣耶
    不知道是否能再更詳細的指導呢

    回覆刪除
  21. 請問版主..
    我是用 SDK Tools R8 來 build, xml 是用 tools\ant\main_rules.xml

    可是不管是用 debug 還是 release 都會卡在 -dex 段落, 感覺是無法產生 classes.dex

    不知道是否還需做怎樣的修改呢?

    回覆刪除
  22. getcase, 漁郎沒用 debug 或 release 喔, 請仔細參考一下文內所題的細節, 另外, 可能要請您參照先前相關文章, 自己先舉一反三, 漁郎最近真的忙翻了, 已個把月沒碰 Eclipse 了, 派謝啦 >_<.

    回覆刪除
  23. 先感謝你在百忙之中還抽空回答!

    我有先試著依 http://www.e68club.com/2010/03/eclipseandroid-sdkproguardant-android_4261.html 內容來一步一步的做

    可是當我做到以下--
    在 D:\Android\AndroidSDK\platforms\android-1.5\templates\android_rules.xml 中,修改以下內容 (新版 Android 1.5 Revision 6 已改為引入 D:\Android\AndroidSDK\platforms\android-1.5\ant\ant_rules_r2.xml , 20100521) :

    就卡關了, 因為我在 android-sdk\platforms 找不到 android-1.5 的資料夾, 比較接近的資料夾是 android-3
    然後就一樣有找到ant/ant_rules_r2.xml

    接著, 我依樣把 ant_rules_r2.xml 中的內容加到 build.xml 中之後, 我就先試著執行 ant 了

    不過就一直卡在 -dex 的段落....

    找了兩天針對 SDK Tools 8 的 ant 問題還是不行

    回覆刪除
  24. 我記得 SDK 8 (2.2) 應該是引入 /platforms/android-8/ant 下的 ant_rules_r2 檔喔.

    回覆刪除
  25. 謝謝回答!

    引入那個 ant_rule_r2.xml 是看安裝的 SDK 版號嗎?
    還是依專案使用到的 SDK 版號來做引入的呢?

    不過我專案是用 SDK 7 的, 就算我是把 /platforms/android-7/ant 下的 ant_rules_r2 內容加入

    也一樣是卡在產生 dex 的部份, 也不知道是那邊有錯@@

    希望漁郎在有空之餘能抽空試一下

    回覆刪除
  26. getcase,參考一下 PART-3 的作法吧 (連結在文章最前面),比較不會被 Google 一直改版所困擾,一樣可以使用 Proguard 喔!

    回覆刪除
  27. 請問我依照大大所說的方式去編寫時

    出現以下問題

    [exec] jarsigner: you must enter key password
    [exec] Enter key password for android.keystore:

    但是 我明明就有加這一行

    網路查了一下也似乎找不到方法 請問是哪裡設錯了嗎??

    回覆刪除
  28. 俊偉,請問一下您的 android.keystore 是如何產生的?還有 android.keystore 所在目錄位置為何?

    回覆刪除
  29. HI 大大

    關於 以上的問題 我以解決了 主要是因為 keystore的password和"主Key"的password 不一樣 (怪怪的~)

    之後我設成一樣就能夠執行了

    不過目前卡在執行時 interface 會出錯

    仍再參考和分析

    回覆刪除