簡體   English   中英

將用戶操作發送到backstack中的活動

[英]Dispatch user action to activities in backstack

我正在開發社交應用。 讓我們假設我有一堆活動A -> B -> C -> D D在前台,用戶按下“喜歡”按鈕到那里(帖子,評論,用戶等)。為了刷新他們的數據,通知所有其他有關此操作的活動的最佳方法是什么? 我在這里看到3個選項:

  1. 使用本地數據庫和一些加載器自動刷新數據。 但是,如果我們使用具有共享數據的不同數據模型(例如BasicUserInfoUserInfoDetailedUserInfo ),則需要大量代碼。
  2. 將EventBus與粘性事件一起使用(Otto的生產者)。 在這種情況下,我必須只通知backstack活動並忽略那些將要創建的活動。 此外,我必須管理事件覆蓋。
  3. 使用WeakReferences的簡單觀察者模式來進行反向堆棧活動。 但后來我遇到了將要重新實例化的被殺活動的問題。

真實的例子:

在Instagram:我打開一些特定用戶的個人資料(A),在那里我打開一些特定的帖子(B),再打開個人資料(A)等等 - > B - > A - > B - > A ....所以它每次從Web加載數據。 在步驟“n + 1”上出現對該帖子的新評論。 如果我開始通過我的backstack回來,我將看到Instagram已經向所有B活動發送了這個“新”評論,而沒有從web重新加載任何數據。 所以我很有意思他們是怎么做到的。

通知系統(事件,觀察者, BroadcastReceiver ,...)的主要用例是當您希望收件人在發生某些事情時或多或少立即采取行動時。

我認為這不是這種情況:后台活動不需要立即采取行動,因為它們不可見。 此外,他們甚至可能不再存在(殺死/凍結)。 他們實際需要的是在他們回到前台時(可能在重新創建之后)獲取最新數據。

為什么不簡單地在onStart()onResume()觸發刷新(使用Loader或你已經使用的任何東西)?
如果需要保持'喜歡'狀態,你可以在DonPause() 如果沒有,喜歡的對象可以存儲在一個全局變量中(這實際上是一個粘性事件)

您可以使用LocalBroadcastManager通知您的堆疊活動您的Liked事件已發生

假設你的活動D:

private void liked() {
  Log.d("liked", "Broadcasting message");
  Intent intent = new Intent("like-event");
  // You can also include some extra data.
  intent.putExtra("message", "my like event occurs!");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

現在它將通知所有在此廣播接收者注冊的活動

例如,在您的活動A,B,C中:

@Override
public void onCreate(Bundle savedInstanceState) {

  ...

  // Register to receive messages.
  // We are registering an observer (mMessageReceiver) to receive Intents
  // with actions named "custom-event-name".
  LocalBroadcastManager.getInstance(this).registerReceiver(mLikeEventReceiver ,
      new IntentFilter("like-event"));
}

// Our handler for received Intents. This will be called whenever an Intent
// with an action named "like-event" is broadcasted.
private BroadcastReceiver mLikeEventReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Get extra data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onDestroy() {
  // Unregister since the activity is about to be closed.
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mLikeEventReceiver );
  super.onDestroy();
}

