问题
I have referred from this link and tried this code So far.But I don't know to implement this code in gluon-mobile.What I intent to do is that I want to start an Indent that does some Job in background at a specific time.My problem is that I don't how gluon-mobile behaves.So help me complete it.
package com.application;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import javafxports.android.FXActivity;
public class Schedular {
AlarmManager manager=null;
FXActivity activity=null;
public Schedular()
{
manager=(AlarmManager) FXActivity.getInstance().getSystemService(Context.ALARM_SERVICE);
activity=FXActivity.getInstance();
}
public void schedule()
{
Intent indent = new Intent(activity,Alarm.class);
PendingIntent pIntent = PendingIntent.getBroadcast(activity, 0, indent, 0);
manager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime()+2*1000,indent); //This is Where I went Wrong
}
}
I want to schedule a task at specific timing given by the user at the Run-time,That's what the function public void schedule(Calendar calendar,boolean flag) does.
Alarm.class
public class Alarm extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
// Your code to execute when the alarm triggers
// and the broadcast is received.
new bluetooth.turnOn();
}
And I have added Alarm.class to AndroidManifest.xml as
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.application" android:versionCode="1" android:versionName="1.0">
<supports-screens android:xlargeScreens="true"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="26"/>
<application android:label="Wifischeduler" android:name="android.support.multidex.MultiDexApplication" android:icon="@mipmap/ic_launcher">
<activity android:name="javafxports.android.FXActivity" android:label="Wifischeduler" android:configChanges="orientation|screenSize">
<meta-data android:name="main.class" android:value="com.application.Main"/>
<meta-data android:name="debug.port" android:value="0"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.gluonhq.impl.charm.down.plugins.android.NotificationActivity"
android:parentActivityName="javafxports.android.FXActivity">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="javafxports.android.FXActivity"/>
</activity>
<activity android:name="com.application.AndroidPlatform$PermissionRequestActivity" />
<receiver android:name="com.application.Alarm" /> // this is where I have included the Alarm.class
<receiver android:name="com.gluonhq.impl.charm.down.plugins.android.AlarmReceiver" />
</application>
When the schedule method is called the app exits...
I have run adb logcat -v threadtime and got this output on cmd
--------- beginning of crash
07-22 09:21:44.878 13516 13516 E AndroidRuntime: FATAL EXCEPTION: main
07-22 09:21:44.878 13516 13516 E AndroidRuntime: Process: com.application, PID: 13516
07-22 09:21:44.878 13516 13516 E AndroidRuntime: java.lang.RuntimeException: Unable to instantiate receiver com.application.Alarm: java.lang.IllegalAccessException: java.lang.Class<com.application.Alarm> is not accessible from java.lang.Class<android.app.ActivityThread>
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:2739)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.app.ActivityThread.access$1900(ActivityThread.java:153)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1452)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.os.Looper.loop(Looper.java:154)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5529)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: Caused by: java.lang.IllegalAccessException: java.lang.Class<com.application.Alarm> is not accessible from java.lang.Class<android.app.ActivityThread>
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at java.lang.Class.newInstance(Native Method)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:2734)
07-22 09:21:44.878 13516 13516 E AndroidRuntime: ... 8 more
07-22 09:21:44.885 1804 3742 D StatusBar: onNotificationPosted: Key: 0|com.application|123456|charm://down/Id/abcd12340|10276 GroupKey: 0|com.application|123456|charm://down/Id/abcd12340|10276 Connected: true
07-22 09:21:44.885 1804 1804 D StatusBar: GroupChild: false GroupsContainsKey: false IsUpdate: false IsGroupSummary: false hasIcon: true
07-22 09:21:44.886 1804 1804 D PhoneStatusBar: addNotification pkg=com.application;basepkg=com.application;id=123456
07-22 09:21:44.888 3047 10359 I GCoreUlr: Successfully inserted 1 locations
07-22 09:21:44.922 1804 1804 W ProgressBarDelegate: Unknown Drawable subclass, src=android.graphics.drawable.ScaleDrawable@eb9196
07-22 09:21:44.922 1804 1804 W ProgressBarDelegate: Unknown Drawable subclass, src=android.graphics.drawable.ScaleDrawable@aace117
07-22 09:21:44.932 3451 3518 I octvm_klo: klo lock
I have no idea why I can't Instantiate a Receiver (Alarm.class),
@FXML void start()
{
try{
Schedule schedule =(Schedule) Class.forName("com.application.Schedular").newInstance();
schedule.schedule();
}
回答1:
Your code didn't exactly work for me: I had to change the intent to the pIntent in schedule(), and I changed Schedule to Class on the Java implementation (how do you import it anyway?). Other than that, it was running fine for me (using Android target 25), and I didn't have any exception.
I'll post the initial solution (based on your approach), and after that a better way to do it.
Initial solution
While the preferred way to add platform specific code is following Charm Down design pattern (see https://bitbucket.org/gluon-oss/charm-down), a very quick and dirty way of getting the Android code working is this:
Android
Scheduler.java
package com.gluonhq.scheduler.android;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import javafxports.android.FXActivity;
public class Scheduler {
private final AlarmManager manager;
public Scheduler() {
manager = (AlarmManager) FXActivity.getInstance().getSystemService(Context.ALARM_SERVICE);
Intent indent = new Intent(FXActivity.getInstance(), AlarmReceiver.class);
PendingIntent pIntent = PendingIntent.getBroadcast(FXActivity.getInstance(), 0, indent, 0);
manager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 2000, pIntent);
}
}
AlarmReceiver.java
package com.gluonhq.scheduler.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Alarm ON", Toast.LENGTH_LONG).show();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.gluonhq.scheduler" android:versionCode="1" android:versionName="1.0">
<supports-screens android:xlargeScreens="true"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="25"/>
<application android:label="GluonScheduler" android:name="android.support.multidex.MultiDexApplication" android:icon="@mipmap/ic_launcher">
<activity android:name="javafxports.android.FXActivity" android:label="GluonScheduler" android:configChanges="orientation|screenSize">
<meta-data android:name="main.class" android:value="com.gluonhq.scheduler.GluonScheduler"/>
<meta-data android:name="debug.port" android:value="0"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver android:name="com.gluonhq.scheduler.android.AlarmReceiver" />
</application>
</manifest
Main/Java
This is a Basic View from a Single view template project using the Gluon IDE plugin:
GluonScheduler.java
package com.gluonhq.scheduler;
import com.gluonhq.charm.glisten.application.MobileApplication;
import com.gluonhq.charm.glisten.visual.Swatch;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
public class GluonScheduler extends MobileApplication {
public static final String BASIC_VIEW = HOME_VIEW;
@Override
public void init() {
addViewFactory(BASIC_VIEW, () -> new BasicView(BASIC_VIEW));
}
@Override
public void postInit(Scene scene) {
Swatch.BLUE.assignTo(scene);
((Stage) scene.getWindow()).getIcons().add(new Image(GluonScheduler.class.getResourceAsStream("/icon.png")));
}
}
BasicView.java
package com.gluonhq.scheduler;
import com.gluonhq.charm.glisten.control.AppBar;
import com.gluonhq.charm.glisten.control.Icon;
import com.gluonhq.charm.glisten.mvc.View;
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
public class BasicView extends View {
public BasicView(String name) {
super(name);
Button button = new Button("Schedule task");
button.setGraphic(new Icon(MaterialDesignIcon.ALARM_ON));
button.setOnAction(e -> {
try {
Class c = Class.forName("com.gluonhq.scheduler.android.Scheduler");
c.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(BasicView.class.getName()).log(Level.SEVERE, null, ex);
}
});
VBox controls = new VBox(button);
controls.setAlignment(Pos.CENTER);
setCenter(controls);
}
@Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Basic View");
}
}
build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.3.6'
}
}
apply plugin: 'org.javafxports.jfxmobile'
repositories {
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
mainClassName = 'com.gluonhq.scheduler.GluonScheduler'
dependencies {
compile 'com.gluonhq:charm:4.3.5'
}
jfxmobile {
downConfig {
version = '3.3.0'
// Do not edit the line below. Use Gluon Mobile Settings in your project context menu instead
plugins 'display', 'lifecycle', 'statusbar', 'storage'
}
android {
manifest = 'src/android/AndroidManifest.xml'
}
ios {
infoPList = file('src/ios/Default-Info.plist')
forceLinkClasses = [
'com.gluonhq.**.*',
'javax.annotations.**.*',
'javax.inject.**.*',
'javax.json.**.*',
'org.glassfish.json.**.*'
]
}
}
Deploy to your Android device (./gradlew androidInstall) and run. Press the button and two seconds later you will see the toast:
Preferred solution
AlarmService
While the above code works, it is not very convenient to use Class.forName.
Following the Charm Down services (LocalNotification for instance), I'd do it this way instead (you have to keep the same package names):
Java
AlarmService.java
package com.gluonhq.charm.down.plugins;
public interface AlarmService {
public void schedule();
}
AlarmServiceFactory.java
package com.gluonhq.charm.down.plugins;
import com.gluonhq.charm.down.DefaultServiceFactory;
public class AlarmServiceFactory extends DefaultServiceFactory<AlarmService> {
public AlarmServiceFactory() {
super(AlarmService.class);
}
}
Android
AndroidAlarmService.java
package com.gluonhq.charm.down.plugins.android;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import com.gluonhq.charm.down.plugins.AlarmService;
import com.gluonhq.scheduler.android.AlarmReceiver;
import javafxports.android.FXActivity;
public class AndroidAlarmService implements AlarmService {
private final AlarmManager manager;
private final FXActivity activity;
public AndroidAlarmService() {
activity = FXActivity.getInstance();
manager = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
}
@Override
public void schedule() {
Intent indent = new Intent(activity, AlarmReceiver.class);
PendingIntent pIntent = PendingIntent.getBroadcast(activity, 0, indent, 0);
manager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 2000, pIntent);
}
}
(keeping the same AlarmReceiver class as above).
Finally, on your view:
BasicView.java
button.setOnAction(e ->
Services.get(AlarmService.class).ifPresent(AlarmService::schedule));
Now you can deploy and run again, and the result will be the same. But obviously this preferred way has many more advantages.
- Using the service you can also run on desktop or deploy to iOS: the app will work (when clicking the button it won't do anything).
- You can implement solutions for desktop and iOS if you need to.
- You can easily add more functionality through the interface.
回答2:
Replace the indent
by
manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 10L, pIntent);
Also to the constructor of indent, pass desired "activity" or "service"
Intent intent = new Intent(context, AlarmReceiver.class); //AlarmReceiver is an activity that will be launched after an hour
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
60 * 1000, pIntent); //60mins from now
来源:https://stackoverflow.com/questions/45197207/schedule-a-task-in-android-using-alarm-service-service-in-gluon-mobile