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();

   }

}