I am trying to change the wallpaper of Android, in some periodic interval.
I used WorkManager to run a worker, that downloads the wallpaper in the background and sets it.
As long as the app is running, the wallpaper is changed. When I close the wallpaper, it stops. I am using PeriodcWork in Workmanager.
This is my code
public class OneTimeWorker extends Worker {
Context context = getApplicationContext();
private String URL;
#NonNull
#Override
public Result doWork() {
new FetchWallpaper().execute();
return Result.SUCCESS;
}
private class FetchWallpaper extends AsyncTask<Void, Void, Bitmap>
{
#Override
protected Bitmap doInBackground(Void... voids) {
String imageUrl="";
Bitmap result = null;
try
{
URL = "myurl.com";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
Response responses = null;
try {
responses = client
.newCall(request)
.execute();
String jsonData = responses.body().string();
JSONArray jsonArr = new JSONArray(jsonData);
JSONObject c = jsonArr.getJSONObject(new Random().nextInt(jsonArr.length()));
imageUrl = c.getString("wallpaper");
result = Picasso.with(getApplicationContext())
.load(imageUrl)
.get();
} catch (Exception e) {
e.printStackTrace();
}
}
catch (Exception e)
{
}
return result;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());
try {
wallpaperManager.setBitmap(bitmap);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
You better use Android-Job by Evernote.
Internally uses WorkManager, provides smooth, nice - chained methods & reliability while it's easy to implement.
Automatically chooses between JobManager/WorkManager/GCM/AlarmManager to ensure that your code must execute.
I myself using this and it's good.
Related
I am trying to change the wallpaper of Android every 15 minutes or something like this. A user can choose the time and I am running a periodic work using Workmanager.
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(SomeWorker.class, 15, TimeUnit.MINUTES).build();
WorkManager.getInstance().enqueue(periodicWorkRequest);
This way I am calling my Worker Class. The working class is this
public class SomeWorker extends Worker {
Context context = getApplicationContext();
private String URL;
#NonNull
#Override
public Result doWork() {
new FetchWallpaper().execute();
return Result.SUCCESS;
}
private class FetchWallpaper extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... voids) {
try
{
URL = "myurl.com";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
Response responses = null;
try {
responses = client
.newCall(request)
.execute();
String jsonData = responses.body().string();
JSONArray jsonArr = new JSONArray(jsonData);
JSONObject c = jsonArr.getJSONObject(new Random().nextInt(jsonArr.length()));
String imageUrl = c.getString("wallpaper");
Bitmap result= Picasso.with(getApplicationContext())
.load(imageUrl)
.get();
WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());
try {
wallpaperManager.setBitmap(result);
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
Date currentTime = Calendar.getInstance().getTime();
}
catch (Exception e)
{
Date currentTime = Calendar.getInstance().getTime();
}
return null;
}
}}
On that Particular line,
new FetchWallpaper().execute();
I am getting the error saying it must call from the main thread. I am new to Android, I don't know if this is the good approach.
Please let me know if there is any better approach to perform such kind of task.
The Worker class already calls doWork on a background thread - you don't need to use AsyncTask at all.
Just move everything from your doInBackground method directly into the Worker's doWork.
You can not update UI from doInBackground method. If you want to do something on UI you must do that on Main UI thread. So write setBitmap code in onPostExecute method as onPostExecute on on Main UI Thread.
To do that set third parameter of AsyncTask as String
AsyncTask<Void, Void, String>
So that return type of doInBackground method will be String
protected String doInBackground(Void... voids)
...
...
return imageUrl;
}
And Your onPostExecute method will be like
#Override
protected void onPostExecute(String imageUrl) {
super.onPostExecute(imageUrl);
Bitmap result= Picasso.with(getApplicationContext())
.load(imageUrl)
.get();
WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());
try {
wallpaperManager.setBitmap(result);
} catch (Exception ex) {
ex.printStackTrace();
}
}
new AsyncTask<Void, Bitmap, Bitmap>() {
#Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap = null;
try {
InputStream inputStream;
inputStream = new java.net.URL(url).openStream();
bitmap = BitmapFactory.decodeStream(inputStream);
}catch (Exception e) {
logAppE(TAG, "BITMAP ERROR -> " + e.getMessage());
}
return bitmap
}
#Override
protected void onPostExecute(Bitmap s) {
try {
Glide.with(context).asGif().load(s).into(imgViewGIF);
} catch (Exception e) {
logAppE(TAG, "BITMAP -> " + e.getMessage());
}
}
}.execute();
I have an image "manager" that downloads images.
Previously I used the Picasso library for this as follows
class DownloadImage implements Runnable {
String url;
Context context;
public DownloadImage(String url, Context context) {
this.url = url;
this.context = context;
}
#Override
public void run() {
try {
String hash = Utilities.getSha1Hex(url);
FileOutputStream fos = context.openFileOutput(hash, Context.MODE_PRIVATE);
Bitmap bitmap = Picasso.with(context)
.load(url)
.resize(1024, 0) // Height 0 to ensure the image is scaled with respect to width - http://stackoverflow.com/a/26782046/1360853
.onlyScaleDown()
.memoryPolicy(MemoryPolicy.NO_CACHE)
.get();
// Writing the bitmap to the output stream
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
fos.close();
bitmap.recycle();
} catch (IOException e) {
Timber.e(e, "For url %s", url);
} catch (OutOfMemoryError e) {
Timber.e(e, "out of memory for url %s", url);
}
}
}
But this creates a Bitmap object which not only consumes a lot of memory, it is also considerably slower and unnecessary.
I have modified this Runnable to use okhttp3 instead:
class DownloadImage implements Runnable {
String url;
Context context;
public DownloadImage(String url, Context context) {
this.url = url;
this.context = context;
}
#Override
public void run() {
try {
String hash = Utilities.getSha1Hex(url);
final FileOutputStream fos = context.openFileOutput(hash, Context.MODE_PRIVATE);
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Sink sink = null;
BufferedSource source = null;
try {
source = response.body().source();
sink = Okio.sink(fos);
source.readAll(sink);
} catch (Exception e) {
Timber.e(e, "Downloading an image went wrong");
} finally {
if (source != null) {
source.close();
}
if (sink != null) {
sink.close();
}
fos.close();
okHttpClient.connectionPool().evictAll(); // For testing
}
}
});
} catch (IOException e) {
Timber.e(e, "For url %s", url);
}
}
}
While this approach is A LOT faster than the previous, for a large number of images I get A/libc: FORTIFY_SOURCE: FD_SET: file descriptor >= FD_SETSIZE. Calling abort(). followed by a microdump, which means I have too many file descriptors open.
For testing sake I have added the okHttpClient.connectionPool().evictAll(); // For testing line, but that didn't work.
I also tried setting builder.connectionPool(new ConnectionPool(4, 500, TimeUnit.MILLISECONDS)); when building the okHttpClient, but that did nothing either.
I am also aware of https://github.com/square/okhttp/issues/2636
I seem to close all streams/sinks/sources, so what is going on here?
The runnables are added to a ThreadPoolExecutor using its execute function, which is created as follows:
// Sets the amount of time an idle thread waits before terminating
private static final int KEEP_ALIVE_TIME = 500;
// Sets the Time Unit to milliseconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.MILLISECONDS;
private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
// A queue of Runnables
private final BlockingQueue<Runnable> mDecodeWorkQueue;
private OkHttpClient okHttpClient;
ThreadPoolExecutor mDecodeThreadPool;
public ImageManager() {
// Instantiates the queue of Runnables as a LinkedBlockingQueue
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
// Creates a thread pool manager
mDecodeThreadPool = new ThreadPoolExecutor(
NUMBER_OF_CORES, // Initial pool size
NUMBER_OF_CORES, // Max pool size
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
mDecodeWorkQueue);
}
Solved it by creating and using the FileOutputStream in the OnResponse body, so that it's not open while the request is being done.
I am working with the data of the GitHub api, using Retrofit. In one activity, I need need 2 references to the Retrofit Builder, hence - 2 callbacks. The calss' definition so far is:
public class UserScreen extends Activity implements Callback<GitHubUser>, Callback<GitHubRepos>
How can the duplicate class be avoided? My definition for the methods of the User call looks like that:
public void loadData(View view) {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
retrofit = new Retrofit.Builder()
.baseUrl(GitHubUserAPI.ENDPOINT)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
// prepare call in Retrofit 2.0
GitHubUserAPI githubUserAPI = retrofit.create(GitHubUserAPI.class);
Call<GitHubUser> callUser = githubUserAPI.getUser(newString);
callUser.enqueue(this);
Call<GitHubUser> callAvatar = githubUserAPI.getAvatar(newString);
callAvatar.enqueue(this);
Call<GitHubUser> callFollowers = githubUserAPI.getFollowers(newString);
callFollowers.enqueue(this);
}
#Override
public void onResponse(Call<GitHubUser> call, final Response<GitHubUser> response) {
final int code = response.code();
if (code == 200) {
final GitHubUser user = response.body();
userNameTV.setText(user.getName());
followersTV.setText("Followers: " + user.getFollowers());
followingTV.setText("Following: " + user.getFollowing());
ImageDownloader task = new ImageDownloader();
Bitmap myImage;
try {
myImage = task.execute(user.getAvatar()).get();
avatarImg.setImageBitmap(myImage);
avatarImg.getLayoutParams().height=150;
avatarImg.getLayoutParams().width=150;
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ImageDownloader extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... urls) {
try {
URL url = new URL(urls[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream inputStream = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(inputStream);
return myBitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void onFailure(Call<GitHubUser> call, Throwable t) {
Toast.makeText(this, "Nope", Toast.LENGTH_LONG).show();
}
Thank you!
I was trying making OkHttp request in AsyncTask using call.execute() -- Synchronous call.
I have two buttons in my layout. Pressing button1 starts AsyncTask, that executes OkHttp request.call.execute().
And pressing button2, I just update a TextView.
Observation: While AsyncTask is running, I can not update TextView.
But, if I don't use AsyncTask and use OkHttpClient.newCall().enqueue() method,then I can update TextView by pressing button2.
Any answer for "Why using AsyncTask in this case not working"?
Source Code Sample:
bpost = (Button) findViewById(R.id.bpost);
bpost.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
i++;
tv.setText(""+i);
}
});
bstart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OkHttpHandler handler = new OkHttpHandler();
byte[] image = new byte[0];
try {
image = handler.execute(url).get();
if (image != null && image.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(image, 0, image.length);
imageView.setImageBitmap(bitmap);
tv.setText("Total btytes download: " + image.length);
}
} catch (Exception e) {
tv.setText("sorry, something went wrong!");
}
}
public class OkHttpHandler extends AsyncTask<String, Void, byte[]> {
OkHttpClient client = new OkHttpClient();
#Override
protected byte[] doInBackground(String... params) {
Request.Builder builder = new Request.Builder();
builder.url(params[0]);
Request request = builder.build();
try {
Response response = client.newCall(request).execute();
return response.body().bytes();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
This is because get() method of AsyncTask waits for the computation to finish in doInBackground method and then retrieves its result. See this link.
This will make your main UIThread in wait mode until doInBackground finish its execution or there is some exception occur(i.e. CancellationException,ExecutionException and InterruptedException).
You should use onPostExecute(Result) override method of AsyncTask.
private class OkHttpHandler extends AsyncTask<String, Void, byte[]> {
OkHttpClient client = new OkHttpClient();
#Override
protected byte[] doInBackground(String... params) {
Request.Builder builder = new Request.Builder();
builder.url(params[0]);
Request request = builder.build();
try {
Response response = client.newCall(request).execute();
return response.body().bytes();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(byte[] bytes) {
super.onPostExecute(bytes);
try {
if (bytes != null && bytes.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
imageView.setImageBitmap(bitmap);
tv.setText("Total btytes download: " + bytes.length);
}
} catch (Exception e) {
tv.setText("sorry, something went wrong!");
}
}
}
This is my first async task, which gets called first, it gets data from server and then onPostExecute it executes other async task, which downloads and sets image.
private class GetData extends AsyncTask<String, Void, Void> {
private final HttpClient client = new DefaultHttpClient();
private String content;
private String error = null;
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... progress) {
}
#Override
protected Void doInBackground(String... params) {
try {
HttpGet httpget = new HttpGet(params[0]);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
content = client.execute(httpget, responseHandler);
} catch (ClientProtocolException e) {
error = e.getMessage();
cancel(true);
} catch (IOException e) {
error = e.getMessage();
cancel(true);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (error == null) {
try {
JSONObject dataDishes = new JSONObject(content);
Log.d("DISHES", dataDishes.toString());
ArrayList<DishModel> dishData = new ArrayList<DishModel>();
for (int i = 0; i < 8; i++) {
DishModel model = new DishModel();
model.setName("Company " + i);
model.setDesc("desc" + i);
//TODO: set data img
new GetImage(model).execute("http://example.com/" + (i + 1) + ".png");
dishData.add(model);
}
ListView listAllDishes = (ListView) getView().findViewById(R.id.listView);
DishRowAdapter adapterAllDishes = new DishRowAdapter(getActivity(),
R.layout.dish_row, dishData);
listAllDishes.setAdapter(adapterAllDishes);
} catch (JSONException e) {
Log.d("DISHES", e.toString());
}
} else {
Log.e("DISHES", error);
}
}
}
This is another async task, it downloads image and onPostExecute it sets image to passed model.
private class GetImage extends AsyncTask<String, Void, Void> {
private DishModel model;
private Bitmap bmp;
public getImage(DishModel model) {
this.model = model;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... progress) {
}
#Override
protected Void doInBackground(String... params) {
try {
URL url = new URL(params[0]);
Log.d("DISHES", params[0]);
try {
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
Log.d("DISHES", e.toString());
}
} catch (MalformedURLException e) {
Log.d("DISHES", e.toString());
}
return null;
}
#Override
protected void onPostExecute(Void result) {
model.setPhoto(bmp);
}
}
It works if I do both data/image download proccess in one AsyncTask doInBackground(String... params), but it doesnt when I split data and image downloading into seperate async tasks. Furthermore I dont get any exceptions or errors.
UPDATE: Images shows up when i switch views..
At first, getImage and getData are classes, and classes names in Java are capitalized.
Technically, you can run another AsyncTask from onProgressUpdate() or onPostExecute() - https://stackoverflow.com/a/5780190/1159507
So, try to put the breakpoint in second AsyncTask call and debug is it called.