2017-10-08 4 views
0

メールメッセージを受信して​​処理するアンドロイドアプリを開発しています。アプリは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

、私の英語を言い訳してください。どんな提案も大歓迎です。私はこのサイトの厳密さについて聞いたことがあるので、優しくしてください、私はここで新しいです。

答えて

0

デッド接続やサーバーを待っていることを確認するために、必ずtimeout propertiesを設定してください。

直接nopコマンドを発行する代わりに、Folder.isOpenまたはFolder.getMessageCountを呼び出す必要があります。必要に応じてnopコマンドを発行します。

フォルダが非同期に閉じられた場合(FolderClosedException)、アイドルループを再開する必要があります。

+0

確かに、どこかでFolderClosedExceptionをスローすると(デバッグするにはあまりにも混乱して)起こると思います。アイドルループを再起動するとどういう意味ですか? –

+0

Folderが非同期に閉じられると、Folder.idle呼び出しは失敗します。それはすべての例外を無視しているので、そのスレッドをタイトなループにしておく可能性があります。その例外をキャッチし、フォルダを再度開いて(意図している場合)、アイドルループを再起動することをお勧めします。 –

+0

コードは私が投稿した断片よりも大きいです。すでに実装されていることはすべてありますが、私はそれを短くしたいと思っていました。私は問題を解決したと思うが、私が起こっていると思っていることについての「supossed」の説明は、許可された文字数に収まらない。 「大きな」コメントを追加するための「正しい」方法がいくつかありますか? –

関連する問題