【问题标题】:Android Wear app not installing through handsetAndroid Wear 应用程序未通过手机安装
【发布时间】:2014-07-28 21:19:23
【问题描述】:

我正在尝试使用here 找到的“使用 Android Studio 方法打包”通过 Android 手机安装可穿戴应用,但它不起作用。该 apk 从未安装到可穿戴设备上。这是 logcat 的输出:

07-28 15:11:54.107      766-820/? W/PackageManager﹕ Unknown permission com.google.android.wearable.READ_SETTINGS in package com.google.android.gms
07-28 15:11:54.117      766-820/? W/PackageManager﹕ Not granting permission com.google.android.gm.permission.AUTO_SEND to package com.google.android.wearable.app (protectionLevel=2 flags=0x88be44)
07-28 15:11:54.117      766-820/? W/PackageManager﹕ Not granting permission android.permission.MEDIA_CONTENT_CONTROL to package com.google.android.wearable.app (protectionLevel=18 flags=0x88be44)
07-28 15:11:55.047      632-632/? D/WearablePkgInstaller﹕ Got PackageUpdateReceiver message Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:my.package.name flg=0x4000010 cmp=com.google.android.wearable.app/com.google.android.clockwork.companion.packagemanager.PackageUpdateReceiver (has extras) }
07-28 15:11:55.177      632-632/? D/WearablePkgInstaller﹕ Got PackageUpdateReceiver message Intent { act=android.intent.action.PACKAGE_ADDED dat=package:my.package.name flg=0x4000010 cmp=com.google.android.wearable.app/com.google.android.clockwork.companion.packagemanager.PackageUpdateReceiver (has extras) }

作为旁注,我可以手动打包(也在上面的链接中描述),当我在手机上运行 apk 时,它会安装在可穿戴设备上。我正在使用 buildToolsVersion 20.0.0,我正在运行 Android Studio 0.8.2,并且在我的手机模块的 build.gradle 中有这条线:

wearApp project(':wearable')

我已经没有关于如何调试它的想法,日志似乎毫无用处。有什么想法吗?

编辑:将为手机和可穿戴模块发布 Manifest 和 build.gradle 的相关部分。

手机清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="my.package.name"
          android:installLocation="auto"
          android:versionCode="259"
          android:versionName="4.6.1">

    <!-- =========== -->
    <!-- PERMISSIONS -->
    <!-- =========== -->

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

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <!-- ====================== -->
    <!-- APPLICATION PROPERTIES -->
    <!-- ====================== -->

    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="19" />

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:xlargeScreens="true"/>

    <application
        android:name=".AppState"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.MyApp"
        android:hardwareAccelerated="true">


        <!-- ==================== -->
        <!-- GOOGLE PLAY SERVICES -->
        <!-- ==================== -->

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version"/>

        <!-- ================== -->
        <!-- Android Wear -->
        <!-- ================== -->

        <service
            android:name=".wear.DataLayerListenerService" >
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
            </intent-filter>
        </service>

        <!-- This is used for manual packaging I have this commented out when -->
        <!-- packaging with Android Studio-->
        <!--<meta-data android:name="com.google.android.wearable.beta.app"-->
                <!--android:resource="@xml/wearable_app_desc"/>-->

        ...The rest of the activities

    </application>

</manifest>

手机 build.gradle

buildscript {
    repositories {
        maven { url 'http://download.crashlytics.com/maven' }
    }

    dependencies {
    }
}

apply plugin: 'com.android.application'
apply plugin: 'crashlytics'
apply plugin: 'newrelic'

repositories {
    mavenCentral()
    maven { url 'http://download.crashlytics.com/maven' }
}

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile 'com.android.support:support-v4:20.0.+'
    compile 'com.android.support:appcompat-v7:20.0.+'
    compile project(':facebook')
    compile project(':mopub-sdk')
    compile project(':GooglePlay')
    compile 'com.newrelic.agent.android:android-agent:3.+'
    compile 'com.crashlytics.android:crashlytics:1.+'
    androidTestCompile fileTree(dir: 'tests/libs', include: '*.jar')
    wearApp project(':wearable')
}

