I'm developing an app which needs to get music file by streaming for playing live.
In the request song api I can specify the bandwith (eg: 50kbps, 100kbps, 300, 600 or 1 Mbps).
The more the bandwith is big, the more the file will get time to be fetched. As I don't want the users to be restricted about that I have multiple choices to deal with it:
Detect wether the phone is using 3g, wifi or Edge and specify an bandwith for each connection speed.
Let the user decide the quality of the song he will get: like youtube (but the users won't be people that know much about computing: the more easy is the software, the more it will fit)
Having a way to evaluate properly the connection speed: Like fetching a file, measure the time that it took and set the bandwith.
I know that connection speed could vary a lot if user loose the wifi, or is using 3g moving in the street. And the thing is that I can't change the bandwidth when the song will be playing.
Maybe you have experience about that you would like to share?
Thank you!
Facebook released a library for this:
https://github.com/facebook/network-connection-class
this wasn't existing in 2011..
why not try to change your view of things.
Try to flow with your users. say your user wishes to download 128 kbit quality song. you start the download , WHILE downloading you make an average download time, take a few seconds for this average to stabilize, and if it's below certain value make a pop up to tell the user that his connection is too slow for the current bandwidth and ask him if to lessen the quality or to keep downloading slowly.
This will:
let the users the option to always assume they can get the best quality media.
let u do your check in runtime and change the quality accordingly while downloading without the need to pre check.
keeps your app simple to users.
I know i'm not answering your specific requirement, i'm just offering a different view.
protected String doInBackground(String... urls) {
String response = "";
startTime = System.currentTimeMillis();
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
endTime = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
}
}
return response;
}
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
long dataSize = result.length() / 1024;
takenTime = endTime - startTime;
long s = takenTime / 1000;
double speed = dataSize / s;
Toast.makeText(context, "" + s + "kbps", Toast.LENGTH_SHORT).show();
}
Detect network connection type on Android
You can check all available options here: http://developer.android.com/reference/android/telephony/TelephonyManager.html
This can fix the mobile network type but can't help you with the Wifi speed, you should code it by downloading something from a server you know and calculate the time.
I hope it helps.
Related
I'm developing an Android application for in-house of a certain company, and it needs to log the working time of employees. Therefore, the work with system time is crucial. My application badly needs to know when the user changes the system time. Big deal, you say, see this: Is there a way to detect when the user has changed the clock time on their device?
The problem is that the user may circumvent that solution by doing Force Stop of the application prior to changing the time. The application then won't receive system notification, which is brilliantly described here: https://stackoverflow.com/a/19856367/1309803
I don't mind checking that upon the next launch of the application, but how can I possibly know if the user has changed the time? I'm aware about SystemClock.elapsedRealtime(). I could figure time shift based on delta of those values provided that the user hasn't reboot the device, but this is what I'm unsure of. My application is subscribed to BOOT_COMPLETED event, but that one won't be received either while the application is in stopped state.
And, to cap it all, employees of that company are supposed to work in condition of having no network access, so I can't rely on Web servers. So is there any other possible approach?
Getting the time from the third-party servers is not reliable most of the times and some of them are paid services.
If you want to get the exact time and check with the phone whether it is correct or not, irrespective of the proper way, you can use the following simple trick to get the actual time.
private class GetActualTime extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
try {
HttpURLConnection urlConnection = null;
StringBuilder result = new StringBuilder();
try {
URL url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
int code = urlConnection.getResponseCode();
if (code == 200) {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = bufferedReader.readLine()) != null)
result.append(line);
in.close();
}
else {
return "error on fetching";
}
return result.toString();
} catch (MalformedURLException e) {
return "malformed URL";
} catch (IOException e) {
return "io exception";
} finally {
if (urlConnection != null) {urlConnection.disconnect();
}
}
} catch (Exception e) { return "null"; }
}
#Override
protected void onPostExecute(String time) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat mdformat = new SimpleDateFormat("h:mm");
String times = mdformat.format(calendar.getTime());
try {
String areatime = time.substring(time.indexOf(String.valueOf(times)), time.indexOf(String.valueOf(times)) + 5).trim();
Toast.makeText(this, "The actual time is " + areatime, Toast.LENGTH_SHORT).show();
}
catch(IndexOutOfBoundsException e){
Toast.makeText(this, "Mobile time is not same as Internet time", Toast.LENGTH_SHORT).show();
}
}
}
}
Call the class in the onCreate();
new GetActualTime().execute("https://www.google.com/search?q=time");
So this is actually getting the time from Google. This works pretty awesomely in my projects. In order to check whether the system time is wrong, you can use this trick. Instead of depending on the time servers, you can trust Google.
As it is more sensitive in checking, even a minute ahead or lag will catch the exception. You can customise the code if you want to handle that.
Previously I've asked some questions regarding what method I should use to save some data from my app to retrieve at a later time after it's been closed/stopped. I got the answers I was looking for, I think. But since then, my efforts to implement such a feature has fallen way short. I've researched various questions I've had, for which I thought I found answers. But it seems that the answers, while maybe correct, are not a match necessarily for each other. What I mean, is they might work separately, but coming from various sources, they don't work together as a whole, and for me they don't work at all. I'm led to believe I want to use SharedPreferences. That may or may not be the case, but that has been the direction of my efforts lately.
So I'll ask this multi-part question.
How would you go about saving an array of integers(or boolean values)?
Before loading that saved array, how would I check if it exists?
How would I load the array to use its values again?
Those are the basis of my issues right now. Even at this point, as frustrated as I may be, I don't mind doing more research if someone can point me in the right direction, but everywhere I've looked seems to be missing information and I'm unable to really understand/see how to code what I want to do.
If you REALLY want to see some code, I can show you all my broken pieces at the moment (what I haven't deleted), but I don't see it doing you any good. That said, I'll answer any questions you may need to help me out.
Thanks in advance.
EDIT: The array will change very little from app version to version. It should be about 500-2000 integers or boolean values (either/or will work the same for me). The array is basically a set of flags that tells the app to do one thing or another depending on the value. The size will only change if I add or remove items between versions. For this reason, after checking if the file/array exists, I'll compare the saved array with one in the app and act accordingly.
I've had similar issues with data that has to be preserved through a reboot. I found two ways to do it.
1) Data is seldom accessed.
Store data in .../files in some format that can be easily saved/retrieved. I used JSONArrays to hold the data. mContext.getFilesDir() will get you the path, and you can simply see if your file.exists() to determine if the data exists.
You will need to create an object that will:
1) convert your data to the stored format
for(int i = 0; i < mArray.size(); i++ )
{
JSONObject jo = new JSONObject();
jo.put("THINGY", mArray[i]);
ja.put(jo);
}
2) retrieve your data from the store
String js = readFromFile(fileName);
if( !js.isEmpty() )
ja = new JSONArray( js );
for( int i = 0; i < ja.length(); i++
{
// CONVERT THIS ARRAY TO YOUR INT...
}
3) read/write files like this:
private void writeToFile(String fileName, String data)
{
try
{
FileOutputStream fOut = openFileOutput( fileName, MODE_PRIVATE );
OutputStreamWriter osw = new OutputStreamWriter(fOut);
// Log.d(TAG, "Writing output log...");
osw.write(data);
osw.flush();
osw.close();
}
catch( Exception e )
{
Log.e(TAG, "Cannot create " + fileName );
}
}
private String readFromFile(String fileName)
{
String ret = "";
try
{
InputStream inputStream = openFileInput(fileName);
if ( inputStream != null )
{
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ( (receiveString = bufferedReader.readLine()) != null )
{
stringBuilder.append(receiveString);
}
inputStream.close();
ret = stringBuilder.toString();
}
}
catch (FileNotFoundException e)
{
Log.e(TAG, "File not found: " + e.toString());
}
catch (IOException e)
{
Log.e(TAG, "Can not read file: " + e.toString());
}
return ret;
}
JSON works well in this case because there are easy methods to convert to/from strings.
2) Frequent Access
I used SQLiteDatabase. I use a Singlton and DBHelper.getInstance() to get access to it. This seems like overkill, but it is a good solution if the amount of data you are saving is increasing beyond a simple array of Integers.
A really basic (but sound) place to start is: http://www.vogella.com/tutorials/AndroidSQLite/article.html. Note his page was written for 4.3.
HTH.
I will soon work on a project, which uses a lot of HTTPRequests for mainly JSONs and Images, so I thought it is a good idea to think about caching. Basically I'm looking for a solution for
Start a HTTPRequest with a given lifetime (f.e. 3,6,12 hours)
Check, if that Request is available in the Cache and still valid (lifetime)
If Request is still valid, take it from Cache, otherwise make the Request and save its Response
I found HttpResponseCache class in Android. It is working, however it is not working like I'm expecting.
My test case is an AsyncTask to cache several Images. Code looks like the following:
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
Bitmap myBitmap;
try {
connection.addRequestProperty("Cache-Control","only-if-cached");
//check if Request is in cache
InputStream cached = connection.getInputStream();
//set image if in cache
myBitmap = BitmapFactory.decodeStream(cached);
} catch (FileNotFoundException e) {
HttpURLConnection connection2 = (HttpURLConnection) url.openConnection();
connection2.setDoInput(true);
connection2.addRequestProperty("Cache-Control", "max-stale=" + 60);
connection2.connect();
InputStream input = connection2.getInputStream();
myBitmap = BitmapFactory.decodeStream(input);
}
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
}
Two questions:
I set max-stale=60seconds for testing purposes. However, if I call the same URL 5minutes later, it tells me, it is loading the image from cache. I would assume, that the image is reloaded because the HTTPRequest in the cache is out of date? Or do I have to clean the cache myself?
In my catch block, I have to create a second HttpURLConnection, because I cannot add properties, after I opened an URLConnection (this happens in the connection.getInputStream()?!). Is this bad programming?
After all, I find that HttpResponseCache poorly documented. I came across Volley: Fast Networking, but this seems even less documented, even if it is offering exactly the things I need (Request queuing and prioritization...). What do you use for caching? Any links to libraries, tutorials, are welcome.
UPDATE
I'm not targeting Android versions lower than 4.0 (still maybe intresting for other users?)
Both HttpResponseCache and volley are poorly documented. However, I have found that you
can very easily extend and tweak volley. If you explore source code of volley, especially of: CacheEntry, CacheDispatcher and HttpHeaderParser, you can see how it is implemented.
A CacheEntry holds serverDate, etag, ttl and sofTtl which can represent cache state pretty well, also it has isExpired() and refreshNeeded() methods as convenience.
CacheDispatcher is implemented accurately as well:
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
#Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
One interesting tidbit: If cache is "soft expired", volley will deliver data from local cache immediately, and re-deliver it from server again after some time, for single request.
Finally, HttpHeaderParser does its best to cope to server headers:
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0;
}
}
}
headerValue = headers.get("Expires");
if (headerValue != null) {
serverExpires = parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl) {
softExpire = now + maxAge * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = entry.softTtl;
entry.serverDate = serverDate;
entry.responseHeaders = headers;
So, ensure the server sends proper headers as well as honors etag,time-stamp and cache control headers.
Finally, you can override getCacheEntry() of Request class to return custom CacheEntry make cache behave exactly according to your needs.
Soryy. But why don't you use third-party libs for this? Try to use Volley lib for this. It maintains a cache out of the box and it is async out of the box. It works really good. Tutorials for Volley: one (with caching demonstration), two.
And there is another good one async, caching lib for android with good documentation - Retrofit. Here is Retrofit Caching Example.
And here is their comparison.
To enable caching, all you need to do is just install HTTP response cache at application startup by using below code:
File httpCacheDir = new File(context.getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
Whether the resource need to be fetched from network or cache, is taken care by HttpResponseCache. The age of cache is specified in the response headers of the resource request. For example this image, specifies cache age of 43200 seconds.
You can verify whether the resource is fetched from cache or network, by using following apis:
getHitCount : Number of Http requests which were served by cache.
getNetworkCount : Number of Http requests which were served by network.
Regarding max-stale, you have misunderstood it's purpose. It is used to permit stale cache responses. Here is it's definition from the rfc documentation :
Indicates that the client is willing to accept a response that has
exceeded its expiration time. If max-stale is assigned a value, then
the client is willing to accept a response that has exceeded its
expiration time by no more than the specified number of seconds. If no
value is assigned to max-stale, then the client is willing to accept a
stale response of any age.
Regarding cache control directive only-if-cached, only use it when you need to show something while your application is downloading latest content. So the question of handling new HttpUrlConnection in exception handler does not arise. From the docs:
Sometimes you'll want to show resources if they are available
immediately, but not otherwise. This can be used so your application
can show something while waiting for the latest data to be downloaded.
To restrict a request to locally-cached resources, add the
only-if-cached directive
One suggestion, add finally block, where you release the connection by calling disconnect.
I'm porting some of my Windows Phone 7 apps to Android. When we call services in the WP7 world, the calls are async. We call the service and there is a delegate _completed event that triggers when when the result is returned. Meanwhile we go on about our way.
The java android code pasted below is how I am calling an HTTP service on my cloud server. I developed this code by going through Android tutorials teaching how to call a service in the android world. Apparently, service calls here are synchronus so the instruction starting with InputStream in... doesn't get executed until the result is returned.
Is this how it is supposed to work for Android? If the service does not respond, there is a wait of a couple minutes and then a timeout exception takes place. That's no good. Everything will hang.
What is the reccommended way to call services in android?
Thanks, Gary
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HttpURLConnection urlConnection = null;
try
{
URL url = new URL("http://www.deanblakely.com/REST/api/products");
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
String myString = readStream(in);
String otherString = myString;
otherString = otherString + " ";
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
urlConnection.disconnect();
}
}
Is this how it is supposed to work for Android?
Yes. Do HTTP operations in a background thread, such as the one supplied by an AsyncTask.
If the service does not respond, there is a wait of a couple minutes and then a timeout exception takes place. That's no good. Everything will hang.
You can set a socket timeout to be something shorter than "a couple minutes". For example, you can call setReadTimeout() on your HttpURLConnection to specify a timeout period in milliseconds.
I have a big problem with parsing some json data which I get as response from a web server. The thing that I'm doing is I get the response via POST and than convert the response as string and parse it. But in some devices I get OutOfMemoryError , which I'm trying to fix. Here is how I'm converting the response to string :
public static String convertStreamToString(InputStream is) throws Exception {
ByteArrayOutputStream into = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
for (int n; 0 < (n = is.read(buf));) {
into.write(buf, 0, n);
}
into.close();
return new String(into.toByteArray(), "UTF-8");
}
and here is how I'm using this piece of code :
InputStream response = new BufferedInputStream(connection.getInputStream());
try {
String responsee = convertStreamToString(response);
jsonParser(responsee);
} catch (Exception e) {
e.printStackTrace();
cancelDialog("Error occurred! Please try again later.");
}
Any suggestions how can I fix that problem so don't happen in all devices?
Thanks in advance for any kind of help or advices.
The mobile has limited internal memory.
I have also face same issue. The solution that we found is that download only the necessary information. So you please confirm your requirement how much data you want to inside the mobile. If you filter the unnecessary data then the problem will get resolved.
Before testing the program on extreme condition first check whether simple download happening if it is happening then check the limit of your data means up to how extent it will not give out of memory error. and accordingly that rework your requirement.