Ожидающее намерение, созданное в собственном расширении, работает только из уведомлений, но не из службы
Я разрабатываю мобильное приложение 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>
отметьте на манифесте.