I've just finished working on a multi-threaded network queue and was wondering where I could improve. I'm a little rusty when it comes to waits and such.
The idea is to queue up network actions whilst waiting for a form of network internet connectivity. I also added the ability to prioritize which network actions should be performed first. I also intend to add functionality allowing a NetworkJob
to specify the type of connection (For those wanting to be connected to WiFi).
With that being said, my main questions are:
- Can you see any glaring issues in the synchronized blocks and waits?
- Can you see anywhere I could improve the code for legibility? I'm not totally sure why, but it does seem like quite a mess to me. (Though I might be being over the top.)
- Are there any features that I've missed?
NetworkQueue.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import java.util.PriorityQueue;
/**
* Created by Tom on 14/09/2015.
*/
public final class NetworkQueue extends Thread {
private Context context;
private final PriorityQueue<NetworkJob> actions;
private volatile ConnectionType connection = ConnectionType.NONE;
private final Object networkLock = new Object();
private final Object actionsLock = new Object();
private volatile boolean stop = false;
public NetworkQueue(Context context) {
this.context = context;
this.actions = new PriorityQueue<>(5, new NetworkJobComparator());
context.registerReceiver(new NetworkListener(),
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
@Override
public void run() {
try {
while (!stop) {
synchronized (actionsLock) {
if (actions.isEmpty()) {
Log.v("NetworkQueue", "Awaiting new job");
actionsLock.wait();
Log.v("NetworkQueue", "Received new job");
}
}
if (connection == ConnectionType.NONE) {
synchronized (networkLock) {
Log.v("NetworkQueue", "Awaiting internet connection!");
networkLock.wait();
Log.v("NetworkQueue", "Internet connection obtained!");
}
}
synchronized (actionsLock) {
NetworkJob job = actions.poll();
if (job.perform()) {
continue;
}
actions.add(job);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void addJob(NetworkJob job) {
synchronized (actionsLock) {
if (this.actions.contains(job)) {
Log.v("Network Queue", "Waiting for internet connection, "
+ job.priority() + " priority job: " + job.getClass().getSimpleName() + " already in queue.");
return;
}
this.actions.add(job);
synchronized (actionsLock) {
this.actionsLock.notify();
}
}
}
public void end() {
this.stop = false;
}
private class NetworkListener extends BroadcastReceiver {
ConnectivityManager conn = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
@Override
public void onReceive(Context context, Intent intent) {
NetworkInfo networkInfo = conn.getActiveNetworkInfo();
if (networkInfo == null) {
NetworkQueue.this.connection = ConnectionType.NONE;
return;
}
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
NetworkQueue.this.connection = ConnectionType.WIFI;
synchronized (networkLock) {
networkLock.notifyAll();
}
return;
}
NetworkQueue.this.connection = ConnectionType.ANY;
synchronized (networkLock) {
networkLock.notifyAll();
}
}
}
}
NetworkPriority.java
/**
* Created by thomas on 16/09/15.
*/
public enum NetworkPriority {
/**
* Used when the user is sending data to the server (Responses)
*/
HIGH,
/**
* Used when the user is requesting a specific resource (Usually more important)
*/
MEDIUM,
/**
* Used for general requests
*/
LOW
}
NetworkJobComparator.java
import java.util.Comparator;
/**
* Created by thomas on 16/09/15.
*/
public class NetworkJobComparator implements Comparator<NetworkJob> {
@Override
public int compare(NetworkJob lhs, NetworkJob rhs) {
return lhs.priority().compareTo(rhs.priority());
}
}
NetworkJob.java
public interface NetworkJob {
/**
* The action requiring network connectivity
* @return true only if the network action has succeeded.
*/
boolean perform();
NetworkPriority priority();
}