Download a large file in background only on WiFi - java

So what I'm trying to achieve is creating a service that downloads a large (~200 MB) file to the app's internal directory. It should resume (not restart) when it got canceled and automatically resume after a system reboot. It should sometimes (as written in a propeties file) only download using WiFi connection.
To do so I created an IntentService (is that a suitable solution?) that gets started by an Frgament's UI or by a BroadcastReceiver (after boot-up):
#Override
public void onHandleIntent(final Intent intent) {
c.registerReceiver(receiver, new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION));
c.registerReceiver(receiver, new IntentFilter(
WifiManager.NETWORK_STATE_CHANGED_ACTION));
receiver.onReceive(null, null); // To start the download the first time
}
The last line calls a BroadcastReceiver that handles the download process. This is the way I tried to make sure that there is only one ongoing download at a time:
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean allowed = isConnectionAllowed();
if (allowed && !thread.isAlive()) {
thread.start();
} else if (allowed && thread.isAlive()) {
thread.notify(); // Can this cause any problems in this case?
} else if (!allowed && thread.isAlive()) {
try {
thread.wait();
} catch (InterruptedException e) {
if (StartActivity.DEV_MODE)
Log.i(StartActivity.LOG_TAG, Log.getStackTraceString(e));
}
}
}
};
And the download itselv runs in a seperate Thread.
private Thread thread = new Thread() {
#SuppressWarnings("resource")
#Override
public void run() {
HttpURLConnection connection = null;
BufferedInputStream input = null;
RandomAccessFile output = null;
try {
long already_downloaded = 0;
URL url = new URL(getSource());
File outputFileCache = new File(getDestination());
connection = (HttpURLConnection) url.openConnection();
if (outputFileCache.exists()) {
connection.setAllowUserInteraction(true);
connection.setRequestProperty("Range", "bytes="
+ outputFileCache.length() + "-");
}
connection.setConnectTimeout(14000);
connection.setReadTimeout(20000);
connection.connect();
if (connection.getResponseCode() / 100 != 2)
throw new Exception("Invalid response code!");
else {
String connectionField = connection
.getHeaderField("content-range");
if (connectionField != null) {
String[] connectionRanges = connectionField.substring(
"bytes=".length()).split("-");
already_downloaded = Long.valueOf(connectionRanges[0]);
}
if (connectionField == null && outputFileCache.exists())
outputFileCache.delete();
long fileLength = connection.getContentLength()
+ already_downloaded;
input = new BufferedInputStream(connection.getInputStream());
output = new RandomAccessFile(outputFileCache, "rw");
output.seek(already_downloaded);
byte data[] = new byte[1024];
int count = 0;
int progress = 0;
int progress_last_value = 0;
while ((count = input.read(data, 0, 1024)) != -1
&& progress != 100) {
already_downloaded += count;
output.write(data, 0, count);
progress = (int) ((already_downloaded * 100) / fileLength);
if (progress != progress_last_value) {
// Update notification
}
}
}
} catch (MalformedURLException e) {
if (StartActivity.DEV_MODE)
Log.i(StartActivity.LOG_TAG, Log.getStackTraceString(e));
} catch (IOException e) {
if (StartActivity.DEV_MODE)
Log.i(StartActivity.LOG_TAG, Log.getStackTraceString(e));
} catch (Exception e) {
if (StartActivity.DEV_MODE)
Log.i(StartActivity.LOG_TAG, Log.getStackTraceString(e));
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
if (connection != null)
connection.disconnect();
} catch (IOException e) {
if (StartActivity.DEV_MODE)
Log.i(StartActivity.LOG_TAG, Log.getStackTraceString(e));
}
}
// notify the Fragment using a `LocalBroadcast`.
}
};
And to make sure that the WiFi connection ist kept alive, I use the WifiLock class.
Note: I shortened the code in a few cases but I already managed to write that code.
So I'm not sure whether this is a good solution for my problem:
Should I use an IntentService or a normal Service for that?
Is that solution using a Thread a good one and will it work?
Won't the HttpURLConnection expire?
Can I access the Thread that way? (referres to wait() and notify())
I know this is a lot of code but it was the closest I could get. I hope someone knows a solution for that becuase I couldn't find anything usful on that topic ("only download while WiFi in ON") using Google and StackOverflow.
Thanks in advance,
Felix
(Comment if you need the whole Class)

