I have an android activity, which connects to a java class and sends data packets to it in form of sockets. The class receives the sound packets and throws them to PC speakers. The code is working excellently, but there is a constant jitter/ interruption while the sound is played in PC speakers.
The android activity:
public class SendActivity extends Activity {
private Button startButton, stopButton;
public byte[] buffer;
public static DatagramSocket socket;
private int port = 50005;
AudioRecord recorder;
private int sampleRate = 8000;
#SuppressWarnings("deprecation")
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
audioFormat);
private boolean status = true;
int bufferSizeInBytes;
int bufferSizeInShorts;
int shortsRead;
short audioBuffer[];
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send);
startButton = (Button) findViewById(R.id.start_button);
stopButton = (Button) findViewById(R.id.stop_button);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
status = true;
startStreaming();
}
});
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
status = false;
recorder.release();
Log.d("VS", "Recorder released");
}
});
minBufSize += 5120;
System.out.println("minBufSize: " + minBufSize);
}
public void startStreaming() {
Thread streamThread = new Thread(new Runnable() {
#Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket();
Log.d("VS", "Socket Created");
byte[] buffer = new byte[minBufSize];
Log.d("VS", "Buffer created of size " + minBufSize);
DatagramPacket packet;
//machine's IP
final InetAddress destination = InetAddress
.getByName("192.168.1.20");
Log.d("VS", "Address retrieved");
recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
sampleRate, channelConfig, audioFormat,
minBufSize * 10);
Log.d("VS", "Recorder initialized");
recorder.startRecording();
while (status == true) {
// reading data from MIC into buffer
minBufSize = recorder.read(buffer, 0, buffer.length);
// putting buffer in the packet
packet = new DatagramPacket(buffer, buffer.length,
destination, port);
socket.send(packet);
System.out.println("MinBufferSize: " + minBufSize);
}
} catch (UnknownHostException e) {
Log.e("VS", "UnknownHostException");
} catch (IOException e) {
e.printStackTrace();
Log.e("VS", "IOException");
}
}
});
streamThread.start();
}
}
The android layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".SendActivity" >
<Button
android:id="#+id/stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/start_button"
android:layout_alignBottom="#+id/start_button"
android:layout_toRightOf="#+id/start_button"
android:text="Stop" />
<Button
android:id="#+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="79dp"
android:layout_marginTop="163dp"
android:text="Start" />
</RelativeLayout>
Android Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.audiostreamsample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.audiostreamsample.SendActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The class to receive the data packets and throw them to the PC speakers:
class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 8000;
public static void main(String args[]) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(50005);
/**
* Formula for lag = (byte_size/sample_rate)*2
* Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken.
* Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken.
* Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728.
*/
byte[] receiveData = new byte[5000];
format = new AudioFormat(sampleRate, 16, 1, true, false);
while (status == true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
serverSocket.receive(receivePacket);
ByteArrayInputStream baiss = new ByteArrayInputStream(
receivePacket.getData());
ais = new AudioInputStream(baiss, format, receivePacket.getLength());
toSpeaker(receivePacket.getData());
}
}
public static void toSpeaker(byte soundbytes[]) {
try {
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(6.0206f);
sourceDataLine.start();
sourceDataLine.open(format);
sourceDataLine.start();
System.out.println("format? :" + sourceDataLine.getFormat());
sourceDataLine.write(soundbytes, 0, soundbytes.length);
System.out.println(soundbytes.toString());
sourceDataLine.drain();
sourceDataLine.close();
} catch (Exception e) {
System.out.println("Not working in speakers...");
e.printStackTrace();
}
}
}
If you want to test the app in your IDE, then simply create two different projects, one for the android app and one for the server class.
In the android app just add the IP of your machine and run the app on a device, the mobile and the computer should belong to the same network. Please execute the server class as a java application.
The jitter will be prominent and irritating but the voices will be more or less clear. Please suggest me what to do to get a clearer output.
You need to have some coded support for actual streaming.
There's a little more to consider than just sending datagrams and hoping for the best.
Real networks are not perfect.
Delay: packets take time
Jitter : the time a packet takes in flight is not constant
Dropped packets: sometimes they don't make it.
Reordering : sometimes packets arrive in a different order to the sending.
You should read up on simple media streaming protocols like RTP and perhaps use a library that provides RTP to both ends. RTP commonly sits atop UDP.
TCP streaming for audio can be less helpful than UDP/RTP , as you'd have to turn off Nagling.
You will at a minimum need a small buffer at the receiver end to prevent buffer empty causing sound dropouts.
Related
I am trying to make possible for users to update ann app from device. We have a server where from user can download apk file with new version of an app.
Here is the code which starts after button pressed:
Handler handler = new Handler(Looper.getMainLooper());
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
File folder = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString());
File file = new File(folder.getAbsolutePath(), "localdb.apk");
final Uri uri = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) ?
FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file) :
Uri.fromFile(file);
if (file.exists())
file.delete();
//url to get app from server
String url = Names.UPDATE_URL;
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL sUrl = new URL(url);
connection = (HttpURLConnection) sUrl.openConnection();
connection.connect();
// download the file
input = connection.getInputStream();
output = new FileOutputStream(file);
byte data[] = new byte[4096];
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
output.write(data, 0, count);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
handler.post(new Runnable() {
#Override
public void run() {
Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE)
.setData(uri)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(install);
}
});
}
});
thread.start();
Here is what i have in manifest:
...<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUICKBOOT_POWERON" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="ru.mob.myapp.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
...
and finally an xml file provider_paths:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
So, everything works fine, I download apk with new version, it is named same as already installed,
system asks me to install (update) my application, but when everything is almoust done I get "App is not installed" message without any details.
Does anyone have a solution, hint, adviсe?
I am trying to implement a telnet client in android that is going to continuously read data from a telnet server. I have verified that the server is sending data continuously by connecting to it from a telnet app from play store.
Now im trying to create an app to do the same and display the data on the screen using apache telnetclient. I have successfully connected to the telnet server, and I am able to read the correct lines for 5-10 seconds. The code is posted below.
What works:
Connecting to server.
Reading some lines for 5-10 seconds before the stream stops.
The problem:
The reading seems to stop after around 5-10 seconds, and hangs on readLine() with no new data coming in. Is this a problem with the code? Any help would be appreciated!
UPDATE:
I am trying to run this on an Android 4.2.2 device. I tried it on a newer android 7 device just now, and the code works as intended, I get continuous readings. Is there something in the android version that can be causing this?
--
This is the TestTelnetClient.class
public class TestTelnetClient extends Activity {
private TelnetClient telnet = new TelnetClient();
private InputStream in;
private PrintStream out;
private String server = "192.168.36.105";
TextView outputView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
outputView = (TextView)findViewById(R.id.output);
}
public void sendTelnet(View view) {
TelnetRead telnetRead = new TelnetRead();
telnetRead.execute();
}
private class TelnetRead extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
try {
// Connect to the specified server
telnet.connect(server, 8110);
// Get input and output stream references
in = telnet.getInputStream();
//out = new PrintStream(telnet.getOutputStream());
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String aad = r.readLine();
while (true) {
publishProgress(aad);
aad = r.readLine();
}
//telnet.disconnect();
//finish();
//return aad;
//return "exit";
}
catch (Exception e) {
e.printStackTrace();
return e.toString();
}
}
#Override
protected void onPostExecute(String result) {
try {
outputView.setText(result);
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
#Override
protected void onPreExecute() {
}
//#Override
protected void onProgressUpdate(String... result) {
outputView.setText(result[0]);
}
}
This is the android manifest
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".TestTelnetClient"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
I am building an application which requires the mic to cancel any sound coming from the speaker. It seems that this issue is almost a conspiracy on-line as others with the exact same problem were never responded to for an extended duration.
Android's native hardware accelerated AcousticEchoCanceler does not seem to work on most devices. Tests where made on many devices and the ones that seemed to work Include Nexus 5, and Moto X while almost all Samsung devices tested could not remove background sound.Note: All phones tested return true for AcousticEchoCanceler.isAvailable()
However, there must be a solution since applications such as Skype or WhatsApp seem to cancel sounds outside their app context, i.e. a call is on speaker and the Microphone cancels any feedback received.
This simplified recording app records sound to a file and plays it later when play is clicked.
MainActivity.java
public class MainActivity extends Activity {
Button startRec, stopRec, playBack;
int minBufferSizeIn;
AudioRecord audioRecord;
short[] audioData;
Boolean recording;
int sampleRateInHz = 48000;
private String TAG = "TAG";
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startRec = (Button) findViewById(R.id.startrec);
stopRec = (Button) findViewById(R.id.stoprec);
playBack = (Button) findViewById(R.id.playback);
startRec.setOnClickListener(startRecOnClickListener);
stopRec.setOnClickListener(stopRecOnClickListener);
playBack.setOnClickListener(playBackOnClickListener);
playBack.setEnabled(false);
startRec.setEnabled(true);
stopRec.setEnabled(false);
minBufferSizeIn = AudioRecord.getMinBufferSize(sampleRateInHz,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audioData = new short[minBufferSizeIn];
audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,
sampleRateInHz,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSizeIn);
}
OnClickListener startRecOnClickListener
= new OnClickListener() {
#Override
public void onClick(View arg0) {
playBack.setEnabled(false);
startRec.setEnabled(false);
stopRec.setEnabled(true);
Thread recordThread = new Thread(new Runnable() {
#Override
public void run() {
recording = true;
startRecord();
}
});
recordThread.start();
}
};
OnClickListener stopRecOnClickListener
= new OnClickListener() {
#Override
public void onClick(View arg0) {
playBack.setEnabled(true);
startRec.setEnabled(false);
stopRec.setEnabled(false);
recording = false;
}
};
OnClickListener playBackOnClickListener
= new OnClickListener() {
#Override
public void onClick(View v) {
playBack.setEnabled(false);
startRec.setEnabled(true);
stopRec.setEnabled(false);
playRecord();
}
};
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void startRecord() {
File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");
try {
FileOutputStream outputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);
NoiseSuppressor ns;
AcousticEchoCanceler aec;
if (NoiseSuppressor.isAvailable()) {
ns = NoiseSuppressor.create(audioRecord.getAudioSessionId());
if (ns != null) {
ns.setEnabled(true);
} else {
Log.e(TAG, "AudioInput: NoiseSuppressor is null and not enabled");
}
}
if (AcousticEchoCanceler.isAvailable()) {
aec = AcousticEchoCanceler.create(audioRecord.getAudioSessionId());
if (aec != null) {
aec.setEnabled(true);
} else {
Log.e(TAG, "AudioInput: AcousticEchoCanceler is null and not enabled");
}
}
audioRecord.startRecording();
while (recording) {
int numberOfShort = audioRecord.read(audioData, 0, minBufferSizeIn);
for (int i = 0; i < numberOfShort; i++) {
dataOutputStream.writeShort(audioData[i]);
}
}
audioRecord.stop();
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
void playRecord() {
File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");
int shortSizeInBytes = Short.SIZE / Byte.SIZE;
int bufferSizeInBytes = (int) (file.length() / shortSizeInBytes);
short[] audioData = new short[bufferSizeInBytes];
try {
FileInputStream inputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
int i = 0;
while (dataInputStream.available() > 0) {
audioData[i] = dataInputStream.readShort();
i++;
}
dataInputStream.close();
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC, sampleRateInHz,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);
while(audioTrack.getState() != AudioTrack.STATE_INITIALIZED){
}
audioTrack.play();
audioTrack.write(audioData, 0, bufferSizeInBytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<Button
android:id="#+id/startrec"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Start Recording Test" />
<Button
android:id="#+id/stoprec"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stop Recording" />
<Button
android:id="#+id/playback"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Play Back" />
</LinearLayout>
AndroidManfist.xml permissions
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
To Verify if the device works simply play something in the background and then click Start Recording record a small sector and then click Stop Recording at this point click Play Back and check if you hear the background sound. If you can hear the background sound then AEC is not working.
But why is this inconsistency occurring, or how do I achieve echo cancellation (I am already using WebRTC within my app for noise cancellation within my apps context)
Any help would be appreciated !
I was having the same problem on my S6 device. I played with a variety of settings and found a set that seem to enable AEC. The differences between my and your setup seem to be:
16k sample rate
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
For others, I'm not sure precisely what settings are needed to get AEC working. I do know that my same app with
48k sample rate
NO audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
NO android.permission.MODIFY_AUDIO_SETTINGS
does not successfully AEC.
Problem:
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC, sampleRateInHz,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);
should be:
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC, sampleRateInHz,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM
sessionId); // this param is important, which is audioRecord. getAudioSessionId()
public void downdown(View view) {
try {
URL url = new URL("http://myserver.com:7005/media/databases/myfile.db");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
File SDCardRoot = Environment.getExternalStorageDirectory();
File file = new File(SDCardRoot,"myfile.db");
FileOutputStream fileOutput = new FileOutputStream(file);
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
fileOutput.write(buffer, 0, bufferLength);
downloadedSize += bufferLength;
//actualizaProgreso(downloadedSize, totalSize);
}
fileOutput.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
And manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.downloader"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="17"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Error:
When i press the button download (this button calls downdown function: this function downloads a file from a server) i get the following error:
java.lang.IllegalStateException: Could not execute method of the activity
public void downdown(View view) {
new Thread() {
public void run() {
downdown();
}
}.start();
}
public void downdown(){
try {
URL url = new URL("http://myserver.com:7005/media/databases/myfile.db");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
File SDCardRoot = Environment.getExternalStorageDirectory();
File file = new File(SDCardRoot,"myfile.db");
FileOutputStream fileOutput = new FileOutputStream(file);
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
fileOutput.write(buffer, 0, bufferLength);
downloadedSize += bufferLength;
//actualizaProgreso(downloadedSize, totalSize);
}
fileOutput.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
You cannot have network actions within the main thread.
Look into AsyncTask and multi-Threading.
http://developer.android.com/reference/android/os/AsyncTask.html
Android : Loading an image from the Web with Asynctask
If you really want to improve you skills I suggest not using an instance of thread to start your process. I suggest using ExecutorService
Here is a guide to how to use http://tutorials.jenkov.com/java-util-concurrent/scheduledexecutorservice.html
I have read plenty of tutorial and posts here on SO regarding the use of WakeLock and WifiLock, but still didn't get to a solution of my issue.
I'm writing an app that has, when you start it, the only effect of creating and starting a (foreground) service. This service run two threads that are an UDP broadcast listener (using java.io) and a TCP server (using java.nio). In the onCreate of the service I acquire a wakelock and a wifilock, and I release them in the onDestroy.
Everything works fine as far as the phone is awake, but when the display goes off the UDP broadcast receiver stops receiving broadcast messages, and will not receive any until I switch on again the display. Practically, the locks are not working at all and there is no difference in put them...where am I wrong? I'm sure I'm doing something stupid somewhere, but can't find it by myself.
Here is some code:
This is what Activity does:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onlyStartService = true;
}#Override
protected void onStart() {
super.onStart();
Intent bIntent = new Intent(this, FatLinkService.class);
getApplicationContext().startService(bIntent);
getApplicationContext().bindService(bIntent, flConnection, BIND_AUTO_CREATE);
}
// service connection to bind idle service
private ServiceConnection flConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
linkService.MyLinkBinder flBinder = (LinkService.MylinkBinder) binder;
flServiceInstance = flBinder.getService();
if (onlyStartService) {
condLog("Service bound and finishing activity...");
finish();
}
}
public void onServiceDisconnected(ComponentName className) {
flServiceInstance = null;
}
};
#Override
protected void onStop() {
super.onStop();
if (fatFinish) {
Intent bIntent = new Intent(this, FatLinkService.class);
flServiceInstance.stopServices();
flServiceInstance.stopForeground(true);
flServiceInstance.stopService(bIntent);
condLog("Service stop and unbound");
flServiceInstance = null;
}
getApplicationContext().unbindService(flConnection);
}
This is how service is:
public class LinkService extends Service {
InetAddress iaIpAddr, iaNetMask, iaBroadcast;
private final IBinder mBinder = new MyLinkBinder();
private linklistenBroadcast flBroadServer = null;
private linkTCPServer flTCPServer = null;
private linkUDPClient flBroadClient = null;
List<String> tokens = new ArrayList<String>();
private PowerManager.WakeLock wakeLock;
private WifiManager.WifiLock wifiLock;
public class MylLinkBinder extends Binder {
lLinkService getService() { return LinkService.this; }
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate() {
super.onCreate();
getLocks();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
instantiateServices();
// notifies presence to other fat devices
condLog("Service notifying fat presence...");
flBroadClient = new LinkUDPClient();
flBroadClient.startSending(LinkProtocolConstants.BRCMD_PRESENCE + String.valueOf(LinkProtocolConstants.tcpPort), iaBroadcast, LinkProtocolConstants.brPort);
return START_STICKY;
}
public void getLocks() {
// acquire a WakeLock to keep the CPU running
condLog("Acquiring power lock");
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL , "MyWifiLock");
wifiLock.acquire();
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
}
public void stopServices() {
if (flTCPServer != null)
flTCPServer.stopServer();
if (flBroadServer != null)
flBroadServer.stopSelf();
}
private void instantiateServices() {
populateAddresses(); // just obtain iaIpAddr
if (flTCPServer == null) {
condLog("Instantiating TCP server");
flTCPServer = new LinkTCPServer(iaIpAddr, FatLinkProtocolConstants.tcpPort);
flTCPServer.execute();
}
if (flBroadServer == null) {
condLog("Instantiating UDP broadcast server");
Intent notifyIntent = new Intent(this, LinkMain.class); // this is the main Activity class
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notifyIntent.setAction("FROM_NOTIFICATION");
PendingIntent notifyPIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
Notification fixNotification = new Notification.Builder(getApplicationContext())
.setContentTitle("Link")
.setSmallIcon(R.mipmap.imgLink)
.setContentIntent(notifyPIntent)
.build();
startForeground(1234, fixNotification);
flBroadServer = new LinklistenBroadcast();
flBroadServer.start();
}
}
private final class LinklistenBroadcast extends Thread {
private boolean bStopSelf = false;
DatagramSocket socket;
public void stopSelf() {
bStopSelf = true;
socket.close();
}
#Override
public void run() {
condLog( "Listening broadcast thread started");
bStopSelf = false;
try {
//Keep a socket open to listen to all the UDP trafic that is destinated for this port
socket = new DatagramSocket(null);
socket.setReuseAddress(true);
socket.setSoTimeout(LinkGeneric.BR_SOTIMEOUT_MILS);
socket.setBroadcast(true);
socket.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), FatLinkProtocolConstants.brPort));
while (true) {
condLog("Ready to receive broadcast packets...");
//Receive a packet
byte[] recvBuf = new byte[1500];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
try {
socket.receive(packet);
} catch (InterruptedIOException sException) {
condLog(sockExcept.toString());
break;
} catch (SocketException sockExcept) {
condLog(sockExcept.toString());
}
if (bStopSelf) {
condLog("Broadcast server stopped...");
break;
}
int len = packet.getLength();
String datarecvd = new String(packet.getData()).trim();
//datarecvd = datarecvd.substring(0, len);
//Packet received
String message = new String(packet.getData()).trim();
condLog("<<< broadcast packet received from: " + packet.getAddress().getHostAddress() + " on port: " + packet.getPort() + ", message: " + message);
if (packet.getAddress().equals(iaIpAddr)) {
condLog("Ooops, it's me! discarding packet...");
continue;
}
else
condLog("<<< Packet received; data size: " + len + " bytes, data: " + datarecvd);
//See if the packet holds the right command (message)
// protocol decode
// here do some tuff
} catch (IOException ex) {
condLog(ex.toString());
}
if (socket.isBound()) {
condLog( "Closing socket");
socket.close();
}
condLog( "UDP server thread end.");
flTCPServer = null;
flBroadServer = null;
}
public boolean isThreadRunning() {
return !bStopSelf;
};
}
// Utility functions
public boolean checkBroadcastConnection (DatagramSocket socket, int timeOutcycles) {
int tries = 0;
while (!socket.isConnected()) {
tries++;
if (tries >= timeOutcycles)
return false;
}
return true;
}
#Override
public void onDestroy() {
super.onDestroy();
if (wakeLock != null) {
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
if (wifiLock != null) {
if (wifiLock.isHeld()) {
wifiLock.release();
}
}
}
}
And, finally, here is the manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxxxxx.ink" >
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
android:allowBackup="true"
android:icon="#mipmap/imgLinkmascotte"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".LinkMain"
android:label="#string/app_name"
android:theme="#android:style/Theme.Translucent.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LinkService" />
</application>
</manifest>
I have read this, and I have the doubt the problem is the same but it is actually happening to me on all the phone I have tried (galaxy Tab 7.1, galaxy tag 10, Galaxy SIII, Galaxy Note 3 Neo, Galaxy SIII mini) with android releases from 4.0 to 4.4.
I have already tried the solution posted here, but anything changed (again...it is a bit frustrating).
I have even tried to set the "Keep wifi option in standby" to "Always" in all the phones I've tried, but still nothing.
I have studied the WakeFulIntentService class, that is supposed to work as everybody is saying that it is, but I can't see any significant different in my code.
I really hope someone can help me, I'm really stuck on this since last week.
EDIT: following the waqaslam answer, I have checked on a device that has "Wifi optimisation" option in Wifi-Advaced settings, and it actually works as soon as I uncked the option. So, now, the problem become: can I disable Wifi optimisation in devices that haven't that option shown in the advanced menu? as I wrote below in my comment, this seems not to be related to android release, as I have two devices (both Samsung) with 4.4.2 and they are not showing the option.
New EDIT: from the edited waqaslam answer, I have tried to add multicastlock to my service, but again anything changed. This is getting annoying, there's hardly something easy and clear to do with android.
thank you very much
C.
I think the problem is not with the WakeLocks but the Wi-Fi settings.
In newer versions of android, there's an additional setting in Settings -> Wi-Fi -> Advanced called Wi-Fi optimization, which (if turned-on) disables all the low priority communications (like listening to UDP broadcasts) when the display is off.
Disabling the option should allow your device to listen UDP broadcasts even when the display is switched-off.
You may also use WifiManager.MulticastLock in order to acquire WiFi lock that should listen to these special broadcasts when screen is switched-off.
WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("lockWiFiMulticast");
lock.setReferenceCounted(false);
lock.acquire();
and when done with the lock, then call:
lock.release();
Also, don't forget to add the following permission to your manifest:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
For more info, you may read this.
Writing that just to mark the post as answered. I finally realilzed that there is no solution, or at least there is no solution working well on "all" the phones with "all" the android distribution (or at least from JB).
I have solved my personal issue just revisiting the concept of my app, considering that:
- Sending UDP broadcast is always working even in idle mode
- TCP servers are not affected by WiFi optimisation.
thank you all for the help and suggestion
C.
May be the reason is this .
Starting from Android 6.0 (API level 23), Android introduces two
power-saving features that extend battery life for users by managing
how apps behave when a device is not connected to a power source. Doze
reduces battery consumption by deferring background CPU and network
activity for apps when the device is unused for long periods of time.
App Standby defers background network activity for apps with which the
user has not recently interacted