AIR中不使用ANE实现iOS消息推送

今天整理以前的源码时,发现一个用ANE实现iOS消息推送的半成品。隐约中记得AIR的某个更新版本在运行时中实现了iOS消息推送,于是找了一下资料。

原来,AIR 3.4已经把消息推送功能整合到AIR运行时中了。下面的内容摘自 发行说明 | Flash Player® 11.4、AIR® 3.4

iOS 推送通知
此功能将使用 APNS(苹果推送通知服务)和提供程序(将与 APN
进行通信的第三方服务器)生成通知。已引进新的软件包
flash.notifications。 推送通知的发送完全依赖 Apple
的推送通知服务,APNS 不保证推送通知的发送。 Apple
也建议每次启动应用程序时订阅推送通知。每次客户端应用程序订阅推送通知时,APNS
会向客户端应用程序提供代号 id,并且此代号 id
将发送给将发送远程通知的第三方服务器或提供程序。

下面有一些文章介绍,从实现到范例一应俱全:

ANE Toolkit增加重启自身功能

新功能

让Android应用能够重启自身。iOS就别想了。

本功能由 rect 编写。

文档

http://zrong.github.io/anetoolkit/doc/org/zengrong/ane/tool/RestartCont.html

用法说明

//先关闭当前app
NativeApplication.nativeApplication.exit();
//重启
ANEToolkit.restart.restartApp();

注意事项

需要在应用程序描述文件中加入如下Android标签:

<application android:enabled="true">
    <!-- 以下receiver和service为重启APP所用 -->
    <!-- 若希望修改indent name 可以修改JAVA源码 org.zengrong.ane.funs.restart.AppRestart 中的对应字符串 -->
    <receiver android:name="org.zengrong.ane.funs.restart.BootSystemReceiver" >
        <intent-filter>
            <action android:name="com.android.rect.restart.airApp" />
        </intent-filter>
    </receiver>
    <service android:name="org.zengrong.ane.funs.restart.NotificationService"></service>
    <!-- 重启APP需要内容完毕 -->
</application>

完整的应用程序描述文件可参考 范例项目的应用程序描述文件

范例项目中已经加入 新功能的调用范例

Non-PIE Binary in Adobe AIR

今天从韩国合作伙伴那里得知,几天前提审到AppStore的游戏得到了Apple的警告:

Dear developer,

We have discovered one or more issues with your recent delivery for “ì?€?£?ì ?ê?°”. Your delivery was successful, but you may wish to correct the following issues in your next delivery: Non-PIE Binary – The executable ‘AVOCIOS.app’ is not a Position Independent Executable. Please ensure that your build settings are configured to create PIE executables. If you would like to update your binary for this app, you can reject this binary from the Binary Details page in iTunes Connect. Note that rejecting your binary will remove your app from the review queue and the review process will start over from the beginning when you resubmit your binary.

Regards,
The App Store team

提审AppStore都N次了,这个Non-PIE Binary问题我从未碰到过。从Google搜到的消息,也都是从5月开始的。看来是Apple修改了政策。 继续阅读Non-PIE Binary in Adobe AIR

Ant编译的jar文件,ANE不识别

Ant编译的jar文件,ANE不识别

问题描述

Android的ANE打包需要jar文件。Eclipse可以提供jar文件的导出。

然而,当我使用Ant来自动化完成ANE打包流程的时候,jar文件出了问题。

如果使用Ant生成的jar文件来打包ANE。那么ANE在使用的时候,会发生 ExtensionContext 无法初始化的情况。

也就是说,在调用 ExtensionContext.createExtensionContext(EXTENSION_ID) 的时候,得到的永远是null。

问题分析

以下是构建jar的target: 继续阅读Ant编译的jar文件,ANE不识别

使用ANE整合Facebook

Integrating facebook use ANE

使用ANE整合Facebook

  • 2013-04-02更新: 加入Native Android App设置部分
  • 2013-05-17更新: 加入支持Android SDK3.0的项目介绍

如何在AIR编写的移动应用中整合Facebook?以下是我这两天的研究成果。

