亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android開發(fā)中多進(jìn)程共享數(shù)據(jù)簡析

 更新時間:2016年04月11日 16:11:46   投稿:lijiao  
這篇文章主要為大家簡單分析Android開發(fā)中多進(jìn)程共享數(shù)據(jù),怎么做才能讓這兩邊共享數(shù)據(jù),感興趣的小伙伴們可以參考一下

 背景 最近在工作中遇到一個需求,需要在接收到推送的時候?qū)⑼扑瞳@得的數(shù)據(jù)存起來,以供app啟動時使用。我們會認(rèn)為這不是So easy嗎?只要把數(shù)據(jù)存到SharedPreferences中,然后讓app打開同一個SharedPreferences讀取數(shù)據(jù)就可以了。但是在實(shí)際的測試中,我們發(fā)現(xiàn)推送進(jìn)程存入的數(shù)據(jù),并不能在app進(jìn)程中獲得。所以這是為什么呢,也許聰明的讀者從我們上面的陳述中已經(jīng)發(fā)現(xiàn)了原因,因為我們有兩個進(jìn)程,推送進(jìn)程負(fù)責(zé)將推送數(shù)據(jù)存入,而app進(jìn)程負(fù)責(zé)讀取,但是正是由于是兩個進(jìn)程,如果它們同時存在,它們各自在內(nèi)存中保持了自己的SP對象和數(shù)據(jù),在推送進(jìn)程中的存入并不能在app進(jìn)程體現(xiàn)出來,并且可能會被app進(jìn)程刷掉更改的數(shù)據(jù)。那么我們怎么做才能讓這兩邊共享數(shù)據(jù)呢?請看下面陳述。

一、多進(jìn)程支持的SharedPreferences(不推薦)
我們原來的做法是使用SharedPreferences, 自然而然想到,SharedPreferences 在MODE_PRIVATE MODE_PUBLIC 之外其實(shí)還可以設(shè)置多進(jìn)程的Flag ———— MODE_MULTI_PROCESS

復(fù)制代碼 代碼如下:
SharedPreferences myPrefs = context.getSharedPreferences(MY_FILE_NAME, Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE);

一旦我們設(shè)置了這個Flag,每次調(diào)用Context.getSharedPreferences 的時候系統(tǒng)會重新從SP文件中讀入數(shù)據(jù),因此我們在使用的時候每次讀取和存入都要使用Context.getSharedPreferences 重新獲取SP實(shí)例。即使是這樣,由于SP本質(zhì)上并不是多進(jìn)程安全的,所以還是無法保證數(shù)據(jù)的同步,因此該方法我們并沒有使用,我們也不推薦使用。

二、Tray
如果SP不是多進(jìn)程安全的,那么是否有多進(jìn)程安全的,又有SP功能的第三方項目呢。答案是有的,Tray——一個多進(jìn)程安全的SharedPreferences,我們可以在Github上找到它,如果是AndroidStudio,可以直接使用Gradle引入,可謂是十分方便,如下是使用的代碼,十分簡單,沒有apply commit,看起來比SP還要簡單。

 // create a preference accessor. This is for global app preferences.
final AppPreferences appPreferences = new AppPreferences(getContext()); // this Preference comes for free from the library
// save a key value pair
appPreferences.put("key", "lorem ipsum");

// read the value for your key. the second parameter is a fallback (optional otherwise throws)
final String value = appPreferences.getString("key", "default");
Log.v(TAG, "value: " + value); // value: lorem ipsum

// read a key that isn't saved. returns the default (or throws without default)
final String defaultValue = appPreferences.getString("key2", "default");
Log.v(TAG, "value: " + defaultValue); // value: default

但是最終我們并沒有選擇使用它,主要的原因是它需要minSdk 為15,而我們是支持sdk14的,所以只能果斷放棄了。

