正确使用的 AsyncTask 中出现奇怪的 NetworkOnMainThreadException
Strange NetworkOnMainThreadException in a properly used AsyncTask
我在 AsyncTask
中为 Microsoft SQL 2008 连接使用 jtds 1.3.0。我的 doInBackground()
方法 return 是一个 ResultSet
对象。然后我从 onPostExecute()
方法中的 ResultSet
读取行。通常我的查询应该 return 4701 行。但是我在第 58-60 行左右得到 NetworkOnMainThreadException,然后连接停止(我能够读取前 ~60 行)。我的原始查询是:
SELECT ID,TITLE,CODE FROM COMPANIES
为了实验,我尝试了:
SELECT TOP 50 ID,TITLE,CODE FROM COMPANIES --No problem.
SELECT TOP 100 ID,TITLE,CODE FROM COMPANIES --Throws exception.
logcat输出:
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[=12=]0(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.onPostExecute(FragmentMain.java:69)
at com.example.sql.FragmentMain.onPostExecute(FragmentMain.java:52)
at android.os.AsyncTask.finish(AsyncTask.java:632)
at android.os.AsyncTask.access0(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)
生成错误的代码片段:
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();
最后,我在 gradle 个文件中的 sdk 设置:
minSdkVersion 14
targetSdkVersion 22
和依赖项:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile files('libs/jtds-1.3.0.jar')}
为了简单起见,我没有包括我的 AsyncTask
class。基本上我打开一个连接并在 doInBackground()
中调用 Connection.execute()
。如果有人想重现错误,我可以编辑我的 post.
谢谢
这是我的 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;
}
}
我终于找到了解决办法。从 this link 我看到如果超过 ResultSet
的获取大小,它会尝试连接到互联网并获取下一部分数据。因此,使用 ResultSet.setFetchSize(cacheSize)
或将 ResultSet.next()
方法调用移至 AsyncTask
即可解决问题。
我在 AsyncTask
中为 Microsoft SQL 2008 连接使用 jtds 1.3.0。我的 doInBackground()
方法 return 是一个 ResultSet
对象。然后我从 onPostExecute()
方法中的 ResultSet
读取行。通常我的查询应该 return 4701 行。但是我在第 58-60 行左右得到 NetworkOnMainThreadException,然后连接停止(我能够读取前 ~60 行)。我的原始查询是:
SELECT ID,TITLE,CODE FROM COMPANIES
为了实验,我尝试了:
SELECT TOP 50 ID,TITLE,CODE FROM COMPANIES --No problem.
SELECT TOP 100 ID,TITLE,CODE FROM COMPANIES --Throws exception.
logcat输出:
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[=12=]0(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.onPostExecute(FragmentMain.java:69)
at com.example.sql.FragmentMain.onPostExecute(FragmentMain.java:52)
at android.os.AsyncTask.finish(AsyncTask.java:632)
at android.os.AsyncTask.access0(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)
生成错误的代码片段:
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();
最后,我在 gradle 个文件中的 sdk 设置:
minSdkVersion 14
targetSdkVersion 22
和依赖项:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile files('libs/jtds-1.3.0.jar')}
为了简单起见,我没有包括我的 AsyncTask
class。基本上我打开一个连接并在 doInBackground()
中调用 Connection.execute()
。如果有人想重现错误,我可以编辑我的 post.
谢谢
这是我的 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;
}
}
我终于找到了解决办法。从 this link 我看到如果超过 ResultSet
的获取大小,它会尝试连接到互联网并获取下一部分数据。因此,使用 ResultSet.setFetchSize(cacheSize)
或将 ResultSet.next()
方法调用移至 AsyncTask
即可解决问题。