ActivityManager: не удается запустить службу - PullRequest
0 голосов
/ 27 октября 2018

С тех пор, как я пытался обновить мое Android-приложение из Eclipse до Android Studio (преобразование в простой проект), у меня возникали проблемы.Я думаю, что я сгладил большинство из них, но я не могу получать уведомления от GCM (что является важной частью приложения).

Вот сообщение, которое я с телефона Android получаю сообщение GCM(эти сообщения отправляются с сервера мультикастинга на несколько устройств Android):

    --------- beginning of main
2018-10-26 15:46:36.222 2921-2921/com.ddv.android.gcm.support.app V/GCMBroadcastReceiver: onReceive: com.google.android.c2dm.intent.RECEIVE
2018-10-26 15:46:36.222 2921-2921/com.ddv.android.gcm.support.app V/GCMBroadcastReceiver: GCM IntentService class: com.ddv.android.gcm.support.app.GCMIntentService
2018-10-26 15:46:36.225 2921-2921/com.ddv.android.gcm.support.app V/GCMBaseIntentService: Acquiring wakelock
2018-10-26 15:46:36.226 1686-3213/? W/ActivityManager: Unable to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10 pkg=com.ddv.android.gcm.support.app cmp=com.ddv.android.gcm.support.app/com.activate.gcm.GCMIntentService (has extras) } U=0: not found

Вот мой AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ddv.android.gcm.support.app"
    android:versionCode="1"
    android:versionName="1.0" >
...
<permission
        android:name="com.ddv.android.gcm.support.app.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.ddv.android.gcm.support.app.permission.C2D_MESSAGE" />

    <!-- This app has permission to register and receive data message. -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="com.ddv.android.gcm.support.app.permission.RECEIVE" />
...
    <application
        android:icon="@drawable/ddv_launcher"
        android:label="@string/app_name"
        android:theme="@style/PulseAppTheme" 
        android:allowBackup="false"
        android:name=".GlobalVariables" >
        <receiver
            android:name="com.google.android.gcm.GCMBroadcastReceiver"
            android:enabled="true"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>

                <!-- Receives the actual messages. -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <!-- Receives the registration id. -->
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="com.ddv.android.gcm.support.app" />
            </intent-filter>
        </receiver>

        <service android:name="com.ddv.android.gcm.support.app.GCMIntentService"
            android:enabled="true">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
...

Я также попытался изменить <service android:name="com.ddv.android.gcm.support.app.GCMIntentService" на <service android:name=".GCMIntentService" с теми же результатами.

По сути, я могу сказать, что устройство Android получает сообщение GCM.И он знает, что GXM IntentService - это мой пользовательский com.ddv.android.gcm.support.app.GCMIntentService, но он ничего с этим не делает ... И он на самом деле не выдает никакой ошибки.Сообщение, которое я вижу в logcat, выглядит как предупреждение, если что-нибудь.

Я убедился, что мой GCMIntentService находится в основном пакете приложения.И что у меня есть все правильные разрешения, поэтому я не уверен, что мне не хватает или почему я не могу заставить это работать.

При обновлении приложения мне пришлось изменить способ регистрации моего GCM.и возможно у меня все еще есть это неправильно.Но следующий код не выдает никаких ошибок, и когда я отправляю сообщение GCM с сервера, все мои устройства Android получают сообщение сверху (создается впечатление, что они зарегистрированы правильно и что-то получают от Google).

Регистрационный код GCM:

if (gcm == null) {
    gcm = GoogleCloudMessaging.getInstance(RegisterActivity.this);
}
regId = gcm.register(SENDER_ID);
Log.d(TAG, "########################################");
Log.d(TAG, "Current Device's Registration ID is: " + regId);
SharedPreferences prefs = RegisterActivity.this.getSharedPreferences("com.google.android.gcm", 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("regId", regId);
editor.apply();
Log.d(TAG, "prefs.getString(\"regId\", \"\"): " + prefs.getString("regId", ""));
Log.d(TAG, "GCMRegistrar.getRegistrationId: " + GCMRegistrar.getRegistrationId(RegisterActivity.this));
ServerUtilities.registerToGCMServer(RegisterActivity.this, regId);

Код ServerUtilities:

static boolean registerToGCMServer(final Context context, final String regId) {
    Log.i(TAG, "registering device on GCM Server (regId = " + regId + ")");
    String baseUrl = getHost(context) + context.getString(R.string.GCM_PATH) + context.getString(R.string.GCM_REGISTER_PATH);
    Map<String, String> params = new HashMap<String, String>();
    params.put("regId", regId);
    long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
    // Once GCM returns a registration id, we need to register it in the
    // demo server. As the server might be down, we will retry it a couple
    // times.
    for (int i = 1; i <= MAX_ATTEMPTS; i++) {
        Log.d(TAG, "Attempt #" + i + " to register");
        try {
            displayMessage(context, context.getString(R.string.server_registering, i, MAX_ATTEMPTS));
            post(baseUrl, params);
            GCMRegistrar.setRegisteredOnServer(context, true);
            String message = context.getString(R.string.server_registered);
            CommonUtilities.displayMessage(context, message);
            return true;
        } catch (IOException e) {
            // Here we are simplifying and retrying on any error; in a real
            // application, it should retry only on unrecoverable errors
            // (like HTTP error code 503).
            Log.e(TAG, "Failed to register on attempt " + i, e);
            if (i == MAX_ATTEMPTS) {
                break;
            }
            try {
                Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
                Thread.sleep(backoff);
            } catch (InterruptedException e1) {
                // Activity finished before we complete - exit.
                Log.d(TAG, "Thread interrupted: abort remaining retries!");
                Thread.currentThread().interrupt();
                return false;
            }
            // increase backoff exponentially
            backoff *= 2;
        }
    }
    String message = context.getString(R.string.server_register_error, MAX_ATTEMPTS);
    CommonUtilities.displayMessage(context, message);
    return false;
}

Да, правая последняя часть информации.Вот что я ожидаю от своего приложения: в моем GCMIntentService.java я ожидаю вызова перезаписанной функции onMessage(), которая отображает предупреждение на основе полученного сообщения GCM.

public class GCMIntentService extends GCMBaseIntentService {
...
    @Override
    public void onMessage(Context context, Intent intent) {
        Log.i(TAG, "Received message");
        String alertText = intent.getExtras().getString("text");
        String ackID = intent.getExtras().getString("id");
        String detailID = intent.getExtras().getString("detail_id");

        // Sets the class to be called when the notification is selected
        intent.setClass(context, AlertFragmentActivity.class);
        intent.putExtra("alert", (Parcelable) (new Alert(ackID, detailID, alertText)));

        // I think this only goes through if the app is already running?
        displayAlert(context, intent);
        // notifies user
        generateNotification(context, intent);
    }
}

Я знаю, что в конечном итоге мне придется обновиться до FCM, но это все равно должно работать в это время (до апреля 2019 года, когда GCM будет полностью удален).

1 Ответ

0 голосов
/ 29 октября 2018

Поскольку я обновил свою регистрацию с GMCRegistrar.register() до GoogleCloudMessaging.register(), мне пришлось обновить свой широковещательный приемник до WakefulBroadcastReceiver.

Эта ссылка дала мне код, необходимый длязахватить обновленные сообщения GCM.

Полные шаги, которые я предпринял:

  1. Создан новый пользовательский WakefulBoradcastReciever для получения новых сообщений GCM.

CGCMBroadcastReciever.java

package com.ddv.android.gcm.support.app;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;

public class CGCMBroadcastReceiver extends WakefulBroadcastReceiver {
    private static final String TAG = "CGCMBroadcastReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive");
        ComponentName comp = new ComponentName(context.getPackageName(),
                CGCMIntentService.class.getName());
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}
Создан новый IntentService для обработки нового намерения, созданного при получении сообщения GCM.

CGCMIntentService.java

package com.ddv.android.gcm.support.app;

import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager;
import android.util.Log;

