メールメッセージを受信して処理するアンドロイドアプリを開発しています。アプリはIMAPサーバーに接続し、接続を維持しておく必要があります。新しいメールメッセージをすぐに確認して処理できます(メールには、メールAPIサーバーからのjsonデータが含まれています)。このアプリには、手動接続とライブ接続という2つのモードがあります。ここに私のコードのいくつかされていますjavamailアイドルがメッセージをトリガするのを停止します.Addded、スレッドロック
class Idler {
Thread th;
volatile Boolean isIdling=false;
boolean shouldsync=false;//we need to see if we have unseen mails
Object idleLock;
Handler handler=new Handler();
IMAPFolder inbox;
public boolean keppAliveConnection;//keep alive connection, or manual mode
//This thread should keep the idle connection alive, or in case it's set to manual mode (keppAliveConnection=false) get new mail.
Thread refreshThread;
synchronized void refresh()
{
if(isIdling)//if already idling, just keep connection alive
{
refreshThread =new Thread(new Runnable() {
@Override
public void run() {
try {
inbox.doCommand(new IMAPFolder.ProtocolCommand() {
@Override
public Object doCommand(IMAPProtocol protocol) throws ProtocolException {
//Why not noop?
//any call to IMAPFolder.doCommand() will trigger waitIfIdle, this
//issues a "DONE" command and waits for idle to return(ideally with a DONE server response).
// So... I think NOOP is unnecessary
//protocol.simpleCommand("NOOP",null); I'm not issuing noop due to what I said^
//PD: if connection was broken, then server response will never arrive, and idle will keep running forever
//without triggering messagesAdded event any more :'(I see any other explanation to this phenomenon
return null;
}
});
} catch (MessagingException e) {
e.printStackTrace();
}
}
},"SyncThread");
refreshThread.start();
}
else
{
getNewMail();//If manual mode keppAliveConnection=false) get the new mail
}
}
public Idler()
{
th=new Thread(new Runnable() {
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
while (true)
{
try {
if(refreshThread !=null && refreshThread.isAlive())
refreshThread.interrupt();//if the refresher thread is active: interrupt. I thing this is not necessary at this point, but not shure
initIMAP();//initializes imap store
try {
shouldsync=connectIMAP()||shouldsync;//if was disconnected or ordered to sync: needs to sync
}
catch (Exception e)
{
Thread.sleep(5000);//if can't connect: wait some time and throw
throw e;
}
shouldsync=initInbox()||shouldsync;//if inbox was null or closed: needs to sync
if(shouldsync)//if needs to sync
{
getNewMail();//gets new unseen mail
shouldsync=false;//already refreshed, clear sync "flag"
}
while (keppAliveConnection) {//if sould keep idling "forever"
synchronized (idleLock){}//MessageCountListener may be doing some work... wait for it
isIdling = true; //set isIdling "flag"
handler.removeCallbacksAndMessages(null);//clears refresh scheduled tasks
handler.postDelayed(new Runnable() {
@Override
public void run() {
refresh();
}
},1200000);//Schedule a refresh in 20 minutes
inbox.idle();//start idling
if(refreshThread !=null && refreshThread.isAlive())
refreshThread.interrupt();//if the refresher thread is active: interrupt. I thing this is not necessary at this point, but not shure
handler.removeCallbacksAndMessages(null);//clears refresh scheduled tasks
isIdling=false;//clear isIdling "flag"
if(shouldsync)
break;//if ordered to sync... break. The loop will handle it upstairs.
synchronized (idleLock){}//MessageCountListener may be doing some work... wait for it
}
}
catch (Exception e) {
//if the refresher thread is active: interrupt
//Why interrupt? refresher thread may be waiting for idle to return after "DONE" command, but if folder was closed and throws
//a FolderClosedException, then it could wait forever...., so... interrupt.
if (refreshThread != null && refreshThread.isAlive())
refreshThread.interrupt();
handler.removeCallbacksAndMessages(null);//clears refresh scheduled tasks
}
}
}
},"IdlerThread");
th.start();
}
private synchronized void getNewMail()
{
shouldsync=false;
long uid=getLastSeen();//get last unprocessed mail
SearchTerm searchTerm=new UidTerm(uid,Long.MAX_VALUE);//search from las processed message to the las one.
IMAPSearchOperation so=new IMAPSearchOperation(searchTerm);
try {
so.run();//search new messages
final long[] is=so.uids();//get unprocessed messages count
if (is.length > 0) {//if some...
try {
//there are new messages
IMAPFetchMessagesOperation fop=new IMAPFetchMessagesOperation(is);
fop.run();//fetch new messages
if(fop.messages.length>0)
{
//process fetched messages (internally sets the last seen uid value & delete some...)
processMessages(fop.messages);
}
inbox.expunge();//expunge deleted messages if any
}
catch (Exception e)
{
//Do something
}
}
else
{
//Do something
}
}
catch (Exception e)
{
//Do something
}
}
private synchronized void initIMAP()
{
if(store==null)
{
store=new IMAPStore(mailSession,new URLName("imap",p.IMAPServer,p.IMAPPort,null,p.IMAPUser,p.IMAPPassword));
}
}
private boolean connectIMAP() throws MessagingException {
try {
store.connect(p.IMAPServer, p.IMAPPort, p.IMAPUser, p.IMAPPassword);
return true;
}
catch (IllegalStateException e)
{
return false;
}
}
//returns true if the folder was closed or null
private synchronized boolean initInbox() throws MessagingException {
boolean retVal=false;
if(inbox==null)
{//if null, create. This is called after initializing store
inbox = (IMAPFolder) store.getFolder("INBOX");
inbox.addMessageCountListener(countListener);
retVal=true;//was created
}
if(!inbox.isOpen())
{
inbox.open(Folder.READ_WRITE);
retVal=true;//was oppened
}
return retVal;
}
private MessageCountListener countListener= new MessageCountAdapter() {
@Override
public void messagesAdded(MessageCountEvent ev) {
synchronized (idleLock)
{
try {
processMessages(ev.getMessages());//process the new messages, (internally sets the last seen uid value & delete some...)
inbox.expunge();//expunge deleted messajes if any
} catch (MessagingException e) {
//Do something
}
}
}
};
}
問題がある:時々、ユーザーがさわやかであるか、アプリの自動更新され、アライブ接続モードでは、1またはこの両方の条件が私を保持したときに新しいメッセージを受け取ることからこれはjavamailソースコードからのものです。
1:
//I don't know why sometimes it enters monitor state here.
private synchronized void throwClosedException(ConnectionException cex)
throws FolderClosedException, StoreClosedException {
// If it's the folder's protocol object, throw a FolderClosedException;
// otherwise, throw a StoreClosedException.
// If a command has failed because the connection is closed,
// the folder will have already been forced closed by the
// time we get here and our protocol object will have been
// released, so if we no longer have a protocol object we base
// this decision on whether we *think* the folder is open.
if ((protocol != null && cex.getProtocol() == protocol) ||
(protocol == null && !reallyClosed))
throw new FolderClosedException(this, cex.getMessage());
else
throw new StoreClosedException(store, cex.getMessage());
}
2:IdlerThreadの状態を監視入る "refresherThreadは" の待ち状態に入る。このスレッドの両方の一つとして
void waitIfIdle() throws ProtocolException {
assert Thread.holdsLock(messageCacheLock);
while (idleState != RUNNING) {
if (idleState == IDLE) {
protocol.idleAbort();
idleState = ABORTING;
}
try {
// give up lock and wait to be not idle
messageCacheLock.wait();//<-----This is the line is driving me crazy.
} catch (InterruptedException ex) { }
}
}
(実行 "停止" wait &モニタ状態)この状態になると私のアプリは役に立たない。私の国では、モバイルデータネットワークは非常に不安定で、遅い&高価(GSM)なので、障害が回復し、転送されるすべてのビットに注意する必要があります。
接続が黙って失敗し、リフレッシャーのスレッドがそのジョブを開始したときに問題が発生したと思います。アイドルがアクティブな場合はDONEコマンドを発行しますが、アイドル時にFolderClosedExceptionをスローしようとすると、一方または両方のスレッドが無期限にロックされます。
私の質問は次のとおりです。なぜこのような状況が発生し、それを防止するのですか?アイドルループをロックされずに安全に動作させ続けるにはどうすればいいですか?
私は疲労まで多くのことを試みましたが、結果はありませんでした。
ここに私の問題を解決することなく読んだスレッドがあります。私の国ではインターネットも非常に高価ですので、私が望むだけ調べることはできませんし、私が訪れたすべてのURLを一覧表示することはできません。
JavaMail: Keeping IMAPFolder.idle() alive
JavaMail: Keeping IMAPFolder.idle() alive
Javamail : Proper way to issue idle() for IMAPFolder
、私の英語を言い訳してください。どんな提案も大歓迎です。私はこのサイトの厳密さについて聞いたことがあるので、優しくしてください、私はここで新しいです。
確かに、どこかでFolderClosedExceptionをスローすると(デバッグするにはあまりにも混乱して)起こると思います。アイドルループを再起動するとどういう意味ですか? –
Folderが非同期に閉じられると、Folder.idle呼び出しは失敗します。それはすべての例外を無視しているので、そのスレッドをタイトなループにしておく可能性があります。その例外をキャッチし、フォルダを再度開いて(意図している場合)、アイドルループを再起動することをお勧めします。 –
コードは私が投稿した断片よりも大きいです。すでに実装されていることはすべてありますが、私はそれを短くしたいと思っていました。私は問題を解決したと思うが、私が起こっていると思っていることについての「supossed」の説明は、許可された文字数に収まらない。 「大きな」コメントを追加するための「正しい」方法がいくつかありますか? –