參考: [ http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html] [1 ] [ 如何使用LocalBroadcastManager? [ https://androidcookbook.com/Recipe.seam?recipeId=4547] [3 ]

  1. [1]: http//developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

  2. [2]: 如何使用LocalBroadcastManager?

  3. [3]: https//androidcookbook.com/Recipe.seam?recipeId = 4547

處理此類事情的經典方法是使用BroadcastReceivers

這是一個示例接收器:

public class StuffHappenedBroadcastReciever extends BroadcastReceiver {


    private static final String ACTION_STUFF_HAPPENED = "stuff happened";
    private final StuffHappenedListener stuffHappenedListener;

    public StuffHappenedBroadcastReciever(@NonNull Context context, @NonNull StuffHappenedListener stuffHappenedListener) {

        this.stuffHappenedListener = stuffHappenedListener;
        context.registerReceiver(this, new IntentFilter(ACTION_STUFF_HAPPENED));
    }

    public static void notifyStuffHappened(Context context, Bundle data) {

        Intent intent = new Intent(ACTION_STUFF_HAPPENED);
        intent.putExtras(data);

        context.sendBroadcast(intent);

    }

    @Override
    public void onReceive(Context context, Intent intent) {

        stuffHappenedListener.onStuffHappened(intent.getExtras());

    }


    public interface StuffHappenedListener {

        void onStuffHappened(Bundle extras);
    }
}

以及如何將其附加到活動:

public class MainActivity extends AppCompatActivity implements StuffHappenedBroadcastReciever.StuffHappenedListener {

    private StuffHappenedBroadcastReciever mStuffHappenedReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mStuffHappenedReceiver = new StuffHappenedBroadcastReciever(this, this);
    }

    @Override
    protected void onDestroy() {
        unregisterReceiver(mStuffHappenedReceiver);

        super.onDestroy();
    }

    @Override
    public void onStuffHappened(Bundle extras) {
        // do stuff here
    }
}

只要活動處於活動狀態,就會調用“onStuffHappened”。

我同意@bwt的方法。 當您想立即通知某些內容時,這些通知系統應該很重要。

我的方法是緩存系統。 您不需要處理“如果活動是后台堆疊或新創建的”,您始終需要在活動的onResume中查詢所需內容。 因此,您將始終獲得最新的數據。

從服務器獲取數據時,還需要在本地數據庫中復制數據模型。 在您ping服務器之前,例如用戶帖子中有類似內容時,您需要將其設置為本地數據庫中的Flag ,並將您的Http請求發送到您的服務器,說“嘿,這個帖子很受歡迎”。 稍后當您收到此請求的響應時,如果成功與否,請嘗試再次修改此標志。

因此,在您的活動中,如果您從onResume本地數據庫查詢,您將獲得最新的數據。 對於其他用戶帖子的更改,您可以使用BroadcastReceiver來反映Visible活動中的更改。

PS:您可以檢查Realm.io以獲得相對較快的查詢,因為您需要更快地進行本地數據庫調用。

正如許多其他答案所未解釋的那樣,這是一個典型的例子,其中BroadcastReceiver可以輕松完成工作。

我還建議將LocalBroadcastManager類與BroadcastReceiver結合使用。 從BroadcastReceiver的文檔:

如果您不需要跨應用程序發送廣播,請考慮將此類與LocalBroadcastManager一起使用,而不是使用下面描述的更一般的工具。 這將為您提供更高效的實現(無需跨進程通信),並允許您避免考慮與其他應用程序能夠接收或發送您的廣播相關的任何安全問題。

為了刷新他們的數據?

這就是答案。 活動不應該擁有數據。 活動提供數據並允許用戶對其采取行動。 數據本身應該位於一個單獨的實體中,可以通過活動實例(模型)與其進行交互。

此外, 不應該假設在后棧中始終存在活動實例。 當用戶導航回來時,系統可以銷毀這些活動,然后通過系統將其重新創建為不同的對象。 創建整個活動時,數據始終會刷新。

將數據處理分離到專門的類,可以通過活動輕松訪問,並可以按需提供數據/事件綁定到活動。 綁定Service是一個很好的候選人。

就不從Web加載數據而言,您可以設置最近訪問的數據的本地緩存(緩存,因為移動設備有嚴格的存儲限制,服務器和數據庫不是這樣)。 因此,用戶端的任何更改也會在傳播到服務器的同時提交到此緩存。 所有這些都更好地由專門的數據類封裝,而不是依賴於活動類中的反向堆棧或特殊代碼。

作為模式,您可以為所涉及的數據實體構建Model類。 編寫Web API接口實現以與服務器通信。 然后,在API接口之前放置一個緩存層。 緩存將保留對API層的傳出更改和傳入更新,並在不需要服務器調用時簡單地反映數據請求。

Cache必須主要做3件事:

  1. Evict:隨着新數據的到來,丟棄最不重要的數據,因此緩存仍然是固定大小的。 大多數緩存實現( 如此 )都會自動執行此操作。
  2. 無效:有時,由於用戶操作或服務器端的外部事件,某些數據必須刷新。
  3. 過期:數據可以設置時間限制,並將被自動驅逐。 在強制定期刷新數據時,這很有用。

現在大多數緩存實現處理原始字節。 我建議使用Realm這樣的東西,一個對象數據庫並將其包裝在緩存中。 因此,請求用戶推文的典型流程是:

  1. 顯示活動。
  2. Activity綁定到數據服務,並表達了對“推文”的興趣。
  3. 數據服務在高速緩存數據庫的Tweets表中查找最后獲取的推文列表,並立即返回該消息。
  4. 但是,數據服務還調用服務器在db在本地具有最新推文的時間戳之后給出推文。
  5. 服務器返回最新的推文集。
  6. 數據服務使用新的傳入數據更新表達對推文感興趣的所有綁定活動。 此數據也在緩存db中本地復制。 此時,tweets表也通過刪除舊記錄進行優化。
  7. 活動從數據服務取消綁定,因為活動正在消失,停止,銷毀等。
  8. 如果沒有綁定活動對“推文”感興趣,數據服務會停止加載更多推文。

通過維護與服務器的套接字連接並實時接收有趣的更改,您的數據實現可以更進一步。 只要有新數據傳入,服務器就會調用上面的第5步。

TLDR; 將數據管理與“活動”分開,然后您可以獨立於UI關注點進行演變。

如果您必須對某些數據進行所有活動的更改,您可以遵循界面模式。例如,您有一個自定義類類ActivityData,其中包含您需要在所有活動中更新的內容

步驟1:

創建如下界面

public interface ActivityEventListener
{


    ActivityData getData( Context context,
            Activity activity );


}

第2步:

創建一個BaseActivity,用於引用您在應用程序中具有的所有活動,並按如下方式實現接口

public class BaseActivity extends Activity
{
    protected AcitivityData mData= null;


    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
    }

    protected ActivityEventListener mListener = new ActivityEventListener()
    {


        @Override
        public ActivityData getData( Context context,
                Activity activity )
        {
            // TODO Auto-generated method stub
            return mData;
        }


    };
}

第3步:使用BaseActivity擴展您自己的活動,例如A或B或C .......

公共類A擴展BaseActivity {

  ActivityData mData;
@Override
protected void onCreate( Bundle savedInstanceState )
{

     mData = mListener.getData(this,this);

     updateWidgets(mData);

}

}

其中updateWidgets是一個函數,您可以在其中定義UI元素並使用與接口相關的數據

由於您的所有活動B / C /等都可以獲得ActivityData的參考。 活動表單backstack將通過onStart()開始執行,用戶可以處理ActivityData中存在的詳細信息

在您喜歡上次活動的情況下,您可以更新ActivtyData的對象,當后台堆棧活動恢復或啟動時,您可以獲得更新的數據,因為您的Backstack活動擴展了BaseActiivty具有接口。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM