我对如此厌倦了摔跤Android的实际操作,比设计师预期的正常操作更进一步。我想补充一点,接口契约也是行为,而不仅仅是保持参数相同的情况。例如,在HONEYCOMB之后对AsyncTask操作的更改:它默认为序列化方法。
问题:
我们开发专有应用程序;它们可能永远不会出现在Play商店中,我们拥有它们运行的所有设备。我们使用Android设备来驱动通过RS 232或以太网接口连接的各种类型的硬件。
背景
我的情况是,我们有一个外部设备,使用TCP / IP套接字定期(异步)连接到Android平板电脑。因为存在与每个连接相关联的状态信息,所以我需要在(至少)两个活动的持续时间内保持IP套接字。一般流程是:
:start
Device ----> Connect (to android device)---->Tablet
Device ----> Send session info-------------->Tablet
Device <---- Send response<------------------Tablet
(a new activity may start here)
Device <---- Send instruction<---------------Tablet
Device ----> Send response------------------>Tablet
Device <---- Send instruction<---------------Tablet
Device ----> Send response------------------>Tablet
Device <---- Send instruction<---------------Tablet
Device ----> Send response------------------>Tablet
goto start
以上内容略有释义,但它给出了系统的要点。
最初的方法是让一个进程内服务运行(与应用程序相关联),并使用一个单独的线程来处理异步连接,并对来自设备的请求进行编组。共享对象维护了服务器线程和连接的状态。
但是,前台应用程序经常需要向设备发送命令。为此,它确保服务器和设备之间存在现有连接,锁定可重入锁(以阻止对套接字的并发访问)并启动AsyncTask,该命令将命令发送到设备并等待响应几乎在所有情况下,都会在几毫秒内回来。显然,我无法在UI线程上发出套接字接收,否则会导致“NetworkOnMainThreadException”(发送工作正常。另外,我想避免轮询结果,因为它很俗气且效率低。因此,我发出了一个“得到(1000L)“一旦设备响应或在Android”没有响应“错误开始之前超时就会返回。
这种方法在Gingerbread上完美运行,但我们现在需要将系统升级到ICS或更好。不幸的是,当我在Honeycomb北部的Android版本上发布AsyncTask时,它永远不会打到doInBackground。在调查中,似乎默认情况下,在AsyncTask上发布的任何内容一次只允许一个线程运行,并且看起来主服务线程可能被视为额外线程。 (虽然我没有发现任何明确说明的内容)。
我想拒绝重写整个设备界面,因为它会影响多个产品,而且必须重新测试它们,考虑到硬件的性质,这将是一个非常耗时的过程。
所以,我的问题有两个:
1)有没有人知道如何在没有大量代码更改的情况下将AsyncTask行为修改为具有较少线程限制的内容?
2)如果没有,是否有更好的模式可用于此类问题,因为如果我必须重写,我想要正确地做;最好不用去点什么?
我无法完整地发布代码,因为它全部涵盖在NDA协议中,但是,如果对结构有特定的问题,我可能会删除一些代码段。
答案 0 :(得分:1)
我认为你在这里看到的是各种Android版本中线程的一些潜在变化。我认为他们是这样做的,所以线程会运行在Android 3的独立核心上。并且,因为很少有程序员擅长实际的并发性,所以他们再次关闭。我想如果你看一下上面的帖子,你可能会把它们弄清楚它们的样子。
BTW这解决了第二个问题。
答案 1 :(得分:1)
(1):简单的方法是使用AsyncTask.executeOnExecutor(Executor exec, Params... params)
,你可以自己配置得很好。
(2):一种模式是使用线程和处理程序的标准Java并发。 Here is a good starting point.您可以剥离后台线程并为其提供处理程序,因此它可以传回消息。
另一种模式是使用Android Service,它可能不像java并发那样适合任务。
答案 2 :(得分:0)
以下是所有代码 - 似乎工作正常socketserver.zip
这是业务结束:
package com.retailsci.socketserver;
/**
* Created with IntelliJ IDEA.
* User: paul
* Date: 2013/08/12
* Time: 9:06 PM
* To change this template use File | Settings | File Templates.
*/
import java.lang.ref.WeakReference;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.lang.InterruptedException;
import java.util.Enumeration;
import android.os.AsyncTask;
import java.io.BufferedInputStream ;
import java.io.BufferedOutputStream ;
import java.io.OutputStreamWriter ;
import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle ;
import android.os.IBinder ;
import android.os.RemoteException ;
import android.view.View ;
import android.view.View.OnClickListener ;
import android.widget.Button ;
import android.widget.Toast ;
import android.widget.TextView ;
import android.util.Log ;
import android.os.Handler;
import org.apache.http.conn.util.InetAddressUtils;
public class ServerThread implements Runnable
{
public static final String TAG = "RetailScience::ServerThread" ;
public static final int READ_OPERATION = 1 ;
public static final int WRITE_OPERATION = 2 ;
public static final int READLENGTH_OPERATION = 3 ;
public static final int WRITELENGTH_OPERATION = 4 ;
int ServerPort ;
ServerSocket serverSocket ;
Socket client ;
boolean Connected ;
String IPAddress ;
CircularByteBuffer InDataBuffer ;
CircularByteBuffer OutDataBuffer ;
public ServerThread (int ServerPort)
{
Log.v(TAG, "constructor - start") ;
this.ServerPort = ServerPort ;
IPAddress = IPv4Address() ;
Log.v(TAG, "constructor - end") ;
}
public boolean isConnected ()
{
return(Connected) ;
}
public String getIPAddress ()
{
return (IPAddress) ;
}
public void closeAll ()
{
Connected = false ; /* set the connection state */
Log.v(TAG, "Closing all the sessions");
try
{
client.close () ;
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "Socket Thread - Exception on Client Close " + e.toString());
}
try
{
serverSocket.close () ;
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "Socket Thread - Exception on serverSocket Close " + e.toString());
}
serverSocket = null ;
client = null ;
InDataBuffer = null ;
OutDataBuffer = null ;
System.gc() ;
/* Make sure the other threads can have a go now */
Thread.yield () ;
}
public boolean write (byte [] data, int offset, int length)
{
boolean okay = false ;
if (isConnected ())
{
try
{
OutDataBuffer.getOutputStream().write (data, offset, length) ;
okay = true ;
Thread.yield() ; /* Give the background thread a chance */
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "UI Thread - Probably a Connection Exception " + e.toString());
}
}
return (okay) ;
}
public void flush () /* Clear out anything in the IC buffers */
{
if (isConnected ())
{
try
{
int BytesReady = 0 ;
do
{
Thread.yield() ; /* Give the background thread a chance */
BytesReady = InDataBuffer.getInputStream().available () ;
Log.v(TAG, "FLUSH bytes" + BytesReady) ;
if (BytesReady > 0)
{
byte[] junk = new byte [BytesReady] ;
InDataBuffer.getInputStream().read (junk, 0, BytesReady) ;
}
Thread.sleep (50) ; /* Give the other thread time to pull */
/* anything else out of the IC buffer */
}
while (BytesReady > 0);
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "UI Thread - Probably a Connection Exception " + e.toString());
}
}
}
public int available ()
{
int BytesReady = -1 ;
if (isConnected ())
{
try
{
BytesReady = InDataBuffer.getInputStream().available () ;
if (BytesReady > 0)
{
Log.v(TAG, "Bytes in incoming buffer " + BytesReady) ;
}
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "UI Thread - Probably a Connection Exception " + e.toString());
}
}
return (BytesReady) ;
}
public int read (byte [] data, int offset, int length) throws IOException
{
int BytesRead = -1 ;
if (isConnected ())
{
Log.v(TAG, "********* reading data " + length) ;
try
{
BytesRead = 0 ;
int totalBytesRcvd = 0 ; // Total bytes received so far
while (totalBytesRcvd < length)
{
if ((BytesRead =
InDataBuffer.getInputStream().read (data,
totalBytesRcvd + offset,
length - totalBytesRcvd)) == -1)
{
Log.e(TAG, "UI Thread Connection Exception in Read");
throw new SocketException("Connection closed prematurely");
}
Log.v(TAG, "********* bytes read " + BytesRead) ;
totalBytesRcvd += BytesRead ;
Log.v(TAG, "********* total bytes read " + totalBytesRcvd) ;
}
BytesRead = length ;
}
catch (Exception e)
{
final String error = e.getLocalizedMessage();
Log.e(TAG, "UI Thread - Probably a Connection Exception " + e.toString());
}
}
return (BytesRead) ;
}
//---get the local IPv4 address---
// Taken from "Android(TM) Application Development Cookbook", by Wei-Meng Lee
// p. 212
// (Very useful book by the way)
private String IPv4Address()
{
try {
for (Enumeration<NetworkInterface> networkInterfaceEnum = NetworkInterface
.getNetworkInterfaces(); networkInterfaceEnum
.hasMoreElements();)
{
NetworkInterface networkInterface = networkInterfaceEnum.nextElement();
for (Enumeration<InetAddress> ipAddressEnum
= networkInterface.getInetAddresses(); ipAddressEnum.hasMoreElements();)
{
InetAddress inetAddress = (InetAddress) ipAddressEnum.nextElement();
// ---check that it is not a loopback address and
// it is IPv4---
if (!inetAddress.isLoopbackAddress()
&&
InetAddressUtils.isIPv4Address(inetAddress.getHostAddress()))
{
return inetAddress.getHostAddress();
}
}
}
}
catch (SocketException e)
{
Log.e(TAG, "IPv4Address " + e.toString());
}
return null;
}
public void run ()
{
try
{
if (IPAddress != null)
{
Log.v(TAG, "Starting...");
// handler.post(new Runnable()
// {
// @Override
// public void run()
// {
// textView1.setText(textView1.getText()
// + "Server listening on IP: " + SERVER_IP
// + "\n");
// }
// });
//---create an instance of the server socket---
//--- Allow 2 outstanding connections
serverSocket = new ServerSocket(ServerPort, 1);
/* allow the socket to be reused - or we'll get INUSE exception*/
serverSocket.setReuseAddress(true) ;
int Counter = 0 ;
Log.v(TAG, "Server Thread Running");
while (true)
{
//---wait for incoming clients---
Socket client = serverSocket.accept();
Log.v(TAG, "Connection Detected");
Connected = true ; /* set the connection state */
//---the above code is a blocking call;
// i.e. it will block until a client connects---
try
{
InDataBuffer = new CircularByteBuffer() ;
OutDataBuffer = new CircularByteBuffer() ;
BufferedInputStream InStream = new BufferedInputStream (client.getInputStream()) ;
BufferedOutputStream OutStream = new BufferedOutputStream (client.getOutputStream()) ;
int BytesDataBuffer ;
while (true)
{
if (Counter >= 1500) /* About 15 seconds */
{
/* Done' print this out too often */
Counter = 0 ;
Log.v(TAG, "Server Thread Running");
}
++Counter ;
BytesDataBuffer = InStream.available() ;
if (BytesDataBuffer > 0)
{
Log.v(TAG, "Reading Data Bytes " + BytesDataBuffer);
byte [] TempBuffer = new byte [BytesDataBuffer] ;
InStream.read (TempBuffer, 0, BytesDataBuffer) ;
InDataBuffer.getOutputStream().write (TempBuffer, 0, BytesDataBuffer);
Log.v(TAG, "Read Data Bytes " + BytesDataBuffer);
}
BytesDataBuffer = OutDataBuffer.getInputStream().available() ;
if (BytesDataBuffer > 0)
{
Log.v(TAG, "Writing Data Bytes " + BytesDataBuffer);
byte [] TempBuffer = new byte [BytesDataBuffer] ;
OutDataBuffer.getInputStream().read (TempBuffer, 0, BytesDataBuffer) ;
OutStream.write (TempBuffer, 0, BytesDataBuffer);
OutStream.flush() ; /* Make sure the buffer is empty */
Log.v(TAG, "Wrote Data Bytes " + BytesDataBuffer);
}
Thread.sleep(100) ; /* Always yield for a bit */
}
}
catch (Exception e)
{
Log.e(TAG, "Socket Thread - Read Exception " + e.toString());
Connected = false ; /* set the connection state */
}
} /* while (true) */
}
else
{
Log.e(TAG, "Socket Thread - No Network Capability");
Connected = false ; /* set the connection state */
}
}
catch (Exception e)
{
Log.e(TAG, "Socket Thread - Probably a Connection Exception" + e.toString());
Connected = false ; /* set the connection state */
}
closeAll () ;
Log.v(TAG, "*********************************************** Socket Thread - Connection Terminated");
Log.v(TAG, "*********************************************** Socket Thread - Connection Terminated");
Log.v(TAG, "*********************************************** Socket Thread - Connection Terminated");
Log.v(TAG, "*********************************************** Socket Thread - Connection Terminated");
}
}
读写的例子有:DeviceIF.java(在zip文件中)但是,这里有一些主要的原语:
ServerThread SocketIF ;
Thread BackgroundThread ;
To start it
SocketIF = new ServerThread (12347) ; /* 12347 is the server I/C port */
BackgroundThread = new Thread(SocketIF);
BackgroundThread.setDaemon(true) ; /* Make this a Daemon Thread */
BackgroundThread.start();
To stop it
SocketIF.closeAll () ;
Thread.yield () ; /* give the thread time to find out it's dead */
if (BackgroundThread != null)
{
if (BackgroundThread.isAlive())
{
BackgroundThread.interrupt (); /* Get rid of the thread */
Thread.yield () ; /* give the thread time to find out it's dead */
}
}
To read
try
{
if (SocketIF.isConnected ())
{
byte [] ResponseBuffer = new byte [7] ;
int Result = SocketIF.read (ResponseBuffer, 0, 7) ;
}
}
catch (Exception e)
{
final String error = e.getLocalizedMessage() ;
Log.e(TAG, "Exception : " + error);
}
To Write
int Result = ERROR_FAILED ;
try
{
if (SocketIF.isConnected ()) /* see if it's still open */
{
// flushICData () ; Good idea to do this before you issue a request
int RequestLen = 0 ;
final byte [] RequestBuffer = new byte [20] ;
RequestBuffer [RequestLen++] = (byte) 0 ; /* This will eventually hold the length */
RequestBuffer [RequestLen++] = (byte) '1' ;
RequestBuffer [0] = (byte) ((RequestLen - 1) & 0xFF) ; /* Put in the length one byte's enough */
SocketIF.write (RequestBuffer, 0, RequestLen) ;
Result = ERROR_OKAY ;
}
else
{
Result = ERROR_NO_CONNECTION ;
}
}
catch (Exception e)
{
final String error = e.getLocalizedMessage() ;
Log.e(TAG, "Exception : " + error);
Result = ERROR_EXCEPTION ;
}
在我得到一堆仇恨之前,我意识到架构中的一些缺陷;我保持简单,以便更容易遵循(这就是为什么我没有将读/写内容包装在流中)。我也没有在读取中添加超时,但这将是一个轻而易举的补充。
我从其他来源借来的代码在来源中显示:
(我无法将URL链接放入后一个代码,因为我没有足够的代表来发布两个以上的f * * *链接)
出于说明的目的,这里是如果在写入数据流和获得响应之间存在显着延迟的情况下如何处理传入数据。这是从Countdowntimer中的onTick事件调用的(因为上面提到的计时器取消问题,在我的情况下是Workingtimer)
protected void TickHandler ()
{
try
{
/* See if we have connected yet */
if (TerminalIF.isConnected ())
{
/* See if we have connected yet */
Button btn ;
btn = (Button) findViewById (R.id.getpinbutton);
btn.setEnabled(true) ;
btn = (Button) findViewById (R.id.displaybutton);
btn.setEnabled(true) ;
btn = (Button) findViewById (R.id.getcardstatebutton);
btn.setEnabled(true) ;
btn = (Button) findViewById (R.id.rebootbutton);
btn.setEnabled(true) ;
btn = (Button) findViewById (R.id.pingbutton);
btn.setEnabled(true) ;
if (TerminalIF.responseReady ())
{
byte [] DataResult = TerminalIF.ReceivePacket () ;
Log.v(TAG, "Data Received") ;
Toast.makeText(getApplicationContext(),
"Device Data Received",
Toast.LENGTH_SHORT).show() ;
resetTimer (DEFAULT_INPUT_TIMER_TIMEOUT) ;
}
}
}
catch (Exception e)
{
final String error = e.getLocalizedMessage() ;
Log.e(TAG, "Exception : " + error);
}
}
是的,我可以使用AsyncTasks和处理程序完成它,但这是一个直接的插件模块,几乎可以在几乎没有任何修改的情况下使用。转换成服务也很容易。
而且,在我被告知无关紧要,冗长或回答我自己的问题之前,我只是觉得我会分享这份工作。
最后一件事,这是一个小试验用具:
PINPad.java
import java.io.*;
import java.net.*;
import java.lang.* ;
public class PINPad
{
InputStream in ;
OutputStream out ;
byte [] receive (int len) throws IOException
{
byte [] result = null ;
if (len > 0)
{
byte [] temp = new byte [len] ;
// Receive the same string back from the server
int totalBytesRcvd = 0; // Total bytes received so far
int bytesRcvd ; // Bytes received in last read
while (totalBytesRcvd < temp.length)
{
if ((bytesRcvd = in.read(temp, totalBytesRcvd, temp.length - totalBytesRcvd)) == -1)
{
System.out.println("Error on receive");
throw new SocketException("Connection closed prematurely");
}
else
{
System.out.println("Received " + bytesRcvd + " bytes");
}
totalBytesRcvd += bytesRcvd;
} // data array is full
result = temp ;
}
return result ;
}
void send (byte [] data, int offset, int len) throws IOException
{
System.out.println("Sending.... " + len + " bytes");
if (len > 0)
{
try
{
out.write (data, offset, len) ;
}
catch (Exception e)
{
System.out.println("Error on send");
throw new SocketException("Connection closed prematurely");
}
}
System.out.println("Sent " + len + " bytes");
}
void loop (Socket echoSocket)
{
boolean okay = true ;
try
{
in = echoSocket.getInputStream();
out = echoSocket.getOutputStream();
}
catch (Exception e)
{
okay = false ;
}
while (okay)
{
try
{
System.out.println("Receive length");
byte [] len = receive (1) ;
int packetlength = (int) len[0] ;
System.out.println("Subsequent packet length = " + packetlength);
byte [] packet = receive (packetlength) ;
System.out.println("Got a packet of = " + packet.length);
char packettype = (char) packet [0] ;
byte [] retbuf = new byte [50] ;
int reqlen = 0 ;
switch (packettype)
{
case 'A' : /* PING */
System.out.println("==== PING");
retbuf [reqlen++] = (byte) 0 ; /* placeholder for the length */
retbuf [reqlen++] = (byte) packettype ;
retbuf [reqlen++] = (byte) '0' ; /* Error code = OK */
retbuf [0] = (byte) (reqlen - 1) ; /* Now put the length in */
send(retbuf, 0, reqlen);
break ;
case 'B' : /* GET PIN */
System.out.println("==== GET PIN");
retbuf [reqlen++] = (byte) 0 ; /* placeholder for the length */
retbuf [reqlen++] = (byte) packettype ;
retbuf [reqlen++] = (byte) '0' ; /* Error code = OK */
retbuf [0] = (byte) (reqlen - 1) ; /* Now put the length in */
send(retbuf, 0, reqlen);
break ;
case 'C' : /* GET CARD STATE */
System.out.println("==== GET CARD STATE");
retbuf [reqlen++] = (byte) 0 ; /* placeholder for the length */
retbuf [reqlen++] = (byte) packettype ;
retbuf [reqlen++] = (byte) '0' ; /* Error code = OK */
retbuf [reqlen++] = (byte) '1' ; /* Card is present */
retbuf [0] = (byte) (reqlen - 1) ; /* Now put the length in */
send(retbuf, 0, reqlen);
break ;
case 'D' : /* DISPLAY */
System.out.println("==== DISPLAY");
retbuf [reqlen++] = (byte) 0 ; /* placeholder for the length */
retbuf [reqlen++] = (byte) packettype ;
retbuf [reqlen++] = (byte) '0' ; /* Error code = OK */
retbuf [0] = (byte) (reqlen - 1) ; /* Now put the length in */
send(retbuf, 0, reqlen);
break ;
case 'E' : /* REBOOT */
System.out.println("==== REBOOT");
okay = false ;
break ;
case 'F' : /* CLOSE */
System.out.println("==== CLOSE SESSION");
okay = false ;
break ;
default :
System.out.println("==== UNKNOWN!!!!!!");
retbuf [reqlen++] = (byte) 0 ; /* placeholder for the length */
retbuf [reqlen++] = (byte) packettype ;
retbuf [reqlen++] = (byte) '3' ; /* error */
retbuf [0] = (byte) (reqlen - 1) ; /* Now put the length in */
send(retbuf, 0, reqlen);
break ;
}
}
catch (Exception e)
{
okay = false ;
}
}
try
{
out.close();
in.close();
}
catch (Exception e)
{
okay = false ;
}
}
public static void main(String[] args)
{
Socket echoSocket = null;
PINPad IF = new PINPad () ;
while (true)
{
try
{
echoSocket = new Socket("192.168.168.106", 12347);
// out = new PrintWriter(echoSocket.getOutputStream(), true);
// in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
if (echoSocket != null)
{
System.out.println("Connected to remote");
IF.loop (echoSocket) ;
echoSocket.close();
System.out.println("Waiting for next connection");
}
}
catch (UnknownHostException e)
{
System.err.println("Don't know about host: 192.168.168.107.");
// System.exit(1);
}
catch (IOException e)
{
System.err.println("Couldn't get I/O for the connection to: 192.168.168.107.");
}
catch (Exception e)
{
System.err.println("Couldn't get I/O for the connection to: 192.168.168.107.");
}
try
{
Thread.sleep (1000) ;
}
catch (Exception e)
{
System.err.println("Thread Interrupted....");
System.exit(1);
}
}
}
}