android {
    compileSdkVersion 19
    buildToolsVersion '20.0.0'

    //Build type is debug to avoid conflict with Proguard
    testBuildType = "debug"

    defaultConfig {
        testApplicationId "my.package.name.test"
        testInstrumentationRunner "com.zutubi.android.junitreport.JUnitReportTestRunner"
    }

    lintOptions {
        // We do not want to abort the build due to lint errors
        abortOnError false
    }

    sourceSets {
        // Main is the default unless stated otherwise
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
        // Testing
        androidTest.setRoot('tests')
        androidTest {
            java.srcDirs = ['tests/src']
            res.srcDirs = ['tests/res']
        }
        // Cannot add beta icons in here because custom flavour source sets are created
        // during compilation and name duplication will result in a crash
    }

    signingConfigs {
        debug {
            storeFile file("**")
            storePassword "***"
            keyAlias "***"
            keyPassword "***"
        }
        release {
            storeFile file("***")
            storePassword "***"
            keyAlias "***"
            keyPassword "***"
        }
    }

    buildTypes {
        // Development configuration
        debug {
            debuggable true
            jniDebugBuild true
            signingConfig signingConfigs.debug
            runProguard false
        }

        // Release configuration
        release {
            debuggable false
            jniDebugBuild false
            signingConfig signingConfigs.release

              // COMMENTED PROGUARD OUT FOR NOW TO SEE IF IT WILL HELP
//            // Configure ProGuard
//            runProguard true
//            // General configuration
//            proguardFile 'proguard/proguard.cfg'
//            // Add all of our component-specific configurations (excluding the Android generic, as we want it to be last)
//            FileTree tree =  fileTree(dir: 'proguard', include: '*.txt', exclude: 'Android.txt')
//            tree.each {File file ->
//                proguardFile file.getCanonicalPath()
//            }
//            // Add a fallback configuration for all Android apps
//            proguardFile 'proguard/Android.txt'
        }

        // Release configuration, but debuggable and without ProGuard
        // Used for testing features like G+ and in-app billing where a release config is required
        staging {
            debuggable true
            jniDebugBuild true
            signingConfig signingConfigs.release
            runProguard false
        }
    }

    productFlavors {
        production {
            applicationId "my.package.name"
        }
        internalBeta {
            applicationId "my.internalbetapackage.name"
            // Beta icons
            sourceSets.internalBeta.res.srcDirs = ['res-beta/internal']
        }
        externalBeta {
            applicationId "my.externalbetapackage.name"
            // Beta icons
            sourceSets.externalBeta.res.srcDirs = ['res-beta/external']
        }
        testing{
            applicationId "my.package.name"
        }
    }

    // Without this, gradle will complain that duplicate files were added to the APK, see:
    // http://stackoverflow.com/questions/20673888/duplicate-files-copied-android-studio-0-4-0
    packagingOptions {
        exclude 'META-INF/LICENSE.txt' // twitter4j
        exclude 'META-INF/ASL2.0'      // jackson
        exclude 'META-INF/LICENSE'     // jackson
        exclude 'META-INF/NOTICE'      // jackson
    }

}

task makeTestApks {
    dependsOn "assembleProductionRelease"
    dependsOn "assembleProductionTest"
}

可穿戴清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="my.package.name">

    <uses-feature android:name="android.hardware.type.watch" android:required="false"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.DeviceDefault" >
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity
            android:name=".WearReaderActivity"
            android:label="MyApp" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <intent-filter>
                <action android:name="my.package.name.READ"/>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".wear.DataLayerListenerService" >
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
            </intent-filter>
        </service>
    </application>

</manifest>

可穿戴的 build.gradle

repositories {
    mavenCentral()
}

apply plugin: 'com.android.application'
apply plugin: 'newrelic'

android {
    compileSdkVersion 19
    buildToolsVersion '20.0.0'
    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 19
        versionCode 259
        versionName "4.6.1"
    }

    signingConfigs {
        debug {
            keyAlias '***'
            keyPassword '***'
            storeFile file('sameAsHandset/debug.keystore')
            storePassword '***'
        }
        release {
            storeFile file("sameAsHandset/android.keystore")
            storePassword "***"
            keyAlias "***"
            keyPassword "***"
        }
    }

    buildTypes {
        release {
            debuggable true
            jniDebugBuild false
            signingConfig signingConfigs.release
        }
    }

    productFlavors {
        production {
            applicationId "my.package.name"
        }
        internalBeta {
            applicationId "my.internalBetaPackage.name"
            // Beta icons
            sourceSets.internalBeta.res.srcDirs = ['res-beta/internal']
        }
        externalBeta {
            applicationId "my.externalBetaPackage.name"
            // Beta icons
            sourceSets.externalBeta.res.srcDirs = ['res-beta/external']
        }
        testing{
            applicationId "my.package.name"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':GooglePlay')
    compile 'com.newrelic.agent.android:android-agent:3.+'
}

