Android中经常使用多进程,如果使用了多进程,那在App的启动会存在以下几个方面的问题:
1. Application会多次创建
onBaseContextAttached 与 onCreate 方法均会多次执行。
2. 静态成员
代码中使用的静态成员将不能跨进程使用。
3. 缓存问题
SP不能共享,文件共享与数据库虽然有方法进程共享使用,但需要考虑其并发操作。
4. 断点调试
跨进程的断点无法跨进程断点,如果需要在某个代码地方断点,需要知道该地方运行于哪个进程,Logcat切换至该进程,即可断点。
5. 使用MultiDex后,启动会变慢(当然单进程时也会变化,此处意思是比单进程还要慢)
进行App的启动优化, 进程只初始化自己需要的:
1. 优化进程的初始化
每个进程只需要初始化自己进程需要的相关资源。如在onCreate中增加:
@Override
public void onCreate() {
...
String mainProcessName = getPackageName();
String currentProcessName = getCurrentProcessName();
if(currentProcessName.equals(mainProcessName)) {
//TODO: mainProcess init
} else (currentProcessName.equals(pushProcessName)) {
//TODO: pushProcess init
} else {
//TODO: exception handler
}
...
}
/**
* 获取当前进程名
*/
public static String getCurrentProcessName() {
int pid = android.os.Process.myPid();
String processName = FBApplication.getInstance().getPackageName(); /*取不到默认为主进程*/
ActivityManager am = (ActivityManager) FBApplication.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return processName;
}
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
if (processInfos == null || processInfos.isEmpty()) {
return processName;
}
for (ActivityManager.RunningAppProcessInfo process : processInfos) {
if (process == null) {
continue;
}
if (process.pid == pid) {
processName = process.processName;
}
}
return processName;
}
2. MultiDex
由于使用了MultiDex分包加载机制,而其初始化时在onBaseContextAttached使用的,但是在多进程App中该方法也会被执行多次,
所以MultiDex.install(context);会被执行多次。也就是多个进程都会去加载dex一遍,占用很多时间。还可能会造成ANR
此处优化几种方案:
1. 美团:精简主 Dex 包,应用启动起来后再异步加载第二个 Dex 包。这是一个很不错的想法,但是实现起来有一定的难度。需要编写脚本自定义打包控制dex的生成情况,
区分哪些类要放在主 Dex 包中,而且一般项目中都会用到很多第三方 SDK,这很可能导致主 Dex 包的精简程度不能达到我们想要的状态。
2. 微信: 参考并改进了 Facebook 的解决方案, 新开一个进程来执行 Multidex.install() 方法。
代码如下;
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
if (MultiDexUtils.isMainProcess(context)) { // 判断是否是主进程,避免重复执行
MultiDexUtils.setMainActivityStarted(this, false); // 保存本地数据,标记主页面是否已经开启
MultiDexUtils.setLoadDexActivityClosed(this, false); // 保存本地数据,标记加载Dex进程是否已经关闭
MultiDexUtils.startLoadDexActivity(context); // 打开加载 Dex 的进程页面,这样我们的APP就变成后台进程了
}
}
<activity
android:name=".LoadDexActivity"
android:alwaysRetainTaskState="false"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:process=":loadDex"
android:screenOrientation="portrait">
</activity>
加载 Dex 的进程:
public class LoadDexActivity extends Activity {
private static final int MULTIDEX_ERROR = 0;
private static final int MULTIDEX_ACTIVITY_STARTED = 1;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MULTIDEX_ERROR:
case MULTIDEX_ACTIVITY_STARTED: // 退出当前进程
MultiDexUtils.setLoadDexActivityClosed(getApplication());
finish();
System.exit(0);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loaddex);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
new Thread() {
@Override
public void run() {
Message message = handler.obtainMessage();
long startTime = System.currentTimeMillis();
long timeout = 10 * 1000; // 加载超时时间
try {
MultiDex.install(getApplication());
Thread.sleep(500);
// 等待主界面启动
while (!MultiDexUtils.isMainActivityStarted(getApplication()) &&
(System.currentTimeMillis() - startTime) < timeout) {
Thread.sleep(200);
}
message.what = MULTIDEX_ACTIVITY_STARTED;
handler.sendMessage(message);
} catch (Exception e) {
message.what = MULTIDEX_ERROR;
handler.sendMessage(message);
}
}
}.start();
}
@Override
public void onBackPressed() {
//cannot backpress
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
MultiDexUtils.setMainActivityStarted(getApplication()); // 告诉loadDex进程,主界面已启动
...
}
public class MultiDexUtils {
public static final String KEY_ACTIVITY_STARTED = "activity-started-";
public static final String KEY_LOADDEX_CLOSED = "loaddex-closed-";
/**
* 当前进程是否是主进程
*/
public static boolean isMainProcess(Context context) {
return "com.***.***(进程名一般是包名)".equals(getCurProcessName(context));
}
/**
* 设置-主界面已经打开
*/
public static void setMainActivityStarted(Context context) {
setMainActivityStarted(context, true);
}
/**
* 设置-主界面是否已经打开
*/
public static void setMainActivityStarted(Context context, boolean b) {
SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
sp.edit().putBoolean(KEY_ACTIVITY_STARTED + getPackageInfo(context).versionCode, b).commit();
}
/**
* 是否需要等待主界面
*/
public static boolean isMainActivityStarted(Context context) {
SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
return sp.getBoolean(KEY_ACTIVITY_STARTED + getPackageInfo(context).versionCode, false);
}
/**
* 判断加载页面是否关闭
*/
public static boolean isLoadDexActivityClosed(Context context) {
SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
return sp.getBoolean(KEY_LOADDEX_CLOSED + getPackageInfo(context).versionCode, false);
}
/**
* 设置加载页面已经关闭
*/
public static void setLoadDexActivityClosed(Context context) {
setLoadDexActivityClosed(context, true);
}
/**
* 设置-加载页面是否已经关闭
*/
public static void setLoadDexActivityClosed(Context context, boolean b) {
SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
sp.edit().putBoolean(KEY_LOADDEX_CLOSED + getPackageInfo(context).versionCode, b).commit();
}
/**
* 开启等待页面,新的进程
*/
public static void startLoadDexActivity(Context context) {
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.***.***(包名)", LoadDexActivity.class.getName());
intent.setComponent(componentName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 获取进程名
*/
public static String getCurProcessName(Context context) {
try {
int pid = android.os.Process.myPid();
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
return appProcess.processName;
}
}
} catch (Exception e) {
// ignore
}
return null;
}
/**
* 获取包信息
*/
private static PackageInfo getPackageInfo(Context context) {
PackageManager pm = context.getPackageManager();
try {
return pm.getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
// Log.i(TAG, e.getLocalizedMessage());
}
return new PackageInfo();
}
}
浙公网安备 33010602011771号