守望者--AIR技术交流

标题: [AIR接入Android 平台]打造登录付费ANE全教程 [打印本页]

作者: 破晓    时间: 2014-12-30 22:33
标题: [AIR接入Android 平台]打造登录付费ANE全教程

本文来自:http://bbs.9ria.com/thread-197503-1-1.html


目前为止我为项目共接入了大概7-8家或大或小的平台。深知AIR移动项目接入运营平台在打造ANE过程中会有各种各样的问题。在这里我打算把我遇到的问题 和解决方式记录下来,也许你用得着 也许你已经遇到过 也许你已经解决掉。接下来我会从我的air项目和各种android应用运营平台方的SDK接入过程 中 遇到的问题进行探讨。其中有些问题已经解决 有些问题解决得很差强人意,有些问题尚未解决。也请已经解决了这些问题的朋友  能和我交流。当然我对原生android开发刚刚入门 有一些看法或多或少存在错误。也请看到的朋友能Email我指出。(rectvv[at]gmail[dot]com)


再次声明下,这里仅仅写下我遇到的问题 和我个人的解决方式 关于ANE还有很多很多我没遇到的问题。同样如果同一个问题 您有更好的解决方式 希望能指导下我。小弟不胜感激。


首先关于最近大家都在群里讨论 AIR是生是死的问题,ASER是否转行的问题,我觉得应该把当下的事情做好,就像CODE COMPLETE里面说的一样。程序员应该是超越语言的。当然我目前还没到这水平。接下来进入正题。

在这之前 我之前写了一些关于ANE更早的一些文章:

http://www.shadowkong.com/archives/tag/ane

————————————————–

我的项目信息

使用的跨平台技术:Adobe Air

使用的GPU加速框架(引擎?):Starling

使用的AIR SDK版本:3.5(AIR3.5打包STARLING项目出来的的APK对于HTC的机器会有一个BUG,更新到AIR3.7之后 HTC的BUG解决了 但是对三星的一款机器又出一个新BUG 目前我还在寻找完美的解决方式  当前我的解决方式并不完美 详细的BUG描述会在后面统计的时候给出)

在接入的那么多平台中 在这里选择一个平台SDK看起来最繁琐 在打包ANE的时候 需要做的处理最多的 平台 做为例子。当然由于这个平台的SDK里面明确表示不允许 向他人 传播。所以我在后面的源码 与 附件中 会把 该平台的相关JAR 文档 等等去掉。但是如果你也接这个平台 那你可以完全拿过来套用。


接入的平台信息

针对设备:android

需要打包入AIR APK的资源有:res  assets两种资源处理方式都有。(在之前我就写过一篇关于ANE如何处理资源的问题的文章)平台方接入的第三方(对于游戏是第四方):微博授权登录   QQ授权登录

平台方需要加入的activity情况:这个平台需要加入的activity声明是最多的 在这个平台的 -app.xml也是最臃肿的,这会涉及到一个SDK activity 与 游戏activity 如何通信切换的问题。在之前就遇到过一个问题 把游戏主activity传给SDK的时候 执行完SDK操作  返回不了游戏activity 详细的解决方式 请看我前面的一篇文章。

平台方需要加入的JAR数量:6个(平台方的JAR最后全部都需要打包成一个JAR 才能生成ANE 这个问题的解决方式后面会给出)

——————————————————-


第一步 准备工作

1.在这之前需要先安装 JDK  JRE  并且添加环境变量如图:

(路径自己按照各自环境配置)这两个工具 请自行下载

配置JAR 配置JDK:例如我的路径:C:\Program Files\Java\jdk1.7.0_17\bin  加入 环境变量 系统变量


[attach]79[/attach]


[attach]80[/attach]


2.安装FB6 eclipse androidSDK  按照正常教程配置android开发环境 推荐下载androidSDK :2.2,2.3,4.0  

我的项目全部统一使用android SDK2.3.3

这些编辑器 SDK IDE等全部自行下载。。>.<


3.一般平台SDK都会需要在assets加APK或者资源,这就需要命令行 编译 打包。所以建议在环境变量中加入 amxmlc例如我的是在其中加入:D:\FB\Adobe Flash Builder 4.6\sdks\AIR3.5\bin  命名为amxmlc  这样就可以在CMD中直接使用命令行编译了,若想命令行打包 则也一样的 直接加入 ADT所在路径即可。(这并不是必须的  仅仅为了更好的使用命令行)



作者: 破晓    时间: 2014-12-30 22:40
[AIR接入Android 平台]第二步 编写 JAVA端接口 与 AS端接口项目

博文地址:http://www.shadowkong.com/archives/1106

这和编写其他所有ANE类似。网上可以找到一堆的教程。但是我觉得都针对性不太强。如果你已经对编写ANE有足够的把握 那这篇文章完全可以不看。

一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码

而且针对接入平台运营商的情况。这种ANE基本都具有四个功能:

初始化SDK  -> 登录 -> 付费 -> 退出

所以我决定针对接入android平台而写一个基本的ANE教程。我接入的所有平台的ANE 都是基于教程中给出的源码而做的改进。

ANE作为AIR调用本地代码的机制 他的组成分为两部分:

在开始之前 先限定一些共同的ID:

接口 ID:com.rect.ane

初始化SDK标识:rect_function_init

登录标识:rect_function_login

付费标识:rect_function_pay

