Android dPNクライアントのブロッキング読み書き(1)
7394 ワード
もっと読む
Android PNサーバーはminaを省略して表にしないで、クライアントのsocket通信用はasmackで、期間はxmppプロトコル通信を使って、このxmppは通用しますが、xml形式を使ってお互いに送信します。その間にいっぱいのxml解析を加えなければなりません。ほとんどの紙幅はこれをしています。これにはあまり興味がありません。ここはただ簡単に記録してみます。ソースの中でclientとserverの読み取りと書き込みが滞っています。参考になるところを探しています。
クライアントが起動したら、接続を管理するXMPPConnection初期化を担当する:
まず、packetWriterはどのようにサーバにデータを送信しますか?
PacketReader初期化:
上記の初期化方法において最後のreetPaser():
これで終わります。午前中にこれらを見ました。収穫があって、頭が痛いです。
Android PNサーバーはminaを省略して表にしないで、クライアントのsocket通信用はasmackで、期間はxmppプロトコル通信を使って、このxmppは通用しますが、xml形式を使ってお互いに送信します。その間にいっぱいのxml解析を加えなければなりません。ほとんどの紙幅はこれをしています。これにはあまり興味がありません。ここはただ簡単に記録してみます。ソースの中でclientとserverの読み取りと書き込みが滞っています。参考になるところを探しています。
クライアントが起動したら、接続を管理するXMPPConnection初期化を担当する:
if (isFirstInitialization) {
packetWriter = new PacketWriter(this);
packetReader = new PacketReader(this);
読み書きはそれぞれ担当します。次に二つを起動します。 // Start the packet writer. This will open a XMPP stream to the server
packetWriter.startup();
// Start the packet reader. The startup() method will block until we
// get an opening stream packet back from server.
packetReader.startup();
// Make note of the fact that we're now connected.
connected = true;
まず、packetWriterはどのようにサーバにデータを送信しますか?
protected void init() {
this.writer = connection.writer;
done = false;
writerThread = new Thread() {
public void run() {
writePackets(this);
}
};
writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")");
writerThread.setDaemon(true);
}
待ちスレッドwriteThreadを開いて、writePacketsを走って、この方法の主なコード: // Write out packets from the queue.
while (!done && (writerThread == thisThread)) {
Packet packet = nextPacket();
if (packet != null) {
writer.write(packet.toXML());
if (queue.isEmpty()) {
writer.flush();
}
}
}
この中のqueue、スレッド安全:private final BlockingQueue queue;
queueにデータがあるとwriteがサーバに行きます。もしデータがないと、nextPacketにブロックされます。 private Packet nextPacket() {
Packet packet = null;
// Wait until there's a packet or we're done.
while (!done && (packet = queue.poll()) == null) {
try {
synchronized (queue) {
queue.wait();
}
}
catch (InterruptedException ie) {
// Do nothing
}
}
return packet;
}
見てください。queue.wait()、書き込みスレッドがここに詰まって、節電します。wait()がある以上、notifyAll()があります。 public void sendPacket(Packet packet) {
if (!done) {
// Invoke interceptors for the new packet that is about to be sent. Interceptors
// may modify the content of the packet.
connection.firePacketInterceptors(packet);
try {
queue.put(packet);
}
catch (InterruptedException ie) {
ie.printStackTrace();
return;
}
synchronized (queue) {
queue.notifyAll();
}
// Process packet writer listeners. Note that we're using the sending
// thread so it's expected that listeners are fast.
connection.firePacketSendingListeners(packet);
}
}
他のスレッドが書きたいデータputをqueueに入れると、writer Threadが呼び覚まされ、引き続き動作し、標準的な生産消費パターンが表示されます。この過程はやはり比較的簡単で、一目で分かります。PacketReaderに比べて、そんなに気を使いません。PacketReader初期化:
protected void init() {
//...
readerThread = new Thread() {
public void run() {
parsePackets(this);
}
};
//...
resetParser();
}
次にパーパーパーPacketsを見ます。 private void parsePackets(Thread thread) {
try {
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
//...
//... , xml
//...
eventType = parser.next();
} while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);
}
はい、循環体はありましたが、reader.read()が見えませんでした。どこで読みますか?また入力フローはどこですか?上記の初期化方法において最後のreetPaser():
private void resetParser() {
try {
parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(connection.reader);
}
catch (XmlPullParserException xppe) {
xppe.printStackTrace();
}
}
ここで入力ストリームconnection.readerをパーパーパーに渡しました。パーサーからしか検索できないようです。XmlPulParsser Factoryでは public XmlPullParser newPullParser() throws XmlPullParserException {
final XmlPullParser pp = new KXmlParser();
for (Map.Entry entry : features.entrySet()) {
pp.setFeature(entry.getKey(), entry.getValue());
}
return pp;
}
パーサーはorg.kxml 2.io.KXml Parsserから来ました。引き続きkxml 2ソースのカバンを探しています。本当に困りました。 public void setInput(Reader reader) throws XmlPullParserException {
this.reader = reader;
//...
if (reader == null)
return;
//...
}
socket入力フローがパーパーに渡されていることが分かります。ついでにパーパーパックス方法で使用するパー.next()などを見て、peek()方法を呼び出しました。 /** Does never read more than needed */
private final int peek(int pos) throws IOException {
while (pos >= peekCount) {
int nw;
if (srcBuf.length <= 1)
nw = reader.read();
else if (srcPos < srcCount)
nw = srcBuf[srcPos++];
else {
srcCount = reader.read(srcBuf, 0, srcBuf.length);
if (srcCount <= 0)
nw = -1;
else
nw = srcBuf[0];
srcPos = 1;
}
if (nw == '\r') {
wasCR = true;
peek[peekCount++] = '
';
}
else {
if (nw == '
') {
if (!wasCR)
peek[peekCount++] = '
';
}
else
peek[peekCount++] = nw;
wasCR = false;
}
}
return peek[pos];
}
ここにやっとreader.readが現れました。ここで読書をブロックします。これはどんなに苦しい過程ですか?xml解析のために、一行のコードを数百行のコードに変えなければなりません。電気がかかります。ご存知ですか?これで終わります。午前中にこれらを見ました。収穫があって、頭が痛いです。