Ok, I understood, that when you bind to a remote Service, it won't bind until you return from the callback. I need to bind to a service and execute some method from it immediately. Is there any way? My code looks like:
try {
Parser parser = FrameworkBridge.getFrameworkBridge(this).getParser();
if (parser != null) {
for (Article a : parser.getArticlesList("http://www.bt.dk/mecommobile/latest/news_article/%s/%s?output_type=xml", 1379, 20)) {
listAdapter.add(a);
}
}
} catch (RemoteException e) {
Log.d(TAG, "Service communication failure");
} catch (FrameworkNotInstalledException e) {
Log.d(TAG, "No framework installed. Install it!");
}
Here FrameworkBridge.getFrameworkBridge(this).getParser() performs all service connection routine and returns remote interface. The problem is -- when I'm in the code above, the connection is not performed yet, therefore parser is null. How can I make it connect before exiting the code?
onServiceConnected(..) will tell you when the service is connected and the remote interface is established. Don't try to call any methods in the service until this is triggered.
mContext.bindService( new Intent("name of the class"), this, Context.BIND_AUTO_CREATE);
the above method call on the copy of the context you store should automatically bind the service when you need it. I am not 100% sure, but this should help you out somewhat.
Related
I use Android WorkManager to download file from Firebase Storage.
The code is as the following
#NonNull
#Override
public Result doWork() {
dowloadFile-1
dowloadFile-2
dowloadFile-3
dowloadFile-4
return Result.success();
}
// Create and start an unique work.
// It mean there is only one work running at the time
public static void start(Context context) {
Constraints constraints = new Constraints.Builder()
// The Worker needs Network connectivity
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
int backoffDelay = context.getResources().getInteger(R.integer.sync_retry_backoff_delay);
OneTimeWorkRequest worker = new OneTimeWorkRequest.Builder(FirebaseSyncService.class)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, backoffDelay, TimeUnit.MINUTES)
.build();
WorkManager
.getInstance(context)
.enqueueUniqueWork(SYNC_WORK_ID, ExistingWorkPolicy.KEEP, worker);
}
I use getWorkInfosForUniqueWorkLiveData() to check state of unique work
And the scenario are as the following
Step-1. Connect internet then start work
Step-2. The program completed download file-1 and file-2
The work state now is RUNNING
Step-3. Disconnect internet
Work will be stopped and WorkManager will retry by adding new Work to queue
The work state now is ENQUEUED
Step-4. Re-connect internet
Expected:
After reconnecting internet, I expect the WorkManager will perform new work from beginning and the state is RUNING again.
I want to download file-1 and file-2 again.
Actual
WorkManager continue previous work and resume from downloading file-3. But the state is ENQUEUED.
Even if I change ExistingWorkPolicy.KEEP -> ExistingWorkPolicy.REPLACE, the behavior is the same.
Thank you for all supports
check these in your worker() Java file.
For doing network call you maybe using try catch, if not you have to use and then check in catch block you have to add return Result.Failure. Otherwise, even if you disconnect your internet it will continuous working(doWork() function),even work state is ENQUEUED and you will see exception in log.(Not sure reason, seems to be bug...)
I'm not faced these issue, when i use with Kotlin CoroutineWorker. its happening only if i use Worker
Note : I'll not suggest this type of coding, use Kotlin CoroutineWorker and forLoop for multiple series of file downloads. here i'm showing only for Demo
purpose.
#NonNull
#Override
public Result doWork() {
try {
dowloadFile-1
}catch (Exception e){
return Result.Failure; // this is important.Otherwise worker not finish,even work state is ENQUEUED
}
try {
dowloadFile-2
}catch (Exception e){
return Result.Failure;
}
try {
dowloadFile-3
}catch (Exception e){
return Result.Failure;
}
try {
dowloadFile-4
}catch (Exception e){
return Result.Failure;
}
//all success
return Result.success();
}
Quick Tip, In your case - Its better to use ExistingWorkPolicy.REPLACE, and Network call in background thread(use CoroutineWorker())
i am building my app on android repository by Fernando Cejas and i have a problem with subscribing to observable after calling dispose.
When i come to dashboard, i call method subscribeOnUserMessages.execute(new Subscriber(), new Params(token)), which is method in UseCase class
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
In child class SubscribeOnUserMessages i simply call repository like this
return messageRepository.subscribeOnUserMessages(params);
In my socket implementation i create like this
return Observable.create(emitter -> {
if (!isThereInternetConnection()) {
Timber.w("Network connection exception");
emitter.onError(new NetworkConnectionException());
return;
}
/*
* Open socket if not opened
*/
openSocket(params.getToken());
String channelName = CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid();
if (subscribedChannels.contains(channelName)) {
Timber.d("Channel %s is already subscribed", channelName);
return;
}
JSONObject auth;
try {
auth = createAuthJson(CHANNEL, channelName, params.getToken());
} catch (JSONException e) {
Timber.e("Couldn't create auth json");
emitter.onError(e);
return;
}
mSocket.emit(SUBSCRIBE, auth);
Timber.d("Emitted subscribe with channel: %s ", CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
subscribedChannels.add(CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
Timber.d("Subscribing on event: %s\n with user: %s", EVENT_USER_NEW_MESSAGE, params.getAuthenticated().getUuid());
if (mSocket.hasListeners(EVENT_USER_NEW_MESSAGE)) {
Timber.v("Socket already has listener on event: %s", EVENT_USER_NEW_MESSAGE);
return;
}
mSocket.on(EVENT_USER_NEW_MESSAGE, args -> {
if (args[1] == null) {
emitter.onError(new EmptyResponseException());
}
Timber.d("Event - %s %s", EVENT_USER_NEW_MESSAGE, args[1].toString());
try {
MessageEntity messageEntity = messageEntityJsonMapper.transform(args[1]);
emitter.onNext(messageEntity);
} catch (JSONException e) {
Timber.e(e, "Could not parse message json");
emitter.onError(e);
}
});
});
Symptoms are that first time i subscribe everything is going through to presentation layer. When i dispose after going to second screen and come back i only see logs coming to socket implementation, but not going through.
My question is: Is there a method for subscribing to same observable again? I've already tried to save that observable in my use case in singleton and subscribe to that observable, didn't help.
Without additional info and details regrading socket implementation it is hard to spot the problem exactly, but, from the code you've posted, you don't have dispose logic, so while you might properly call dispose() to the Observable at the correct lifecycle event, your socket will actually stay open, and it might not got disconnected/closed properly ever.
That might lead to a problems opening and connecting to the socket at the 2nd time, as you might try to reopen already open socket and depends on your internal socket impl that might be a problem.
(I can see in the comment that openSocket if not already opened, but still there might be problem elsewhere calling some method on the socket multiple times or setting listeners, again depends on the socket impl)
As a general guidelines, you should add dispose logic using emitter.setCancellable()/emitter.setDisposable() in order to dispose properly the socket resources when you no longer need them, thus - when applying subscribe again (whether the same object or not) will invoke your subscription logic again that will reopen the socket and listen to it.
It is not clear to me if you like to keep the socket open when you moving to a different screen (I don't think it is a good practice, as you will keep this resource open and might never get back to the screen again to use it), but if that's the case as #Phoenix Wang mentioned, you can use publish kind operators to multicast the Observable, so every new Subscriber will not try to reopen the socket (i.e. invoking the subscription logic) but will just get notify about messages running in the already opened socket.
I'm working on a project that improves Automation Test for Android's App. What I want to do is very "easy": I have this very simple SIP Client with a basic UI and developed just reading the API guides on the android developer website (https://developer.android.com/guide/topics/connectivity/sip.html) that receives and makes SIP calls.
I need to control remotely this app from my PC, connected at the same local network or the same wifi, by sending commands or similar (without interact with the phone) to the app itslef running normally on my phone.For a specific example I posted the method initiateCall() that calls sipAddress(in the app, sipAddress is taken from a Text Box), what I want to do is:
Starting the app on my phone
calling the method initiateCall() from my pc giving a sipAddress as a parameter (I must not use the UI from the app running, that's why I need to give the sipAddress)
check if an outgoing call starts from the app running on my phone
I thought that the solution must be something about web-services,but I don't have any better ideas and i don't know how to start and where to start solving this problem,that's why i need you help.
public void initiateCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// set up the listener for outgoing calls
#Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
updateStatus(call, 2);
}
#Override
public void onCallEnded(SipAudioCall call) {
updateStatus("Call End");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress,
listener, 30);
} catch (Exception e) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
You could do it REST API style. You would need to set up a minimalistic webserver.
If you access for example the url phoneip/ctrl/makecall?number=yournumber a serverside method us called if set up correctly. Then you can call you method and use the GET or POST variables as arguments.
You would have to look into Java Webserver Libraries/Frameworks. You can pick a lightweight one for that purpose. For example this one.
You could then also add security features (authentification to protect it) quite easily.
Example with sparkjava
import static spark.Spark.*;
....
get("/ctrl/makecall", (request, response) -> {
String phonenum = request.queryParams("number"); //may not be accurate; you have to determine the GET variable called "number" in that case; you can rename it; see docs!!!
//call your method with proper arguments
});
I used the Android built-in sip library to write an app that makes calls via my server. The calls are being made correctly, but most of the time, the calls aren't ended correctly.
This is my code to end the call:
public void stopCalling(){
try {
call.endCall();
call.close();
} catch (SipException e) {
e.printStackTrace();
}
}
But it does not Ended properly.
Is there any other way to ended the sip call.
I've made my own SIP client for Android. I'm facing an issue. I've made the test on SipDroid : if I call someone and hang up before he unhooked, the call end of the both side.
With my client, when I hang up, the call is not ended for the receiver, leading to a communication on one side.
Here is my code for ending a call :
if (call != null)
{
Log.i(TAG, "Trying to end call");
try
{
call.endCall();
}
catch (SipException e)
{
Log.i(TAG, "Service failed to end the call", e);
}
call.close();
updateStatus("Call Ended");
}
Where call is a SipAudioCall.
I get my call like this :
call = manager.makeAudioCall(profile.getUriString(), sipAddressToCall, listener, _Params.callWait);
I don't know about your API, but the SIP protocol itself has the "CANCEL" request to "hang up" before the call is answered.
On a normal, ongoing call, you send "BYE".