Strange NetworkOnMainThreadException in a properly used AsyncTask - android

I'm using jtds 1.3.0 in AsyncTask for Microsoft SQL 2008 connection. And my doInBackground() method returns a ResultSet object. Then I read rows from the ResultSet in onPostExecute() method. Normally my query should return 4701 rows. But I get NetworkOnMainThreadException around 58th-60th rows then connection stops(I'm able to read the first ~60 rows). My original query is:
SELECT ID,TITLE,CODE FROM COMPANIES
For experimenting I've tried:
SELECT TOP 50 ID,TITLE,CODE FROM COMPANIES --No problem.
SELECT TOP 100 ID,TITLE,CODE FROM COMPANIES --Throws exception.
The logcat output:
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1166)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163)
at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
at libcore.io.Streams.readFully(Streams.java:81)
at java.io.DataInputStream.readFully(DataInputStream.java:99)
at java.io.DataInputStream.readFully(DataInputStream.java:95)
at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:881)
at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:762)
at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:477)
at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:114)
at net.sourceforge.jtds.jdbc.ResponseStream.readInt(ResponseStream.java:329)
at net.sourceforge.jtds.jdbc.TdsData.readData(TdsData.java:728)
at net.sourceforge.jtds.jdbc.TdsCore.tdsRowToken(TdsCore.java:3080)
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2347)
at net.sourceforge.jtds.jdbc.TdsCore.getNextRow(TdsCore.java:772)
at net.sourceforge.jtds.jdbc.JtdsResultSet.next(JtdsResultSet.java:611)
at com.example.sql.FragmentMain$1.onPostExecute(FragmentMain.java:69)
at com.example.sql.FragmentMain$1.onPostExecute(FragmentMain.java:52)
at android.os.AsyncTask.finish(AsyncTask.java:632)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5602)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
Error-generating code piece:
new DatabaseTask(
((ActivityMain) getActivity()).getConnectionURL()
, getString(R.string.query_all_companies))
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
if (D) Log.d(TAG, "Querying the companies..");
}
#Override
protected void onPostExecute(ResultSet resultSet)
{
super.onPostExecute(resultSet);
try
{
if (resultSet != null)
{
int i = 0;
while (resultSet.next()) /*while loop causes error according to logcat.*/
{
if (D) Log.i(TAG,i++);
}
}
} catch (SQLException e)
{
e.printStackTrace();
}
}
}.execute();
Finally, my sdk settings in gradle files:
minSdkVersion 14
targetSdkVersion 22
and dependencies:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile files('libs/jtds-1.3.0.jar')}
I didnt include my AsyncTask class for the simplicity. Basically I open a connection and call Connection.execute() in doInBackground(). If anyone wants to reproduce the error, I can edit my post.
Thank you
Here is my AsyncTask class.
public class DatabaseTask extends AsyncTask<Object,String,ResultSet>
{
private final boolean D = true;
private final String TAG = "DatabaseTask";
private String url;
private String sqlStatement;
public DatabaseTask(String url, String sqlStatement)
{
this.url = url;
this.sqlStatement = sqlStatement;
}
private void addParams(PreparedStatement preparedStatement,Object... params) throws SQLException
{
if (params != null && params.length > 0)
{
for (int i = 0; i < params.length; i++)
{
Object param = params[i];
if (param instanceof String)
{
preparedStatement.setString(i+1, param.toString());
if (D) Log.d(TAG, "String parameter \'" + param + "\' added.");
} else if (param instanceof Integer)
{
preparedStatement.setInt(i+1, (Integer) param);
if (D) Log.d(TAG, "Integer parameter \'" + param + "\' added.");
} else if (param instanceof Double)
{
preparedStatement.setDouble(i+1, (Double) param);
if (D) Log.d(TAG, "Double parameter \'" + param + "\' added.");
} else if (param instanceof BigDecimal)
{
preparedStatement.setBigDecimal(i+1, (BigDecimal) param);
if (D) Log.d(TAG, "BigDecimal parameter \'" + param + "\' added.");
}
else
if (D) Log.e(TAG,"SQL parameter type is not supported.");
}
}
}
#Override
protected ResultSet doInBackground(Object... params)
{
try
{
Class.forName("net.sourceforge.jtds.jdbc.Driver").newInstance();
Connection connection;
connection = DriverManager.getConnection(url,
ActivityMain.DB_USER_NAME,
ActivityMain.DB_PASSWORD);
if (connection != null)
{
if (D) Log.d(TAG, "Connection successful.");
connection.setAutoCommit(true);
PreparedStatement preparedStatement = connection.prepareStatement(sqlStatement);
addParams(preparedStatement, params);
preparedStatement.execute();
return preparedStatement.getResultSet();
}
else
{
if (D) Log.d(TAG, "Connection failed.");
}
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (InstantiationException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (SQLException e)
{
if (D) Log.e(TAG,e.getMessage());
e.printStackTrace();
}
return null;
}
}

I've finally found the solution. From this link I saw that if the fetch size of ResultSet is exceeded, it tries to connect to internet and fetch the next portion of data. So, either using ResultSet.setFetchSize(cacheSize) or moving the ResultSet.next() method call into the AsyncTask solves the problem.

Related

Issue with DatagramSocket on Android 7.1.1

I'm faced with a very strange issue on QA's Google Pixel with Android 7.1.1 (N_MR1).
We use UDP Server and Client for handshake during establish the TCP connection.
QA reports that handshake to Pixel doesn't work. After exploring Logcat I found that UdpServerTask throws an exception:
java.net.BindException: Address already in use
at java.net.PlainDatagramSocketImpl.bind0(Native Method)
at java.net.AbstractPlainDatagramSocketImpl.bind(AbstractPlainDatagramSocketImpl.java:96)
at java.net.DatagramSocket.bind(DatagramSocket.java:387)
What I tried so far:
enabled Reuse address feature (see code) - no luck
forced usage of IPv4 (see code) - same, no luck
in the loop, checked ports range (32100 - 32110) - also doesn't help. Also all ports throw the same exception java.net.BindException: Address already in use
hardcoded IP's "0.0.0.0" and "10.1.x.x" (see code) - the same
restarted device, changed WiFi network - didn't help as well
Also, I checked who uses ports on the device (NetStat+ app) - IP's and ports are free, no one used. But when I tried to call bind() - exception happens.
At the same time UDP client (called on demand) works fine - I can send UDP packets via target port.
Also what noticed - on my Nexus with Android 7.1.1 and devices with lower Android version I can't reproduce the issue.
Test example
public class UDPServer {
int PORT = 32100;
long TIMEOUT = 30000;
private void log(String msg) {
System.out.println(msg);
}
private boolean isActive = false;
public ArrayList<UdpServerTask> tasks = new ArrayList<>();
public void process(final byte[] data) {
AsyncTask<Void, Void, Void> loadTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
//process data
return null;
}
};
Utils.executeTask(loadTask);
}
public void startAddress(String host) {
UdpServerTask loadTask = new UdpServerTask(host, PORT);
tasks.add(loadTask);
Utils.executeTask(loadTask);
}
public void runUdpServer() {
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
stop_UDP_Server();
isActive = true;
AsyncTask<Void, Void, Void> mainTask = new AsyncTask<Void, Void, Void>() {
ArrayList<String> ips = new ArrayList<>();
#Override
protected Void doInBackground(Void... params) {
log("UDP starting servers ");
ips.add(null);
ips.add("0.0.0.0");
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
continue;
}
for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
InetAddress broadcast = interfaceAddress
.getBroadcast();
if (broadcast == null || broadcast instanceof Inet6Address) {
continue;
}
if (!ips.contains(broadcast.getHostAddress())) {
ips.add(broadcast.getHostAddress());
}
}
}
} catch (final Throwable e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
for (String host : ips) {
startAddress(host);
}
}
};
Utils.executeTask(mainTask);
}
public boolean reallyStopped() {
return !isActive && tasks.isEmpty();
}
public void stop_UDP_Server() {
isActive = false;
AsyncTask<Void, Void, Void> mainTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
log("UDP start stopping");
for (UdpServerTask task : tasks) {
task.cancelServer();
}
tasks.clear();
return null;
}
};
Utils.executeTask(mainTask);
while (!reallyStopped()) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
private class UdpServerTask extends AsyncTask<Void, Void, Void> {
String ip;
int port;
public UdpServerTask(String ip, int port) {
this.ip = ip;
this.port = port;
}
DatagramSocket ds = null;
public void cancelServer() {
log("UDP server cancelServer");
if (ds != null && !ds.isClosed()) {
try {
ds.close();
ds = null;
} catch (Exception e) {
e.printStackTrace();
}
}
log("UDP server stopped");
}
#Override
protected Void doInBackground(Void... params) {
long time = System.currentTimeMillis();
boolean firstAttempt = true;
while (System.currentTimeMillis() - time <= TIMEOUT && isActive) {
try {
if (ds != null && !ds.isClosed()) {
try {
ds.close();
ds = null;
} catch (Exception e) {
e.printStackTrace();
}
}
log("UDP try create connection " + this.ip + ":" + this.port);
if (firstAttempt) {
ds = new DatagramSocket(new InetSocketAddress(TextUtils.isEmpty(this.ip) ? null : InetAddress.getByName(this.ip), this.port));
} else {
ds = new DatagramSocket(null);
}
ds.setBroadcast(true);
if (!firstAttempt) {
ds.setReuseAddress(true);
ds.bind(new InetSocketAddress(TextUtils.isEmpty(this.ip) ? null : InetAddress.getByName(this.ip), this.port));
}
long start = System.currentTimeMillis();
while (!ds.isBound()) {
if (System.currentTimeMillis() - start >= TIMEOUT) {
throw new Exception("Cann't bind to " + this.ip + ":" + this.port);
}
Thread.sleep(150);
}
log("UDP Server Started on " + this.ip + ":" + this.port);
while (isActive) {
final byte[] lMsg = new byte[4096];
final DatagramPacket dp = new DatagramPacket(lMsg, lMsg.length);
ds.receive(dp);
log("process UDP from " + dp.getAddress().toString() + ":" + dp.getPort());
process(dp.getData());
}
log("UDP Server Stopped on " + this.ip + ":" + this.port);
} catch (final Throwable e) {
e.printStackTrace();
firstAttempt = false;
log("UDP Server Failed " + this.ip + ":" + this.port + " " + e);
try {
Thread.sleep(TIMEOUT / 10);
} catch (Exception ex) {
}
}
}
if (ds != null && !ds.isClosed())
try {
ds.close();
ds = null;
} catch (Exception e) {
e.printStackTrace();
}
log("UDP Server finish task");
return null;
}
}
}
The issue is in the port you use. On my Pixel phone the following port ranges are defined in the /proc/sys/net/ipv4/ip_local_reserved_ports file:
32100-32600,40100-40150
If I change the port number in your code to anything out of this range (and above 1024, of course), it works fine and I'm able to send data to the app from the other host.
Linux Kernel documentation describes this file like this:
ip_local_reserved_ports - list of comma separated ranges
Specify the ports which are reserved for known third-party applications. These
ports will not be used by automatic port assignments (e.g. when
calling connect() or bind() with port number 0). Explicit port
allocation behavior is unchanged.
So, when you explicitly pass the port number to the bind method it should still be possibly to use those ports. Apparently this doesn't work. In my opinion, there is a bug somewhere in the network-stack provided by the Linux Kernel implementation used in Android. But this requires additional investigation.
You may also find useful the following list of ip_local_reserved_ports contents on different phones:
https://census.tsyrklevich.net/sysctls/net.ipv4.ip_local_reserved_ports

