Skip to content

Background job with WorkManager #1488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GrEg00z opened this issue Sep 24, 2019 · 8 comments
Closed

Background job with WorkManager #1488

GrEg00z opened this issue Sep 24, 2019 · 8 comments
Assignees
Milestone

Comments

@GrEg00z
Copy link

GrEg00z commented Sep 24, 2019

Environment
Provide version numbers for the following components (information can be retrieved by running tns info in your project folder or by inspecting the package.json of the project):

  • CLI: 6.1.0
  • Cross-platform modules: 6.1.1
  • Android Runtime: 6.1.0
  • nativescript-angular : 8.2.1

Gradle :

  • androidx.work:work-runtime:2.2.0
  • androidx.lifecycle:lifecycle-runtime:2.1.0

Describe the bug
I'm trying to implement WorkManager inside my nativescript app, to be able to schedule a background job (doc : https://developer.android.com/topic/libraries/architecture/workmanager/basics) .
The compilation and starting app is OK, but when Im trying to enqueue my job, it's not working because the Android Nativescript runtime is unable to initialize itself.

From Android Studio, I get this error :

E/WM-WorkerFactory: Could not instantiate com.tns.jobs.MyWorker
	 java.lang.reflect.InvocationTargetException
		 at java.lang.reflect.Constructor.newInstance0(Native Method)
		 at java.lang.reflect.Constructor.newInstance(Constructor.java:334)
		 at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:97)
		 at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:228)
		 at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127)
		 at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
		 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
		 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
		 at java.lang.Thread.run(Thread.java:764)
	  Caused by: java.lang.NullPointerException: Attempt to read from field 'int com.tns.Runtime.currentObjectId' on a null object reference
		 at com.tns.Runtime.initInstance(Runtime.java:745)
		 at com.tns.jobs.MyWorker.<init>(MyWorker.java:14)

To Reproduce
Declare a class in nativescript that extend Worker class :

@JavaProxy("com.tns.jobs.MyWorker")
export class MyWorker extends androidx.work.Worker {
	constructor(context, params) {
		super();
		return global.__native(this);
	}

	public doWork() {
	   // Do your work here.

	   //
	   return androidx.work.ListenableWorker.Result.success();
	}

	public onStopped() {
	  // Cleanup because you are being stopped.
	  console.log("onStopped from MyWorker !!!")
	}
}

Then a typescript class which will enqueue the job :

export class MyWorkerHandler {

    getJobConstrains() {
        return (<any>new androidx.work.Constraints.Builder())
                    .build();
    }

    getWorkRequest() {
        let constrains = this.getJobConstrains();
		return (<any>new androidx.work.OneTimeWorkRequest.Builder(java.lang.Class.forName("com.tns.jobs.MyWorker")))
            .setConstraints(constrains)        
            .build()
    }

    enqueue() {
        let worker = this.getWorkRequest();
        
		(<any>androidx.work.WorkManager).getInstance()
          .enqueueUniqueWork("jojoworker", androidx.work.ExistingWorkPolicy.REPLACE , worker); 
             
        let lifecycleowner = androidx.lifecycle.ProcessLifecycleOwner.get();
        
        (<any>androidx.work.WorkManager).getInstance().getWorkInfoByIdLiveData(worker.getId())
            .observe(lifecycleowner, new androidx.lifecycle.Observer<androidx.work.WorkInfo>({
                onChanged : function(workInfo) {

                    console.log("OnChange 1 : ", workInfo)

				}
			}))
    }
}

Here is the java class MyWorker generated by nativescript :

@com.tns.JavaScriptImplementation(javaScriptFile = "./bundle.js")
public class MyWorker extends androidx.work.Worker
    implements com.tns.NativeScriptHashCodeProvider {
  public MyWorker(
      android.content.Context param_0, androidx.work.WorkerParameters param_1) {
    super(param_0, param_1);
    com.tns.Runtime.initInstance(this);
  }

  public androidx.work.ListenableWorker.Result doWork() {
    java.lang.Object[] args = new java.lang.Object[0];
    return (androidx.work.ListenableWorker.Result)
        com.tns.Runtime.callJSMethod(
            this, "doWork", androidx.work.ListenableWorker.Result.class, args);
  }

  public void onStopped() {
    java.lang.Object[] args = new java.lang.Object[0];
    com.tns.Runtime.callJSMethod(this, "onStopped", void.class, args);
  }

  public int hashCode__super() {
    return super.hashCode();
  }

  public boolean equals__super(java.lang.Object other) {
    return super.equals(other);
  }
}

The problem is at the line : com.tns.Runtime.initInstance(this);
If Im looking inside the class com.tns.Runtime, I can see this :

