序言
前段时间实际项目开发中遇到一个很奇怪的空指针问题,根据app的奔溃日志定位到源码,发现对象在使用前已经初始化了,为何还会报空指针异常呢,感觉此bug不应该出现。然后就一遍遍的问题排查,始终无头绪,后面偶然想起app是双进程运行,按正常的逻辑这个bug不应出现,那会不会和双进程有关呢。于是就立马写demo看看。
问题重现
这里先假设大家都是Android开发者,因此这里只讲解重要的流程步骤。
在Androidstudio中新建一个demo项目。写一个单例class:
public class SingleTest2 {
private static Object o;
public static void init() {
o = new Object();
}
private SingleTest2() {
}
public static Object getObject() {
return o;
}
}
然后写一个自己的App extends Application,再写一个MyService extends Service。然后把MyService 配置为app私有独立进程,如下:
<!--app私有进程,不能与其它app共享此进程-->
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":myService"></service>
如此,若service被启动的话,它就运行在单独的进程(即一个app有两个进程在运行)。然后在App的onCreate方法中调用SingleTest2的init方法,如下
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate() called");
String processName = getProcessName(this, android.os.Process.myPid());
//增加了进程判断,双进程防止执行两次
if (TextUtils.equals(processName, BuildConfig.APPLICATION_ID)) {
SingleTest2.init();
}
}
/**
* 获取进程名称
*
* @param cxt 上下文
* @param pid 进程id
* @return
*/
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
在MyService中获取单例对象
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Object one = SingleTest2.getObject();
Log.e(TAG, " one = [" + one.hashCode() + "]");
return super.onStartCommand(intent, flags, startId);
}
如上完成后,启动app,然后启动MyService服务,奇迹就出现了
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference
at com.aosp.MyService.onStartCommand(MyService.java:25)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3741)
at android.app.ActivityThread.-wrap23(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1747)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
分析得出结论
明明SingleTest2的静态o对象在App的onCreate方法中已经初始化过了,但是MyService的onStartCommand方法中却还是报了空指针异常,这是什么原因。这里可以确定的是SingleTest2的初始化肯定是先完成,以及中间没被赋空,没被垃圾收集回收掉,排除这些猜想得出结果:
多进程app会启动多个Android虚拟机,每个虚拟机有自己独立的堆栈空间,不能互相访问。
此app的内存分配情况如下