Related

How can I delay a funtion call? [duplicate]

This question already has answers here:
How to call a method after a delay in Android
(35 answers)
Closed 4 years ago.
public class NotificationReceivedCheckDelivery extends NotificationExtenderService {
#Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
OverrideSettings overrideSettings = new OverrideSettings();
overrideSettings.extender = new NotificationCompat.Extender() {
#Override
public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
// Sets the background notification color to Yellow on Android 5.0+ devices.
return builder.setColor(new BigInteger("FFFFEC4F", 16).intValue());
}
};
OSNotificationDisplayedResult displayedResult = displayNotification(overrideSettings);
Log.d("ONES",receivedResult.payload.title);
JSONObject AdditionalData = receivedResult.payload.additionalData;
Log.d("Adata",AdditionalData.toString());
String uuid= null;
try{
// {"uuid":"adddd"}
uuid = AdditionalData.getString("uuid");
}
catch (JSONException e){
Log.e("Error JSON","UUID",e);
}
// Create Object and call AsyncTask execute Method
new FetchNotificationData().execute(uuid);
return true;
}
private class FetchNotificationData extends AsyncTask<String,Void, String> {
#Override
protected String doInBackground(String... uuids) {
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
try {
URL url = new URL("http://test.com/AppDeliveryReport?uuid="+uuids[0]);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
return forecastJsonStr;
} catch (IOException e) {
Log.e("PlaceholderFragment", "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("PlaceholderFragment", "Error closing stream", e);
}
}
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.i("json", s);
}
}
}
I want to delay calling the FetchNotificationData function with a random seconds.
This is a delivery report url request function. Whenever a notification from onesignal received at the app it will call the url. I don't want to blast the server with huge request at once. So I want to delay call with random seconds so that server will have to serve few calls on a given time.
You can use handler to delay call to function
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(Splash.this, "I will be called after 2 sec",
Toast.LENGTH_SHORT).show();
//Call your Function here..
}
}, 2000); // 2000 = 2 sec
you can use Handler like this
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// your FetchNotificationData function
}
},timeInMiliSec);
just remember to import Handler as android.os, not java.util.logging
timer = new Timer();
final int FPS = 3;
TimerTask updateBall = new UpdateBallTask();
timer.scheduleAtFixedRate(updateBall, 0, 1000 * FPS);
class:
class UpdateBallTask extends TimerTask {
public void run() {
// do work
}
}
///OR
final Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
handler.postDelayed(this, 100);
// do work
handler.removeCallbacksAndMessages(null);
}
};
handler.postDelayed(r, 100);

Android Apk which is downloaded programmatically from Google Drive, is not installing, says "Unable to parse package"

I'm working on sample project. I'm trying to download apk from Google drive (by sharable link) and trying to install an update to already existing application
This is my code to download and install the update:
class DownloadApkTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL("https://drive.google.com/file/d/0B2U-9Im6jS66dHFXLTFIX0JQbnM/view?usp=sharing");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
String error = "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/tcr_update.apk");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
output.write(data, 0, count);
}
} catch (Exception e) {
Log.d("TAG", "doInBackground: "+e.getMessage());
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
Below code is for opening the application installer
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
File apkFile = new File("/sdcard/tcr_update.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
startActivity(intent);
}
}

How to read sensor data in android bluetooth not single byte

I've read data from sensor in android app via bluetooth using following code
try {
int byteCount = inputStream.available();
if (byteCount > 0) {
count++;
byte[] rawBytes = new byte[byteCount];
inputStream.read(rawBytes);
final String string = new String(rawBytes, "UTF-8");
handler.post(new Runnable() {
public void run() {
textView.append(string);
textView.append("\n");
}
});
}
} catch (IOException ex) {
stopThread = true;
}
This code read 10 as 1 and 0, So I want to read whole string . What will be required code for it?

