2

我正在尝试创建我的应用程序的自定义构建,我可以与我的生产应用程序并排安装。

我已经成功地让我的自定义暂存版本通过按照这个答案用 AAPT 重新打包清单来并排安装。我的问题是让 GCM 为这两个应用程序工作。

我打电话GCMRegistrar.register,但我似乎从未得到对应用程序的暂存版本的响应(GCM 通知仍然适用于应用程序的生产版本)。

我在我的 Google Play 开发者帐户中创建了一个新的 Draft 应用程序(我想测试应用内计费,因此需要在 Google Play 中将应用程序作为草稿托管),并在我的 Google API 控制台中为暂存应用程序创建了一个新项目.

我尝试使用的域如下所示:

  • 生产 = com.mydomain.myapp
  • 暂存 = com.mydomain.myapp.staging

我遇到的第一个问题是 AAPT 不会更改 GCM 权限,因此 GCM 会失败:

Application does not define permission com.mydomain.myapp.staging.permission.C2D_MESSAGE

所以我添加了一个自定义构建步骤来进一步更新我的 AndroidManifest.xml。以下是相关部分:

<manifest 
    ...
    package="com.mydomain.myapp">

    <permission
        android:name="com.mydomain.myapp.staging.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.mydomain.myapp.staging.permission.C2D_MESSAGE" />

    <permission
        android:name="com.mydomain.myapp.staging.MESSAGING_PERMISSION"
        android:label="Blah"
        android:protectionLevel="normal" >
    </permission>

    <receiver
        android:name="com.google.android.gcm.GCMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

            <category android:name="com.mydomain.myapp" />
        </intent-filter>
    </receiver>

<permission>标签似乎需要新的包名来避免我上面提到的错误 。

<manifest>标签需要保留原包名,否则应用崩溃。这是因为重新打包似乎是在安装时动态发生的(即,如果您在重新打包后反编译您的应用程序,您将看到此包名称未更改),因此您不应更改此包名称实例。

我不确定最终<receiver>名称应该是原始名称还是重新包装的名称,但我都尝试过,但似乎都不起作用。

这里是来自 Google API 控制台的 API 访问信息:

STAGING
Key for Android apps (with certificates)
API key: <key>
Android apps: <SHA1>;com.mydomain.myapp.staging

PRODUCTION
Key for Android apps (with certificates)
API key: <key>
Android apps: <SHA1>;com.mydomain

两者都有一个“浏览器应用程序的密钥(带有引用者)”。

我不确定是否真的需要“Android 应用程序密钥”,但GCM 文档很难弄清楚这一点。

有人有什么想法吗?我想知道“Android 应用程序的密钥”是否导致了问题,或者我的登台应用程序是生产应用程序的子域这一事实。

另一个复杂因素是从 Google API 控制台传播更改需要多长时间 - 有人知道我需要等待多长时间才能重新测试以确保更改到位?

4

2 回答 2

2

我不确定如何AAPT工作,但我知道应该如何为 GCM 定义清单。

下面标记的所有地方PACKAGE都应该包含相同的包名。

<manifest 
    ...
    package="PACKAGE">

<permission
    android:name="PACKAGE.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />

<uses-permission android:name="PACKAGE.permission.C2D_MESSAGE" />

<receiver
    android:name="com.google.android.gcm.GCMBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

        <category android:name="PACKAGE" />
    </intent-filter>
</receiver>

另一个问题可能是您的意图服务。如果您使用的是扩展的服务类,则会在应用程序的主包GCMBaseIntentServiceGCMBroadCastReceiver查找此类,因此在您的暂存版本中,它具有不同的包名称,它不会找到它。

这种情况下的解决方案是使用一个子类GCMBroadCastReceiver并覆盖指定服务类路径的方法。

在这里的回答与你的问题有关。

于 2013-06-28T19:11:49.123 回答
0

感谢@Eran 的回答!如果它可以帮助其他人,我在下面列出了我的工作配置(在运行我的自定义构建步骤之后)。

标签中的包名<manifest>仍需保留原包名,但所有其他实例都需要使用重新打包后的名称。

AndroidManifest.xml:

<manifest 
    ...
    package="ORIGINAL_PACKAGE_NAME">

    <permission
        android:name="NEW_PACKAGE_NAME.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="NEW_PACKAGE_NAME.permission.C2D_MESSAGE" />

    <permission
        android:name="NEW_PACKAGE_NAME.MESSAGING_PERMISSION"
        android:label="Blah"
        android:protectionLevel="normal" >
    </permission>

    <service android:name=".MyGCMIntentService" />

    <receiver
        android:name=".MyGCMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

            <category android:name="NEW_PACKAGE_NAME" />
        </intent-filter>
    </receiver>

自定义 GCMBroadcastReceiver:

public class MyGCMBroadcastReceiver extends GCMBroadcastReceiver {

    public MyGCMBroadcastReceiver() {
        super();
        Log.d("Creating MyGCMBroadcastReceiver");
    }

    @Override
    protected String getGCMIntentServiceClassName(Context context) {
        String className = MyGCMIntentService.class.getName();
        Log.i("getGCMIntentServiceClassName", className);
        return className;
    }
}
于 2013-06-28T23:57:25.533 回答