退出SDK标识:rect_function_exit

1.JAVA端项目新建库项目 如图


[attach]81[/attach]


注意包名最好和我们在之前限定的接口 ID一致。然后记得勾选为library


[attach]82[/attach]


然后一直确定 生成项目  Rectane。在eclipse中右键项目属性 – java构建路径 – 源代码 – Rectane/gen – 包括选择[包括]点击编辑  在[包括模式]中 点击 添加 输入 nothing 按确定。完成后如图


[attach]83[/attach]


接下来加入AIR SDK中的 FlashRuntimeExtensions.jar 文件到项目下的lib文件夹 然后导入到项目中 如图


[attach]84[/attach]


接下来就是贴代码的时间了:RectContext.java

  1. package com.rect.ane;

  2. import java.util.HashMap;
  3. import java.util.Map;

  4. import com.adobe.fre.FREContext;
  5. import com.adobe.fre.FREFunction;

  6. /**
  7. * @author Rect
  8. * @version  Time:2013-5-8
  9. */
  10. public class RectContext extends FREContext {
  11. /**
  12. * INIT sdk
  13. */
  14. public static final String RECT_FUNCTION_INIT = "rect_function_init";
  15. /**
  16. * 登录Key
  17. */
  18. public static final String RECT_FUNCTION_LOGIN = "rect_function_login";
  19. /**
  20. * 付费Key
  21. */
  22. public static final String RECT_FUNCTION_PAY = "rect_function_pay";
  23. /**
  24. * 退出Key
  25. */
  26. public static final String RECT_FUNCTION_EXIT = "rect_function_exit";
  27. @Override
  28. public void dispose() {
  29. // TODO Auto-generated method stub

  30. }

  31. @Override
  32. public Map getFunctions() {
  33. // TODO Auto-generated method stub
  34. Map map = new HashMap();
  35. //         //映射
  36. map.put(RECT_FUNCTION_INIT, new RectInit());
  37. map.put(RECT_FUNCTION_LOGIN, new RectLogin());
  38. map.put(RECT_FUNCTION_PAY, new RectPay());
  39. map.put(RECT_FUNCTION_EXIT, new RectExit());
  40. return map;
  41. }

  42. }
复制代码

RectExtension.java

  1. package com.rect.ane;

  2. import com.adobe.fre.FREContext;
  3. import com.adobe.fre.FREExtension;

  4. /**
  5. * @author Rect
  6. * @version  Time:2013-5-8
  7. */
  8. public class RectExtension implements FREExtension {

  9. @Override
  10. public FREContext createContext(String arg0) {
  11. // TODO Auto-generated method stub
  12. return new RectContext();
  13. }

  14. @Override
  15. public void dispose() {
  16. // TODO Auto-generated method stub

  17. }

  18. @Override
  19. public void initialize() {
  20. // TODO Auto-generated method stub

  21. }

  22. }
复制代码

更多的详细代码 请看附件中的 Rectane_java 项目
在源码中会有更详细的表述2.AS 端项目
AS项目是作为和JAVA对应的。负责侦听返回 和调用JAVA函数
这里使用 FB 建立一个 手机库项目 然后在里面加两个类:
RectEvents.as

  1. package com.rect.ane
  2. {
  3. /**
  4. *
  5. * @author Rect  2013-5-6
  6. *
  7. */
  8. public class RectEvents
  9. {
  10. public function RectEvents()
  11. {
  12. }
  13. /**************************平台通知************************************/
  14. /**
  15. *init
  16. */
  17. public static const DEMO_SDK_STATUS:String = "DemoInit";
  18. /**
  19. * 用户登录
  20. */
  21. public static const DEMO_LOGIN_STATUS : String = "DemoLogin";

  22. /**
  23. * 用户注销
  24. */
  25. public static const DEMO_LOGOUT_STATUS : String = "DemoExit";

  26. /**
  27. * 充值
  28. */
  29. public static const DEMO_PAY_STATUS : String = "DemoPay";
  30. }
  31. }
复制代码

