2011-08-12 18 views
0

私はここで間違ったトラックにいる可能性があるので、私はこれを見つけ出すために投稿しています。AsyncTaskをn層モデルと組み合わせて使用​​

私は、データをロードするために多数のサービスリクエストを使用するアンドロイドアプリを開発しています。

Iは、n層のパターン、次の私のコードを配置し:私は開始私の活動からドメイン層(ドメイン)/ Webサービスアクセス層(DAL)/ビジネスロジック層(サービス)/ UI層(UI)

AsyncTask:サービスレイヤーから項目をロードします。アイテムがロードされている間、私は連続的なループ円のアイコンで進行ダイアログを表示します。これはあまりユーザーフレンドリーではないので、進行状況インジケーターを使用して進捗状況を表示したいと思います。

私は自分のdalを簡単に変更できるので、最初に取得するアイテムのIDのリストを取得し、一括で一度に1つずつ取り出します。このようにして、ユーザーに進捗状況を報告することができ、新しい項目(または2つ以上)がロードされるたびにuiを更新します。

問題は私がこれについてどうやって行くのか分からないことです。 AsyncTaskのすべての例は、AsyncTaskを継承するクラス内ですべてが発生し、これがUIスレッドから実行されるという原則に基づいて動作します。

AsyncTaskクラスの定義方法と、データアクセスレイヤーからUIへの進捗状況をどのように報告することができるかについての短いコード例がありますか?

私はすべてのコードをUIに入れても問題はありませんが、それはあまり良い練習ではありません。

私が何をしたいのコード例:どこかMainApplicationで

私が注文のリストを保つ:

public List<Order> orders = null; 

私の活動は、AsyncTaskを呼び出して、それを開始します:

OrderActivity.java

public void loadOrdersAsync() { 
    // Create async task instance 
    OrderLoaderAsyncTask loader = new OrderLoaderAsyncTask(); 

    // Start loading 
    loader.doInBackground(); 
} 

OrderLoadAsyncTask.java

public class OrderLoaderAsyncTask extends AsyncTask { 
    @Override 
    public Integer doInBackground(Context.... params) { 
     OrderService service = new OrderService(); 
     MainApplication.orders = service.getAll(); 
    } 

    @Override 
    public void onProgressUpdate(Integer... values) { 
     super.onProgressUpdate(values); 

     // Update progress bar 
     updateProgressBar(values); 
    } 
} 

OrderService.java私が進行を追跡することができますが、どのように私は私のAsyncTaskにorderslistを構築するためのロジックを移動せずにUIに、この進捗状況を報告します私のサービスに今すぐ

public class OrderService { 
    public List<Order> getAll() { 
     // Check to see if we have a cached version 
     // If we don't, we fetch the orders from REST webservice 
     OrderRepository repository = new OrderRepository(); 

     // Get list of ID's to fetch 
     List<Integer> ids = new ArrayList<Integer>; 
     List<Order> orders = new ArrayList<Order>; 
     ids = repository.getIDs(); 

     // For each id, fetch order from webservice 
     for (Integer id : ids) { 
      Order order = repository.getOneById(id); 

      orders.add(order); 

      // Report progress, but how? 
     } 

     return orders; 
    } 
} 

クラス?サービス層内から進捗状況を報告するにはどうすればよいですか?または私のリポジトリレイヤーからでもですか?

答えて

1

RESTサービスはリクエストレスポンスのようなもので、RESTサーバー内のすべてのデータレイヤーにロードされた後にのみデータを受信します。

私はタイマーで500mSを更新するプログレスバーでそれを行います。 Timerは別のスレッドを起動するので、アクティビティのrunOnUiThread()メソッド内でUIを更新する必要があります。

例コード:

public class myActivity extends Activity { 

    private Timer myTimer; 

    @Override 
    public void onCreate(Bundle b) { 
     super.onCreate(b); 
     setContentView(R.layout.main); 

     myTimer = new Timer(); 
     myTimer.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       TimerMethod(); 
      } 

     }, 0, 500); // 500mS starting with no delay 
    } 

    private void TimerMethod() 
    { 
      // this runs each time Timer fires 
     this.runOnUiThread(Timer_Tick); 
    } 

    private Runnable Timer_Tick = new Runnable() { 
     public void run() { 

      // do something with my progress bar 
     UpdateMyProgressBar(); 

     } 
    }; 
} 
+0

