Androidポーリングベストプラクティスサービス+AlarmManager+Thread

17228 ワード

転身:ネットに浸かっている日ポーリング、android、service、alarmmanager
Androidでは,サーバ内のデータ変化情報をユーザに通知するには,一般にプッシュとポーリングの2つの方法がある.
メッセージプッシュは、サービス側がクライアントに自発的にメッセージを送信するものであり、データが変化したことを最初に知ったのはサーバ自身であるため、プッシュの利点はリアルタイム性が高いことである.しかし、サーバのアクティブプッシュには、クライアントを永続的に接続できるサービス・エンド・プログラムを単独で開発する必要がありますが、xmmpプロトコルに基づくプッシュ・スキームを実現するオープン・ソースのコードが多く、グーグルのプッシュ・スキームも使用できます.しかし、サービス側がプロアクティブにプッシュする必要はなく、一定の時間間隔でクライアントがプロアクティブにクエリーを開始する場合があります.
例えば、このようなappがあれば、リアルタイム性の要求は高くなく、毎日10回の最新データを取得すれば要求を満たすことができます.このような状況は明らかにポーリングが適しており、プッシュは浪費され、消費電力がかかります.ただし、ポーリングやプッシュにかかわらず、アプリケーションが実行中であるかどうかにかかわらず、ユーザーに通知を送信できるようにする必要があるため、サービスが必要です.サービスを使用してこの目的を達成するには、次の2つの方法があります.
方案一:サービス+****Thread
サービスでwhileループ付きスレッドを開き、サーバからデータを絶えずクエリー(一定時間間隔)し、ユーザーに通知する必要があることを発見した場合にnotificationを送信します.このシナリオのコードは、概ね次のとおりです.
import org.apache.http.Header;
import org.json.JSONObject;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
/**
 *
 *        ,       ,                 
 *
 * @author jerry
 *
 */
public class PushSmsService extends Service {
   private MyThread myThread;
   private NotificationManager manager;
   private Notification notification;
   private PendingIntent pi;
   private AsyncHttpClient client;
   private boolean flag = true;
   @Override
   public IBinder onBind(Intent intent) {
     // TODO Auto-generated method stub
     return null;
   }
   @Override
   public void onCreate() {
     System.out.println("oncreate()");
     this.client = new AsyncHttpClient();
    this.myThread = new MyThread();
     this.myThread.start();
     super.onCreate();
   }
   @Override
   public void onDestroy() {
     this.flag = false;
     super.onDestroy();
   }
   private void notification(String content, String number, String date) {
     //           
     manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
     notification = new Notification(R.drawable.ic_menu_compose, content,
     System.currentTimeMillis());
     notification.defaults = Notification.DEFAULT_ALL; //       ,    、  、  
     notification.flags = Notification.FLAG_AUTO_CANCEL; //         ,            
     notification.flags |= Notification.FLAG_NO_CLEAR;//         ,           
     Intent intent = new Intent(getApplicationContext(),
     ContentActivity.class);
     intent.putExtra("content", content);
     intent.putExtra("number", number);
     intent.putExtra("date", date);
     pi = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);

     notification.setLatestEventInfo(getApplicationContext(), number
     + "    ", content, pi);
     //          
     manager.notify(0, notification);
   }
 private class MyThread extends Thread {
   @Override
   public void run() {
     String url = "        ";
     while (flag) {
       System.out.println("    ");
       try {
         //   10           
         Thread.sleep(10000);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
       //   get          
       client.get(url, new AsyncHttpResponseHandler() {
         @Override
         public void onSuccess(int statusCode, Header[] headers,
           byte[] responseBody) {
           try {
             JSONObject result = new JSONObject(new String(
             responseBody, "utf-8"));
             int state = result.getInt("state");
             //          
             if (state % 2 == 0) {
               String content = result.getString("content");
               String date = result.getString("date");
               String number = result.getString("number");
               notification(content, number, date);
             }
           } catch (Exception e) {
             e.printStackTrace();
           }
         }
       @Override
       public void onFailure(int statusCode, Header[] headers,
             byte[] responseBody, Throwable error) {
         Toast.makeText(getApplicationContext(), "      ", 0)
               .show();
       }
      });
   }
   }
 }
}