RectExtension.as


  1. package com.rect.ane
  2. {
  3. import flash.events.EventDispatcher;
  4. import flash.events.IEventDispatcher;
  5. import flash.events.StatusEvent;
  6. import flash.external.ExtensionContext;

  7. /**
  8. *
  9. * @author Rect  2013-5-6
  10. *
  11. */
  12. public class RectExtension extends EventDispatcher
  13. {
  14. public static const RECT_FUNCTION_INIT:String = "rect_function_init";//与java端中Map里的key一致
  15. public static const RECT_FUNCTION_LOGIN:String = "rect_function_login";//与java端中Map里的key一致
  16. public static const RECT_FUNCTION_PAY:String = "rect_function_pay";//与java端中Map里的key一致
  17. public static const RECT_FUNCTION_EXIT:String = "rect_function_exit";//与java端中Map里的key一致

  18. public static const EXTENSION_ID:String = "com.rect.ane";//与extension.xml中的id标签一致
  19. private var extContext:ExtensionContext;

  20. /**单例的实例*/
  21. private static var _instance:RectExtension;
  22. public function RectExtension(target:IEventDispatcher=null)
  23. {
  24. super(target);
  25. if(extContext == null) {
  26. extContext = ExtensionContext.createExtensionContext(EXTENSION_ID, "");
  27. extContext.addEventListener(StatusEvent.STATUS, statusHandler);
  28. }

  29. }

  30. //第二个为参数,会传入java代码中的FREExtension的createContext方法
  31. /**
  32. * 获取实例
  33. * @return DLExtension 单例
  34. */
  35. public static function getInstance():RectExtension
  36. {
  37. if(_instance == null)
  38. _instance = new RectExtension();
  39. return _instance;
  40. }

  41. /**
  42. * 转抛事件
  43. * @param event 事件
  44. */
  45. private function statusHandler(event:StatusEvent):void
  46. {
  47. dispatchEvent(event);
  48. }

  49. /**
  50. *init发送函数
  51. * @param key 暂时传什么都可以  留着可能要用
  52. * @return
  53. *
  54. */
  55. public function FeiliuInit(key:int):String{
  56. if(extContext ){
  57. return extContext.call(RECT_FUNCTION_INIT,key) as String;
  58. }
  59. return "call login failed";
  60. }

  61. /**
  62. *登录发送函数
  63. * @param key 暂时传什么都可以  留着可能要用
  64. * @return
  65. *
  66. */
  67. public function FeiliuLogIn(key:int):String{
  68. if(extContext ){
  69. return extContext.call(RECT_FUNCTION_LOGIN,key) as String;
  70. }
  71. return "call login failed";
  72. }
  73. /**
  74. *付费发送函数
  75. * @param key 暂时传什么都可以 留着以后可能要用
  76. * @return
  77. *
  78. */
  79. public function FeiliuPay(data:Vector.,len:int = 5):String{
  80. if(extContext && data.length == len){
  81. return extContext.call(RECT_FUNCTION_PAY,data)as String;
  82. }
  83. return "call pay failed";
  84. }

  85. /**
  86. *退出SDK时候调用   这个函数只在退出游戏的时候调用
  87. * @param key
  88. * @return
  89. *
  90. */
  91. public function ExitSDKHandle(key:int):String{
  92. if(extContext){
  93. return extContext.call(RECT_FUNCTION_EXIT,key) as String;
  94. }
  95. return "call exit failed";
  96. }
  97. }
  98. }
复制代码

仔细看这两个类就可以发现 在前面说的 接口ID 和 各个函数调用的 ID 都必须保持一致
3.extension.xml 文件-


[attach]85[/attach]


XML文件中要注意的是 必须保存一致4.打包ANE  在 1 2 3进行完了之后 我们去取到一下文件
A>.JAVA项目下的 :Rect\Rectane_java\bin\rectane.jar
B>AS库项目下的:Rect\RectANE_as\bin\RectANE.swc
C>XML文件:extension.xml
D>新建一个KEY文件:rect.p12(可用FB新建)
F>解压RectANE 获得:catalog.xml library.swf
总共 6个文件然后新建文件夹  Rect_make_ane
在 Rect_make_ane文件夹下 新建 android-ARM文件夹
然后上述6个文件分别放在下面:
rectane.jar – > Rect_make_ane/android-ARM/rectane.jar
catalog.xml – > Rect_make_ane/android-ARM/catalog.xml
library.swf – > Rect_make_ane/android-ARM/library.swf
RectANE.swc -> Rect_make_ane/RectANE.swc
extension.xml – > Rect_make_ane/extension.xm
lrect.p12 -> Rect_make_ane/rect.p12

具体的看源码中的文件夹在打包ANE之前 接入平台的时候 还有一步 就是 合成 把 平台jar和 rectane.jar 一起合成一个jar 后续文章会详细介绍然后使用命令行打包 我在这里写成了一个批处理:(注意路径自己配
)“[你的AIR SDK路径]\bin\adt” -package -storetype PKCS12 -keystore rect.p12 -storepass 1234 -target ane com.rect.ane extension.xml -swc *.swc -platform Android-ARM -C Android-ARM .
保存为 ane_pacler.bat 在 Rect_make_ane文件夹下
然后运行这里bat 就可以生成 ane了至此  ane 成功编写出来。
注意:真正接入平台 的ANE  还需要 在JAVA项目中导入平台jar 然后在各个函数中编写各种操作。在把平台jar 和 rectane.jar一起打包成一个jar  再打包ANE我在这里把 平台jar 合成最终的 jar 略去 在后面的文章中会具体详细的说明过程。


[attach]86[/attach]






作者: 破晓    时间: 2014-12-30 22:42
[AIR接入Android 平台]第三步 打包处理多个JAR变成一个JAR提供给

一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码

接入android 运营平台 一般平台方都会提供一个或者更多的jar包。但是我们的ane只能打包一个jar包。这就需要我们把这些jar全部集合到一个去。

我的方法是基于一个兄台的教程的改进:http://bbs.9ria.com/thread-160445-1-1.html

拿我接过的最繁琐的一个SDK来说,它要求我必须连入6个jar。如图


[attach]87[/attach]


需要把 其余的jar 都合并到 flane.jar中。