三、ContentProvider
既然Tray不支持sdk15以下的,那么我們是否可以使用Tray的原理自己實(shí)現(xiàn)一個呢?在閱讀Tray的源碼時我們發(fā)現(xiàn)其實(shí)它是在ContentProvider的基礎(chǔ)上做的,而ContentProvider是Android官方支持的多進(jìn)程安全的。以下是使用ContentProvider的一個例子。

 public class ArticlesProvider extends ContentProvider { 
  private static final String LOG_TAG = "shy.luo.providers.articles.ArticlesProvider"; 
 
  private static final String DB_NAME = "Articles.db"; 
  private static final String DB_TABLE = "ArticlesTable"; 
  private static final int DB_VERSION = 1; 
 
  private static final String DB_CREATE = "create table " + DB_TABLE + 
              " (" + Articles.ID + " integer primary key autoincrement, " + 
              Articles.TITLE + " text not null, " + 
              Articles.ABSTRACT + " text not null, " + 
              Articles.URL + " text not null);"; 
 
  private static final UriMatcher uriMatcher; 
  static { 
      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
      uriMatcher.addURI(Articles.AUTHORITY, "item", Articles.ITEM); 
      uriMatcher.addURI(Articles.AUTHORITY, "item/#", Articles.ITEM_ID); 
      uriMatcher.addURI(Articles.AUTHORITY, "pos/#", Articles.ITEM_POS); 
  } 
 
  private static final HashMap<String, String> articleProjectionMap; 
  static { 
      articleProjectionMap = new HashMap<String, String>(); 
      articleProjectionMap.put(Articles.ID, Articles.ID); 
      articleProjectionMap.put(Articles.TITLE, Articles.TITLE); 
      articleProjectionMap.put(Articles.ABSTRACT, Articles.ABSTRACT); 
      articleProjectionMap.put(Articles.URL, Articles.URL); 
  } 
 
  private DBHelper dbHelper = null; 
  private ContentResolver resolver = null; 
 
  @Override 
  public boolean onCreate() { 
      Context context = getContext(); 
      resolver = context.getContentResolver(); 
      dbHelper = new DBHelper(context, DB_NAME, null, DB_VERSION); 
 
      Log.i(LOG_TAG, "Articles Provider Create"); 
 
      return true; 
  } 
 
  @Override 
  public String getType(Uri uri) { 
      switch (uriMatcher.match(uri)) { 
      case Articles.ITEM: 
          return Articles.CONTENT_TYPE; 
      case Articles.ITEM_ID: 
      case Articles.ITEM_POS: 
          return Articles.CONTENT_ITEM_TYPE; 
      default: 
          throw new IllegalArgumentException("Error Uri: " + uri); 
      } 
  } 
 
  @Override 
  public Uri insert(Uri uri, ContentValues values) { 
      if(uriMatcher.match(uri) != Articles.ITEM) { 
          throw new IllegalArgumentException("Error Uri: " + uri); 
      } 
 
      SQLiteDatabase db = dbHelper.getWritableDatabase(); 
 
      long id = db.insert(DB_TABLE, Articles.ID, values); 
      if(id < 0) { 
          throw new SQLiteException("Unable to insert " + values + " for " + uri); 
      } 
 
      Uri newUri = ContentUris.withAppendedId(uri, id); 
      resolver.notifyChange(newUri, null); 
 
      return newUri; 
  } 
 
  @Override 
  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 
      SQLiteDatabase db = dbHelper.getWritableDatabase(); 
      int count = 0; 
 
      switch(uriMatcher.match(uri)) { 
      case Articles.ITEM: { 
          count = db.update(DB_TABLE, values, selection, selectionArgs); 
          break; 
      } 
      case Articles.ITEM_ID: { 
          String id = uri.getPathSegments().get(1); 
          count = db.update(DB_TABLE, values, Articles.ID + "=" + id 
                  + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs); 
          break; 
      } 
      default: 
          throw new IllegalArgumentException("Error Uri: " + uri); 
      } 
 
      resolver.notifyChange(uri, null); 
 
      return count; 
  } 
 
  @Override 
  public int delete(Uri uri, String selection, String[] selectionArgs) { 
      SQLiteDatabase db = dbHelper.getWritableDatabase(); 
      int count = 0; 
 
      switch(uriMatcher.match(uri)) { 
      case Articles.ITEM: { 
          count = db.delete(DB_TABLE, selection, selectionArgs); 
          break; 
      } 
      case Articles.ITEM_ID: { 
          String id = uri.getPathSegments().get(1); 
          count = db.delete(DB_TABLE, Articles.ID + "=" + id 
                  + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs); 
          break; 
      } 
      default: 
          throw new IllegalArgumentException("Error Uri: " + uri); 
      } 
 
      resolver.notifyChange(uri, null); 
 
      return count; 
  } 
 
  @Override 
  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
      Log.i(LOG_TAG, "ArticlesProvider.query: " + uri); 
 
      SQLiteDatabase db = dbHelper.getReadableDatabase(); 
 
      SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder(); 
      String limit = null; 
 
      switch (uriMatcher.match(uri)) { 
      case Articles.ITEM: { 
          sqlBuilder.setTables(DB_TABLE); 
          sqlBuilder.setProjectionMap(articleProjectionMap); 
          break; 
      } 
      case Articles.ITEM_ID: { 
          String id = uri.getPathSegments().get(1); 
          sqlBuilder.setTables(DB_TABLE); 
          sqlBuilder.setProjectionMap(articleProjectionMap); 
          sqlBuilder.appendWhere(Articles.ID + "=" + id); 
          break; 
      } 
      case Articles.ITEM_POS: { 
          String pos = uri.getPathSegments().get(1); 
          sqlBuilder.setTables(DB_TABLE); 
          sqlBuilder.setProjectionMap(articleProjectionMap); 
          limit = pos + ", 1"; 
          break; 
      } 
      default: 
          throw new IllegalArgumentException("Error Uri: " + uri); 
      } 
 
      Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? Articles.DEFAULT_SORT_ORDER : sortOrder, limit); 
      cursor.setNotificationUri(resolver, uri); 
 
      return cursor; 
  } 

  @Override 
  public Bundle call(String method, String request, Bundle args) { 
      Log.i(LOG_TAG, "ArticlesProvider.call: " + method); 
 
      if(method.equals(Articles.METHOD_GET_ITEM_COUNT)) { 
          return getItemCount(); 
      } 
 
      throw new IllegalArgumentException("Error method call: " + method); 
  } 
 
  private Bundle getItemCount() { 
      Log.i(LOG_TAG, "ArticlesProvider.getItemCount"); 
 
      SQLiteDatabase db = dbHelper.getReadableDatabase(); 
      Cursor cursor = db.rawQuery("select count(*) from " + DB_TABLE, null); 
 
      int count = 0; 
      if (cursor.moveToFirst()) { 
          count = cursor.getInt(0); 
      } 
 
      Bundle bundle = new Bundle(); 
      bundle.putInt(Articles.KEY_ITEM_COUNT, count); 
 
      cursor.close(); 
      db.close(); 
 
      return bundle; 
  } 
 
  private static class DBHelper extends SQLiteOpenHelper { 
      public DBHelper(Context context, String name, CursorFactory factory, int version) { 
          super(context, name, factory, version); 
      } 
 
      @Override 
      public void onCreate(SQLiteDatabase db) { 
          db.execSQL(DB_CREATE); 
      } 
 
      @Override 
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
          db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); 
          onCreate(db); 
      } 
  } 
} 