Why do i have a null pointer exception on database calls and what can i do to solve?

new AsyncTask<Ticket, Void, List<TPVLine>>() {
#Override
protected List<TPVLine> doInBackground(Ticket... params) {
List<TPVLine> lines;
while (true){
Log.d(TAG, "Waiting for data base response");
try {
lines = params[0].getLines();
Log.d(TAG, "Data base response completed");
break;
}catch (SQLiteException | NullPointerException ex){
ActiveAndroid.clearCache();
Log.d(TAG, "Cleaning cache");
Log.wtf(TAG, ex.toString());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return lines;
}
#Override
protected void onPostExecute(List<TPVLine> aVoid) {
super.onPostExecute(aVoid);
linesTPV = new ArrayList<TPVLine>();
if (aVoid != null){
linesTPV = aVoid;
}
linesTPV.addAll(noSavedLines);
mainActivity.getTpvFragment().resetPrice();
notifyDataSetChanged();
if (linesTPV.size() == 0){
mainActivity.getTpvFragment().getListContainer().setVisibility(View.INVISIBLE);
mainActivity.getTpvFragment().getMessageContainer().setVisibility(View.VISIBLE);
}else {
mainActivity.getTpvFragment().getListContainer().setVisibility(View.VISIBLE);
mainActivity.getTpvFragment().getMessageContainer().setVisibility(View.INVISIBLE);
}
notifyDataSetChanged();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mainActivity.getCurrentTicket());
This are the calls, first in Ticket.java
public List<TPVLine> getLines() {
return new Select().from(TPVLine.class).where("Ticket = ?", this.getId()).execute();
}
The second is in TPVLine.java
public static List<TPVLine> getLines(Ticket ticket){
return new Select().from(TPVLine.class).where("Ticket = ?", ticket.getId()).orderBy("Id ASC").execute();
}
The issue is caused when i call TPVLine.class, i make sure first that Ticket != null. I'm using ActiveAndroid to manage the database
you are returning null instead of lines in your asynctask doInBackground event.
return lines;

OnPostExecute Method is Executing before Doinbackground Fetching Data from Server To Local

In my Android async task class, I'm fetching data from an Azure server to local database inside DoinBackground method
But before finishing the DoinBackground method, it's executing the OnPostExecute method
Inside OnPostExecute method I am disabling the ProgressBar
Help me to solve this issue.
My code:
public class AsyncTaskSync_UserGroupMappingTableClass extends AsyncTask<String, String, Boolean>
{
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected Boolean doInBackground(String... values)
{
try
{
mToDoTable_Form5_SPINNER_DataTable456_ServerAzure
.execute(new TableQueryCallback<FormsObjectTable2TaskHopsSPinnerValues>() {
public void onCompleted(List<FormsObjectTable2TaskHopsSPinnerValues> result, int count, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
if (!result.equals("")) {
for (int i = 0; i < result.size(); i++) {
/*Table 5 SPinner Data Table*/
IdValue_TableValue5 = result.get(i).getId();
ImeiStringval1_TableValue5 = result.get(i).getImeINumberValOne();
Spinner_IDStringVal1_TableValue5 = result.get(i).getSpinner_id_StringOne();
Spinner_data_StringVal1_TableValue5 = result.get(i).getSPinner_data_Value_StringOne();
Log.i("From SErver DataBase", " Spinner : " + ImeiStringval1_TableValue5 + " : " + Spinner_IDStringVal1_TableValue5 + " : " + Spinner_data_StringVal1_TableValue5);
Asynxfor_DATAinsert5_SpinnerTable(IdValue_TableValue5, ImeiStringval1_TableValue5, Spinner_IDStringVal1_TableValue5, Spinner_data_StringVal1_TableValue5);
}
} else {
Log.i("Data Retrieval Not Found", "No Data In Server For Specific IMEI......!");
}
} else {
Log.i("SOme Exception", "Data Retrieval From Server FORMTABLE1 Data......!");
exception.printStackTrace();
}
}
});
}
catch (Exception e)
{
e.printStackTrace();;
Log.i("Data Retrieval", "Exception Occur......!");
}
// PrgDialog.dismiss();
return null;
}
#Override
protected void onPostExecute(Boolean results)
{
try
{
Log.i("DONE ", "Data Sync Done Successfully 5 Spinner Values");
PrgDialog.dismiss();
}
catch (Exception e)
{
e.printStackTrace();
Log.i("Exception ", "Post Excecute");
}
}
};
Edit 1
My Logcat message:
// From OnPostExecute first Executing also disabling the Progressbar
DONE: Data Sync Done Successfully Form Master 1
// From Doinbackground
From Server database
The requests you are doing inside doInBackground are made asynchronusly that's means that doInBackground is already execute before you get the TableQueryCallback. In other words It's a thread which is launching another thread. I think you do not need to surround it in an AsyncTask, you could handle your respond on TableQueryCallback.onCompleted() with a Handler or an Interface.

Azure mobile service for android query table

I'm using azure sdk for android and follow the tutorial https://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-android-get-started-data/.
When I'm trying to connect and insert data to mobile service table all is ok, but when I query the table in activity my app gets stuck, though there are only several entries in the table and execute method successfully returns Future.
public static MobileServiceClient mClient;
public static void connect(Context context) {
try {
mClient = new MobileServiceClient(storageLink, key, context);
} catch (MalformedURLException e) {
Log.e("AzureService.connect", "Storage access failed" + storageLink);
}
}
public static InstallationData get(final String deviceId) {
MobileServiceTable<InstallationData> table= mClient.getTable(InstallationData.class);
final MobileServiceList<InstallationData> result;
try {
result = table.where().field("deviceid").eq(deviceId).execute().get();
for (InstallationData item : result) {
return item;
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
public static void store(final InstallationData item) {
mClient.getTable(InstallationData.class).insert(item, new TableOperationCallback<InstallationData>() {
public void onCompleted(InstallationData entity, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
Log.d("AzureService.store()", "Data about " + item.getDeviceid() + "" + "is successfully updated");
} else {
exception.printStackTrace();
Log.e("AzureService.store()", "Data about " + item.getDeviceid() + "" + "is failed to update");
}
}
});
}
Thank you in advance!

AsyncTask slow with OnClick

Goodmorning,
I have a button on my android app that launches a search on the web (through google endpoints) through an AsyncTask. My problem is that the button does not "unclick" until the AsyncTask is completed, which may take several seconds. When the internet connection is slow, this even makes the application crash, in any case the application is completely stuck until the AsyncTask is completed. Now the reason for using AsyncTask was exactly to eliminate this problem, so I don't really get what happens!
Here is the OnClickListener:
SearchListener = new OnClickListener() {
#Override
public void onClick(View v) {
String cname=TextCourse.getText().toString();
if (!cname.isEmpty()){
try {
CollectionResponseWine listavini= new QueryWinesTask(messageEndpoint,cname,5).execute().get();
} catch (InterruptedException e) {
showDialog("Errore ricerca");
e.printStackTrace();
} catch (ExecutionException e) {
showDialog("Errore ricerca");
e.printStackTrace();
}
} else{
showDialog("Inserisci un piatto");
}
}
};
and here is the AsyncTask that is being called:
private class QueryWinesTask
extends AsyncTask<Void, Void, CollectionResponseWine> {
Exception exceptionThrown = null;
MessageEndpoint messageEndpoint;
String cname;
Integer limit;
public QueryWinesTask(MessageEndpoint messageEndpoint, String cname, Integer limit) {
this.messageEndpoint = messageEndpoint;
this.cname=cname;
this.limit=limit;
}
#Override
protected CollectionResponseWine doInBackground(Void... params) {
try {
CollectionResponseWine wines = messageEndpoint.listwines().setCoursename(cname).setLimit(limit).execute();
return wines;
} catch (IOException e) {
exceptionThrown = e;
return null;
//Handle exception in PostExecute
}
}
protected void onPostExecute(CollectionResponseWine wines) {
// Check if exception was thrown
if (exceptionThrown != null) {
Log.e(RegisterActivity.class.getName(),
"Exception when listing Messages", exceptionThrown);
showDialog("Non ci sono vini associati al tuo piatto. Aggiungine uno!");
}
else {
messageView.setText("Vini piu' votati per " +
cname + ":\n\n");
for(Wine wine : wines.getItems()) {
messageView.append(wine.getName() + " (" + wine.getScore() + ")\n");
}
}
}
}
...execute().get() is blocking. It makes UI thread wait for Task to complete.
Don't do get(). Use onPostExecute() to get the result (wines) of task and update the UI.

Categories

Resources