我们改进上面那个兄台的bat如下:


  1. @echo off
  2. ::转到当前盘符
  3. %~d0
  4. ::打开当前目录
  5. cd %~dp0
  6. ::你做的主JAR包的路径
  7. set MainJar=flane.jar
  8. ::第三方JAR包的路径
  9. set jar1=androidsdklibrary.jar
  10. set jar2=gamehelper.jar
  11. set jar3=oauth20.jar
  12. set jar4=oauth20-api.jar
  13. set jar5=sinaweibosso.jar
  14. set jar6=tencent_openapi.jar
  15. ::第三方JAR包顶级包名称
  16. set packageName1=com
  17. set packageName2=org
  18. set packageName3=android
  19. echo =========== start combin ==============
  20. ::解压第三方包
  21. jar -xf %jar1%
  22. jar -xf %jar2%
  23. jar -xf %jar3%
  24. jar -xf %jar4%
  25. jar -xf %jar5%
  26. jar -xf %jar6%
  27. ::合并主JAR包
  28. jar -uf %MainJar% %packageName1%
  29. ::如果还有别的顶级包可以接着合并,例如:
  30. jar -uf %MainJar% %packageName2%
  31. jar -uf %MainJar% %packageName3%
  32. echo =========== over ==============
  33. echo 再点一下就结束了--小Q
  34. pause
复制代码

就可以了。这个方法适用于合并N个jar。
声明下 这并不是我的原创。感谢上面那位兄台的分享。




作者: 破晓    时间: 2014-12-30 22:45
[AIR接入Android 平台]处理res资源之文字国际化

一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码

文字国际化 其实是android编码的规范之一。但是不同的运营商提供的SDK 这块处理的都不一样。

就目前我接触过的平台来说。当乐 UC 91 360 算做得很好。

但是其他小平台  特别是有一个运营商 他们客户端技术才一个人。

都很不注重android文字国际化的问题。打包进ane的资源 布局文件XML必须是规范的国际化处理的。

否则就会在打包APK的时候报错。

例如下面:


[attach]88[/attach]


一般这个错误在打包ANE的时候是不会报的。而在打包APK的时候就会报出来。如图所示 这个错误主要是由于  ANE中的资源文件夹RES下的layout文件夹下的布局XML中的text 被SDK客户端程序员直接用代码写的。所以会直接写在layout的布局XML里面。而一般android官方开发标准推荐更标准化的写法。把layout中的text的内容写入 value文件夹下的string.xml中。而我们的ADT打包就是遵从这种标准。例如layout某XML的一段:<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”账号 : “
android:textColor=”#A67D3D”
android:textSize=”17sp” />

红色部分的写法是错误的 正确的写法是 把红色部分变为:

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/text001 “
android:textColor=”#A67D3D”
android:textSize=”17sp” />

在res下的value文件夹下的string.xml中加入:
<string name=”text001“>账号 :</string>

这样才是规范的res文件。

