Android 开发过程当中,基于功能隔离、进程安全、进程保活等等考虑,咱们常常须要为应用划分进程,而后不得不面临跨进程通讯和跨进程共享数据的挑战。java
跨进程通讯android
相对来讲,跨进程通讯比较简单,经常使用的方式有:nginx
1.全局广播git
广播是最简洁的跨进程通讯方式,发送——接收广播便可完成异步通讯。github
2.AIDL数据库
使用AIDL进行跨进程调用、通讯是不错的选择,可以支持更复杂的接口调用,通讯是同步完成的。可是实现上须要与其余进程的Service创建链接,而后经过AIDL定义的接口进行调用,实现上稍显复杂。安全
笔者常常使用的是这两种方式,具体使用哪一种看场景决定,没有最好的方案,只有最适合的方案。若是小伙伴们有更多方式,欢迎留言交流,让我也学习学习。
框架
跨进程共享数据异步
跨进程共享数据也是很常见的需求,一份应用数据,各个进程都须要读取、更新使用,咱们要作的就是,在各进程均可以访问到这份数据的前提下,保证数据的同步。ide
经常使用的方式有:
1.SharedPreferences
使用存储模式为MODE_MULTI_PROCESS的SharedPreferences来实现数据存储,各个进程都须要创建本身的SharedPreference实例,经过它来访问数据,系统机制保证数据的同步。不过这种方式不彻底可靠,已经被官方弃用,新的Android 版本已经再也不支持。
2.ContentProvider
ContentProvider是官方推荐应用间共享数据的方式,也是被你们最普遍使用的方式,由系统来保证进程间数据同步的安全性和可靠性,稳定可靠。ContentProvider提供了增删改查的接口,与数据库结合,至关于为其余进程提供了一个远程数据库,功能强大,只是实现上至关于定义了一套远程访问数据库的接口协议,稍显复杂。
3.第三方框架
常见的有github上的Tray,做为SharedPreferences跨进程版本的替代方案,使用者的反馈是不错的,不过我尚未使用研究过它:D
一样的,没有最好的方案,只有最适合的方案,使用哪种方案,须要根据实际场景,分析各自的优势和局限性,做出合理的选择。就如同在选择使用Sqlite仍是SharedPreferences的时候,对于须要复杂操做的数据,好比大量且须要复杂的增删改查的数据,应该使用Sqlite等数据库实现来存储,跨进程时就用数据库做支持的ContentProvider,对于简单的只须要简单查询,读写的数据,用SharedPreferences足矣,跨进程时,使用SharedPreferences做支持的ContentProvider足矣。
在跨进程共享简单数据,如配置、用户信息等等的时候,我经常使用的作法就是利用ContentProvider的跨进程安全特性,以SharedPreferences做为支撑,实现跨进程的SharedPreferences:
import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import java.util.Iterator; /** * Created by Irwin on 2017/8/29. */ public class GlobalProvider extends ContentProvider { public static final Uri AUTHORITY_URI = Uri.parse("content://[YOUR PACKAGE NAME]"); public static final Uri CONTENT_URI = AUTHORITY_URI; public static final String PARAM_KEY = "key"; public static final String PARAM_VALUE = "value"; private final String DB_NAME = "global.sp"; private SharedPreferences mStore; public static Cursor query(Context context, String... keys) { return context.getContentResolver().query(CONTENT_URI, keys, null, null, null); } public static String getString(Context context, String key) { return getString(context, key, null); } public static String getString(Context context, String key, String defValue) { Cursor cursor = query(context, key); String ret = defValue; if (cursor.moveToNext()) { ret = cursor.getString(0); if (TextUtils.isEmpty(ret)) { ret = defValue; } } cursor.close(); return ret; } public static int getInt(Context context, String key, int defValue) { Cursor cursor = query(context, key); int ret = defValue; if (cursor.moveToNext()) { try { ret = cursor.getInt(0); } catch (Exception e) { } } cursor.close(); return ret; } public static Uri save(Context context, ContentValues values) { return context.getContentResolver().insert(GlobalProvider.CONTENT_URI, values); } public static Uri save(Context context, String key, String value) { ContentValues values = new ContentValues(1); values.put(key, value); return save(context, values); } public static Uri remove(Context context, String key) { return save(context, key, null); } @Override public boolean onCreate() { mStore = getContext().getSharedPreferences(DB_NAME, Context.MODE_PRIVATE); return true; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { int size = projection == null ? 0 : projection.length; if (size > 0) { String[] values = new String[size]; for (int i = 0; i < size; i++) { values[i] = getValue(projection[i], null); } return createCursor(projection, values); } String key = uri.getQueryParameter(PARAM_KEY); String value = null; if (!TextUtils.isEmpty(key)) { value = getValue(key, null); } return createSingleCursor(key, value); } protected Cursor createSingleCursor(String key, String value) { MatrixCursor cursor = new MatrixCursor(new String[]{key}, 1); if (!TextUtils.isEmpty(value)) { cursor.addRow(new Object[]{value}); } return cursor; } protected Cursor createCursor(String[] keys, String[] values) { MatrixCursor cursor = new MatrixCursor(keys, 1); cursor.addRow(values); return cursor; } @Nullable @Override public String getType(@NonNull Uri uri) { return ""; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { if (values != null && values.size() > 0) { save(values); } else { String key = uri.getQueryParameter(PARAM_KEY); String value = uri.getQueryParameter(PARAM_VALUE); if (!TextUtils.isEmpty(key)) { save(key, value); } } return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { String key = selection == null ? selection : uri.getQueryParameter(PARAM_KEY); if (!TextUtils.isEmpty(key)) { remove(key); return 1; } return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { if (values != null && values.size() > 0) { save(values); return values.size(); } String key = uri.getQueryParameter(PARAM_KEY); String value = uri.getQueryParameter(PARAM_VALUE); if (!TextUtils.isEmpty(key)) { save(key, value); return 1; } return 0; } protected String getValue(String key, String defValue) { return mStore.getString(key, defValue); } protected void save(ContentValues values) { String key; String value; Iterator<String> iterator = values.keySet().iterator(); SharedPreferences.Editor editor = mStore.edit(); while (iterator.hasNext()) { key = iterator.next(); value = values.getAsString(key); if (!TextUtils.isEmpty(key)) { if (value != null) { editor.putString(key, value); } else { editor.remove(key); } } } editor.commit(); } protected void save(String key, String value) { SharedPreferences.Editor editor = mStore.edit(); if (value != null) { editor.putString(key, value); } else { editor.remove(key); } editor.commit(); } protected void remove(String key) { SharedPreferences.Editor editor = mStore.edit(); editor.remove(key); editor.commit(); } }
根据须要添加快捷访问的方法,使用起来简单方便:
//读取共享 GlobalProvider.getInt(context,PARAMETER_KEY,DEFAULT_VALUE_WHILE_NULL); //写入共享 GlobalProvider.save(context, PARAMETER_KEY, PARAMETER_VALUE);
固然,对于须要复杂操做的共享数据,仍是乖乖滴基于数据库根据本身的业务需求实现完整的ContentProvider吧!