來北京實習之後,涉及到了多進程Binder通信的技術。做了相關學習和一些代碼工作後,有一些感想和大家分享下。
雖然在App中使用多進程增加了程序的內存空間,但是也有一些負面的影響。比如:
1. 很多情況下,業務需要接入第三方平臺,接入的代碼有時運行在第三方應用的進程中,容易出錯
2. 單例模式在單進程中用的很爽,但在多進程中就顯得雞肋了,也是比較容易疏忽的地方
3. 使用AIDL完成的穩定性和效率並不是很高,本人做的項目拉取AIDL傳來的值一直存在不穩定的問題
針對最後一個問題,相信有一定工作經驗的Android工程師都遇到過。
我也對此進行了思考,做出了以下幾點猜想:
1. AIDL中使用代理Proxy對象進行處理,降低了直接處理的效率
2. AIDL客戶端發出請求後,服務器端掛起,有個等待時間
3. AIDL雖然通過內存映射完成數據傳輸,但也需要一次拷貝,沒有共享內存不用拷貝的優勢
4. 這也是最重要的一點,在完成Binder通信時Process的構造函數中,申請內存的交換空間的大小是有限的,最大隻有1M
在這種情況下,項目組使用ContentProvider傳遞一些簡單的基礎變量。經測試,確實穩定高效很多。雖然ContentProvider也是採用的Binder實現的,但是通過ContentProvider通信,相當於重新開闢了一條client和內核交換數據的通道,減少了原來通道傳輸大量數據的壓力。
github地址:https://github.com/leirenbaobao/ShareInProvider
此代碼的功能就是跨進程獲取一個string變量。在代碼就是“張三、李四”的人名。
主Activity是兩個button和一個TextView,一個賦值(對其他進程的share),一個取值(從其他進程中取),TextView用來顯示結果。
主Activity:
public class MainActivity extends ActionBarActivity {
private Button mButtonOpen;
private Button mButtonTake;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.txt);
mButtonOpen = (Button)findViewById(R.id.btn_open);
mButtonOpen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
StringResolver.putString(MainActivity.this, "key", "張三");
}
});
mButtonTake = (Button)findViewById(R.id.btn_action);
mButtonTake.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mTextView.setText(StringResolver.getString(MainActivity.this, "key", "李四"));
}
});
}
}
Resolver處理類,兩個方法。
/**
* Created by DAV on 15/6/25.
* Contentresovler處理類
*/
public class StringResolver {
private static final int MATCH_RIGHT_NUM = 1;
private static final String CONTENT_TYPE = "prefs_detail";
private static final String CONTENT_KEY = "prefs_key";
private static final String CONTENT_VALUE = "prefs_value";
private static final String sAuthority = "com.baidu.www.shareinprovider.url";
private static final String TAG = "StringResolver";
public static void putString(Context cxt, String key, String str){
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sMatcher.addURI(sAuthority, CONTENT_TYPE, MATCH_RIGHT_NUM);
Uri sUri = Uri.parse("content://"+ sAuthority + "/" + CONTENT_VALUE);
ContentValues contentValues = new ContentValues();
contentValues.put(CONTENT_KEY, key);
contentValues.put(CONTENT_VALUE, str);
cxt.getContentResolver().update(sUri, contentValues, null, null);
}
public static String getString(Context cxt, String key, String defStr){
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sMatcher.addURI(sAuthority, CONTENT_TYPE, MATCH_RIGHT_NUM);
Uri sUri = Uri.parse("content://"+ sAuthority + "/" + CONTENT_VALUE);
ContentValues contentValues = new ContentValues();
contentValues.put(CONTENT_KEY, key);
contentValues.put(CONTENT_VALUE, defStr);
Uri uri = cxt.getContentResolver().insert(sUri, contentValues);
Log.v(TAG, uri.toString());
return uri.toString();
}
}
核心provider類
/**
* Created by DAV on 15/6/25.
*/
public class StringProvider extends ContentProvider{
private static final int MATCH_RIGHT_NUM = 1;
private static final String CONTENT_TYPE = "prefs_detail";
private static final String CONTENT_KEY = "prefs_key";
private static final String CONTENT_VALUE = "prefs_value";
private static final String sAuthority = "com.baidu.www.shareinprovider.url";
private static final String TAG = "StringProvider";
private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@Override
public boolean onCreate() {
sMatcher.addURI(sAuthority, CONTENT_VALUE, MATCH_RIGHT_NUM);
return true;
}
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
return null;
}
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}
@Override
public String getType(Uri uri) {
int code = sMatcher.match(uri);
switch (code){
case MATCH_RIGHT_NUM:
return sAuthority + "." + CONTENT_TYPE;
default:
break;
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
Log.v(TAG, "come in insert");
int code = sMatcher.match(uri);
switch (code){
case MATCH_RIGHT_NUM:
Log.v(TAG, "insert");
return getPrefsValue(getContext(), contentValues);
default:
break;
}
return null;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
int code = sMatcher.match(uri);
Log.v(TAG, uri.toString() + code);
switch (code){
case MATCH_RIGHT_NUM:
Log.v(TAG, "update");
putPrefsValue(getContext(), contentValues);
break;
default:
break;
}
return 0;
}
private Uri getPrefsValue(Context cxt, ContentValues values){
String key = values.getAsString(CONTENT_KEY);
String defValue = values.getAsString(CONTENT_VALUE);
SharedPreferences prefs = cxt.getSharedPreferences("NameShare", Context.MODE_PRIVATE);
return Uri.parse(prefs.getString(key, defValue));
}
private void putPrefsValue(Context cxt, ContentValues values){
String key = values.getAsString(CONTENT_KEY);
String value = values.getAsString(CONTENT_VALUE);
SharedPreferences prefs = cxt.getSharedPreferences("NameShare", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(key, value);
editor.commit();
}
}
其中provider類在Manifest中註冊爲另外進程。
<provider
android:authorities="com.baidu.www.shareinprovider.url"
android:name=".StringProvider"
android:exported="true"
android:process=":remote"/>
試驗了一下,效果還是不錯的。