1. 如何阅读开发文档?

Facebook的开发文档很全,但对于一个时间紧、任务重、被Boss一天催10遍,急于实现整合的开发者来说,或许没有那么多时间去详细阅读所有文档。这里整理了一个顺序:

1.1 The Login Dialog

无论如何,登录是第一步。这篇文档不但介绍了关于登录的所有细节,也详细介绍了关于Permissions的用法。去吧:The Login Dialog

1.2 Dialogs Overview

各种SDK中,都提供了Dialogs的相关方法。那么Dialog是什么呢?这篇文档让你了解全部。去吧:Dialogs Overview 继续阅读使用ANE整合Facebook

ANE Toolkit增加电源管理功能

为了迎接GitHub解封,我怀着悸动无笔的心情为ANE Toolkit增加了新的功能。今天天气真好,晴空万里无云。

新功能

所有可用方法及其用法说明

举例说明

这是acquire方法的定义: 继续阅读ANE Toolkit增加电源管理功能

PowerManager使用注意事项

Android SDK中的PowerManager用来管理设备电源、重启、锁定休眠状态、唤醒等等操作。我已经把PowerManager的功能加入到了ANEToolkit中,这里记录一下开发过程中遇到的几个要注意的东东。

关于权限

PowerManager的所有功能,需要以下三类权限

  • android.permission.DEVICE_POWER isScreenOn需要这个权限
  • android.permission.WAKE_LOCK WakeLock类中的方法需要这个权限
  • android.permission.REBOOT reboot方法需要这个权限

PowerManager.WakeLock

使用acquire方法可以锁定自动休眠。

例如在下载大文件的时候,自动休眠会中断WIFI信号导致下载失败。为了避免这种情况,可以使用acquire方法来保持设备为不休眠状态。

acquire 有两种调用方式:

  • acquire(long timeout)
    超时后取消锁。中断休眠一段时间,时间到了就自动取消中断。应该多采用这种方式。
  • acquire()
    永久性锁。必须调用release才能解锁。

使用acquire锁定休眠有计数锁和计数锁两种机制,使用 setReferenceCounted (boolean value) 可以设置是否使用计数锁。默认使用的是计数锁。

这两种机制的区别在于,前者无论 acquire() 了多少次,只要通过一次 release() 即可解锁。而后者正真解锁是在 (--count == 0) 的时候,同样当 (count == 0) 的时候才会去申请加锁。

reboot

即使是你为应用加入了REBOOT权限,在调用reboot方法的时候,依然会遇到异常,告知你没有权限执行这个方法。

11-13 18:30:28.409: W/System.err(11290): java.lang.SecurityException: Neither user 10150 nor current process has android.permission.REBOOT.

这是因为 REBOOT 权限,只有系统程序才可以获得,用户程序无法获取这个权限。

参考资料

改变AIR for Android消息通知栏默认图标

改变AIR for Android消息通知栏默认图标

如果从愤怒的角度来说,这个勉强可以算作AIR的BUG,但我知道不是。估计这事儿也只有我能碰上。且听我细细道来……

show notification in Android

在Android中显示消息通知,是个很简单的事情,见下面的代码:

Intent __activityIntent = _context.getPackageManager().getLaunchIntentForPackage(_setting.getPackageName());
if(__activityIntent == null) throw new NullPointerException("无法获取到名称为【"+_setting.getPackageName()+"】的Intent!");
Notification __msg = new Notification(R.drawable.ic_launcher, $ticket, System.currentTimeMillis());
ApplicationInfo __info = _context.getApplicationInfo();
__activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent __intent = PendingIntent.getActivity(_context, getRequestCode(), __activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
__msg.ledARGB = $color;
__msg.ledOnMS = 300;
__msg.ledOffMS = 1000;
__msg.flags |= Notification.FLAG_SHOW_LIGHTS;
__msg.flags |= Notification.FLAG_AUTO_CANCEL;
__msg.defaults |= Notification.DEFAULT_SOUND;
__msg.setLatestEventInfo(_context, $title, $msg, __intent);
NotificationManager __nm = (NotificationManager) _context.getSystemService(Context.NOTIFICATION_SERVICE);
__nm.notify(0, __msg);

上面的代码基于Android 2.2,Android 3.0以后有更好的方法,google也不推荐使用这样的方法。但我们为了兼容旧设备,只能这么用。

将这段代码编译后打包成ANE,在AS中调用,在Android设备中调试运行,就可以弹出一个通知栏,显示的图标是AIR的程序配置文件中配置的图标。

想要知道如何打包ANE,可以参考Adobe的官方教程(中文)

但是本文讲的不是这么简单的东西,本文讲的是一个相当纠结的问题。

问题出现

这个方法在我的设备中一直运行得很好,直到有一天,当我要发布它的时候,出问题了。

显示在Notification bar区域的图标,变成了AIR的红色图标,而不是我的应用的图标了。就像下面这样:

AIR的默认图标

而我的应用的图标,原本是这样的:

正确的图标

这个问题让我百思不得其解,为什么在调试的时候正常,在正式的发布版之后就不正常了么?郁闷的寻找了一段时间之后,一个偶然的机会让我发现了该问题的原因。

问题原因

我们知道,AIR在打包成Android apk文件的时候,可以选择AIR运行时的处理方式。我们可以选择“共享AIR运行时(apk)”和“运行时绑定(apk-captive-runtime)”两种方式。

在调试的时候,Flash Builder会直接将apk打包成共享AIR运行时版本。而在发布的时候,我们一般都会选择运行时绑定。至于原因,你懂的。

而这两种运行时打包方式对于图标的处理方式是不一样的。我解压了同一个项目的“共享运行时”和“运行时绑定”apk文件,发现他们的res/drawable目录中的图像文件不同。在“共享运行时”的apk文件中,该目录只有一个alert形式的半透明图标,而“运行时绑定”的apk文件中,则多出了一个AIR的默认图标。

比较解压文件

看完这张图,出现AIR默认图标的原因已经找到了,下面是分析。

问题的分析

由于应用需要支持多种分辨率,Notification bar的图标并不是使用一个图标文件来指定的,而是使用一个编号。也就是上面代码中的 R.drawable.ic_launcher 。这是一个int类型的值。

在ANE的代码中指定的这个常量,其实和AIR项目并没有什么关系,ANE项目是没有界面的,所使用的资源与AIR项目的资源也完全不同。将ANE打包到AIR项目中之后,就会改用AIR项目的资源。

但为什么在ANE中指定的图标编号值,在AIR项目中依然有作用呢(仅限“共享AIR运行时”)?

为了弄清这个问题,我创建了一个原生的Android项目。我发现默认情况下,它使用的图标也指向 R.drawable.ic_launcher ,而且这个常量的值与ANE项目中的值完全相同,都是 0x7f020000

我可以这样认为,这是Android项目的默认程序图标常量值。既然是这样,那么AIR也会遵循这个常量值。因此,在ANE中指定的图标常量值正好和AIR中的图标常量值相同,这是个“正确的巧合”。

在“共享AIR运行时”的时候,因为apk的 res/drawable 目录中没有其他的系统图标,Notification 会自动去 res/drawable-hdpi;res/drawable-ldpi;res/drawable-mdpi 3个图标文件夹下寻找匹配的图标。这3个文件夹中保存的就是我们在AIR程序配置文件中指定的程序图标。

在“运行时绑定”的APK文件中,由于AIR添加了一个默认图标,Notification 显示的时候就直接中又直接调用了 res/drawable 中的这个图标,因此显示的就是默认图标了。

问题解决

有了上面的分析,我只要在指定Notification图标的时候,指定一个图标资源的对应常量值,就能够得到正确的图标了。但可惜的是,除了我自己要求AIR包含的文件外,我并不知道AIR在打包的时候将哪些图标文件放在了APK包中,也不知道它们的常量是什么。

在使用 Android SDK 开发的应用中,这些常量都在SDK自动生成的R类中,我很容易得到他们。但AIR并没有告诉我怎么得到这些资源。

看来我只能自己想办法。

我发现,Android SDK自动生成的 R.java 文件中的常量值是有规律的,比如:

  • drawable 资源都以 0x7f02 开头;
  • string 资源都以 0x7f04 开头;
  • id 资源都已0x7f07`开头;
  • 0000开始顺号排列。

如下所示:

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int ic_action_search=0x7f020000;
        public static final int ic_launcher=0x7f020001;
    }
    public static final class id {
        public static final int menu_settings=0x7f070002;
        public static final int textView1=0x7f070000;
        public static final int toggleButton1=0x7f070001;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int activity_main=0x7f060000;
    }
    public static final class string {
        public static final int app_name=0x7f040000;
        public static final int hello_world=0x7f040001;
        public static final int menu_settings=0x7f040002;
        public static final int title_activity_main=0x7f040003;
    }
    public static final class style {
        public static final int AppTheme=0x7f050000;
    }
}

我可以这样认为,0x7f020000 就是第一个图标文件的常量值,而第二个图标文件应该是 0x7f020001 ,第三个是 0x7f020002 ,第四个……唔,没有第四个,如果使用 0x7f020003 ,AIR会直接崩溃退出。

测试证明,我的猜想是正确的。至此问题解决。

感受

和Adobe打交道这么多年,已经被无数的BUG折磨得“百度不亲”。Flex 的 BUG 因为有源码,可以自己动手解决。而 Flash Player和AIR的BUG就只能想办法绕过 。现在做 AIR for mobile 开发也有一段时间了,碰到了不少棘手的调试问题 ,忍受了 ipa 那乌龟一般的编译速度和 iTunes 那烂到无敌的用户体验,最后在这个不是 BUG 的问题上纠结了2天时间,彻底无语了……

从Adobe的角度看,在自己的产品中保留一个自己的默认图标,好像也无可厚非。从我的角度看,既然选择用 AIR 技术,碰到这样的问题,只能怪我手贱。

AIR for mobile给我的感觉,就像是一个保险箱,在我往里面放东西的时候,非常顺手。但我要修理它的时候,却发现我没有工具、没有手册、也没有指导。

当然,个人能力有限,也许我对Android更加了解之后,这个问题根本就不是问题了。

有哪位Android专家能给点建议么?

在ANE中连接Socket服务器的注意事项

在ANE中连接Socket服务器的注意事项

前戏

也许你会奇怪,既然AS提供了Socket实现,为什么还要用ANE来实现Socket连接?

在ANE插件中启动AIR开发的Android应用 一文的最后,我提到了一个应用案例,我现在将这个案例明确的说明一下。

对于游戏开发者来说,我们希望能推送给用户一些消息。如果使用常规的手段,只能在用户打开游戏的时候,才能和服务器通信,收到这些消息。

如果用户几天不上线,那么可能会错过这些消息,导致游戏中的公告、奖励不能及时到达。

要解决这个问题,我们可以在Android系统中注册一个Service。这个Service长期保持与服务器的连接,或者隔段时间连接一次服务器,收到消息后马上推送给用户。

这种Service,使用AIR是无法实现的,必须用ANE来解决。因此,我们不可能使用AS的Socket来连接服务器,必须用Android SDK提供的Socket连接方法。

阻力

在JAVA中实现Socket客户端的方法很简单,这里提供一些简单(且不完整)的代码: 继续阅读在ANE中连接Socket服务器的注意事项

在ANE插件中启动AIR开发的Android应用

在ANE插件中启动AIR开发的Android应用

在Android原生应用开发中,启动一个应用非常容易:

Intent __intent = new Intent(this, YourAppActivity.class);
startActivity(__intent); 

但在ANE插件开发中,要启动AIR开发的Android应用,就不那么容易了。

因为我并不知道AIR应用的Activicy类名是什么,无法设置Indent。

ANE包含在AIR应用中,我或许可以在ANE中得到AIR应用的Activity类名,但我尝试了下面的方法,不顶用: 继续阅读在ANE插件中启动AIR开发的Android应用