SharedPreferences
ContentProvider
Application
1. 遇到的問題
05-23 16:11:15.871: E/AndroidRuntime(21899): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
05-23 16:11:15.871: E/AndroidRuntime(21899): at com.csmijo.test.utils.SharedPrefUtil.getSharedPreferences(SharedPrefUtil.java:13)
05-23 16:11:15.871: E/AndroidRuntime(21899): at com.csmijo.test.utils.SharedPrefUtil.getValue(SharedPrefUtil.java:51)
寫好的一個 SDK 在第三方集成使用時(shí)祝沸,發(fā)現(xiàn)了這樣的bug哑芹。于是根據(jù) trace 信息,查看代碼發(fā)現(xiàn)封裝類在操作 SharedPreferences
時(shí)并沒有什么問題冠蒋,但是為什么會出現(xiàn) NullPointerException
的問題呢?梯浪?
于是溝通拿到測試 Apk 后,安裝發(fā)現(xiàn)是因?yàn)?第三方APP在打開某個Activity時(shí)啟動了新的進(jìn)程株依,所以導(dǎo)致了上述的問題驱证。
找到原因后,于是開始嘗試解決恋腕。抹锄。。
2. 通過 SharedPreferences 實(shí)現(xiàn)多進(jìn)程間的數(shù)據(jù)共享(不推薦)
因?yàn)橹暗臄?shù)據(jù)都是使用 SharedPreferences
進(jìn)行存儲,如果改用其他多進(jìn)程的通信方式感覺改動的地方比較大伙单,所以優(yōu)先嘗試使用修改 SharedPreferences
的方式進(jìn)行获高。
通過查看 API 文檔發(fā)現(xiàn),在 API Level > 11 即 Android 3.0 可以通過 Context.MODE_MULTI_PROCESS 屬性來實(shí)現(xiàn)多進(jìn)程間的數(shù)據(jù)共享.
但是在 API 23 時(shí)該屬性被廢棄吻育。官方文檔中明確寫明 This class does not support use across multiple processes. 即 SharedPreferences
不適用于多進(jìn)程間共享數(shù)據(jù)念秧,推薦使用 ContentProvider
。
3. 更新(使用 ContentProvider 的一次嘗試 )
之前自己嘗試了一下使用 contentProvider + SharedPreferences 來實(shí)現(xiàn)布疼,沒有成功摊趾,還是水平太次啊。最近在逛掘金醬的時(shí)候發(fā)現(xiàn)很早之前就有人實(shí)現(xiàn)了這樣的方式游两,這里貼一下我找的的博客及代碼砾层,以防誤人子弟啊。
以下是我嘗試失敗的例子贱案,這里記錄一下肛炮。
3. 使用 ContentProvider 的一次嘗試 (失敗)
平時(shí)自定義 ContentProvider
時(shí)宝踪,都是和 Sqlite
配合使用侨糟,將數(shù)據(jù)存儲在數(shù)據(jù)庫中,于是突發(fā)奇想瘩燥,能不能把 ContentProvider
和 SharedPreferences
聯(lián)合使用秕重,使用 SharedPreferences
來存儲數(shù)據(jù)。
于是直接上手鼓搗了半天颤芬,最后在自定義 ContentProvider
的 query()
方法面前敗下陣來悲幅。
這是因?yàn)?public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
方法需要返回一個 Cursor
的對象套鹅。查看代碼后發(fā)現(xiàn) Cursor
是一個接口站蝠,于是想著實(shí)現(xiàn)一個自定義的接口即可。
結(jié)果一陣敲碼完成之后卓鹿,run一下菱魔,結(jié)果呵呵了,提示如下的bug:
Caused by: java.lang.ClassCastException: android.content.ContentResolver$CursorWrapperInner cannot be cast to csmijo.com.test.MyCursor
at csmijo.com.test.SharedPrefUtil.getValue(SharedPrefUtil.java:78)
于是開始找原因吟孙,發(fā)現(xiàn)在使用ContentResolver.query()
方法時(shí)發(fā)生了如下的調(diào)用:
而正常情況下返回 ContentResolver$CursorWrapperInner
類的一個實(shí)例澜倦,而這個類是一個 private final class
,所以沒有辦法繼承杰妓。該類的繼承結(jié)構(gòu)如下圖所示:
所以我們直接實(shí)現(xiàn) Cursor
接口作為返回值的做法是錯誤的藻治。
4. 使用 ContentProvider 完成多進(jìn)程間數(shù)據(jù)共享
嘗試之后失敗,但是問題還是要解決的嘛巷挥,所以就乖乖使用 ContentProvider
和 Sqlite
的方案進(jìn)行問題解決桩卵。 具體的 ContentProvider
使用方法可以參考 官方教程。
在調(diào)試 ContentProvider
的時(shí)候,使用了 Stetho雏节, 它可以在 Chrome 中非常方便的查看數(shù)據(jù)庫中的資源胜嗓,還支持 SQL 查詢,特別的方便钩乍,推薦一下辞州。
5. 多進(jìn)程 Application 的多次加載
通過上面的 ContentProvider
解決了 SharedPreferences
共享數(shù)據(jù)的問題,自己調(diào)試了一下寥粹,發(fā)現(xiàn) Application
的初始化又有個坑变过。
因?yàn)槭褂昧硕噙M(jìn)程,每個進(jìn)程相當(dāng)于一個單獨(dú)的應(yīng)用程序涝涤,所以每個進(jìn)程啟動時(shí)都會調(diào)用一次 Applicaiton.onCreate()
方法牵啦,這樣就導(dǎo)致了我在 onCreate()
方法中進(jìn)行的操作執(zhí)行多遍。
解決方案是根據(jù)進(jìn)程來分別進(jìn)行初始化妄痪。
獲取當(dāng)前運(yùn)行進(jìn)程的名稱:
方法一
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
這是目前網(wǎng)上主流的方法哈雏,但是沒有方法二效率高
方法二
public static String getCurrentProcessName() {
FileInputStream in = null;
try {
String fn = "/proc/self/cmdline";
in = new FileInputStream(fn);
byte[] buffer = new byte[256];
int len = 0;
int b;
while ((b = in.read()) > 0 && len < buffer.length) {
buffer[len++] = (byte) b;
}
if (len > 0) {
return new String(buffer, 0, len, "UTF-8");
}
} catch (Throwable e) {
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
6. 總結(jié)
以上就是這次多進(jìn)程數(shù)據(jù)共享爬坑的過程,希望能夠提供一些借鑒作用衫生。