【问题讨论】:

  • 您是否尝试过来自 SDK Manager 的 DataLayer 示例?它展示了如何为手机和穿戴设备构建和打包应用程序,您应该检查它以确保它可以正常工作。然后您可以比较 XML 文件,看看您的应用程序可能有什么不同。
  • @WaynePiekarski 是的,我有,它工作正常。我尝试将它们与我的进行比较,一切看起来都很好,但是我的应用程序比具有不同风格的应用程序更复杂,因此存在很多差异
  • 我不确定你是否可以使用 api 19 而不是 api 20 进行编译。磨损和移动应用程序应该具有相同的权限。
  • @GabrieleMariotti 示例 DataLayer 用 19 编译。无论如何用 20 进行了尝试,但仍然无法正常工作。我相信你的第二个建议是不正确的,可穿戴设备不能拥有手机没有的权限,但手机拥有可穿戴设备没有的权限就可以了。我的可穿戴设备没有任何权限。
  • @odiggity 关于第二点你是对的!我的错。

标签: android apk wear-os


【解决方案1】:

要具体回答@odiggity,在您手机应用的build.gradle 文件中,您应该提及wear 应用文件夹的确切名称。如果您在 Android Studio 中创建了项目,那么您的 build.gradle 应该如下所示:

                      wearApp project(':wear')

这可能是由于以下原因:

  • Wear & Mobile 应用的"permissions" 不同(Wear 应用权限应该是移动应用权限的子集)。

这在开发者网站上的任何地方都没有记录,但是我根据我的个人经验发现了这一点。我能想到此限制背后的原因是,Google 希望防止偷偷摸摸的开发人员利用用户及其隐私。

  • Package name 的 Wear 和 Mobile 应用不匹配。
  • Wear 和 Mobile 应用的 VersionNumberVersionName 不匹配
  • 移动和穿戴应用的Application ID(build.gradle 文件)不匹配。
  • 默认情况下,可穿戴包仅包含在 release 构建中
  • 两个应用程序必须是 signed 且具有相同的密钥
  • 从 Android Studio 3.0 开始,使用 run 创建的 apk 版本为 test only builds,不允许在佩戴时安装(returnCode -15,请参阅 this information
  • 资产压缩

如果您使用 Eclipse 进行开发,请确保关闭 “资产压缩”,否则 raw 文件夹中的“.apk”文件将获得 double compressedphone won't be able to recognize,如果移动应用是否是包装穿戴应用。

最佳解决方案:

使用 Android Studio。

  1. 创建 Android 项目
  2. 选择 Phone & Wear 项目
  3. 按照步骤创建项目
  4. 将 Wear 应用中使用的所有权限复制到移动应用的清单中,反之亦然

调试

当您在可穿戴设备上找不到穿戴应用程序时,您可以随时查看两个设备的日志以了解发生了什么。您可以过滤 WearablePkgInstaller 以查找与可穿戴包安装相关的所有日志记录。

从您设备上的 Wear OS 应用触发“高级设置”中的“重新同步应用”选项并检查日志。仅同步您的应用的可穿戴设备的另一种方法是重新安装您的应用。此时,可穿戴设备也会同步到您的包裹。

设备日志应该列出如下内容:

11-07 14:58:53.127 3330-8739/? I/WearablePkgInstaller: Setting DataItem to install wearable apps for com.spotify.music

使用 com.spotify.music 作为您的应用 ID。这只是 Spotify 的一个例子。

在手表上(通过蓝牙或 USB 进行调试),您可以找到使用相同过滤器显示问题或成功的日志记录:

11-07 15:00:02.533 1032-1048/? I/WearablePkgInstaller: Package com.spotify.music was installed.

大多数错误消息都是不言自明的。您可以在the source code of WearPackageInstallerService class 中找到这些错误的许多示例。然而有些只是一个returnCode。对于这些返回码,请检查 this PackageManager source code 中的值。

【讨论】:

  • +1 好友 - 对我来说这是权限,它们必须完全相同。
  • 谢谢。花了一个小时弄清楚发生了什么,这是因为我的 mobile/AndroidManifest.xml 缺少穿戴应用程序权限。
  • 我发现移动清单可能是穿戴权限的超集。但是,wear manifest 不需要拥有所有的移动权限。我想这是因为如果在移动安装期间没有显示,用户就无法知道他们接受了哪些佩戴权限
  • @KenWolf 不完全一样,但穿戴应用程序中的权限也必须在移动应用程序中定义。所以穿戴应用权限必须是移动应用权限的子集。移动应用程序本身可以拥有更多权限。此外,如果这是问题日志将清楚地说明阻止安装的权限。
【解决方案2】:

我对产品口味有意见。我对手持和穿戴应用程序都有所了解。为了解决我的问题,我在 build.gradle 手持应用程序文件中使用了下一个代码:

freeWearApp project(path: ':wear', configuration: 'wearfreeRelease')
fullWearApp project(path: ':wear', configuration: 'wearfullRelease')

其中,freefull 是来自手持模块的风格,wearfreewearfull 是来自wear mudule 的风格, Release 是磨损模块构建类型的名称。

并且不要忘记 publishNonDefault true 在wear gradle 的android 块中。

【讨论】:

  • 这对我有用,谢谢!更多关于此的文档可以在这里找到:tools.android.com/tech-docs/new-build-system/…
  • @PavelGP 您能否详细说明适合您的解决方案
  • 嗯,那是一年前的事了)但是我在上面的帖子中描述了我的案例。我在手持模块和穿戴模块中都有一些风格,以配置来自手持应用程序的风格必须与我使用上述解决方案的穿戴应用程序模块中的适当风格一起使用。仅此而已。据我所知,此代码位于手持应用程序的 build.gradle 的依赖项部分。
【解决方案3】:

即使在检查了包名称、应用程序 ID 并确保穿戴模块没有手持模块没有的任何权限之后,我还是遇到了这个问题。

我的问题是我在 AndroidManifest.xml 中为可穿戴模块请求权限android.permission.BIND_NOTIFICATION_LISTENER_SERVICE。看来这应该只为手持模块请求。

【讨论】:

    【解决方案4】:

    据我所知,目前不支持产品风味,请使用手动构建(资产或原始)或删除产品风味。

    【讨论】:

      【解决方案5】:

      根据我的经验,它仅在构建签名版本时才有效。在部署调试版本时,它从未为我安装磨损应用程序。通过签名版本,它可以与 buildToolsVersion "19.1.0" 一起使用。

      在这种特定情况下,我可以看到您正在使用“compile project(':GooglePlay')”。那是最新版本吗? (编译'com.google.android.gms:play-services-wearable:+')

      【讨论】:

      • 我在docs 中发现了这一点:注意:当您使用调试密钥为应用程序签名时,可穿戴应用程序的自动安装不起作用,并且仅适用于发布密钥.有关如何正确打包可穿戴应用的完整信息,请参阅打包可穿戴应用。
      【解决方案6】:

      只是想添加一些我在很多答案中都没有看到的东西。

      您必须在手持应用和穿戴应用中匹配applicationId

      我之前认为您需要在手持设备和手表清单中添加&lt;uses-feature android:name="android.hardware.type.watch" /&gt;,并添加:android:required="true" 用于可穿戴清单,android:required="false" 用于设备。

      这是错误的。您不需要手持清单中的上述内容。事实上,随着 Wear 2.0 的推出,如果 &lt;uses-feature android:name="android.hardware.type.watch" /&gt; 在您的手持应用程序中,Google 已经进行了一些更改,不允许您上传任何 minSdk 低于 23 的 apk。如有任何混淆,请见谅。

      【讨论】:

      • 所以你的意思是我需要 handheld 上的&lt;uses-feature android:name="android.hardware.type.watch" /&gt;?!
      • 是的!它需要在设备应用的 AndroidManifest.xml 文件中!
      • @JonasB 不,他错了。您不需要在手持设备清单中使用&lt;uses-feature android:name="android.hardware.type.watch" /&gt;,只需在可穿戴设备上即可。
      • @SuperThomasLab 我不确定你为什么这么认为。如果您的手持应用程序中有任何功能可以将内容发送到手表,您确实需要 我正在更新帖子以提供更多信息,但每次我已经尝试过......当我对项目进行发布构建时,除非我包含 uses-feature 命令,否则它不会捆绑穿戴应用程序。
      • @SuperThomasLab 是正确的。我对答案进行了更改。
      【解决方案7】:

      我最近一直有这个问题,最后通过下面的方法解决了;

      • 卸载以前的应用程序
      • 重启手机
      • 重启磨损
      • 断开并重新连接磨损
      • 在 Android Wear 应用程序中重新同步应用程序

      然后它突然出现在手表上。在此之前,我尝试了所有方法并确信构建无法正常工作

      【讨论】:

      • 我看不出这有什么帮助。如果您想从 Wear 应用程序触发安装,您可以重新安装应用程序或手动触发 Wear os 应用程序中所有应用程序的重新同步。无需重启,基于 linux 的系统重启几乎没有任何好处。如果在强制同步检查日志后仍然没有发生,看看是什么阻止了安装。
      【解决方案8】:

      遇到了同样的问题,原来是 newrelic 正在检测 Android Wear 应用程序。尝试评论该行: // 应用插件:'newrelic'

      我找不到告诉 newrelic 远离磨损应用程序的方法...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-19
        • 2019-08-03
        • 2015-07-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多