public static void initInstance(Object instance) {
	Frame frame = ManualInstrumentation.start("Runtime.initInstance");

	try {
		Runtime runtime = getCurrentRuntime();
		int objectId = runtime.currentObjectId;
		if(objectId != -1) {
			runtime.makeInstanceStrong(instance, objectId);
		} else {
			runtime.createJSInstance(instance);
		}
	} finally {
		frame.close();
	}
}
public static Runtime getCurrentRuntime() {
	Runtime runtime = (Runtime)currentRuntime.get();
	return runtime;
}

So what I suggest is the current Runtime is not initialized, because getCurrentRuntime() return null, so runtime.currentObjectId throw an exception

Expected behavior

Previously, I was using the library Firebase JobDispatcher (doc : https://github.com/firebase/firebase-jobdispatcher-android). It's using Android service, so the behaviour was different, but the generated class from nativescript was like that :

@com.tns.JavaScriptImplementation(javaScriptFile = "./bundle.js")
public class MyJob extends com.firebase.jobdispatcher.JobService
    implements com.tns.NativeScriptHashCodeProvider {
  public MyJob () {
    super();
    java.lang.Object[] args = new java.lang.Object[0];
    com.tns.Runtime.callJSMethod(this, "init", void.class, true, args);
  }

  public boolean onStartJob(com.firebase.jobdispatcher.JobParameters param_0) {
    ..
  }

  public boolean onStopJob(com.firebase.jobdispatcher.JobParameters param_0) {
    ...
  }

  public void onCreate() {
    super.onCreate();
    if (!com.tns.Runtime.isInitialized()) {
      com.tns.Runtime runtime = com.tns.RuntimeHelper.initRuntime(this);
      runtime.run();
    }
    com.tns.Runtime.initInstance(this);
  }

  public int hashCode__super() {
    return super.hashCode();
  }

  public boolean equals__super(java.lang.Object other) {
    return super.equals(other);
  }
} 

If you look the method onCreate(), you can see that there is a check on runtime initialization, and if not, the runtime is initialized.
I suppose that is the missing part on my class, but I really don't know how to initialize the runtime by myself, before com.tns.Runtime.initInstance(this) is called in my generated class constructor

Sample project

As you can see, I put the minimum possible code on my class, you can simply copy paste my code and see what's going on

Additional context

My job consists to get the current location of the phone, every 15 minutes.
I'm still using JobDispatcher library to schedule this job in background, and its still working in my app (doc : https://github.com/firebase/firebase-jobdispatcher-android)

But now this library is deprecated., and they strongly recommand to switch on WorkManager system.
The main info is :

Additionally, FJD will stop working once your app starts targeting an Android version after Android Q

So until nativescript will target to Android Q, I hope to find a solution to use WorkManager (or a workaround to JobDispatcher)

@vmutafov vmutafov self-assigned this Sep 26, 2019
@vmutafov
Copy link
Contributor

Hello @GrEg00z, thank you for the good description of the issue. I will take a look into the problem.

@vmutafov
Copy link
Contributor

vmutafov commented Oct 4, 2019

Hi @GrEg00z :) I've made some changes in the runtime to handle the Android Worker classes. Could you try your sample with the runtime from the master branch: tns platform add android@next The fix is scheduled for the 6.2.0 release which is at the end of October.

@GrEg00z
Copy link
Author

GrEg00z commented Oct 10, 2019

Hey @vmutafov, I tried the android platform at next version (6.2.0-2019-10-10-044952-01), but unfornatly I can't start the application.

I get this error immadiatly when the app starts (1s after splashscreen appareas) :
Unable to create application com.tns.NativeScriptApplication : com.tns.NativeScriptException: metadata folder couldn't be opened!

@vmutafov
Copy link
Contributor

Hey @GrEg00z , are you by any chance using a Windows OS?

@GrEg00z
Copy link
Author

GrEg00z commented Oct 10, 2019

Yes Im using windows 10 family edition (18362.356)

Also, if I'm using the android runtime 6.1.2, or 6.1.1, my app stays locked to the splashscreen, without any error message or others, I just get this after few minutes in the console :

Successfully installed on device with identifier '01e136aecc973ccc'.
Unable to apply changes on device: 01e136aecc973ccc. Error is: Socket connection timed out..

On 6.1.0, I don't have any problem with the app

@GrEg00z
Copy link
Author

GrEg00z commented Oct 23, 2019

Any news about this ?

@GrEg00z
Copy link
Author

GrEg00z commented Oct 28, 2019

Up please !! Nobody has problem with tns-android 6.1.1 or 6.1.2 ??

@StefanAleksik
Copy link

Hey did you manage to make it work? For me it works when I am running the code on devices with SDK > 26, but when running it on on devices with lower SDK I get the following error JS: ERROR Error: Uncaught (in promise): Error: java.lang.NoClassDefFoundError: java.time.Duration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants