Using an AsyncTask inside a Service Class? - java

I have to upload data to a server. I am using a service that is running on the same process as my application. Should I use a Separate thread for upload process or Should I use a AsyncTask to upload data to server ?
More specifically can I use AsyncTask inside a service class ? And should I use it ? This service should always be running in memory in order to send data to the server every 5 seconds.

No problem to use AsyncTask in a service.
NOTE / FIX : I was wrong when I said the service runs in background, it only applis to IntentService. As noted in the comments and in the documentation, a service does not create it's own thread :
Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work.
That means you must use an AsyncTask (or another thread in any case) to perform your upload task.

Yes you can, the below code will run every 5 seconds. Use your regular connection code for sending part.
public class AsyncTaskInServiceService extends Service {
public AsyncTaskInServiceService() {
super("AsyncTaskInServiceService ");
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
#Override
protected void onHandleIntent(Intent intent) {
final Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//Connect to database here
try {
} catch (JSONException e) {
e.printStackTrace();
}
}
}, 0, 5000);
}
}

Use AsyncTask in a service in android
package ​com.emergingandroidtech.Services;
import ​android.app.Service;
import​ android.content.Intent;
import​ android.os.IBinder;
import ​android.util.Log;
import​ android.widget.Toast;
import​ java.net.MalformedURLException;
import​ java.net.URL;
import android.os.AsyncTask;
public​ class ​MyService ​extends​ Service​
{
​​​​#Override
​​​​public​ IBinder ​onBind(Intent​ arg0)​
{
​​​​​​​​return ​null;
​​​​}
​​​​
#Override ​
​​​public​ int ​onStartCommand(Intent ​intent,​int​ flags,​int ​startId)​
{
​​​​​​​​
//​We​ want ​this ​service ​to ​continue ​running ​until​ it ​is ​explicitly
​​​​​​​​//​stopped,​so​ return ​sticky. ​​​​​​​​
Toast.makeText(this,​“Service​Started”,​Toast.LENGTH_LONG).show();
​​​​​​​​try
{
​​​​​​​​​​​​new DoBackgroundTask().execute(
​​​​​​​​​​​​​​​new URL(“http://www.google.com/somefiles.pdf”),
​​​​​​​​​​​​​​​​​​​​new URL(“http://emergingandroidtech.blogspot.in”)); ​​​​​​​​
}
catch (MalformedURLException e)
{
​​​​​​​​​​​​e.printStackTrace();
​​​​​​​​}
​​​​​​​​return ​START_STICKY; ​
​​​}
​​​​
#Override
​​​​public ​void ​onDestroy()
​{
​​​​​​​​super.onDestroy();
​​​​​​​​Toast.makeText(this,​“Service​Destroyed”,​Toast.LENGTH_LONG).show(); ​​​
​} ​​​​
​​​​private ​int ​DownloadFile(URL​ url)
​{
​try​
{
​​​​​​​​​​​​//---simulate​ taking ​some​time ​to ​download ​a​ file--- ​​​​​​​​​​​​
Thread.sleep(5000);
​​​​​​​​}
​catch​(InterruptedException ​e)​
{
​​​​​​​​​​​​e.printStackTrace(); ​
​​​​​​​}
​​​​​​​​//---return ​an ​arbitrary ​number​ representing ​
​​​​​​//​the ​size​ of ​the ​file ​downloaded--- ​
​​​​​​​return​ 100; ​
​​​}
​​​​
private class DoBackgroundTask extends AsyncTask<URL, Integer, Long>
{
​​​​​​​​protected Long doInBackground(URL... urls)
{
​​​​​​​​​​​​int count = urls.length;
​​​​​​​​​​​​long totalBytesDownloaded = 0;
​​​​​​​​​​​​for (int i = 0; i < count; i++)
{
​​​​​​​​​​​​​​​​totalBytesDownloaded += DownloadFile(urls[i]);
​​​​​​​​​​​​​​​​//---calculate percentage downloaded and
​​​​​​​​​​​​​​​​// report its progress--- ​
​​​​​​​​​​​​​​​publishProgress((int) (((i+1) / (float) count) * 100)); ​
​​​​​​​​​​​}
​​​​​​​​​​​​return totalBytesDownloaded; ​​
​​​​​​}
​​​​​​​​
protected void onProgressUpdate(Integer... progress)
{
​​​​​​​​​​​​Log.d(“Downloading files”, ​​​​​​​​​​​​​​​​​​​​String.valueOf(progress[0]) + “% downloaded”); ​​​​​​​​​​​​
Toast.makeText(getBaseContext(), ​​​​​​​​​​​​​​​​String.valueOf(progress[0]) + “% downloaded”, ​​​​​​​​​​​​​​​​Toast.LENGTH_LONG).show(); ​​​​​​​​
}
​​​​​​​​
protected void onPostExecute(Long result)
{
​​​​​​​​​​​​Toast.makeText(getBaseContext(), ​​​​​​​​​​​​​​​​​​​​“Downloaded “ + result + “ bytes”, ​​​​​​​​​​​​​​​​​​​​Toast.LENGTH_LONG).show(); ​​
​​​​​​​​​​stopSelf();
​​​​​​​​} ​
​​​}
}
Try this it may be work.
Thank you.

Related

PocketSphinx is not listening

I would like to implement the library PocketSphinx in my Android project but I fail with it since nothing happens. It doesn't work and I don't get any errors.
This is how I tried it:
Added pocketsphinx-android-5prealpha-release.aar to /app/libs
Added assets.xml to /app
Aded the following to /app/build.gradle:
ant.importBuild 'assets.xml'
preBuild.dependsOn(list, checksum)
clean.dependsOn(clean_assets)
Added sync (with all sub-files) into /app/assets
Cloned the following repos into my root-directory:
git clone https://github.com/cmusphinx/sphinxbase
git clone https://github.com/cmusphinx/pocketsphinx
git clone https://github.com/cmusphinx/pocketsphinx-android
Executed gradle build
This is how my code looks like:
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import ch.yourclick.kitt.R;
import edu.cmu.pocketsphinx.Assets;
import edu.cmu.pocketsphinx.Hypothesis;
import edu.cmu.pocketsphinx.RecognitionListener;
import edu.cmu.pocketsphinx.SpeechRecognizer;
import edu.cmu.pocketsphinx.SpeechRecognizerSetup;
public class SttService extends Service implements RecognitionListener {
private static final String TAG = "SstService";
/* Named searches allow to quickly reconfigure the decoder */
private static final String KWS_SEARCH = "wakeup";
private static final String FORECAST_SEARCH = "forecast";
private static final String DIGITS_SEARCH = "digits";
private static final String PHONE_SEARCH = "phones";
private static final String MENU_SEARCH = "menu";
/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "oh mighty computer";
/* Used to handle permission request */
private static final int PERMISSIONS_REQUEST_RECORD_AUDIO = 1;
private SpeechRecognizer recognizer;
private HashMap<String, Integer> captions;
public SttService() {
// Prepare the data for UI
captions = new HashMap<>();
captions.put(KWS_SEARCH, R.string.kws_caption);
captions.put(MENU_SEARCH, R.string.menu_caption);
captions.put(DIGITS_SEARCH, R.string.digits_caption);
captions.put(PHONE_SEARCH, R.string.phone_caption);
captions.put(FORECAST_SEARCH, R.string.forecast_caption);
Log.e(TAG, "SttService: Preparing the recognition");
// Recognizer initialization is a time-consuming and it involves IO,
// so we execute it in async task
new SetupTask(this).execute();
}
private static class SetupTask extends AsyncTask<Void, Void, Exception> {
WeakReference<SttService> activityReference;
SetupTask(SttService activity) {
this.activityReference = new WeakReference<>(activity);
}
#Override
protected Exception doInBackground(Void... params) {
try {
Assets assets = new Assets(activityReference.get());
File assetDir = assets.syncAssets();
activityReference.get().setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
Log.e(TAG, "onPostExecute: Failed to init recognizer " + result);
} else {
activityReference.get().switchSearch(KWS_SEARCH);
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (recognizer != null) {
recognizer.cancel();
recognizer.shutdown();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE))
switchSearch(MENU_SEARCH);
else if (text.equals(DIGITS_SEARCH))
switchSearch(DIGITS_SEARCH);
else if (text.equals(PHONE_SEARCH))
switchSearch(PHONE_SEARCH);
else if (text.equals(FORECAST_SEARCH))
switchSearch(FORECAST_SEARCH);
else
Log.e(TAG, "onPartialResult: " + text);
}
/**
* This callback is called when we stop the recognizer.
*/
#Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.e(TAG, "onResult: " + text);
}
}
#Override
public void onBeginningOfSpeech() {
}
/**
* We stop recognizer here to get a final result
*/
#Override
public void onEndOfSpeech() {
if (!recognizer.getSearchName().equals(KWS_SEARCH))
switchSearch(KWS_SEARCH);
}
private void switchSearch(String searchName) {
recognizer.stop();
// If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
if (searchName.equals(KWS_SEARCH))
recognizer.startListening(searchName);
else
recognizer.startListening(searchName, 10000);
String caption = getResources().getString(captions.get(searchName));
Log.e(TAG, "switchSearch: "+ caption);
}
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = SpeechRecognizerSetup.defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
.setRawLogDir(assetsDir) // To disable logging of raw audio comment out this call (takes a lot of space on the device)
.getRecognizer();
recognizer.addListener(this);
/* In your application you might not need to add all those searches.
They are added here for demonstration. You can leave just one.
*/
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
// Create grammar-based search for selection between demos
File menuGrammar = new File(assetsDir, "menu.gram");
recognizer.addGrammarSearch(MENU_SEARCH, menuGrammar);
// Create grammar-based search for digit recognition
File digitsGrammar = new File(assetsDir, "digits.gram");
recognizer.addGrammarSearch(DIGITS_SEARCH, digitsGrammar);
// Create language model search
File languageModel = new File(assetsDir, "weather.dmp");
recognizer.addNgramSearch(FORECAST_SEARCH, languageModel);
// Phonetic search
File phoneticModel = new File(assetsDir, "en-phone.dmp");
recognizer.addAllphoneSearch(PHONE_SEARCH, phoneticModel);
}
#Override
public void onError(Exception error) {
Log.e(TAG, "onError: " + error.getMessage());
}
#Override
public void onTimeout() {
switchSearch(KWS_SEARCH);
}
}
My code is almost the same as pocketsphinx-android-demo. The only differences are that I am doing this in a service class, instead of an Activity and I am not asking the user for microphone permission since I do that in the MainActity already. Well, my code has some warnings but no errors.
When I run my app, I get this message (see the full stack trace):
E/SstService: switchSearch: To start demonstration say "oh mighty
computer".
But when I say "oh mighty computer" (or anything else), nothing happens. I don't even get an error. So I have no idea where I am stuck and what I am doing wrong.
If there is someone familiar with that library, any help will be appreciated!

How to get a json from api using sync or wait for async task in android?

I've created a class to download raw data from url(api): GetRawData - GitHub
I extend the class:
GetTVShowDetailsJsonData - GitHub
So, my problem is, I call GetTVShowDetailsJsonData.java on my activity
public class ProcessTVShowsDetails extends GetTVShowDetailsJsonData {
private ProgressDialog progress;
public ProcessTVShowsDetails(TVShow show) {
super(show, TVShowDetailsActivity.this);
}
public void execute() {
// Start loading dialog
progress = ProgressDialog.show(TVShowDetailsActivity.this, "Aguarde...", "Estamos carregando os dados da série.", true);
// Start process data (download and get)
ProcessData processData = new ProcessData();
processData.execute();
}
public class ProcessData extends DownloadJsonData {
protected void onPostExecute(String webData) {
super.onPostExecute(webData);
mTVShowDetails = getTVShowsDetails();
bindParams();
// Close loading dialog.
if (progress.isShowing()) progress.dismiss();
}
}
}
But inside this call, I want to call another download another JSON from other url, using GetSeasonJsonData.java, is the same GetTVShowDetailsJsonData.java do, but for another PARSE.I need to wait the task being completed or maybe do in synchronous way, to add the result from GetSeasonJsonData.java inside the first result from GetTvShowDetailsJsonData. How can I do that?
Just a note, i need to run more than one time, and I already try this, but doesn't work:
// Process and execute data into recycler view
public class ProcessTVShowsDetails extends GetTVShowDetailsJsonData {
private ProgressDialog progress;
public ProcessTVShowsDetails(TVShow show) {
super(show, TVShowDetailsActivity.this);
}
public void execute() {
// Start loading dialog
progress = ProgressDialog.show(TVShowDetailsActivity.this, "Aguarde...", "Estamos carregando os dados da série.", true);
// Start process data (download and get)
ProcessData processData = new ProcessData();
processData.execute();
}
public class ProcessData extends DownloadJsonData {
protected void onPostExecute(String webData) {
super.onPostExecute(webData);
mTVShowDetails = getTVShowsDetails();
//Process SeasonData
for(int seasonNumber = 1; seasonNumber <= mTVShowDetails.getNumberOfSeasons(); seasonNumber++) {
ProcessSeason processSeason = new ProcessSeason(mTVShowDetails.getId(), seasonNumber);
processSeason.execute();
}
bindParams();
// Close loading dialog.
if (progress.isShowing()) progress.dismiss();
}
}
}
// Process Season Data
public class ProcessSeason extends GetTVShowSeasonJsonData {
public ProcessSeason(int showId, int serieNumber) {
super(showId, serieNumber, TVShowDetailsActivity.this);
}
public void execute() {
// Start process data (download and get)
ProcessData processData = new ProcessData();
processData.execute();
}
public class ProcessData extends DownloadJsonData {
protected void onPostExecute(String webData) {
super.onPostExecute(webData);
}
}
}

Understanding the method and object calls

I am trying to understand how this piece of code all work together. While testing, in tomcat i see that when the device receives the signal, i get the msg "==Deleted all data # + uri". The tablet essentially receives a signal from the computer to begin this process of wiping. As soon as it gets the signal and initializes connection this is displayed on tomcat and then it begins wipe. How does it do this wipe?
DSer.java
public abstract class DSer implements Wipeable{
protected void clearExistingData(ContextWrapper cw) {
this.clearExistingData(cw, getContentURI());
}
protected void clearExistingData(ContextWrapper cw, Uri uri) {
cw.getContentResolver().delete(uri, null, null);
Log.d("DataSerializer", "== Deleted all data #" + uri);
}
#Override
public void wipeData(ContextWrapper cw, Vector<DSFileInfo> files) {
this.clearExistingData(cw);
}
Wipeable.java
import java.util.Vector;
import android.content.ContextWrapper;
public interface Wipeable {
public void wipeData(ContextWrapper cw, Vector<DSFileInfo> files);
}

libgdx lag when sound.play() while collecting apple

I'am doing simple libgdx game. I have lag (game stop for 0. 5 sec) when i use sound.play()
edit this bug apear on android 4.0 on 2.3 everything is running fine.
method. I play sound by this code:
if(CollisionDetector.detect(touchArea, hoodie.getTouchArea())){
GameScreen.totalScore++;
setPosition();
System.out.println("played");
Assets.eatSound.play();
}
And i use this method to load sound:
static long waitForLoadCompleted(Sound sound,float volume) {
long id;
while ((id = sound.play(volume)) == -1) {
long t = TimeUtils.nanoTime();
while (TimeUtils.nanoTime() - t < 100000000);
}
return id;
}
What am i doing wrong? Or what can i do to fix this lag ?
edit:
I have just tried to do thread with sound.play() but it also doesn't work:
new Thread(new Runnable() {
#Override
public void run() {
// do something important here, asynchronously to the rendering thread
// post a Runnable to the rendering thread that processes the result
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
// process the result, e.g. add it to an Array<Result> field of the ApplicationListener.
eatSound2.play();
}
});
}
}).start();
My Sound asset class looks like this but i still have lag with sound.
package com.redHoodie;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.utils.Disposable;
public class SoundEffect implements Disposable {
private static final int WaitLimit = 1000;
private static final int ThrottleMs = 100;
Sound eatSound;
Sound endSound;
public SoundEffect(){
eatSound = Gdx.audio.newSound(Gdx.files.internal("eatSound.ogg"));
endSound = Gdx.audio.newSound(Gdx.files.internal("sadend.wav"));
checkedPlay(eatSound);
}
protected long checkedPlay (Sound sound) {
return checkedPlay(sound, 1);
}
protected long checkedLoop (Sound sound) {
return checkedLoop(sound, 1);
}
protected long checkedPlay (Sound sound, float volume) {
int waitCounter = 0;
long soundId = 0;
boolean ready = false;
while (!ready && waitCounter < WaitLimit) {
soundId = sound.play(volume);
ready = (soundId != 0);
waitCounter++;
try {
Thread.sleep(ThrottleMs);
} catch (InterruptedException e) {
}
}
return soundId;
}
protected long checkedLoop (Sound sound, float volume) {
int waitCounter = 0;
long soundId = 0;
boolean ready = false;
while (!ready && waitCounter < WaitLimit) {
soundId = sound.loop(volume);
ready = (soundId != 0);
waitCounter++;
try {
Thread.sleep(ThrottleMs);
} catch (InterruptedException e) {
}
}
return soundId;
}
#Override
public void dispose() {
// TODO Auto-generated method stub
}
}
I had the same problem. It was because my .mp3 file was too short. Mine was 0.167 seconds long. I added 1.2 seconds of silence with Audacity, and it fixed the problem.
Lately I run into the same issue (except I'm using wav instead mp3 files). My app was lagging when I play many (like 10 or 20) sounds at the same time (same render method). "Solved" this by playing only 1 sound at the time. Generally it's hard to distinct many sounds at the same time. Also on desktop it works fine, but problem appears on android (9 or 8).
If someone still facing this issue as me there is the alternative solution with one limitation: no option to use sound id.
You can change default LibGDX behavior and use AsynchronousAndroidAudio by overriding this method in your AndroidLauncher class:
#Override
public AndroidAudio createAudio(Context context, AndroidApplicationConfiguration config) {
return new AsynchronousAndroidAudio(context, config);
}
See the official documentation for more info and also the pull request
Also, if for any reasons you need sound id you can take this implementation as an example and find a workaround for your project.
Fix is available starting from LibGDX 1.9.12

Passing function as a parameter in java

I'm getting familiar with Android framework and Java and wanted to create a general "NetworkHelper" class which would handle most of the networking code enabling me to just call web-pages from it.
I followed this article from the developer.android.com to create my networking class: http://developer.android.com/training/basics/network-ops/connecting.html
Code:
package com.example.androidapp;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;
/**
* #author tuomas
* This class provides basic helper functions and features for network communication.
*/
public class NetworkHelper
{
private Context mContext;
public NetworkHelper(Context mContext)
{
//get context
this.mContext = mContext;
}
/**
* Checks if the network connection is available.
*/
public boolean checkConnection()
{
//checks if the network connection exists and works as should be
ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected())
{
//network connection works
Log.v("log", "Network connection works");
return true;
}
else
{
//network connection won't work
Log.v("log", "Network connection won't work");
return false;
}
}
public void downloadUrl(String stringUrl)
{
new DownloadWebpageTask().execute(stringUrl);
}
//actual code to handle download
private class DownloadWebpageTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException
{
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 );
conn.setConnectTimeout(15000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d("log", "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException
{
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result)
{
//textView.setText(result);
Log.v("log", result);
}
}
}
In my activity class I use the class this way:
connHelper = new NetworkHelper(this);
...
if (connHelper.checkConnection())
{
//connection ok, download the webpage from provided url
connHelper.downloadUrl(stringUrl);
}
Problem I'm having is that I should somehow make a callback back to the activity and it should be definable in "downloadUrl()" function. For example when download finishes, public void "handleWebpage(String data)" function in activity is called with loaded string as its parameter.
I did some googling and found that I should somehow use interfaces to achieve this functionality. After reviewing few similar stackoverflow questions/answers I didn't get it working and I'm not sure if I understood interfaces properly: How do I pass method as a parameter in Java? To be honest using the anonymous classes is new for me and I'm not really sure where or how I should apply the example code snippets in the mentioned thread.
So my question is how I could pass the callback function to my network class and call it after download finishes? Where the interface declaration goes, implements keyword and so on?
Please note that I'm beginner with Java (have other programming background though) so I'd appreciate a throughout explanation :) Thank you!
Use a callback interface or an abstract class with abstract callback methods.
Callback interface example:
public class SampleActivity extends Activity {
//define callback interface
interface MyCallbackInterface {
void onDownloadFinished(String result);
}
//your method slightly modified to take callback into account
public void downloadUrl(String stringUrl, MyCallbackInterface callback) {
new DownloadWebpageTask(callback).execute(stringUrl);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//example to modified downloadUrl method
downloadUrl("http://google.com", new MyCallbackInterface() {
#Override
public void onDownloadFinished(String result) {
// Do something when download finished
}
});
}
//your async task class
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
final MyCallbackInterface callback;
DownloadWebpageTask(MyCallbackInterface callback) {
this.callback = callback;
}
#Override
protected void onPostExecute(String result) {
callback.onDownloadFinished(result);
}
//except for this leave your code for this class untouched...
}
}
The second option is even more concise. You do not even have to define an abstract method for "onDownloaded event" as onPostExecute does exactly what is needed. Simply extend your DownloadWebpageTask with an anonymous inline class inside your downloadUrl method.
//your method slightly modified to take callback into account
public void downloadUrl(String stringUrl, final MyCallbackInterface callback) {
new DownloadWebpageTask() {
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
callback.onDownloadFinished(result);
}
}.execute(stringUrl);
}
//...
NO interface, NO lib, NO Java 8 needed!
Just using Callable<V> from java.util.concurrent
public static void superMethod(String simpleParam, Callable<Void> methodParam) {
//your logic code [...]
//call methodParam
try {
methodParam.call();
} catch (Exception e) {
e.printStackTrace();
}
}
How to use it:
superMethod("Hello world", new Callable<Void>() {
public Void call() {
myParamMethod();
return null;
}
}
);
Where myParamMethod() is our passed method as parameter (in this case methodParam).
Yes, an interface is the best way IMHO. For example, GWT uses the command pattern with an interface like this:
public interface Command{
void execute();
}
In this way, you can pass function from a method to another
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
The out of the box solution is that this is not possible in Java. Java does not accept Higher-order functions. It can be achieved though by some "tricks". Normally the interface is the one used as you saw. Please take a look here for further information. You can also use reflection to achieve it, but this is error prone.
Using Interfaces may be the best way in Java Coding Architecture.
But, passing a Runnable object could work as well, and it would be much more practical and flexible, I think.
SomeProcess sp;
public void initSomeProcess(Runnable callbackProcessOnFailed) {
final Runnable runOnFailed = callbackProcessOnFailed;
sp = new SomeProcess();
sp.settingSomeVars = someVars;
sp.setProcessListener = new SomeProcessListener() {
public void OnDone() {
Log.d(TAG,"done");
}
public void OnFailed(){
Log.d(TAG,"failed");
//call callback if it is set
if (runOnFailed!=null) {
Handler h = new Handler();
h.post(runOnFailed);
}
}
};
}
/****/
initSomeProcess(new Runnable() {
#Override
public void run() {
/* callback routines here */
}
});
Reflection is never a good idea since it's harder to read and debug, but if you are 100% sure what you're doing, you can simply call something like set_method(R.id.button_profile_edit, "toggle_edit") to attach a method to a view. This is useful in fragment, but again, some people would consider it as anti-pattern so be warned.
public void set_method(int id, final String a_method)
{
set_listener(id, new View.OnClickListener() {
public void onClick(View v) {
try {
Method method = fragment.getClass().getMethod(a_method, null);
method.invoke(fragment, null);
} catch (Exception e) {
Debug.log_exception(e, "METHOD");
}
}
});
}
public void set_listener(int id, View.OnClickListener listener)
{
if (root == null) {
Debug.log("WARNING fragment", "root is null - listener not set");
return;
}
View view = root.findViewById(id);
view.setOnClickListener(listener);
}
Interface callback support generic type.
In Callbackable.java
public interface Callbackable<T> {
void call(T obj);
}
How to use:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
doSomeNetworkAction(new Callbackable<Integer>() {
#Override
public void call(Integer obj) {
System.out.println("have received: " + obj + " from network");
}
});
}
// You can change Integer to String or to any model class like Customer, Profile, Address...
public void doSomeNetworkAction(Callbackable<Integer> callback) {
// acb xyz...
callback.call(666);
}
}

Categories

Resources