I am playing around with JobScheduler and I am finding out limitations. I would like to perform a request following a Job, however, I am unable to add parameters to JobScheduler without receiving an error.
has no zero argument constructor
Here is example code (this works)
public class MyJob extends JobService {
private JobParameters params;
private MyLargeTask largeTask;
#Override
public boolean onStartJob(JobParameters params) {
// get param to use if if needed ...
this.params = params;
largeTask = new MyLargeTask();
largeTask.execute();
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
if (largeTask != null)
largeTask.cancel(true);
return false;
}
// large task used ...
private class MyLargeTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void aVoid) {
jobFinished(params, false);
super.onPostExecute(aVoid);
}
#Override
protected Void doInBackground(Void... params) {
// Instead of making a request here, I have to call method outside of the asynctask
return null;
}
}
}
If I try to add a constructor, I have no idea how to access it since the only reference to MyJob is during the process of retrieving JobInfo.
ComponentName componentName = new ComponentName(getApplicationContext(), MyJob.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName).setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED).setRequiresCharging(true).build();
JobScheduler jobScheduler = (JobScheduler) getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
So I am unable to add a constructor such as
public MyJob(RequestModel request) {
...
}
Is there a pattern that can address this issue? My only thought is to create a static method where I execute requests and then call that method in doInBackground. But all of that can be avoided if I can create a constructor.
You can add parameters to a job via JobInfo.Builder.setExtras(PersistableBundle extras).
You can't pass normal Java objects, because your app's process may be killed by the time that the job is run.
Related
I have a job service that i want to excecute for every three hours, I have made the jobservice class but I don't know how to excecute it every three hours.
here is my Jobservice class
public class CleanupJobService extends JobService {
private static final String TAG = CleanupJobService.class.getSimpleName();
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Cleanup job started");
new CleanupTask().execute(params);
//Work is not yet complete
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
//No need to reschedule any jobs
return false;
}
/* Handle access to the database on a background thread */
private class CleanupTask extends AsyncTask<JobParameters, Void, JobParameters> {
#Override
protected JobParameters doInBackground(JobParameters... params) {
String where = String.format("%s = ?", DatabaseContract.TaskColumns.IS_COMPLETE);
String[] args = {"1"};
int count = getContentResolver().delete(DatabaseContract.CONTENT_URI, where, args);
Log.d(TAG, "Cleaned up " + count + " completed tasks");
return params[0];
}
#Override
protected void onPostExecute(JobParameters jobParameters) {
//Notify that the work is now done
jobFinished(jobParameters, false);
}
}
}
and registered it on Manifest
<service
android:name=".data.CleanupJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
Any idea to resolve this ? Thanks!
solved, I am using jobInfo class to solve this, the code should look like this
ComponentName jobService = new ComponentName(getContext(), CleanupJobService.class);
JobInfo task = new JobInfo.Builder(CLEANUP_JOB_ID, jobService)
.setPeriodic(jobInterval)
.setPersisted(true)
.build();
jobInterval is the predefined time that we want the service to excecute, the type is Long and have a millis format.
I'm trying to implement a basic login screen for an android app. The flow is as follows:
1) User enters login information and hits submit
2) A LoginRequest which extends AsyncTask is created and executed.
3) The doInBackground will fire some http calls to validate the user credentials
4) The onPostExecute should be getting called to set the loginResults
5) Ui thread sees the login results and continues accordingly.
I'm been simplifying the code to get to the root issue but haven't had any luck so far. Here is the simplified code that still repros the issue.
Inside my activity:
private void tryLogin(String email, String password)
{
this.showProgress(true);
LoginHelper loginHelper = new LoginHelper();
LoginResult result = loginHelper.tryLogin(email, password);
this.showProgress(false);
}
This gets called from my submit buttons on click listener.
Inside LoginHelper:
TestClass test = new TestClass();
public LoginResult tryLogin(String mobileNumber, String password, int deviceId)
{
String loginUrl = "...";
new LoginRequest(test).execute(loginUrl);
while (test.result == null)
{
try {
Thread.sleep(1000);
}
catch (Exception e)
{
//...
}
}
return test.result;
}
This will execute the AsyncTask and wait for the result being continuing.
LoginRequest:
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
TestClass test;
public LoginRequest(TestClass test)
{
this.test = test;
}
#Override
protected LoginResult doInBackground(String... params) {
LoginResult ret = null;
ret = new LoginResult(1,"test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
this.test.result = result;
}
}
I run this through the debugger with breakpoints inside the doInBackground and onPostExecute. The doInBackground executes correctly and returns the LoginResult value, but the onPostExecute breakpoint never gets hit, and my code will wait in the while loop in LoginHelper.
You are basically checking the whole time the variable 'result' of your LoginRequest. But that's not, how AsyncTask works.
From Docs:
AsyncTask allows you to perform asynchronous work on your user
interface. It performs the blocking operations in a worker thread and
then publishes the results on the UI thread, without requiring you to
handle threads and/or handlers yourself.
You can do your work in doInBackground() method and the publish you results in onPostExecute().
onPostExecute runs on UI Thread, to allow you change elements, show the result or whatever you want to do. Your problem is, that you are the whole time blocking the UI Thread with your checking method in tryLogin()
So how to solve it?
Remove the checking method:
public void tryLogin(String mobileNumber, String password, int deviceId)
{
// Starts AsynTasks, handle results there
String loginUrl = "...";
new LoginRequest().execute(loginUrl);
}
in AsyncTask:
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
// Removed Constructor, if you need to pass some other variables, add it again
#Override
protected LoginResult doInBackground(String... params) {
// TODO: Change this to actual Http Request
LoginResult ret = null;
ret = new LoginResult(1, "test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
// Now the result arrived!
// TODO: Use the result
}
}
More Thoughts:
You probably want to store user credentials. If so, make sure the are safe. Link
You might want, depending on results, change some UI. Here's an example:
AsyncTask:
public class LoginRequest extends AsyncTask
private Activity activity;
// Constructor
public LoginRequest(Activity activity) {
this.activity = activity;
}
#Override
protected LoginResult doInBackground(String... params) {
// TODO: Change this to actual Http Request
LoginResult ret = null;
ret = new LoginResult(1, "test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
ActivityLogin acLogin = (ActivityLogin) activity;
if(result.equals("ok")) {
Button loginButton = (Button) acLogin.findViewById(R.id.login-button);
loginButton.setBackgroundColor(Color.GREEN);
//Finish LoginActivity
acLogin.finish();
}
else {
//TODO: Fail Handling
}
}
}
And the start it like this:
new LoginRequest(loginActivity).execute(loginUrl);
I didnt tested the code.
It's AsyncTask so it's calling the LoginRequest and while(test.result) at the same time. You got stuck in the while loop because test.result is not done returning yet. test.result is done in onPostExecute(), so if you move that while loop in that function it will work and onPostExecute() will get called. One way to solve this problem is to implement a callback interface. Put the while loop in the overrided callback method.
refer to my answer here: how to send ArrayList(Bitmap) from asyncTask to Fragment and use it in Arrayadapter
Try This
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
{
TestClass test;
LoginResult ret = null;
public LoginRequest(TestClass test)
{
this.test = test;
}
#Override
protected Boolean doInBackground(String... params) {
ret = new LoginResult(1,"test");
return true;
}
#Override
protected void onPostExecute(Boolean success) {
if(success)
this.test.result = result;
}
}
Temporary solution : You can add this.test.result = result; in the doInbackground() method.
#Override
protected LoginResult doInBackground(String... params) {
LoginResult ret = null;
ret = new LoginResult(1, "test");
this.test.result = result;
return ret;
}
Please post full code to get proper solution.
Hey everyone just getting started with Android testing and I am trying to test an async task. Here is the async task code. I am following this SO post Android AsyncTask testing with Android Test Framework. The runTestOnUiThread is not found in AndroidTestCase however. If I understand this correctly if its not run on the ui thread then the test finishes before the async task completes? Any help is greatly appreciated !
public class BackendTest extends AndroidTestCase {
private static MyApi myApiService = null;
private Context context;
public void testAsyncJoke () throws Throwable{
// create a signal to let us know when our task is done.
final CountDownLatch signal = new CountDownLatch(1);
final AsyncTask<Pair<Context, String>, Void, String> myTask = new AsyncTask<Pair<Context, String>, Void, String>() {
#Override
protected String doInBackground(Pair<Context, String>... params) {
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl("https://androidnanodegreprojectfour.appspot.com/_ah/api/");
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
signal.countDown();
}
};
// Execute the async task on the UI thread! THIS IS KEY!
runTestOnUiThread(new Runnable() {
#Override
public void run() {
myTask.execute("Do something");
}
});
signal.await(30, TimeUnit.SECONDS);
// The task is done, and now you can assert some things!
assertTrue("Happiness", true);
}
}
I developed chat app using xmpp by smack client. I used a background thread for incoming msg notification and working fine. But now when I am in chat view then I don't want notification of incoming msg. so I removed chatmangerlistener. but it is not working .
I used my second method that when I'll come in chat view then my background thread will be close. but i saw that background thread is not closing or stoping. isCancelling method is giving me false.
this is code :-
public class incomingmsg extends AsyncTask<String, Void, String>
{
String msg;
protected String doInBackground(String... urls) {
connection = XMPPLogic.getInstance().getConnection();
// register listeners
ChatManager chatmanager = connection.getChatManager();
chatmangerlistnr = new ChatManagerListener()
{
#Override
public void chatCreated(final Chat chat, final boolean createdLocally) {
chat.addMessageListener(new MessageListener()
{
#Override
public void processMessage(Chat chat, Message message) {
msg = message.getBody();
System.out.println("Received message: "
+ (message != null ? message.getBody() : "NULL"));
GeneratNotification(msg);
}
});
}
};
connection.getChatManager().addChatListener(chatmangerlistnr);
// idle for 20 seconds
/* final long start = System.nanoTime();
while ((System.nanoTime() - start) / 1000000 < 20000) // do for 20 seconds
{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
*/
System.out.println("is cancellable "+this.isCancelled());
return msg;
}
protected void onPostExecute(String r) {
// GeneratNotification(r);
}
}
I m confusion if isCancellable() method is false then how can i stop it? or how can I remove my chatlistener?
please expert help me.
Cancelling a task
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
Check the documentation for more.
to start you async:
incomingmsg aTask = new incomingmsg();
aTask.execute(...);
to stop you async
aTask.cancel(true);
by convention uses the name of its class starting with capital letter
public class incomingmsg extends AsyncTask<String, Void, String>...
change to:
public class Incomingmsg extends AsyncTask<String, Void, String>...
but it just a good practice
Full example:
AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
//do this test ever step of you async task.
if(!isCancelled()){
//do something here
}
return null;
}
#Override
protected void onPostExecute(Void result) {
myTask = null;
}
};
//here u start u task:
myTask.execute(null, null, null);
if(myTask != null){
//here u stop u task:
myTask.cancel(true);
}
I am using strict mode for android 3.2 and am getting StrictModeDiskReadViolation during onCreate in my Activity.
I tried to moved the code that does an SQL query to:
a new Thread.
a new AsyncTaskLoader.
a new AsynTask.
The problem is only AsyncTask made the Violation dissappear and I'm wondering why the other two methods didn't work?
Here is my code:
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
Dao<Listino, Integer> dao = DatabaseHelper.getHelper(ListinoActivity.this).getListinoDao();
if (dao.countOf() == 1)
{
long id = dao.queryForAll().get(0).getId();//long non int
final Intent intent = new Intent(ListinoActivity.this, ListinoProdottiActivity.class);
intent.putExtra("listino_id", id);
intent.setAction(Intent.ACTION_MAIN);
ListinoActivity.this.finish();
ListinoActivity.this.startActivity(intent);
}
} catch (SQLException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
MyToast.makeText(ListinoActivity.this, "Errore ottenere i listini", MyToast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
}
return null;
}
};
asyncTask.execute();
AsyncTaskLoader async = new AsyncTaskLoader(this) {
#Override
public Object loadInBackground() {
//do stuff, violation still here
return null;
}
};
async.loadInBackground();
Thread t = new Thread() {
#Override
public void run() {
super.run();
//do stuff, violation still here
}
};
t.run();
You did not fork a Thread. To fork a Thread, you call start(). You called run(), which simply ran your run() method on the current thread.
And you did not come even close to using the Loader framework properly. The code you have there not only suffers from the same flaw as what you did with your Thread, but that is not how you use a Loader.