FFmpeg - cannot find ExecuteBinaryResponseHandler - Android/Java - java

I am trying to make a module for react-native that will change a video into a gif. I have little to no experience with android studios/java, but I would love to learn more! I am using this library to convert the video to a gif. Here is my code:
package com.reactlibrary;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.github.hiteshsondhi88.libffmpeg.FFmpeg;
public class RNGifMakerModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
public RNGifMakerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
#Override
public String getName() {
return "RNGifMakerModule";
}
#ReactMethod
public void alert(String message) {
Toast.makeText(getReactApplicationContext(), "Error", Toast.LENGTH_LONG).show();
String[] cmd = {"-i"
, message
, "Image.gif"};
conversion(cmd);
}
public void conversion(String[] cmd) {
FFmpeg ffmpeg = FFmpeg.getInstance(this.reactContext);
try {
// to execute "ffmpeg -version" command you just need to pass "-version"
ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
#Override
public void onStart() {
}
#Override
public void onProgress(String message) {
}
#Override
public void onFailure(String message) {
}
#Override
public void onSuccess(String message) {
}
#Override
public void onFinish() {
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
e.printStackTrace();
}
}
}
And I get this error:
Error:(43, 31) error: cannot find symbol class ExecuteBinaryResponseHandler
This seems odd to be, because in the documentation for ffmpeg-android-java it says to use almost exactly the same code.
Bounty
The bounty will be awarded to you if you can find a way to convert a video.mp4 into a gif. You do not necessarily have to use FFmpeg, but your solution has to work with java/android studios.

First of all you should init ffmpeg correctly.
FFmpeg ffmpeg = FFmpeg.getInstance(this.reactContext);
// please add following method after
ffmpeg.loadBinary(new FFmpegLoadBinaryResponseHandler() {
#Override
public void onFailure() {
// probably your device not supported
}
#Override
public void onSuccess() {
// you should init flag here (isLoaded, isReady etc.)
}
Only after onSuccess() you can work with commands.
Then please check following answer by LordNeckbeard.
So your code should be something like this:
if (isFFmpegLoaded) {
// ffmpeg.execute(commands from link from the answer)
}
Please do not forget to remove all spaces from command's string and "ffmpeg" word.
To keep command more readable I will recommend to build command like this:
final String[] command = new String[11]; // example of the first command in the answer
command[0] = "-y";
command[1] = "-ss";
command[2] = "30";
command[3] = "-t";
command[4] = "3";
command[5] = "-i";
command[6] = "-t";
command[7] = "filePath";
command[8] = "-vf";
command[9] = "fps=10,scale=320:-1:flags=lanczos,palettegen";
command[10] = "palette.png";
Please make sure that you have storage permission to work with file just in case you are working on external storage.
Based on this strategy ffmpeg works well for me. Thanks and good luck!

First of all, you should use: File - Invalidate Caches/Restart - Invalidate and Restart and try to reimport ExecuteBinaryResponseHan‌dler. If the problem hasn't been resolved you can try the small hack. Inside your project create package com.github.hiteshsondhi88.libffmpeg and class:
public class ExecuteBinaryResponseHandler implements FFmpegExecuteResponseHandler {
#Override
public void onSuccess(String message) {
}
#Override
public void onProgress(String message) {
}
#Override
public void onFailure(String message) {
}
#Override
public void onStart() {
}
#Override
public void onFinish() {
}
}
It should be as on image:
Then inside your build.gradle file in defaultConfig block add multiDexEnabled true
Then you will be able to use that class

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!

Download multiple concurrent files

I need download multiple files in one time (About 100 files)
It does not matter whether the download is synchronized
And the important thing is that all files be downloaded.
My code for getting urls and file names:
for (int i = 0; i < AssetData.size(); i++){
String item = AssetData.get(i).toString();
String name[] = item.split("/");
String Url = setting.Main_Domain+"/"+item;// Url for downloading
String fname =name[name.length-1] ;// File name like: test.txt
File file2 = new File(getFilesDir(),item.replace(fname,"")); // Parent File like: data/user/0/com.test.app/data/
if(!file2.exists()){file2.mkdir();}
}
The size of the files is small and all together is about 3 megabytes.
You can implement your code with using this library. You can download multiple files concurrent or you can start next download after one is completed.
https://github.com/MindorksOpenSource/PRDownloader
This is how your code will look
int downloadId = PRDownloader.download(url, dirPath, fileName)
.build()
.setOnStartOrResumeListener(new OnStartOrResumeListener() {
#Override
public void onStartOrResume() {
}
})
.setOnPauseListener(new OnPauseListener() {
#Override
public void onPause() {
}
})
.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel() {
}
})
.setOnProgressListener(new OnProgressListener() {
#Override
public void onProgress(Progress progress) {
}
})
.start(new OnDownloadListener() {
#Override
public void onDownloadComplete() {
}
#Override
public void onError(Error error) {
}
});
Isn't this simple. :)
This seems to me ion is a pretty elegant solution for you. It's easy to use and has many futures like connection pooling and reuse via HTTP Connection...

Convert Webm or m4a file to mp3

Is There any way to convert webm and m4a files to mp3 In android studio.
because changing the file extension makes it seem like mp3 file but it is unseekable after changing extension...the webm and m4a files only contain audio.
You can do with the implementation of https://github.com/adrielcafe/AndroidAudioConverter easily.
You need to import it to your project using
repositories {
maven {
url "https://jitpack.io"
}
}
dependencies {
compile 'com.github.adrielcafe:AndroidAudioConverter:0.0.8'
}
and as per the github page you will need to compile com.github.hiteshsondhi88.libffmpeg dependencie also.
Here is the fucntion for ffmpeg i have used..... i simply imported the ffmpeg library..
private void calculate(final String title , final String Type, final int id){
FFmpeg ffmpeg = FFmpeg.getInstance(getApplicationContext());
try {
// to execute "ffmpeg -version" command you just need to pass "-version"
ffmpeg.execute(new String[]{"-i",Environment.getExternalStoragePublicDirectory(DIRECTORY_MUSIC)+"/"+title+"."+Type, Environment.getExternalStoragePublicDirectory(DIRECTORY_MUSIC)+"/"+title+".mp3"}, new ExecuteBinaryResponseHandler() {
#Override
public void onStart() {}
#Override
public void onProgress(String message) {
Log.d("ffmpegevent", "onProgress: "+message);
}
#Override
public void onFailure(String message) {
Log.d("ffmpegevent", "onProgress: "+message);
Log.d("ffmpegevent", "onFailure: ");
}
#Override
public void onSuccess(String message) {
Log.d("ffmpegevent", "onSuccess: ");
}
#Override
public void onFinish() {
Log.d("ffmpegevent", "onProgress: Finished");
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
}
}

Adobe Air ANE for android with java native extension trying to receive broadcast

I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}

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