import com.ddv.android.gcm.support.app.alerts.Alert;
import com.ddv.android.gcm.support.app.alerts.AlertFragmentActivity;
import com.google.android.gcm.GCMBroadcastReceiver;
import com.google.android.gms.gcm.GoogleCloudMessaging;

import static com.ddv.android.gcm.support.app.CommonUtilities.displayAlert;

public class CGCMIntentService  extends IntentService {

    private static final String TAG = "CGCMIntentService";

    private static final String DDV_GROUP_KEY = "ddv_group_key";

    // This "notifcationStyle" will hold all of the notifications that come in
    // It will only be cleared when a user goes to the Alert page
    public static NotificationCompat.InboxStyle notificationStyle = new NotificationCompat.InboxStyle();

    public CGCMIntentService() {
        super("GcmIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent");
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        String messageType = gcm.getMessageType(intent);

        Log.i(TAG, "Received message");
        String alertText = intent.getExtras().getString("text");
        String ackID = intent.getExtras().getString("id");
        String detailID = intent.getExtras().getString("detail_id");

        // Sets the class to be called when the notification is selected
        intent.setClass(CGCMIntentService.this, AlertFragmentActivity.class);
        intent.putExtra("alert", (Parcelable) (new Alert(ackID, detailID, alertText)));

        // I think this only goes through if the app is already running?
        displayAlert(CGCMIntentService.this, intent);
        // notifies user
        generateNotification(CGCMIntentService.this, intent);

        CGCMBroadcastReceiver.completeWakefulIntent(intent);
    }

    /**
     * Issues a notification to inform the user that server has sent a message.
     */
    @SuppressWarnings("deprecation")
    public static void generateNotification(Context context, Intent intent) {

        String message = "Alert from " + intent.getStringExtra("text");
        //String id = intent.getStringExtra("id");

        int icon = R.drawable.ic_stat_example;
        long when = System.currentTimeMillis();
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        // set intent so it does not start a new activity
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        // build notification
        NotificationCompat.Builder nBuilder = new NotificationCompat.Builder(context)
                .setContentTitle("DDV Alert")
                .setContentText(message)
                .setContentIntent(pendingIntent)
                .setSmallIcon(icon)
                .setWhen(when)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setGroup(DDV_GROUP_KEY)
                .setGroupSummary(true)
                .setStyle(notificationStyle.addLine(message))
                .setVibrate(new long[] { 0, 100, 200, 300, 400, 500, 400, 300, 200, 100, 0,
                        0, 100, 200, 300, 400, 500, 400, 300, 200, 100, 0,
                        0, 100, 200, 300, 400, 500, 400, 300, 200, 100, 0 })
                .setSound(Uri.parse("android.resource://com.ddv.android.gcm.support.app/raw/" + R.raw.annoying_alarm93))
                .setLights(0xff00ff00, 300, 1000)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setAutoCancel(true);

        String title = context.getString(R.string.app_name);

        //Set notification sound to max
        AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        audio.setStreamVolume(AudioManager.STREAM_NOTIFICATION, audio.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION), 0);

        //If user is not on a call send the notification
        //Else wait for the call to end to send the notification
        TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if(telephony.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
            // start notification
            notificationManager.notify(0, nBuilder.build());
        } else {
            // create a handler to listen for the call to end
            Handler handler = new Handler(context.getMainLooper());
            GCMIntentService activity = new GCMIntentService();
            handler.post(activity.new TelephonyRunnable(context, telephony, notificationManager, nBuilder.build()));
        }
    }
}
Обновите AndroidManifest.xml, чтобы использовать новые пользовательские службы.

AndroidManifest.xml

    <receiver
        android:name=".CGCMBroadcastReceiver"
        android:enabled="true"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>

            <!-- Receives the actual messages. -->
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <!-- Receives the registration id. -->
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

            <category android:name="com.ddv.android.gcm.support.app" />
        </intent-filter>
    </receiver>

    <!--
      Application-specific subclass of GCMBaseIntentService that will
      handle received messages.

      By default, it must be named .CGCMIntentService, unless the
      application uses a custom BroadcastReceiver that redefines its name.

    -->
    <service android:name=".CGCMIntentService"
        android:enabled="true">
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
...