Wrong messages sequence with socket DataInputStream BufferedInputStream in Android app

I have a problem with receiving irregular sequence of the byte messages I send from another device.
The setup is the following: I have an Android app (client) and Real-Time system (server) with Ethernet both connected in a LAN through router, which talk with raw bytes communication.
From the Android app I send request, which causes the server to respond with several messages - the first one with 8 bytes, the following messages have 27 bytes. I have debugged the server and I am sure the first message it sends is the 8th-byte one, followed by the others.
About the app - I use the Main Activity to handle transmission of data through the socket, and additional thread to handle reception of data.
The thread makes post through Handler to the Main Activity, when new data has been received. In this post is called a process to parse the received data.
TbProtocolProcessor is a class I use to handle my custom protocol. It can create a byte array for me to send as request for specific function, and it has a state-machine to process expected response from the server. InetHandler is nested class I use to handle my connectivity only.
My question is - why would my Android app return me the first message having size 8, but contents like the next messages? Interesting effect is that if I send ONLY the 8-byte message, without any others, it is received and passed to my app correctly.
Here is the code:
public class MainActivity extends AppCompatActivity
{
private TbProtocolProcessor tbProtPrcs = null;
private InetHandler inetHandler = new InetHandler(this);
private static Handler msgHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tbProtPrcs = new TbProtocolProcessor(this);
}
// Implementation of InetControl interface
public void ConnectToIP(String strIP, int port)
{
inetHandler.AttachToIP(strIP, port);
}
public void Disconnect()
{
inetHandler.DetachFromIP();
}
public void GetFilesList()
{
byte[] data = TbProtocolProcessor.buildFilesGetList();
inetHandler.SendData(data, data.length);
TbProtocolProcessor.setExpectedResult(
TbProtocolProcessor.TB_STATE_WAIT_MUL_FILESLIST,
data[1],
1);
}
private class InetHandler
{
protected static final int cTARGET_PORT_UNASSIGNED = 0xFFFF;
protected String targetIP = null;
protected int targetPort = cTARGET_PORT_UNASSIGNED;
protected boolean isConnected = false;
protected Socket socket = null;
protected DataOutputStream sockStrmOut = null;
protected DataInputStream sockStrmIn = null;
protected Context context = null;
public InetHandler(Context ctx) {
if (ctx != null)
{
context = ctx;
}
}
class ClientThread implements Runnable {
byte[] indata = new byte[100];
int inCntr;
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(targetIP);
socket = new Socket(serverAddr, targetPort);
socket.setKeepAlive(true);
// DataOutputStream is used to write primitive data types to stream
sockStrmOut = new DataOutputStream(socket.getOutputStream());
sockStrmIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
if (socket.isConnected()) {
isConnected = true;
//Toast.makeText(context, "CONNECTED", Toast.LENGTH_SHORT).show();
//findViewById(R.id.action_connect).setBackgroundColor(0xFF60FF60);
}
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// TODO:
while (isConnected) {
try {
inCntr = sockStrmIn.read(indata);
}
catch (IOException e) {
e.printStackTrace();
}
if (inCntr > 0) {
msgHandler.post(new Runnable() {
#Override
public void run() {
if ( tbProtPrcs.Process(indata, inCntr) ) {
Toast.makeText(context, "Operation Success", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(context, "Operation ERROR", Toast.LENGTH_SHORT).show();
}
}
});
}
}
}
}
public void AttachToIP(String sIP, int iPort)
{
if ( (isIPValid(sIP)) && (iPort < cTARGET_PORT_UNASSIGNED) )
{
targetIP = sIP;
targetPort = iPort;
// Start the connection thread
new Thread(new ClientThread()).start();
}
}
public void DetachFromIP()
{
try {
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
public boolean SendData(byte[] data, int size)
{
boolean bResult = false;
try
{
if ( (data != null) && (size > 0) && (sockStrmOut != null) ) {
Toast.makeText(context, "Sending...", Toast.LENGTH_SHORT).show();
sockStrmOut.write(data, 0, size);
bResult = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return bResult;
}
public boolean isIPValid (String ip) {
try {
if (ip == null || ip.isEmpty()) {
return false;
}
String[] parts = ip.split( "\\." );
if ( parts.length != 4 ) {
return false;
}
for ( String s : parts ) {
int i = Integer.parseInt( s );
if ( (i < 0) || (i > 255) ) {
return false;
}
}
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
}
}
You're assuming that read() fills the buffer. It isn't specified to do that. See the Javadoc. If you want to fill the buffer you must use readFully().
NB isConnected() cannot possibly be false at the point you're testing it.

Maintaining a TCP Connection in an AsyncTask

I'm using an AsyncTask to establish a TCP Connection and sending/receiving data through it.
My current Code looks like this at the moment:
public class NetworkTask extends AsyncTask<Void, byte[], Boolean> {
Socket nsocket; //Network Socket
InputStream nis; //Network Input Stream
OutputStream nos; //Network Output Stream
boolean bSocketStarted = false;
byte[] buffer = new byte[4096];
#Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute");
}
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
// Connect to address
Log.i(TAG, "doInBackground: Creating socket");
SocketAddress sockaddr = new InetSocketAddress("google.de", 80);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
bSocketStarted = true;
nis = nsocket.getInputStream();
nos = nsocket.getOutputStream();
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
int read = nis.read(buffer, 0, 4096); //This is blocking
while(bSocketStarted) {
if (read > 0){
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
publishProgress(tempdata);
Log.i(TAG, "doInBackground: Got some data");
}
}
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "doInBackground: IOException");
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "doInBackground: Exception");
result = true;
} finally {
try {
nis.close();
nos.close();
nsocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i(TAG, "doInBackground: Finished");
}
return result;
}
public boolean SendDataToNetwork(final byte[] cmd) { //You run this from the main thread.
// Wait until socket is open and ready to use
waitForSocketToConnect();
if (nsocket.isConnected()) {
Log.i(TAG, "SendDataToNetwork: Writing received message to socket");
new Thread(new Runnable() {
public void run() {
try {
nos.write(cmd);
}
catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "SendDataToNetwork: Message send failed. Caught an exception");
}
}
}
).start();
return true;
}
else
Log.i(TAG, "SendDataToNetwork: Cannot send message. Socket is closed");
return false;
}
public boolean waitForSocketToConnect() {
// immediately return if socket is already open
if (bSocketStarted)
return true;
// Wait until socket is open and ready to use
int count = 0;
while (!bSocketStarted && count < 10000) {
try {
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
count += 500;
}
return bSocketStarted;
}
#Override
protected void onProgressUpdate(byte[]... values) {
try {
if (values.length > 0) {
Log.i(TAG, "onProgressUpdate: " + values[0].length + " bytes received.");
String str = new String(buffer, "UTF8");
Log.i(TAG,str);
tv.setText(str);
tv.setMovementMethod(new ScrollingMovementMethod());
}
} catch (Exception e) {
e.printStackTrace();
}
finally {}
}
#Override
protected void onCancelled() {
Log.i(TAG, "Cancelled.");
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
Log.i(TAG, "onPostExecute: Completed with an Error.");
} else {
Log.i(TAG, "onPostExecute: Completed.");
}
}
}
I can instantiate the Task and call SendDataToNetwork from my activity. However, all the text I pass to SendDataToNetwork, for example, 'GET / HTTP/1.1' is continously sent to the server.
How can I modify my Code to maintain the connection in doInBackground and do nothing until I call SendDataToNetwork and after sending bytes to the server just wait until new data is ready to be sent? Basically I want to run the AsyncTask until I explicitly cancel (= close the connection) it.
nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
The test is pointless. If the socket wasn't connected, connect() would have thrown an exception.
Your read loop is also fundamentally flawed, in that it doesn't keep reading. There are standard solutions as to how to read a stream, e.g.:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Your waitForSocketToConnect() method doesn't really do anything useful either.
You need to rethink all this.

Categories