为了批量出来这种事情 我写了一个工具类


  1. package
  2. {
  3.         import flash.desktop.Clipboard;
  4.         import flash.desktop.ClipboardFormats;
  5.         import flash.desktop.NativeDragManager;
  6.         import flash.display.InteractiveObject;
  7.         import flash.display.Loader;
  8.         import flash.display.Sprite;
  9.         import flash.events.Event;
  10.         import flash.events.MouseEvent;
  11.         import flash.events.NativeDragEvent;
  12.         import flash.filesystem.File;
  13.         import flash.filesystem.FileMode;
  14.         import flash.filesystem.FileStream;
  15.         import flash.net.FileFilter;
  16.         import flash.net.FileReference;
  17.         import flash.net.URLLoader;
  18.         import flash.net.URLRequest;
  19.         import flash.text.TextField;
  20.         import flash.text.TextFormat;
  21.         
  22.         import flashButton.backButton;
  23.         
  24.         /**
  25.          *
  26.          * @author Rect  2013-4-16
  27.          *
  28.          */
  29.         [SWF(width="800" , height="600")]
  30.         public class FileXMLHandle extends Sprite
  31.         {
  32.                 private var sp:Sprite;
  33.                 private var mapAry:Array = [];
  34.                 private var test:TextField;
  35.                 private var beginBtn:backButton;
  36.                 private var SelectBtn:backButton;
  37.                 private var outBtn:backButton;
  38.                 private var textSize:TextField;
  39.                 private var textQuality:TextField;
  40.                 private var fileObj:FileReference = new FileReference();
  41.                 private var selectedSaveFile:File;
  42.                 private var fileArr:Array = [];
  43.                 private var key:String;
  44.                 public function FileXMLHandle()
  45.                 {
  46.                         super();
  47.                         init();
  48.                 }
  49.                 private function init():void{
  50.                         sp = new Sprite();
  51.                         test = new TextField();
  52.                         test.defaultTextFormat = (new TextFormat("",18,0xFFFFF));
  53.                         test.text = "拖入此处";
  54.                         test.width = 400;
  55.                         test.height = 400;
  56.                         test.wordWrap = true;
  57.                         test.multiline = true;
  58.                         
  59.                         sp.addChild(test);
  60.                         sp.graphics.beginFill(0x3CB371);
  61.                         sp.graphics.drawRect(0,0,800,600);
  62.                         sp.graphics.endFill();
  63.                         this.addChild(sp);
  64.                         sp.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragInHandler);
  65.                         sp.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDropHandler);
  66.                         
  67.                         selectedSaveFile = new File();
  68.                         selectedSaveFile.addEventListener(flash.events.Event.SELECT, onSelectedSaveFile);
  69.                         var fileFilt:FileFilter = new FileFilter("输出文件(*.xml)", "*.xml");
  70.                         fileArr.push(fileFilt);
  71.                         
  72.                         beginBtn = new backButton("开始");
  73.                         beginBtn.x = 600,beginBtn.y = 110;
  74.                         beginBtn.mouseEnabled = false;
  75.                         beginBtn.addEventListener(MouseEvent.CLICK,onChick);
  76.                         this.addChild(beginBtn);
  77.                         
  78.                         SelectBtn = new backButton("选择string.xml");
  79.                         SelectBtn.x = 600,SelectBtn.y = 10;
  80.                         SelectBtn.addEventListener(MouseEvent.CLICK,onSelectFile);
  81.                         this.addChild(SelectBtn);
  82.                         
  83.                         outBtn = new backButton("选择输出文件夹");
  84.                         outBtn.x = 600,outBtn.y = 60;
  85.                         outBtn.addEventListener(MouseEvent.CLICK,onSelectPath);
  86.                         this.addChild(outBtn);
  87.                         
  88.                         
  89.                 }
  90.                
  91.                 private function onSelectedSaveFile(e:flash.events.Event):void
  92.                 {
  93.                         var str:String = selectedSaveFile.nativePath;
  94.                         var __reg:RegExp = new RegExp('\\\\', '');
  95.                         
  96.                         switch(key)
  97.                         {
  98.                                 case "file":
  99.                                         motherFileArys = str.replace(/\\/g,"/");
  100.                                         break;
  101.                                 
  102.                                 case "path":
  103.                                         outPath = str.replace(/\\/g,"/");
  104.                                         trace(outPath)
  105.                                         break;
  106.                         }
  107.                         
  108.                         if(motherFileArys && outPath)HandleMaps();
  109.                 }
  110.                 private var motherFileArys:String = null;
  111.                 private var outPath:String = null;
  112.                 private var _loaderImages:URLLoader  = new URLLoader();
  113.                 private var stringXML:XML;
  114.                 private var miscXML:XML;
  115.                 private function HandleMaps():void{
  116.                                 _loaderImages.addEventListener(Event.COMPLETE,loaderInitListeners);
  117.                                 _loaderImages.load(new URLRequest(motherFileArys));
  118.                 }
  119.                
  120.                 private function loaderInitListeners(evt:Event):void {
  121.                         _loaderImages.removeEventListener(Event.COMPLETE,loaderInitListeners);
  122.                         stringXML = XML(_loaderImages.data);
  123.                         _loaderImages.close();
  124.                         
  125.                 }
  126.                
  127.                 /**********************************/
  128.                 private function onChick(ev:MouseEvent):void{
  129.                         
  130.                         HandleMap();
  131.                 }
  132.                
  133.                 private function onSelectFile(ev:MouseEvent):void{
  134.                         key = "file";
  135.                         selectedSaveFile.browse(fileArr);
  136.                 }
  137.                
  138.                 private function onSelectPath(ev:MouseEvent):void{
  139.                         key = "path";
  140.                         selectedSaveFile.browseForDirectory("请选择目录");
  141.                 }
  142.                
  143.                 private function textDown(ev:MouseEvent):void{
  144.                         var text:TextField = ev.currentTarget as TextField;
  145.                         text.text = "";
  146.                 }
  147.                 protected function onDragInHandler(event : NativeDragEvent) : void
  148.                 {
  149.                         var transferable :Clipboard = event.clipboard;
  150.                         if(transferable.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
  151.                         {
  152.                                 var files : Array = transferable.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
  153.                                 if(files)
  154.                                 {
  155.                                         var file : File = File(files[0]);
  156.                                        
  157.                                         if(file && file.name.indexOf(".xml") >= 0 || file && file.name.indexOf(".XML") >= 0)
  158.                                         {
  159.                                                 NativeDragManager.acceptDragDrop(event.currentTarget as InteractiveObject);
  160.                                         }
  161.                                 }
  162.                                 
  163.                         }
  164.                 }
  165.                
  166.                 private var motherFileAry:Array = [];
  167.                 private var sonFileAry:Array = [];
  168.                 private var xmlHead:String = '<?xml version="1.0" encoding="utf-8" ?>';
  169.                 protected function onDropHandler(event : NativeDragEvent) : void
  170.                 {
  171.                         motherFileAry = [];
  172.                         sonFileAry = [];
  173.                         var transferable :Clipboard = event.clipboard;
  174.                         var files : Array = transferable.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
  175.                         if(files)
  176.                         {
  177.                                 var file :File;
  178.                                 for(var t:int = 0;t<files.length;t++){
  179.                                         file  = File(files[t]);
  180.                                         test.appendText("\n"+file.url);
  181.                                         motherFileAry.push(file.url);
  182.                                        
  183.                                         sonFileAry.push(outPath+"/"+ file.name);
  184.                                 }
  185.                                 if(sonFileAry.length != 0 ){
  186.                                         if(motherFileArys && outPath)this.beginBtn.mouseEnabled = true;
  187.                                 }else{
  188.                                 }
  189.                                 
  190.                                 trace(motherFileAry,outPath);//(file.name.length - 4))));
  191.                         }
  192.                 }
  193.                
  194.                 private var _loaderImage:URLLoader  = new URLLoader();
  195.                 private function HandleMap():void{
  196.                         if(motherFileAry.length){
  197.                                 _loaderImage.addEventListener(Event.COMPLETE,loaderInitListener);
  198.                                 _loaderImage.load(new URLRequest(motherFileAry.splice(0,1)));
  199.                         }else
  200.                         {
  201.                                 this.beginBtn.mouseEnabled = false;
  202.                                 test.text = ("完成!");
  203.                                 makeValue();
  204.                         }
  205.                 }
  206.                
  207.                 private function makeValue():void
  208.                 {
  209.                         
  210.                         for(var t:int = 0;t<stringArr.length;t++)
  211.                         {
  212.                                 stringXML.appendChild(<string name = {stringName[t]}>{stringArr[t]}</string>);
  213.                         }
  214.                         
  215.                         var filePath:String = motherFileArys ;
  216.                         var f:FileStream = new FileStream();
  217.                         var fl:File = new File(filePath);
  218.                         
  219.                         f.open(fl,FileMode.WRITE);
  220.                         var xmlStr1:String = stringXML.toString();
  221.                         var pattern1:RegExp = /\n/g;
  222.                         xmlStr1=xmlStr1.replace(pattern1, "\r\n");
  223.                         f.writeUTFBytes(String(xmlHead+"\r\n"+ xmlStr1));
  224.                         f.close();
  225.                         
  226.                         
  227.                 }
  228.                
  229.                 private function loaderInitListener(evt:Event):void {
  230.                         
  231.                         _loaderImage.removeEventListener(Event.COMPLETE,loaderInitListener);
  232.                         
  233.                         var xml:XML = XML(_loaderImage.data);
  234.                         
  235.                         walk(xml);
  236.                         
  237.                         makeFile(xml);
  238.                         
  239.                         HandleMap();
  240.                 }
  241.                
  242.                
  243.                 private function makeFile(str:XML):void{
  244.                         
  245.                         var filePath:String = (sonFileAry.splice(0,1)) ;
  246.                         var f:FileStream = new FileStream();
  247.                         var fl:File = new File(filePath);
  248.                         
  249.                         f.open(fl,FileMode.WRITE);
  250.                         
  251.                         var xmlStr:String = str.toString();
  252.                         var pattern:RegExp = /\n/g;
  253.                         xmlStr=xmlStr.replace(pattern, "\r\n");
  254.                         f.writeUTFBytes(String(xmlHead+"\r\n"+ xmlStr));
  255.                         
  256.                         f.close();
  257.                         
  258.                 }
  259.                
  260.                 private var stringArr:Array = [];
  261.                 private var stringName:Array = [];
  262.                 private var stringLen:int = 0;
  263.                
  264.                
  265.                 private function walk( node:XML ):void {
  266.                         
  267.                         var androidNS:Namespace = new Namespace("android","http://schemas.android.com/apk/res/android");
  268.                         node.addNamespace(androidNS);
  269.                         
  270.                         
  271.                         // Loop over all of the child elements of the node
  272.                         for each ( var element:XML in node.elements( ) ) {
  273.                                 // Output the label attribute
  274.                                 var attributes:XMLList = element.attributes( );
  275.                                 
  276.                                 for(var t:int = 0;t<100;t++)
  277.                                 {
  278.                                         if(!attributes[t])
  279.                                                 break;
  280.                                        
  281.                                         var str:String = attributes[t] ;
  282.                                         var pattern4:RegExp=/[\u4e00-\u9fa5]/;
  283.                                         if(pattern4.test(str))
  284.                                         {
  285.                                                 stringArr.push(str);
  286.                                                 stringName.push("DW_string_" + stringLen);
  287.                                                 
  288.                                                 attributes[t] = "@string/DW_string_" + stringLen.toString();
  289.                                                 
  290.                                                 stringLen++;
  291.                                         }
  292.                                 
  293.                                 
  294.                         }
  295.                                 walk( element );
  296.                         }
  297.                 }
  298.         }
  299. }
  300.                         
  301.                
