I need httpd server in my app, so I've choose NanoHttpd.
I've subclassed my own server:
public class StreamingServer extends NanoHTTPD {
private Context mContext;
public StreamingServer(Context context) {
super(8080);
mContext = context;
Log.i("MyServer", "Streaming server created");
}
And I'm trying to launch my server in main activity:
// onCreate:
server = new StreamingServer(getApplicationContext());
Here is my onResume method:
// onResume:
super.onResume();
try {
server.start();
} catch (IOException e) {
Log.i(TAG, "exception!");
e.printStackTrace();
}
Log.i(TAG, "started!");
and onPause:
super.onPause();
if (server != null) {
server.stop();
Log.i(TAG, "stopped!");
}
I've also done proper setup:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
So I believe I've done everything right. But launching my app leads to nothing (Page are unavailable). Some how 2 or 3 launches leads to server launch (and I do NOTHING different at that moment).
What I'm doing wrong?
NanoHttpd expects you to override the "serve()" method. The response object that you return from your overridden method will be returned to the web browser that hits your server. The "README" document on the github site has a fully functioning example (https://github.com/NanoHttpd/nanohttpd) that returns debug information to the browser.
If you want to serve more interesting content, look at the samples/ folder inside the NanoHttpd project, or the webserver/ folder for a fully functional web server.
Related
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
});
Friends!
I'm getting occasional and unexpected HTTP 400 responses from nanohttpd in my Android app. The error is following a specific pattern. I've been looking at this for some time now but I've come to the point where I need a different angle or some other help pointing me in the right direction.
Could you please have a look and share your thoughts or even direct points and suggestions?
Why am I getting this HTTP 400 status code?
And why only under the given circumstances? (I don't want it at all!)
Some Background
I'm running nanohttpd in my Android project as a temporary isolation layer (due to server side not being mature enough yet). I have isolated the nanohttpd server in an Android Service, which I start from my custom Application object once it's created. This way nanohttpd is not bound to the lifecycle of any particular Activity but can live rather independent of the overall application logic and component life cycles.
The Problem
Now, (almost) everything is working nice and dandy: I can start nanohttpd and perform some initial login requests, my expected mock response is even delivered. When I perform my first "GET" request, though, nanohttpd throws a 400 Bad request status at me, but only the first time. If I back out of the Activity being responsible for the particular "GET" request, and launch it again (from the home screen), it delivers the expected payload with a 200 status, flawlessly.
What Have I Done So Far
I have had a closer look at the nanohttpd source code, trying to track down where and why this 400 status is set. It's not that many places this status code is used. Roughly speaking only here, here and here. Since I'm not dealing with multipart content, I'm left with the first and third "here". But - of course - I can not for my life find neither the root cause of the 400 status, nor which exact block is causing the state for me. When I debug the code, everything works just peachy.
Some Code
This is roughly what my nanohttpd Service (MyNanoHttpdService) looks like:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
String errorMessage = null;
if (myNanoHttpd == null) {
String hostUrl = intent.getStringExtra(EXTRA_HOST);
Uri uri = Utils.notEmpty(hostUrl) ? Uri.parse(hostUrl) : Uri.EMPTY;
myNanoHttpd = new MyNanoHttpd(this, uri.getHost(), uri.getPort(), null);
}
if (!myNanoHttpd.isAlive()) {
try {
myNanoHttpd.start();
} catch (IOException e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
errorMessage = stringWriter.toString();
stopSelf();
}
}
final ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_LISTENER);
if (resultReceiver != null) {
int status = myNanoHttpd.isAlive() ? CODE_SUCCESS : CODE_FAILURE;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_MESSAGE, errorMessage);
resultReceiver.send(status, bundle);
}
}
return Service.START_STICKY;
}
And this is how I start the service from my custom Application object, initialize my client side state and fetch some content:
#Override
public void onCreate() {
super.onCreate();
// Yes, that is a Java 8 Lambda you see there!
MyNanoHttpdService
.start(this, "http://localhost:8080")
.withStartupListener((status, message) -> {
if (status == 0) {
// POST REQUEST: Works like a charm
myNetworkHelper.login();
// GET REQUEST: Always fails on first launch
myNetworkHelper.getContent();
} else {
Log.e("LOG_TAG", "Couldn't start MyNanoHttpd: " + message);
}
});
}
It's safe to assume that the wrapping convenience code (the .withStartupListener(...) - which essentially wraps a ResultReceiver used by the above Service - and the myNetworkHelper object) works as expected. Also, in production, the getContent() call would be made from an Activity or Fragment, but for the sake ease I have moved it to the Application for now.
I may have found the root cause for my issue, and possibly even a workaround for the moment.
If I'm correct in my investigation, the issue was caused by unconsumed data from a previous (POST) request, contaminating the current (POST) request.
This line in the NanoHTTPD code base (the header parsing block in the NanoHTTPD.HTTPSession.execute() method, just before calling through to any custom serve(...) method - the third "here" in my question above) was the very line where the HTTP 400 status code was thrown, and just as the code suggests, there was no proper value for the "method" header.
The value - which I expected to be "POST" in clear text - was contaminated with parts of the JSON content body from the previous request. As soon as I realized this, I tried to consume the entire request body in my custom MyNanoHttpd.serve(IHTTPSession session) method, like so:
#Override
public Response serve(IHTTPSesion session) {
InputStream inputStream = session.getInputStream();
inputStream.skip(inputStream.available());
// or
// inputStream.skip(Long.MAX_VALUE);
// or even
// inputStream.close();
...
}
This didn't work, though, as I kept getting various exceptions. I ended up gently modifying the NanoHTTPD code, safely closing the input stream in the finally block of the very NanoHTTPD.HTTPSession.execute() method instead.
I'm, nonetheless, considering reaching out to the NanoHTTPD community to discuss a suitable and sustainable solution.
I have an application that access to a server. When I quit the application, I have to disconnect from the server first, then close the application.
I would like to know if it's possible (and how) to make a Robospice service (background task) that disconnect from the server even if the application is closed (and the Robospice service is still running to finish the deconnection, and then auto kill itself after).
The problem is that the deconnection is too long (sometimes more than 5 secondes) and I would like to avoid blocking the phone during the deconnection, and allow the user to use it's phone.
Another question : is the Robospice librairy will be maintained and improved in the future (if necessary) ?
Yes, RoboSpice works just as well when attached to a Service Context as it works with an Activity one.
But you should probably try executing disconnect in the com.octo.android.robospice.SpiceService#onDestroy method of your implementation. This service is stopped whenever it has no meaningful work to do, so I guess it is the most appropriate solution for your use case.
I've solved my problem (that may be wasn't really a problem !) by ending my application with finish()
In my MainActivity, I use a Robospice service :
private final class LogoutListener implements PendingRequestListener<String> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.e(TAG, spiceException.getMessage());
}
#Override
public void onRequestSuccess(String result) {
// nothing special
}
#Override
public void onRequestNotFound() {
Log.w(TAG, "Not found");
}
}
private void Alarm_Logout(boolean exit) {
Logout request = new Logout(url);
spiceManager.execute(request, new LogoutListener());
this.finish();
}
And the Lougout class :
public class Logout extends OkHttpSpiceRequest<String> {
private String url_logout;
public Logout(String url) {
super(String.class);
this.url_logout = url;
}
#Override
public String loadDataFromNetwork() throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url_logout)
.build();
Response response = client.newCall(request).execute();
return "ok";
}
}
Before I closed the app with System.exit(0) in onRequestSuccess, so I had to wait the Logout complete. Now the app close (with finish()), but the Logout continue in background, and then, when done, the service finish...
On my Activity :
public class MainActivity extends Activity {
private mDbxAccountManager mDbxAccountManager = null;
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
mDbxAccountManager = DbxAccountManager.getInstance(getApplicationContext(), getString(R.string.dbx_app_key), getString(R.string.dbx_app_secret));
...
}
...
public void buttonOnClick(View view) {
if(mDbxAccountManager.hasLinkedAccount()) {
//Do something
}
else {
mDbxAccountManager.startLink(this, 0);
}
...
}
}
And on my Remote Service :
public class CloudService extends Service {
private mDbxAccountManager mDbxAccountManager = null;
#Override
public void onCreate() {
...
mDbxAccountManager = DbxAccountManager.getInstance(getApplicationContext(), getString(R.string.dbx_app_key), getString(R.string.dbx_app_secret));
if(!mDbxAccountManager.hasLinkedAccount()) {
return;
stopSelf();
}
...
}
}
The result is, after I link my app with dropbox using installed dropbox client, the hasLinkedAccount() on my Activity return true, meanwhile the same code on my Remote Service always return false.
I also check the logcat and it showed that my app already linked with dropbox.
My suspect is that the dropbox API create some SharedPreferences when it successfully link with my app, but my Remote Service can't access that or get a cached version of that SharedPreferences... I don't know...
Please help...
Thank you
Edited :
If I reinstall the app, then the result is as expected and hasLinkedAccount() return true, but if I uninstall and install again which cause clearing the user-data, then I link my app again with Dropbox, then the same strange behaviour appear again.
What I'm doing wrong? I'm turning my head almost 24-hours....
Solved!!!
After trying and trying...
I get conclusion that the Service that runs before the app linked with dropbox will always get DbxAccountManager.hasLinkedAccount() return false.
I try to kill the process by calling Process.killProcess(myservicePid) after I link my app with Dropbox and start the service again and it work.
So... I solved it by not starting the service before the app was linked with dropbox and start the service only if it already linked, because stopSelf() on the service doesn't kill the process.
I think this issue have something to do with the Context getApplicationContext() which is passed to DbxAccountManager.getInstance(), and I don't know why looks like the Context is not updated when the dropbox was link with the app.
Thank you.
I have a DropboxHelper Class that is handling downloading and uploading from dropbox.
Downloading works fine but when I try to upload from dropbox the first time the code is called. The following Line is false
if (dropboxFileSystem.isFile(dropboxPath)) {
}
It returns false. Tell the app to try again and this time it sees the file and uploads it to the app. Below is some of the code I am using for the class. Debug seems to incdicate the dropbox api has not completing started / synced the first time
public class DropBoxHelper {
public DropBoxHelper(Context pContext) {
context = pContext;
defineVariables();
}
private void defineVariables() {
dropboxAccountManager = DbxAccountManager.getInstance(context.getApplicationContext(), DROPBOX_APP_KEY, DROPBOX_APP_SECRET);
dropboxPath = new DbxPath(DbxPath.ROOT, DROPBOX_FILE_NAME);
}
public boolean importFromDropbox() {
try {
dropboxFileSystem = DbxFileSystem.forAccount(dropboxAccountManager.getLinkedAccount());
if (dropboxFileSystem.isFile(dropboxPath)) {
DbxFile databaseFileonDropbox = dropboxFileSystem.open(dropboxPath);
try {
// Do Copy
} finally {
Log.i(DEBUG_TAG, "Closing File");
databaseFileonDropbox.close();
}
}
Any ideas on why the copy fails first time.
Thanks
I'm not 100% sure, but I believe you need to use dropboxFileSystem.awaitFirstSync() to make sure at least one sync with the server has happened before you try to find the file.
An alternative might be to just call dropboxFileSystem.open(...) directly and handle the exception that's raised if the file doesn't exist.