A brief analysis of multi-process shared data in Android development _android

Source: Internet
Author: User
Tags static class

The background has recently encountered a need to save the pushed data for use when the app is launched when it receives a push. Do we think this is not easy? Just save the data in Sharedpreferences and let the app open the same sharedpreferences read the data. But in the actual tests, we found that the data that the push process was stored in was not available in the app process. So what is it, perhaps, that the wise reader has found the reason in our statement above, because we have two processes, the push process is responsible for storing the push data, and the app process is responsible for reading it, but it is because of the two processes that, if they exist simultaneously, keep their own SP objects and data in memory , the deposit in the push process is not reflected in the app process and may be brushed off by the app process for changed data. So what can we do to get both sides to share the data? Please see the following statement.

Sharedpreferences supported by multiple processes (not recommended)
Our original approach is to use sharedpreferences, it is natural to think that sharedpreferences mode_private Mode_public can also set up a process flag ———— Mode_ Multi_process

Copy Code code as follows:
Sharedpreferences myprefs = context.getsharedpreferences (my_file_name, context.mode_multi_process | Context.mode_private);

Once we set up this flag, the system will again read the data from the SP file every time we call context.getsharedpreferences. So we use Context.getsharedpreferences to retrieve the SP instance every time we read and save it. Even so, because the SP is not much process-safe in nature, there is no guarantee that the data is synchronized, so we are not using it and we do not recommend it.

Second, Tray
If the SP is not multiple process safe, is there a third-party project with multiple process security and SP capabilities? The answer is yes, tray--a multiple process security sharedpreferences, we can find it on the GitHub, if it is androidstudio, can be directly used Gradle introduced, it is very convenient, the following is the use of code, very simple, Without an apply commit, it looks simpler than an SP.

 Create a preference accessor. This is the for Global app preferences.
Final Apppreferences apppreferences = new Apppreferences (GetContext ()); This is 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", "D Efault ");
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

But in the end we did not choose to use it, the main reason is that it needs to minsdk 15, and we are supporting SDK14, so we can only decisively give up.

Third, ContentProvider
Since tray does not support sdk15 below, can we use the principle of tray to implement one ourselves? While reading Tray's source code, we found that it was actually done on a contentprovider basis, while ContentProvider was the most secure of the many processes that Android officially supported. Here is an example of using ContentProvider.

 public class Articlesprovider extends ContentProvider {private static final String Log_tag = "Shy.luo.providers.arti Cles. 
 
  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.it 
      EM) {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) {SQ 
      Litedatabase db = Dbhelper.getwritabledatabase (); 
 
      int count = 0; Switch (Urimatcher.match (URI)) {case Articles.item: {count = Db.update (db_table, values, selection, sel EctionArgs); 
      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 = Dbhe 
      Lper.getwritabledatabase (); 
 
      int count = 0; Switch (Urimatcher.match (URI)) {case Articles.item: {count = Db.delete (db_table, selection, Selectionar 
          GS); 
      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 sor 
 
      Torder) {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 (sortor der)? 
      Articles.DEFAULT_SORT_ORDER:sortOrder, limit); 
 
      Cursor.setnotificationuri (resolver, URI); 
  return cursor; @Override public Bundle called (string method, string request, Bundle args) {log.i (Log_tag, "articlesprovide 
 
      R.call: "+ method); 
      if (Method.equals (Articles.method_get_item_count)) {return getitemcount (); throw new IllegalArgumentException ("Error method called:" + 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, String name, curso 
      Rfactory 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.execs 
      QL ("DROP TABLE IF EXISTS" + db_table);    OnCreate (DB); 
 } 
  } 
}

We need to create a class that inherits from ContentProvider and overloads the following methods. -OnCreate (), which is used to perform some initialization work. -Query (Uri, string[], String, string[], string), used to return data to the caller. -Insert (Uri, contentvalues), used to insert new data. -Update (Uri, Contentvalues, String, string[]) for updating existing data. -Delete (Uri, String, string[]) to remove data. -GetType (Uri), used to return the MIME type of the data.
Specifically use the reference to the Android Application component content provider Application Example this blog, here no longer repeat. In the above ContentProvider use process, we found that the process is more cumbersome, if the more complex requirements may be compared to use, but our demand here is actually very simple, do not need to make so complicated, So in the end we didn't use this method (you can understand that this blogger compares lazy).

#Broadcast is there a simpler way to do that? With the thought of ContentProvider, we can't think of another Android component, Broadcastreceiver. So can we use broadcast to send the push data we received to the app process? Bingo, it seems that we are looking for a simple and easy way to solve the problem. Let's look at the code.

First, when the push process receives a push message, we push the data into the SP, and if there is no app process, the next time the app process starts, the stored data is read by the app process. And if the app process does exist, then the code will take effect, sending a broadcast using Localbroadcastmanager. Localbroadcastmanager sent broadcasts are not received outside of the app, and the receiver registered with it will not receive broadcasts outside of the app, so it's more efficient.

Pushpref.add (push);

Intent Intent = new Intent (pushhandler.key_get_push);
Intent.putextra (pushhandler.key_push_content, d);
Localbroadcastmanager.getinstance (context). Sendbroadcastsync (Intent);

And we registered a broadreceiver in the app process to receive the broadcast above. The push data is saved to the SP after the broadcast is received.

public class Pushhandler {public static string Key_get_push = ' push_received '; public static string key_push_content =

"Push_content"; Region push processing Push/** * when there is a push, send a request mpushreceiver/private static Broadcastreceiver Mpushreceiver = new BROADCASTRECEI
    Ver () {@Override public void onreceive (context, Intent Intent) {TIMBER.I ("received broadcasts in 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, "Error storing push content");

}
  }
}; public static void Startlisteningtopush () {try {localbroadcastmanager.getinstance (App.getcontext ()). registerreceive
  R (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}

The method is simple, safe and reliable compared with the above method, and it can achieve our requirements better. However, when the demand is more complex, it is recommended to use ContentProvider, because after all, such a method is not a dignified path, a sword walk slant feeling.
Summary
There are many ways to implement a requirement, and we need to find a simple and reliable way to find more information and listen to other people's opinions before writing code.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.