复制代码

Button

  1. package flashButton
  2. {
  3.         import flash.display.Sprite;
  4.         import flash.events.MouseEvent;
  5.         import flash.text.TextField;
  6.         import flash.text.TextFieldAutoSize;
  7.         import flash.text.TextFormat;

  8.         public class backButton extends Sprite
  9.         {
  10.                 private var _buttonName:String;
  11.                 private var _szWide:Number;
  12.                 private var _szHeight:Number;
  13.                 private var _Color:int;
  14.                 public function backButton(
  15.                         buttonName:String,
  16.                         szWide:Number = 180,
  17.                         szHeight:Number = 40,
  18.                         Color:int = 0xFFFFFF)
  19.                 {
  20.                         _buttonName = buttonName;
  21.                         _szWide = szWide;
  22.                         _szHeight = szHeight;
  23.                         _Color = Color;
  24.                         init();
  25.                 }
  26.                 private var _text:TextField;
  27.                 private var _textFormat:TextFormat;
  28.                 private function init():void{
  29.                         _text = new TextField();
  30.                         _text.mouseEnabled = false;
  31.                         _text.text = _buttonName;
  32.                         _text.x = 2;
  33.                         _text.y = 2;
  34.                         _text.width = _szWide-5;
  35.                         _text.height = _szHeight-5;
  36.                         _text.autoSize = TextFieldAutoSize.CENTER;
  37.                         _textFormat = new TextFormat();
  38.                         _textFormat.size = 25;
  39.                         _textFormat.color = 0;//0x228b22;
  40.                         _text.setTextFormat(_textFormat);
  41.                         addChild(_text);
  42.                         this.graphics.beginFill(_Color,.9);
  43.                         this.graphics.drawRoundRect(0,0,_szWide,_szHeight,5,5);
  44.                         this.graphics.endFill();
  45.                         this.addEventListener(MouseEvent.ROLL_OVER,onMouseOver);
  46.                         this.addEventListener(MouseEvent.ROLL_OUT,onMouseOut);
  47.                 }
  48.                 private function onMouseOut(ev:MouseEvent):void{
  49.                         this.graphics.clear();
  50.                         this.graphics.beginFill(_Color,.9);
  51.                         this.graphics.drawRoundRect(0,0,_szWide,_szHeight,5,5);
  52.                         this.graphics.endFill();
  53.                 }
  54.                 private function onMouseOver(ev:MouseEvent):void{
  55.                         this.graphics.clear();
  56.                         this.graphics.beginFill(0xeedd82,.9);
  57.                         this.graphics.drawRoundRect(1,1,_szWide-1,_szHeight-1,5,5);
  58.                         this.graphics.endFill();
  59.                 }
  60.         }
  61. }