我們需要創(chuàng)建一個類繼承自ContentProvider,并重載以下方法。 - onCreate(),用來執(zhí)行一些初始化的工作。 - query(Uri, String[], String, String[], String),用來返回數(shù)據(jù)給調(diào)用者。 - insert(Uri, ContentValues),用來插入新的數(shù)據(jù)。 - update(Uri, ContentValues, String, String[]),用來更新已有的數(shù)據(jù)。 - delete(Uri, String, String[]),用來刪除數(shù)據(jù)。 - getType(Uri),用來返回數(shù)據(jù)的MIME類型。
具體使用參考 Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例這篇博客,這里不再贅述。 在以上對ContentProvider的使用過程中,我們發(fā)現(xiàn)過程比較繁瑣,如果對于比較復(fù)雜的需求可能還比較使用,但是我們這里的需求其實(shí)很簡單,完全不需要搞得那么復(fù)雜,所以最后我們也沒有使用這個方法(你可以理解為本博主比較Lazy)。

#Broadcast 那么是否有更簡單的方法呢?由于想到了ContentProvider,我們不由地想到另一個android組件,BroadcastReceiver。那么我們是否可以使用Broadcast 將我們收到的推送數(shù)據(jù)發(fā)送給app進(jìn)程呢。bingo,這似乎正是我們尋找的又簡單又能解決問題的方法。我們來看下代碼。

首先在推送進(jìn)程收到推送消息時,我們將推送數(shù)據(jù)存入SP,如果這時候沒有app進(jìn)程,那么下次app進(jìn)程啟動的時候該存入的數(shù)據(jù)就會被app進(jìn)程讀取到。而如果這時候app進(jìn)程存在,那么之后的代碼就會生效,它使用LocalBroadcastManager 發(fā)送一個廣播。LocalBroadcastManager發(fā)送的廣播不會被app之外接收到,通過它注冊的Receiver也不會接收到app之外的廣播,因此擁有更高的效率。

pushPref.add(push);

Intent intent = new Intent(PushHandler.KEY_GET_PUSH);
intent.putExtra(PushHandler.KEY_PUSH_CONTENT, d);
LocalBroadcastManager.getInstance(context).sendBroadcastSync(intent);

而我們在app進(jìn)程則注冊了一個BroadReceiver來接收上面發(fā)出的廣播。在收到廣播之后將推送數(shù)據(jù)存入SP。

public class PushHandler {

public static String KEY_GET_PUSH = "PUSH_RECEIVED";
public static String KEY_PUSH_CONTENT = "PUSH_CONTENT";

// region 推送處理push
/**
 * 當(dāng)有推送時,發(fā)一次請求mPushReceiver
 */
private static BroadcastReceiver mPushReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    Timber.i("在NoticeAction中收到廣播");
    PushPref pushPref = App.DI().pushPref();
    try {
      String pushContent = intent.getStringExtra(KEY_PUSH_CONTENT);
      PushEntity pushEntity = App.DI().gson().fromJson(pushContent, PushEntity.class);
      pushPref.add(pushEntity);
    } catch (Exception e){
      Timber.e(e, "存儲推送內(nèi)容出錯");
    }
  }
};

public static void startListeningToPush(){
  try {
    LocalBroadcastManager.getInstance(App.getContext()).registerReceiver(mPushReceiver, new IntentFilter(KEY_GET_PUSH));
  } catch (Exception e) {
    Timber.e(e, "wtf");
  }
}
public static void stopListeningToPush(){
  try {
    LocalBroadcastManager.getInstance(App.getContext()).unregisterReceiver(mPushReceiver);
  } catch (Exception e) {
    Timber.e(e, "wtf");
  }
}
// endregion
} 

該方法相對于上面的方法使用簡單,安全可靠,能夠比較好的實(shí)現(xiàn)我們的需求。不過,在需求比較復(fù)雜的時候還是建議使用ContentProvider,因為畢竟這樣的方法不是堂堂正道,有種劍走偏鋒的感覺。
總結(jié)
實(shí)現(xiàn)一個需求可以有很多方法,而我們需要尋找的是又簡單有可靠的方法,在寫代碼之前不如多找找資料,多聽聽別人的意見。

相關(guān)文章

  • Android自定義Dialog框樣式

    Android自定義Dialog框樣式

    這篇文章主要為大家詳細(xì)介紹了Android自定義Dialog框樣式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Android 出現(xiàn)問題 Gradle

    Android 出現(xiàn)問題 Gradle

    這篇文章主要介紹了Android 出現(xiàn)問題 Gradle "xxx" project refresh failed解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • Android變形(Transform)之Matrix用法

    Android變形(Transform)之Matrix用法

    Android的2D變形(包括縮放,扭曲,平移,旋轉(zhuǎn)等)可以通過Matrix來實(shí)現(xiàn),本文研究了一下;接下來就將我這倆天研究的東西和大家分享下,先來看看Matrix的用法感興趣的你可不要錯過了哈
    2013-02-02
  • Android中自定義水平進(jìn)度條樣式之黑色虛線

    Android中自定義水平進(jìn)度條樣式之黑色虛線

    這篇文章主要介紹了Android中自定義水平進(jìn)度條樣式之黑色虛線 的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • Android自定義ProgressDialog進(jìn)度等待框

    Android自定義ProgressDialog進(jìn)度等待框

    這篇文章主要介紹了Android自定義ProgressDialog進(jìn)度等待框,通過本文大家可以嘗試?yán)肁ndroid自定義ProgressDialog,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android嵌套滾動和協(xié)調(diào)滾動的多種實(shí)現(xiàn)方法

    Android嵌套滾動和協(xié)調(diào)滾動的多種實(shí)現(xiàn)方法

    嵌套的滾動主要方式就是這些,這些簡單的效果我們用協(xié)調(diào)滾動,如?CoordinatorLayout?也能實(shí)現(xiàn)同樣的效果,這篇文章主要介紹了Android嵌套滾動和協(xié)調(diào)滾動的多種實(shí)現(xiàn)方法,需要的朋友可以參考下
    2022-06-06
  • Android本地存儲方法淺析介紹

    Android本地存儲方法淺析介紹

    這篇文章主要介紹了Android本地存儲案例,方法簡單可以實(shí)現(xiàn)存儲并達(dá)到節(jié)省內(nèi)存的效果,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • Android開發(fā)中播放聲音的兩種方法分析

    Android開發(fā)中播放聲音的兩種方法分析

    這篇文章主要介紹了Android開發(fā)中播放聲音的兩種方法,結(jié)合實(shí)例形式簡單分析了Android音頻播放的常用函數(shù)、使用方法及相關(guān)注意事項,需要的朋友可以參考下
    2017-09-09
  • Android2.3實(shí)現(xiàn)Android4.0風(fēng)格EditText的方法

    Android2.3實(shí)現(xiàn)Android4.0風(fēng)格EditText的方法

    這篇文章主要介紹了Android2.3實(shí)現(xiàn)Android4.0風(fēng)格EditText的方法,涉及Android界面布局及控件調(diào)用的相關(guān)技巧,需要的朋友可以參考下
    2016-03-03
  • 從源碼角度分析Android的消息機(jī)制

    從源碼角度分析Android的消息機(jī)制

    這篇文章主要介紹了從源碼角度分析Android的消息機(jī)制,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下
    2021-03-03

最新評論