これは少しのリソースを消費しませんか?さらに、私はそれがAsyncTaskの性質に逆らって、そのようにしていると思います... –

+0

これは非常に良い習慣ではないかもしれませんが、私がやりたいことに完全に合っているので、これをよく –

+0

私は、AsyncTaskを何度も作成しなければならないのではなく、AsyncTaskを1回だけ起動できることを覚えておいてください。タイマーを使用すると、定期的にデータソースをポーリングすることができます(サーバー、JDBC接続、名前)。 AsyncTaskのソースコードを見て、ハンドラを使ったタイマーではなく、私がここで説明したものとはそれ程離れていません。 – ruhalde

1

AsyncTaskには進行状況の更新と呼ばれる機能があり、UIスレッドを進捗状況で更新できます。これは、あなたが記述していることを正確に行うように設計されています。

+0

、ここではドキュメントがhttp://developer.android.com/reference/android/os/AsyncTask.html – Pyrodante

+0

私はAsyncTaskは、その機能を持っていることを知っていることを実施しています。しかし、AsyncTaskはdobackgroundworkメソッドで(サービスクラスのインスタンス上にある)サービスレイヤ関数を呼び出していると言います。サービス層は、データアクセス層内の機能を要求する。私はデータアクセス機能からトップクラスの進捗状況をどのように更新するつもりですか? –

1

私はあなたが、レコードをたくさん持っている場合、それはあなたのデータベースをオーバーロードされます原因1でオブジェクトを1つずつフェッチ行くことをお勧めしません。そのためにページングを使用することを検討してください(実際にユーザーが画面上に項目を表示している場合のみロードしてください)。

これをスピードアップするためにできることは、リストに表示するための最小限のデータ(例:タイトルと説明のみ)を読み込み、詳細情報が必要な場合は後で残りのデータを読み込むことです。

+0

どちらの解決策も本当に私にとっては役に立たない。フェッチするだけのオーダーではなく、オーダーステート、顧客、住所のリストもあります。サービスでは、各レコードのIDのリストを取得することも、その全体をフェッチすることもできます。ただし、1回のリクエストで取り出すアイテムの数を制限できます。いずれの場合でも、注文画面には非常に多くの情報が表示されるため、最低限必要なものはかなり多くの要求を必要とします。 –

+0

さて、私は一度に5(または他の数字)をフェッチする方法を考え出したので、20の注文がある場合は4つの連続したリクエストがあります。そうすることで、UIのプログレスバーを更新し、オーダーの表示を開始することができます。しかし、実際にリクエストを実行しているサービス(またはdal)レイヤーからonProgressUpdateを呼び出す方法についてはまだ問題が残っています。 –

2

私が言いたいことは、あなたが説明しているn層アーキテクチャは、ほとんどのモバイルアプリにとって少し残酷すぎるということです。それは、あなたのアプリケーションを階層化して別々の責任を負う必要はありません。あなたの記述は実際に大きなエンタープライズアプリケーションのための優れたアーキテクチャーレイヤーですが、実際にAndroidのやり方がある場合には、システムとの戦いで厳密にそれらをAndroidのコード結果に適用していることがわかります。

たとえば、「サービス」をアクティビティから完全に分離したいのに、UI内で何かを更新したいということがあります。それ無理。

Androidは依存性注入を広範囲に使用します。コンテキストはしばしばさまざまなメソッドの最初の引数であることがわかります。 UIから何かを完全に分離したい場合は、新しい 'コンテキスト'を作成して作業する必要があることを示してください。 Androidはこれにサービスを提供します。こうすることで、UIとは完全に別の作業を行い、バックグラウンドサービスとUI間のメッセージングにインテントを使用できます。

しかし、いくつかのアプリやAndroidデベロッパーサービスの開始時にサービスが少し複雑になっていることがわかりました(本当にそうではありません)。ただ、アクティビティに結び付けられていないスレッドを作成する唯一の方法は、サービスであることを指摘しておきます。いくつかの人々は、Applicationクラスをオーバーライドし、アクティビティーとスレッドのソフトリファレンスをマッピングすることでトリックを使用します。このようにして構成の変更が発生すると(たとえば方向の変更など)、スレッドは強制終了されず、新しく作成されたアクティビティインスタンスへの参照を取得できます。これらのトリックは私の意見ではアンチパターンです。

問題に戻ってください。

私はそれを簡単に保つことをお勧めします。 UIから新しいスレッドを開始すると、設定が変更されたときにスレッドが強制終了され、すでにネットワークから取得されたデータが失われる可能性があることを意味します。

  1. いくつかのポスト/ゲット機能をラップし、おそらくリクエストごとにいくつかのhttpヘッダーを設定するhttpユーティリティークラスを作成します。
  2. 単純なサービスレイヤを作成します。これらのクラスには、それぞれのメソッドが1つの単純なタスクまたは残りの要求を処理するメソッドが含まれています。あなたの活動で
  3. あなたが好きな、独自の本当のバックグラウンドサービスにAsyncTaskからロジックを移動して行う場合は、この後のを改善することができますいくつかのサービスメソッドを組み合わせAsyncTaskを使用してUI

にフィードバックを提供IntentsとBroadcastReceiversとのいくつかの中間メッセージング。

しかし、サービスなしで、それを簡単に行うためのあなたの便宜のために簡略化した例:

public class OrderActivity extends Activity { 

    private ProgressDialog progressDialog = null; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     new OrderLoaderTask().execute(); 
    } 

    class Order { 
     // Add some data 
    } 

    class OrderService { 

     public List<Integer> getOrderIds() { 
      List<Integer> ids = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7}); 
      return ids;   
     } 

     public Order getOrder(int id) { 
      // Do real http request through some utility class 
      try { 
       Thread.sleep(2000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      return new Order(); 
     } 

    } 

    class OrderLoaderTask extends AsyncTask<Void, Integer, List<Order>> { 

     @Override 
     protected void onPreExecute() { 
      progressDialog = ProgressDialog.show(TestAndroidActivity.this, "loading", "loading"); 
     } 

     @Override 
     protected void onProgressUpdate(Integer... values) { 
      progressDialog.setMessage(String.format("loading (%s/%s)", values[0], values[1])); 
     } 

     @Override 
     protected List<Order> doInBackground(Void... params) { 

      OrderService service = new OrderService(); 

      List<Order> orders = new ArrayList<Order>(); 

      List<Integer> ids = service.getOrderIds(); 
      for (int i=0; i<ids.size(); i++) { 
       orders.add(service.getOrder(ids.get(i))); 
       publishProgress(i+1, ids.size()); 
      } 
      return orders; 
     } 

     @Override 
     protected void onPostExecute(List<Order> result) { 
      if (result != null) { 
       // Do something with the results! 
      } 
      progressDialog.dismiss(); 
     } 

    } 
} 

注:ちょうど、新しいAndroidプロジェクトのコピーにこのコードを作成インポートを修正して実行します!

AsyncTaskでコードを再利用する場合は、独自のファイルに移動し、クラスをpublicにしてメソッドをオーバーライドします。スレッド間に必要な接着コードやインターフェイスがあります。コード例については

new OrderLoaderTask() { 

    protected void onProgressUpdate(Integer[] values) { 
     // Update progress 
    }; 

    protected void onPostExecute(List<Order> result) { 
     // Do something 
    }; 

}.execute(); 
+0

最後のコードサンプルは、私が今やっているのとまったく同じです。しかし、この問題は、注文をフェッチするメソッドがサービスに格納されていないため、再利用するのが難しいことです。さらに、私のアプリケーションはキャッシュファイル内のすべてをローカルにキャッシュするので、サービスもそれを考慮する必要があります。私は、私のアプリケーションにどんなサービスが意味するのかを見ていきたいと思います。 –

+0

複数の場所にコードが必要ですか?そうであれば、AsyncTaskを公開して自分のファイルを作成し、この機能が必要な各アクティビティで匿名AsyncTaskを使用し、onPostExecuteをオーバーライドし、各アクティビティ固有のコードに対してonProgessUpdateを使用します。 –

関連する問題