[AIR接入Android 平台]打造登录付费ANE全教程
本文来自:http://bbs.9ria.com/thread-197503-1-1.html目前为止我为项目共接入了大概7-8家或大或小的平台。深知AIR移动项目接入运营平台在打造ANE过程中会有各种各样的问题。在这里我打算把我遇到的问题 和解决方式记录下来,也许你用得着 也许你已经遇到过 也许你已经解决掉。接下来我会从我的air项目和各种android应用运营平台方的SDK接入过程 中 遇到的问题进行探讨。其中有些问题已经解决 有些问题解决得很差强人意,有些问题尚未解决。也请已经解决了这些问题的朋友能和我交流。当然我对原生android开发刚刚入门 有一些看法或多或少存在错误。也请看到的朋友能Email我指出。(rectvvgmailcom)
再次声明下,这里仅仅写下我遇到的问题 和我个人的解决方式 关于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的资源有:resassets两种资源处理方式都有。(在之前我就写过一篇关于ANE如何处理资源的问题的文章)平台方接入的第三方(对于游戏是第四方):微博授权登录 QQ授权登录平台方需要加入的activity情况:这个平台需要加入的activity声明是最多的 在这个平台的 -app.xml也是最臃肿的,这会涉及到一个SDK activity 与 游戏activity 如何通信切换的问题。在之前就遇到过一个问题 把游戏主activity传给SDK的时候 执行完SDK操作返回不了游戏activity 详细的解决方式 请看我前面的一篇文章。平台方需要加入的JAR数量:6个(平台方的JAR最后全部都需要打包成一个JAR 才能生成ANE 这个问题的解决方式后面会给出)——————————————————-
第一步 准备工作1.在这之前需要先安装 JDKJRE并且添加环境变量如图:(路径自己按照各自环境配置)这两个工具 请自行下载配置JAR 配置JDK:例如我的路径:C:\Program Files\Java\jdk1.7.0_17\bin加入 环境变量 系统变量
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所在路径即可。(这并不是必须的仅仅为了更好的使用命令行)
第二步 编写 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_exit1.JAVA端项目新建库项目 如图
注意包名最好和我们在之前限定的接口 ID一致。然后记得勾选为library
然后一直确定 生成项目Rectane。在eclipse中右键项目属性 – java构建路径 – 源代码 – Rectane/gen – 包括选择[包括]点击编辑在[包括模式]中 点击 添加 输入 nothing 按确定。完成后如图
接下来加入AIR SDK中的 FlashRuntimeExtensions.jar 文件到项目下的lib文件夹 然后导入到项目中 如图
接下来就是贴代码的时间了:RectContext.javapackage com.rect.ane;
import java.util.HashMap;
import java.util.Map;
import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
/**
* @author Rect
* @versionTime:2013-5-8
*/
public class RectContext extends FREContext {
/**
* INIT sdk
*/
public static final String RECT_FUNCTION_INIT = "rect_function_init";
/**
* 登录Key
*/
public static final String RECT_FUNCTION_LOGIN = "rect_function_login";
/**
* 付费Key
*/
public static final String RECT_FUNCTION_PAY = "rect_function_pay";
/**
* 退出Key
*/
public static final String RECT_FUNCTION_EXIT = "rect_function_exit";
@Override
public void dispose() {
// TODO Auto-generated method stub
}
@Override
public Map getFunctions() {
// TODO Auto-generated method stub
Map map = new HashMap();
// //映射
map.put(RECT_FUNCTION_INIT, new RectInit());
map.put(RECT_FUNCTION_LOGIN, new RectLogin());
map.put(RECT_FUNCTION_PAY, new RectPay());
map.put(RECT_FUNCTION_EXIT, new RectExit());
return map;
}
}RectExtension.javapackage com.rect.ane;
import com.adobe.fre.FREContext;
import com.adobe.fre.FREExtension;
/**
* @author Rect
* @versionTime:2013-5-8
*/
public class RectExtension implements FREExtension {
@Override
public FREContext createContext(String arg0) {
// TODO Auto-generated method stub
return new RectContext();
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
@Override
public void initialize() {
// TODO Auto-generated method stub
}
}更多的详细代码 请看附件中的 Rectane_java 项目
在源码中会有更详细的表述2.AS 端项目
AS项目是作为和JAVA对应的。负责侦听返回 和调用JAVA函数
这里使用 FB 建立一个 手机库项目 然后在里面加两个类:
RectEvents.aspackage com.rect.ane
{
/**
*
* @author Rect2013-5-6
*
*/
public class RectEvents
{
public function RectEvents()
{
}
/**************************平台通知************************************/
/**
*init
*/
public static const DEMO_SDK_STATUS:String = "DemoInit";
/**
* 用户登录
*/
public static const DEMO_LOGIN_STATUS : String = "DemoLogin";
/**
* 用户注销
*/
public static const DEMO_LOGOUT_STATUS : String = "DemoExit";
/**
* 充值
*/
public static const DEMO_PAY_STATUS : String = "DemoPay";
}
}RectExtension.as
package com.rect.ane
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
/**
*
* @author Rect2013-5-6
*
*/
public class RectExtension extends EventDispatcher
{
public static const RECT_FUNCTION_INIT:String = "rect_function_init";//与java端中Map里的key一致
public static const RECT_FUNCTION_LOGIN:String = "rect_function_login";//与java端中Map里的key一致
public static const RECT_FUNCTION_PAY:String = "rect_function_pay";//与java端中Map里的key一致
public static const RECT_FUNCTION_EXIT:String = "rect_function_exit";//与java端中Map里的key一致
public static const EXTENSION_ID:String = "com.rect.ane";//与extension.xml中的id标签一致
private var extContext:ExtensionContext;
/**单例的实例*/
private static var _instance:RectExtension;
public function RectExtension(target:IEventDispatcher=null)
{
super(target);
if(extContext == null) {
extContext = ExtensionContext.createExtensionContext(EXTENSION_ID, "");
extContext.addEventListener(StatusEvent.STATUS, statusHandler);
}
}
//第二个为参数,会传入java代码中的FREExtension的createContext方法
/**
* 获取实例
* @return DLExtension 单例
*/
public static function getInstance():RectExtension
{
if(_instance == null)
_instance = new RectExtension();
return _instance;
}
/**
* 转抛事件
* @param event 事件
*/
private function statusHandler(event:StatusEvent):void
{
dispatchEvent(event);
}
/**
*init发送函数
* @param key 暂时传什么都可以留着可能要用
* @return
*
*/
public function FeiliuInit(key:int):String{
if(extContext ){
return extContext.call(RECT_FUNCTION_INIT,key) as String;
}
return "call login failed";
}
/**
*登录发送函数
* @param key 暂时传什么都可以留着可能要用
* @return
*
*/
public function FeiliuLogIn(key:int):String{
if(extContext ){
return extContext.call(RECT_FUNCTION_LOGIN,key) as String;
}
return "call login failed";
}
/**
*付费发送函数
* @param key 暂时传什么都可以 留着以后可能要用
* @return
*
*/
public function FeiliuPay(data:Vector.,len:int = 5):String{
if(extContext && data.length == len){
return extContext.call(RECT_FUNCTION_PAY,data)as String;
}
return "call pay failed";
}
/**
*退出SDK时候调用 这个函数只在退出游戏的时候调用
* @param key
* @return
*
*/
public function ExitSDKHandle(key:int):String{
if(extContext){
return extContext.call(RECT_FUNCTION_EXIT,key) as String;
}
return "call exit failed";
}
}
}仔细看这两个类就可以发现 在前面说的 接口ID 和 各个函数调用的 ID 都必须保持一致
3.extension.xml 文件-
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 略去 在后面的文章中会具体详细的说明过程。
第三步 打包处理多个JAR变成一个JAR提供给
一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码接入android 运营平台 一般平台方都会提供一个或者更多的jar包。但是我们的ane只能打包一个jar包。这就需要我们把这些jar全部集合到一个去。我的方法是基于一个兄台的教程的改进:http://bbs.9ria.com/thread-160445-1-1.html拿我接过的最繁琐的一个SDK来说,它要求我必须连入6个jar。如图
需要把 其余的jar 都合并到 flane.jar中。
我们改进上面那个兄台的bat如下:
@echo off
::转到当前盘符
%~d0
::打开当前目录
cd %~dp0
::你做的主JAR包的路径
set MainJar=flane.jar
::第三方JAR包的路径
set jar1=androidsdklibrary.jar
set jar2=gamehelper.jar
set jar3=oauth20.jar
set jar4=oauth20-api.jar
set jar5=sinaweibosso.jar
set jar6=tencent_openapi.jar
::第三方JAR包顶级包名称
set packageName1=com
set packageName2=org
set packageName3=android
echo =========== start combin ==============
::解压第三方包
jar -xf %jar1%
jar -xf %jar2%
jar -xf %jar3%
jar -xf %jar4%
jar -xf %jar5%
jar -xf %jar6%
::合并主JAR包
jar -uf %MainJar% %packageName1%
::如果还有别的顶级包可以接着合并,例如:
jar -uf %MainJar% %packageName2%
jar -uf %MainJar% %packageName3%
echo =========== over ==============
echo 再点一下就结束了--小Q
pause就可以了。这个方法适用于合并N个jar。
声明下 这并不是我的原创。感谢上面那位兄台的分享。
处理res资源之文字国际化
一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码文字国际化 其实是android编码的规范之一。但是不同的运营商提供的SDK 这块处理的都不一样。就目前我接触过的平台来说。当乐 UC 91 360 算做得很好。但是其他小平台特别是有一个运营商 他们客户端技术才一个人。都很不注重android文字国际化的问题。打包进ane的资源 布局文件XML必须是规范的国际化处理的。否则就会在打包APK的时候报错。
例如下面:
一般这个错误在打包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文件。
为了批量出来这种事情 我写了一个工具类
package
{
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flash.desktop.NativeDragManager;
import flash.display.InteractiveObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NativeDragEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
import flashButton.backButton;
/**
*
* @author Rect2013-4-16
*
*/
public class FileXMLHandle extends Sprite
{
private var sp:Sprite;
private var mapAry:Array = [];
private var test:TextField;
private var beginBtn:backButton;
private var SelectBtn:backButton;
private var outBtn:backButton;
private var textSize:TextField;
private var textQuality:TextField;
private var fileObj:FileReference = new FileReference();
private var selectedSaveFile:File;
private var fileArr:Array = [];
private var key:String;
public function FileXMLHandle()
{
super();
init();
}
private function init():void{
sp = new Sprite();
test = new TextField();
test.defaultTextFormat = (new TextFormat("",18,0xFFFFF));
test.text = "拖入此处";
test.width = 400;
test.height = 400;
test.wordWrap = true;
test.multiline = true;
sp.addChild(test);
sp.graphics.beginFill(0x3CB371);
sp.graphics.drawRect(0,0,800,600);
sp.graphics.endFill();
this.addChild(sp);
sp.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragInHandler);
sp.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDropHandler);
selectedSaveFile = new File();
selectedSaveFile.addEventListener(flash.events.Event.SELECT, onSelectedSaveFile);
var fileFilt:FileFilter = new FileFilter("输出文件(*.xml)", "*.xml");
fileArr.push(fileFilt);
beginBtn = new backButton("开始");
beginBtn.x = 600,beginBtn.y = 110;
beginBtn.mouseEnabled = false;
beginBtn.addEventListener(MouseEvent.CLICK,onChick);
this.addChild(beginBtn);
SelectBtn = new backButton("选择string.xml");
SelectBtn.x = 600,SelectBtn.y = 10;
SelectBtn.addEventListener(MouseEvent.CLICK,onSelectFile);
this.addChild(SelectBtn);
outBtn = new backButton("选择输出文件夹");
outBtn.x = 600,outBtn.y = 60;
outBtn.addEventListener(MouseEvent.CLICK,onSelectPath);
this.addChild(outBtn);
}
private function onSelectedSaveFile(e:flash.events.Event):void
{
var str:String = selectedSaveFile.nativePath;
var __reg:RegExp = new RegExp('\\\\', '');
switch(key)
{
case "file":
motherFileArys = str.replace(/\\/g,"/");
break;
case "path":
outPath = str.replace(/\\/g,"/");
trace(outPath)
break;
}
if(motherFileArys && outPath)HandleMaps();
}
private var motherFileArys:String = null;
private var outPath:String = null;
private var _loaderImages:URLLoader= new URLLoader();
private var stringXML:XML;
private var miscXML:XML;
private function HandleMaps():void{
_loaderImages.addEventListener(Event.COMPLETE,loaderInitListeners);
_loaderImages.load(new URLRequest(motherFileArys));
}
private function loaderInitListeners(evt:Event):void {
_loaderImages.removeEventListener(Event.COMPLETE,loaderInitListeners);
stringXML = XML(_loaderImages.data);
_loaderImages.close();
}
/**********************************/
private function onChick(ev:MouseEvent):void{
HandleMap();
}
private function onSelectFile(ev:MouseEvent):void{
key = "file";
selectedSaveFile.browse(fileArr);
}
private function onSelectPath(ev:MouseEvent):void{
key = "path";
selectedSaveFile.browseForDirectory("请选择目录");
}
private function textDown(ev:MouseEvent):void{
var text:TextField = ev.currentTarget as TextField;
text.text = "";
}
protected function onDragInHandler(event : NativeDragEvent) : void
{
var transferable :Clipboard = event.clipboard;
if(transferable.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
{
var files : Array = transferable.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
if(files)
{
var file : File = File(files);
if(file && file.name.indexOf(".xml") >= 0 || file && file.name.indexOf(".XML") >= 0)
{
NativeDragManager.acceptDragDrop(event.currentTarget as InteractiveObject);
}
}
}
}
private var motherFileAry:Array = [];
private var sonFileAry:Array = [];
private var xmlHead:String = '<?xml version="1.0" encoding="utf-8" ?>';
protected function onDropHandler(event : NativeDragEvent) : void
{
motherFileAry = [];
sonFileAry = [];
var transferable :Clipboard = event.clipboard;
var files : Array = transferable.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
if(files)
{
var file :File;
for(var t:int = 0;t<files.length;t++){
file= File(files);
test.appendText("\n"+file.url);
motherFileAry.push(file.url);
sonFileAry.push(outPath+"/"+ file.name);
}
if(sonFileAry.length != 0 ){
if(motherFileArys && outPath)this.beginBtn.mouseEnabled = true;
}else{
}
trace(motherFileAry,outPath);//(file.name.length - 4))));
}
}
private var _loaderImage:URLLoader= new URLLoader();
private function HandleMap():void{
if(motherFileAry.length){
_loaderImage.addEventListener(Event.COMPLETE,loaderInitListener);
_loaderImage.load(new URLRequest(motherFileAry.splice(0,1)));
}else
{
this.beginBtn.mouseEnabled = false;
test.text = ("完成!");
makeValue();
}
}
private function makeValue():void
{
for(var t:int = 0;t<stringArr.length;t++)
{
stringXML.appendChild(<string name = {stringName}>{stringArr}</string>);
}
var filePath:String = motherFileArys ;
var f:FileStream = new FileStream();
var fl:File = new File(filePath);
f.open(fl,FileMode.WRITE);
var xmlStr1:String = stringXML.toString();
var pattern1:RegExp = /\n/g;
xmlStr1=xmlStr1.replace(pattern1, "\r\n");
f.writeUTFBytes(String(xmlHead+"\r\n"+ xmlStr1));
f.close();
}
private function loaderInitListener(evt:Event):void {
_loaderImage.removeEventListener(Event.COMPLETE,loaderInitListener);
var xml:XML = XML(_loaderImage.data);
walk(xml);
makeFile(xml);
HandleMap();
}
private function makeFile(str:XML):void{
var filePath:String = (sonFileAry.splice(0,1)) ;
var f:FileStream = new FileStream();
var fl:File = new File(filePath);
f.open(fl,FileMode.WRITE);
var xmlStr:String = str.toString();
var pattern:RegExp = /\n/g;
xmlStr=xmlStr.replace(pattern, "\r\n");
f.writeUTFBytes(String(xmlHead+"\r\n"+ xmlStr));
f.close();
}
private var stringArr:Array = [];
private var stringName:Array = [];
private var stringLen:int = 0;
private function walk( node:XML ):void {
var androidNS:Namespace = new Namespace("android","http://schemas.android.com/apk/res/android");
node.addNamespace(androidNS);
// Loop over all of the child elements of the node
for each ( var element:XML in node.elements( ) ) {
// Output the label attribute
var attributes:XMLList = element.attributes( );
for(var t:int = 0;t<100;t++)
{
if(!attributes)
break;
var str:String = attributes ;
var pattern4:RegExp=/[\u4e00-\u9fa5]/;
if(pattern4.test(str))
{
stringArr.push(str);
stringName.push("DW_string_" + stringLen);
attributes = "@string/DW_string_" + stringLen.toString();
stringLen++;
}
}
walk( element );
}
}
}
}
Buttonpackage flashButton
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class backButton extends Sprite
{
private var _buttonName:String;
private var _szWide:Number;
private var _szHeight:Number;
private var _Color:int;
public function backButton(
buttonName:String,
szWide:Number = 180,
szHeight:Number = 40,
Color:int = 0xFFFFFF)
{
_buttonName = buttonName;
_szWide = szWide;
_szHeight = szHeight;
_Color = Color;
init();
}
private var _text:TextField;
private var _textFormat:TextFormat;
private function init():void{
_text = new TextField();
_text.mouseEnabled = false;
_text.text = _buttonName;
_text.x = 2;
_text.y = 2;
_text.width = _szWide-5;
_text.height = _szHeight-5;
_text.autoSize = TextFieldAutoSize.CENTER;
_textFormat = new TextFormat();
_textFormat.size = 25;
_textFormat.color = 0;//0x228b22;
_text.setTextFormat(_textFormat);
addChild(_text);
this.graphics.beginFill(_Color,.9);
this.graphics.drawRoundRect(0,0,_szWide,_szHeight,5,5);
this.graphics.endFill();
this.addEventListener(MouseEvent.ROLL_OVER,onMouseOver);
this.addEventListener(MouseEvent.ROLL_OUT,onMouseOut);
}
private function onMouseOut(ev:MouseEvent):void{
this.graphics.clear();
this.graphics.beginFill(_Color,.9);
this.graphics.drawRoundRect(0,0,_szWide,_szHeight,5,5);
this.graphics.endFill();
}
private function onMouseOver(ev:MouseEvent):void{
this.graphics.clear();
this.graphics.beginFill(0xeedd82,.9);
this.graphics.drawRoundRect(1,1,_szWide-1,_szHeight-1,5,5);
this.graphics.endFill();
}
}
}使用方式:
A.新建任意项目 运行上述类
B.拖动ANE的res下的layout下的XML到这个类中
C.点击按钮
D.正则中只转换中文,一般text中英文比较少 但是也有,一般都不多的。
遇到报错出来的是英文 就手动改就好了 或者你完善一下我的类。反正上面这个类 对于我已经够用了 很多平台的资源布局文件都是只改几个。
处理res资源之找不到资源ID
一点声明:我的水平有限 这里仅仅只能写出我认为可行的方案 和 代码打包ANE会经常遇到找不到资源R.XXX ID的问题,或者是找到的资源ID不对然后报NULL POINT错 直接程序崩溃。AIR与android原生取资源方式的区别:
ADOBE给出的取资源的方式是:_context.getResourceId("id.AlipayTitleItemName");但是原生JAVA取资源的方式是: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有什么特点:
默认是生成R.java处在 包名为APK的ID下。也就是air+(你程序的-app.xml的ID)。例如(air.xxx.xxx.R)
而我们的SDK取ID都是 R.id.AlipayTitleItemName 也就是说取的是SDK的同级包下的R.java。这就会导致取不到资源ID:
也就是说 导致取不到ID的原因之一 有可能是(之所以说有可能 是因为取不到资源ID还有其他各种的原因,起码我遇遇到过的是如此):真正的资源ID 所在的类与SDK资源ID的包名地址不同
现在知道问题所在了。那解决这个问题仔细一想就能知道如下几种方法:
A. 最简单的方式。说服运营商 把SDK的取资源的包改为我们AIR生成资源的包,但是这也是最异想天开的方法。永远别梦想着运营商会针对你一家修改它的SDK。虽然想法美好 但是却是痴心妄想。
B.在ANE源码中新建一个包例如上面举例的情况是 在src下新建 包 在这个包下新建类R.java。然后把运营商提供的JAVADEMO的gen文件夹下的R.java复制过去如图:
C.也是目前我发现最完美的解决方式:灵活使用context.getResourceId 取AIR生成的R.java下的ID 取填补 SDK下的R.java的资源ID。如图:
例如上图的例子中。加一个resHandle.java类。package com.xxx.ane;
import android.util.Log;
import com.adobe.fre.FREContext;
/**
* R.java ID处理
* @author Rect
* @versionTime:2013-5-14
*/
public class ResHandle {
public static void setResourctID(FREContext _context,String TAG)
{
if(wxd.view.R.array.address == _context.getResourceId("array.address") &&
wxd.view.R.anim.elseway == _context.getResourceId("anim.elseway"))
return;
Log.d(TAG, "---------anim-------"+_context.getResourceId("anim.elseway"));
wxd.view.R.anim.elseway = _context.getResourceId("anim.elseway");
wxd.view.R.anim.elseway_in = _context.getResourceId("anim.elseway_in");
wxd.view.R.anim.landscape_anim = _context.getResourceId("anim.landscape_anim");
wxd.view.R.anim.portrait_anim = _context.getResourceId("anim.portrait_anim");
wxd.view.R.anim.zoom_enter = _context.getResourceId("anim.zoom_enter");
wxd.view.R.anim.zoom_exit = _context.getResourceId("anim.zoom_exit");
wxd.view.R.anim.zoomin = _context.getResourceId("anim.zoomin");
wxd.view.R.anim.zoomout = _context.getResourceId("anim.zoomout");
}
}当然 在SDK包下的R.java要做一些处理 使得能访问全部静态变量 例如我的
wxd.view.R.java 如下:
package wxd.view;
publicclass R
{
public static class anim
{
public staticint elseway = 2130968576;
public staticint elseway_in = 2130968577;
public staticint landscape_anim = 2130968578;
public staticint portrait_anim = 2130968579;
public staticint zoom_enter = 2130968580;
public staticint zoom_exit = 2130968581;
public staticint zoomin = 2130968582;
public staticint zoomout = 2130968583;
}
}这样外部就能访问 各个变量了。
然后在 调用SDK打开SDK界面之前 调用这句对R.java进行处理:Log.d(TAG, "---------R.java处理-------");
ResHandle.setResourctID(_context, TAG);
Log.d(TAG, "---------Login开始-------");至此 由于取不到资源ID的问题 就得到了解决。当然再强调一次 取不到资源ID 也有可能是由于其他问题导致的。这仅仅是导致这个问题的原因之一。
命令行打包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
打包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.swfMiGameCenterSDKService.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 例如 。。。 这篇教程是我在ANE中自己新建一个activity来调用SDK的。你看看能不能帮到你。或者你可以把你的情况描述得更清晰点
http://www.shadowkong.com/archives/1062
https://github.com/platformanes/android360 必须的有。不过涉及官方SDK的文档 源码库 等我已经去掉了 只保留ANE源码保留打包命令但是ANE可以直接拿来用。 您好,想请教一个问题:我最近做MM android接入的时候,发现MM的接入jar包中有一部分资源在jar下(不在res ...
先打包APK 然后解压 再把资源放入这种方式很不稳定的。你失败了是因为签名问题吧;对于资源的处理有两种方式:
1.res文件下的布局文件 图片 等打包进去ANE
2.assets 文件夹下的 图片等复制到 AIR项目的src文件夹下 打进APK
更详细的请看:http://www.shadowkong.com/archives/1042
页:
[1]