このうちAsyncHttpClientはネットワーク非同期要求のオープンソースライブラリであり、非同期ネットワーク要求を容易に実現することができる.
この案には多くの不足があり、1つはアプリケーションが長期にわたってバックグラウンドプログラムで実行されていることであり、携帯電話で安全にするのが好きなユーザーであれば、このサービスは彼に殺される可能性が高い.第二に、サービスはバックグラウンドで動作することができますが、携帯電話がスリープしている場合、スレッドが掛けられているようです.ここではAndroidシステムロックのメカニズムに関連しています.つまり、システムがしばらく活躍していないことを検出した後、不要なサービスをオフにしてリソースと電力消費を減らすことができます.これは多くのアプリケーションが示しているのとは異なり、ユーザーの習慣に合わないということです.だから私たちはやはり2つ目の案を選んだ.
シナリオ2:service+AlarmManager+Thread
Alarmとは目覚まし時計を意味し、オリジナルandroidが持参した目覚まし時計アプリでもAlarmManagerは確かに重要ですが、AlarmManagerが目覚まし時計アプリとしてしか使われていないわけではありません.システムレベルのヒントサービスとして、非常に重要な地位にあるはずです.実際にandroidでは多くのものがAlarmManagerを利用して実現できます.AlarmManagerは、特定の時点で指定されたIntentをブロードキャストします.簡単に言えば、時間を設定し、その時間が来ると、AlarmManagerが設定したIntentを放送してくれます.このintentはactivityを指すこともできるし、サービスを指すこともできる.
次にalarmタイミングコールサービスを用いてポーリングを実現する方法を示す:一、新しいポーリングツールクラスPollingUtils.java
public class PollingUtils {
   //      
   public static void startPollingService(Context context, int seconds, Class> cls,String action) {
     //  AlarmManager    
     AlarmManager manager = (AlarmManager) context
           .getSystemService(Context.ALARM_SERVICE);

     //      Service Intent
     Intent intent = new Intent(context, cls);
     intent.setAction(action);
     PendingIntent pendingIntent = PendingIntent.getService(context, 0,
     intent, PendingIntent.FLAG_UPDATE_CURRENT);

     //         
     long triggerAtTime = SystemClock.elapsedRealtime();

     //  AlarmManger setRepeating             (seconds )      Service
     manager.setRepeating(AlarmManager.ELAPSED_REALTIME, triggerAtTime,
     seconds * 1000, pendingIntent);
   }
   //      
   public static void stopPollingService(Context context, Class> cls,String action) {
     AlarmManager manager = (AlarmManager) context
           .getSystemService(Context.ALARM_SERVICE);
     Intent intent = new Intent(context, cls);
     intent.setAction(action);
     PendingIntent pendingIntent = PendingIntent.getService(context, 0,
           intent, PendingIntent.FLAG_UPDATE_CURRENT);
     //         
     manager.cancel(pendingIntent);
   }
}

二、ポーリングタスクの構築PollingServicesを実行する.java
public class PollingService extends Service {
   public static final String ACTION = "com.ryantang.service.PollingService";

   private Notification mNotification;
   private NotificationManager mManager;
   @Override
   public IBinder onBind(Intent intent) {
     return null;
   }
   @Override
   public void onCreate() {
     initNotifiManager();
   }

   @Override
   public void onStart(Intent intent, int startId) {
     new PollingThread().start();
   }
   //        
   private void initNotifiManager() {
     mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
     int icon = R.drawable.ic_launcher;
     mNotification = new Notification();
     mNotification.icon = icon;
     mNotification.tickerText = "New Message";
     mNotification.defaults |= Notification.DEFAULT_SOUND;
     mNotification.flags = Notification.FLAG_AUTO_CANCEL;
   }
   //  Notification
   private void showNotification() {
     mNotification.when = System.currentTimeMillis();
     //Navigator to the new activity when click the notification title
     Intent i = new Intent(this, MessageActivity.class);
     PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i,
         Intent.FLAG_ACTIVITY_NEW_TASK);
     mNotification.setLatestEventInfo(this,
         getResources().getString(R.string.app_name), "You have new message!", pendingIntent);
     mManager.notify(0, mNotification);
   }
   /**
   * Polling thread
   *    Server       
   * @Author Ryan
   * @Create 2013-7-13   10:18:34
   */
   int count = 0;
   class PollingThread extends Thread {
     @Override
     public void run() {
       System.out.println("Polling...");
       count ++;
       //     5       
       if (count % 5 == 0) {
       showNotification();
       System.out.println("New message!");
       }
     }
   }

   @Override
   public void onDestroy() {
     super.onDestroy();
     System.out.println("Service:onDestroy");
   }
}

三、MainActivity.JAvaでのPollingServiceのオン/オフ
public class MainActivity extends Activity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     //Start polling service
     System.out.println("Start polling service...");
     PollingUtils.startPollingService(this, 5, PollingService.class, PollingService.ACTION);
   }

   @Override
   protected void onDestroy() {
     super.onDestroy();
     //Stop polling service
     System.out.println("Stop polling service...");
     PollingUtils.stopPollingService(this, PollingService.class, PollingService.ACTION);
   }
}

第2のシナリオと第1のシナリオの本質的な違いは,タイミングクエリを実現する方法の違いであり,1つはシステムサービスを利用することであり,1つはwhileループを自分で通過することである.システム・サービスを使用すると、より安定性が高く、AlarmManagerが常に実行者であるため、スリープ状態でのポーリング中断の問題が適切に解決されることは明らかです.