Ожидающее намерение, созданное в собственном расширении, работает только из уведомлений, но не из службы

Я разрабатываю мобильное приложение Air для Android, которое использует Air Native Extensions (ANE). Из ANE я вызываю функцию, которая создает Pending Intent, который передается в Notification. Затем нативная функция запускает службу и передает ее ожидающему намерению и уведомлению. Вот код для этой части:

public class StartServiceFunction implements FREFunction {

    @Override
    public FREObject call(FREContext context, FREObject[] args) {
        Log.d("MyApplicationStartServiceFunction", "call(StartService)");

        Context applicationContext = context.getActivity().getApplicationContext();

        try {
                String appPackageName = args[0].getAsString();
                String applicationID = args[1].getAsString();
                String notificationMessage = args[2].getAsString();
                String statusServiceURL = args[3].getAsString();
                String triggerResponse = args[4].getAsString();

                Intent appLaunchIntent = applicationContext.getPackageManager().getLaunchIntentForPackage(appPackageName);      
                PendingIntent pendingLaunchIntent = PendingIntent.getActivity(applicationContext, 0, appLaunchIntent, 0);

                int icon = context.getResourceId("drawable.notification_icon");

                Notification serviceNotification = new Notification.Builder(applicationContext)
                                                            .setContentTitle(applicationID)
                                                            .setContentText(notificationMessage)
                                                            .setSmallIcon(icon)
                                                            .setContentIntent(pendingLaunchIntent)
                                                            .build();
                Log.d("MyApplicationStartServiceFunction", "call(StartService): notification Created");

                Intent serviceIntent = new Intent(applicationContext, MyApplicationService.class);
                serviceIntent.putExtra("serviceNotification", serviceNotification);
                serviceIntent.putExtra("statusServiceURL", statusServiceURL);
                serviceIntent.putExtra("triggerResponse", triggerResponse);
                serviceIntent.putExtra("pendingLaunchIntent", pendingLaunchIntent);

                applicationContext.startService(serviceIntent);  
                Log.d("MyApplicationStartServiceFunction", "call(StartService): startService issued");

                return FREObject.newObject(true);
        } catch (Exception e) {
            Log.d("MyApplicationStartServiceFunction", "Exception: " + e.getMessage());
            e.printStackTrace();
        }

        try {
            return FREObject.newObject(false);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

Эта часть кода работает как шарм.

Далее, когда onStartCommand() метод вызывается внутри службы, я получаю Pending Intent и Notification от Intent передан в качестве аргумента упомянутому методу. Смысл получения уведомления состоит в том, чтобы установить службу как приоритетный процесс (startForeground()), что он делает, а также позволяет пользователю повторно войти в приложение, когда он нажимает на уведомление. Смысл прохождения ожидающего намерения заключается в том, что при выполнении определенных условий служба может активировать его (Pending Intent) и сделайте приложение основным видимым процессом. Вот код, который я использую для этого (имейте в виду, что его часть опущена, так как это не проблема, и работает нормально):

public class MyApplicationService extends Service implements TriggeredResponseListener {

        private Timer timer;
        private PendingIntent pendingLaunchIntent;
        private String statusServiceURL;
        private String triggerResponse;

        @Override
        public IBinder onBind(Intent arg0) {
                Log.d("MyApplicationService", "onBind()");
                return null;
        }

        @Override
        public void onCreate() {
                Log.d("MyApplicationService", "onCreate()");
                super.onCreate();
        }

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
                Log.d("MyApplicationService", "onStartCommand(): " + intent.toString());
                Notification serviceNotification = intent.getParcelableExtra("serviceNotification");            
                startForeground(1,serviceNotification);
                Log.d("MyApplicationService", "onStartCommand(): startForegroundService");

                pendingLaunchIntent = intent.getParcelableExtra("pendingLaunchIntent");
                statusServiceURL = intent.getStringExtra("statusServiceURL");
                triggerResponse = intent.getStringExtra("triggerResponse");
                Log.d("MyApplicationService", "onStartCommand(): getParcelableExtras");

                if(timer == null)
                {
                        timer = new Timer("Printer");
                        HttpServiceTask serviceTask = new HttpServiceTask(statusServiceURL, triggerResponse);
                        serviceTask.addEventListener(this);
                        timer.schedule(serviceTask, 0, 2000);
                        Log.d("MyApplicationService", "onStartCommand(): startTimer");
                }

                return START_REDELIVER_INTENT;
        }

        public void handleTriggeredResponseEvent(TriggeredResponseEvent eventObject) {
                Log.d("MyApplicationService", "handleEvent()");
                destroyTimer();
                HttpServiceTask serviceTask = eventObject.httpServiceTask;
                serviceTask.removeEventListener(this);
                Log.d("MyApplicationService", "handleEvent(): launching pendingLaunchIntent");
                try{
                        pendingLaunchIntent.send();
                } catch (Exception e) {
                        Log.d("MyApplicationService", "handleEvent(): " + e.getMessage());
                        e.printStackTrace();
                }                               
                this.stopSelf();
                Log.d("MyApplicationService", "handleEvent(): stopSelf");
        }

        private void destroyTimer() {
                if (timer != null){
                        timer.cancel();
                        timer = null;
                }
        }

        @Override
        public void onDestroy() {
                Log.d("MyApplicationService", "onDestroy():");
                destroyTimer();
                Log.d("MyApplicationService", "onDestroy(): destroyTimer():");
                super.onDestroy();
        }
}

Этот код ПОЧТИ работает отлично. Это прекрасно работает в большинстве ситуаций, но не в каждой из них.

Метод, включающий действия, связанные с Pending Intent является handleTriggeredResponseEvent() так что давайте сосредоточимся на этом. Как вы можете видеть, когда вызывается этот обработчик, Pending Intent отправлено. Помните, что он должен делать то же самое, что и Notification так как это было сделано из того же Pending Intent,

Если мобильное приложение Air не было закрыто из списка недавних приложений (удерживайте нажатой клавишу Home кнопка), то Pending Intent будет успешно активирован из handleTriggeredResponseEvent() и приложение Air будет основным приложением, видимым пользователю еще раз.

Проблема возникает, когда вы закрываете мобильное приложение Air из списка недавних приложений. Когда условия соблюдены и метод handleTriggeredResponseEvent() вызывается, все работает правильно, даже pendingLaunchIntent.send() работает без каких-либо исключений. Разница лишь в том, что приложение не запускается. Я вижу только черный экран (Air Runtime запущен?), Но не запущено мобильное приложение Air.

С другой стороны, когда я закрыл приложение Air из списка недавних приложений и активировал сервисное уведомление на панели уведомлений, приложение запускается правильно, даже если оно использует тот же Pending Intent как handleTriggeredResponseEvent() метод.

Я проверил все операторы Log, и код работает без ошибок и одинаково во всех сценариях.

Как это может быть? Не должен Pending Intent отправлено из handleTriggeredResponseEvent() метод имеет тот же эффект, что и тот, который отправляется при активации уведомления об услуге?

Я явно что-то упускаю. Может ли это быть что-то в XML-манифесте приложения? Далее следует объявление службы в манифесте:

<application android:debuggable="true">
              <service android:enabled="true" android:exported="true" android:name="com.mycompany.myextension.services.MyService">    
                        <intent-filter>
                                 <action android:name="air.com.mycompany.myextension.DO_CUSTOM_ACTION"/>
                        </intent-filter>
                </service>
</application>

Любая помощь будет высоко оценена. Заранее спасибо.

РЕДАКТИРОВАТЬ: ошибки на LogCat

Я мог зафиксировать ошибки LogCat, и вот они:

01-22 15:21:27.625: I/ActivityManager(381): Killing 25792:air.QOE/u0a122: remove task 
01-22 15:21:27.664: W/ActivityManager(381): Exception when starting activity air.QOE/.AppEntry 
01-22 15:21:27.664: W/ActivityManager(381): android.os.DeadObjectException 
01-22 15:21:27.664: W/ActivityManager(381): at android.os.BinderProxy.transact(Native Method) 
01-22 15:21:27.664: W/ActivityManager(381): at android.app.ApplicationThreadProxy.scheduleLaunchActivity(ApplicationThreadNative.java:743) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.realStartActivityLocked(ActivityStack.java:821) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.startSpecificActivityLocked(ActivityStack.java:949) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:2321) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1862) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1837) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1411) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1356) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.activityPaused(ActivityStack.java:1266) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:4609) 
01-22 15:21:27.664: W/ActivityManager(381): at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:401) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:1729) 
01-22 15:21:27.664: W/ActivityManager(381): at android.os.Binder.execTransact(Binder.java:367) 
01-22 15:21:27.664: W/ActivityManager(381): at dalvik.system.NativeStart.run(Native Method) 
01-22 15:21:27.726: I/ActivityManager(381): Process air.QOE (pid 25792) has died and restarted (pid 25900).

На начальном этапе казалось, что DeadObjectException сообщал мне, что не было никаких действий для обработки намерения, но позже я добавил следующий код:

PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
Log.d("EasyControlService", "launchApplication(): Activities ready for intent: " + activities.size());
if (activities.size() == 0){
    Log.d("EasyControlService", "launchApplication(): there are no activities ready for intent");
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

Это означает, что активность все еще присутствует.

1 ответ

Решение

Себастьян, из того, что я вижу, это, вероятно, происходит, потому что вы запускаете сервис в одном и том же процессе. Вам даже не нужно намерение Pending для запуска приложения, вы можете просто сделать это с обычным намерением. Единственное, что вам нужно сделать - это запустить службу в отдельном процессе. Вы делаете это, добавляя android:process=”:name_of_the_process” на <service> отметьте на манифесте.