复制代码

使用方式:
A.新建任意项目 运行上述类
B.拖动ANE的res下的layout下的XML到这个类中
C.点击按钮
D.正则中只转换中文,一般text中英文比较少 但是也有,一般都不多的。
遇到报错出来的是英文 就手动改就好了 或者你完善一下我的类。反正上面这个类 对于我已经够用了 很多平台的资源布局文件都是只改几个。




作者: 破晓    时间: 2014-12-30 22:50
[AIR接入Android 平台]处理res资源之找不到资源ID

一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码

打包ANE会经常遇到找不到资源R.XXX ID的问题,或者是找到的资源ID不对

然后报NULL POINT错 直接程序崩溃。

AIR与android原生取资源方式的区别
ADOBE给出的取资源的方式是:

  1. _context.getResourceId("id.AlipayTitleItemName");
复制代码

但是原生JAVA取资源的方式是:

  1. findViewById(R.id.AlipayTitleItemName);
复制代码

ADOBE的取资源方式 是我们在ANE中使用到资源的时候的取法,
但是一般运营商提供的SDK都是混淆的jar 取资源的方式都是清一色的findViewById。
而我们不可能去修改SDK 的jar。除非你的逆向能力到一定水平。

其实修改SDK的jar也不是不可能的。对于android逆向有兴趣的同学 可以去试试。

其实android逆向也是一门非常好玩的技术。如果你有兴趣 我们可以一起探讨。


ANE打包资源后生成的R.java特点

前面的文章说了 ANE打包资源是通过把资源文件夹res放到 Android-ARM下。然后打包的时候就可以在ANE内部生成R.java了。我们反编译一个APK看看AIR生成的R.java有什么特点:


[attach]89[/attach]


默认是生成R.java处在 包名为APK的ID下。也就是air+(你程序的-app.xml的ID)。例如(air.xxx.xxx.R)
而我们的SDK取ID都是 R.id.AlipayTitleItemName 也就是说取的是SDK的同级包下的R.java。

这就会导致取不到资源ID:


[attach]90[/attach]


也就是说 导致取不到ID的原因之一 有可能是(之所以说有可能 是因为取不到资源ID还有其他各种的原因,起码我遇遇到过的是如此):

真正的资源ID 所在的类[air.xxx.xxx.xx.R.java]  与SDK资源ID的包名[wxd.view.R.java]地址不同

现在知道问题所在了。那解决这个问题仔细一想就能知道如下几种方法:

A. 最简单的方式。说服运营商 把SDK的取资源的包改为我们AIR生成资源的包,但是这也是最异想天开的方法。永远别梦想着运营商会针对你一家修改它的SDK。虽然想法美好 但是却是痴心妄想。


B.在ANE源码中新建一个包例如上面举例的情况是 在src下新建 包 [wxd.view] 在这个包下新建类R.java。然后把运营商提供的JAVADEMO的gen文件夹下的R.java复制过去  如图:


[attach]91[/attach]


C.也是目前我发现最完美的解决方式:灵活使用context.getResourceId 取AIR生成的R.java下的ID 取填补 SDK下的R.java的资源ID。

如图:


[attach]92[/attach]


例如上图的例子中。加一个resHandle.java类。

  1. package com.xxx.ane;

  2. import android.util.Log;

  3. import com.adobe.fre.FREContext;

  4. /**
  5. * R.java ID处理
  6. * @author Rect
  7. * @version  Time:2013-5-14
  8. */
  9. public class ResHandle {
  10.     public static void setResourctID(FREContext _context,String TAG)
  11.     {
  12.         if(wxd.view.R.array.address == _context.getResourceId("array.address") &&
  13.                 wxd.view.R.anim.elseway == _context.getResourceId("anim.elseway"))
  14.             return;
  15.         
  16.         Log.d(TAG, "---------anim-------"+_context.getResourceId("anim.elseway"));
  17.         wxd.view.R.anim.elseway = _context.getResourceId("anim.elseway");
  18.         wxd.view.R.anim.elseway_in = _context.getResourceId("anim.elseway_in");
  19.         wxd.view.R.anim.landscape_anim = _context.getResourceId("anim.landscape_anim");
  20.         wxd.view.R.anim.portrait_anim = _context.getResourceId("anim.portrait_anim");
  21.         wxd.view.R.anim.zoom_enter = _context.getResourceId("anim.zoom_enter");
  22.         wxd.view.R.anim.zoom_exit = _context.getResourceId("anim.zoom_exit");
  23.         wxd.view.R.anim.zoomin = _context.getResourceId("anim.zoomin");
  24.         wxd.view.R.anim.zoomout = _context.getResourceId("anim.zoomout");
  25.          }
复制代码

当然 在SDK包下的R.java要做一些处理 使得能访问全部静态变量 例如我的
wxd.view.R.java 如下:


  1. package wxd.view;


  2. public  class R
  3. {
  4.   public static class anim
  5.   {
  6.     public static  int elseway = 2130968576;
  7.     public static  int elseway_in = 2130968577;
  8.     public static  int landscape_anim = 2130968578;
  9.     public static  int portrait_anim = 2130968579;
  10.     public static  int zoom_enter = 2130968580;
  11.     public static  int zoom_exit = 2130968581;
  12.     public static  int zoomin = 2130968582;
  13.     public static  int zoomout = 2130968583;
  14.   }
复制代码

这样外部就能访问 各个变量了。
然后在 调用SDK打开SDK界面之前 调用这句对R.java进行处理:

  1. Log.d(TAG, "---------R.java处理-------");
  2.         ResHandle.setResourctID(_context, TAG);
  3.         Log.d(TAG, "---------Login开始-------");
复制代码

至此 由于取不到资源ID的问题 就得到了解决。当然再强调一次 取不到资源ID 也有可能是由于其他问题导致的。这仅仅是导致这个问题的原因之一。




作者: 破晓    时间: 2014-12-30 22:51
命令行打包ANE
一点说明:打包ANE有很多方式。我的方式比较普遍  把步骤分得太细。最初的时候也是为了了解全过程。其实zrong打包ANE的方式才是王道。举个例子 我打包飞流ANE的命令:

"D:\FB\Adobe Flash Builder 4.6\sdks\AIR3.5\bin\adt" -package -storetype PKCS12 -keystore qlwx.p12 -storepass 1234 -target ane com.feiliu.ane extension.xml -swc *.swc -platform Android-ARM -C Android-ARM .


说明:
"D:\FB\Adobe Flash Builder 4.6\sdks\AIR3.5\bin\adt"   本机ADT路径
qlwx.p12   证书文件
1234   证书密码
com.feiliu.ane 生成的ANE文件名字
extension.xml  对接XML



作者: 破晓    时间: 2014-12-30 22:51
打包APK命令解释

一点说明:同样我打包的方式步骤分得太细。打包APK用命令行 因为很多平台都会要求打APK进入我们的AIR APK中
举个例子小米平台的打包APK命令:
"D:\FB\Adobe Flash Builder 4.6\sdks\AIR3.5\bin\adt"  -package -target apk-captive-runtime -storetype pkcs12 -keystore a.p12 -storepass 1234 xiaomi.apk logInTest-app.xml logInTest.swf  MiGameCenterSDKService.apk assets -extdir .


说明:

"D:\FB\Adobe Flash Builder 4.6\sdks\AIR3.5\bin\adt"  你的本机ADT命令
“a.p12”   证书文件
“1234” 证书密码
“xiaomi.apk” 要生成的最终APK名字
“logInTest-app.xml ” AIR项目XML
“logInTest.swf ”AIR项目SWF
“MiGameCenterSDKService.apk” 打进APK去的APK
“assets” 打进APK去的资源文件

有朋友问这里的SWF XML怎么来的。
下面说一种最简单的方式:
右键属性 - 导出 -发行版 - 下一步 - 下一步 (这里步骤不一定准确,但是记住 在FB打包APK前停止操作)
然后停止操作。去项目文件下找bin-release-temp文件夹,此文件夹下会有编译的SWF文件 和 xml文件。


还有更好的方式:

A.ADT打包,上手比较快。把项目的编译 打包分开。由于FB大项目编译打包老是卡死 我已经对FB打包深恶痛绝了。目前完全抛弃了用FB来打包 全部用命令行。


B.ANT打包,上手比较慢一点,但是ANT打包非常强大啊。强大到可以做你想做的任何东西,例如排除jar中的.class文件 例如导入各种lib文件 例如生成jar 例如编译swf 例如 。。。
作者: 破晓    时间: 2014-12-30 22:53
这篇教程是我在ANE中自己新建一个activity来调用SDK的。你看看能不能帮到你。或者你可以把你的情况描述得更清晰点
http://www.shadowkong.com/archives/1062

https://github.com/platformanes/android360   必须的有。不过涉及官方SDK的文档 源码库 等我已经去掉了 只保留ANE源码  保留打包命令  但是ANE可以直接拿来用。
作者: 破晓    时间: 2014-12-30 22:54
您好,想请教一个问题:我最近做MM android接入的时候,发现MM的接入jar包中有一部分资源在jar下(不在res ...






先打包APK 然后解压 再把资源放入  这种方式很不稳定的。你失败了是因为签名问题吧;对于资源的处理有两种方式:
1.res文件下的布局文件 图片 等打包进去ANE
2.assets 文件夹下的 图片等  复制到 AIR项目的src文件夹下 打进APK

更详细的请看:http://www.shadowkong.com/archives/1042




欢迎光临 守望者--AIR技术交流 (http://www.airmyth.com/)