Android Application是单例,正确吗?

作者: 青果果 | 来源:发表于2018-10-21 00:54 被阅读159次

    Android 中Application是单例,这个问题可能大家会毫不犹豫的回答正确
    但是,如果APP中如果有集成一些第三方SDK的
    并且在Application中加了打印的可能就会发现,APP启动的时候
    怎么onCreate中的打印走了多次
    不是说Application只会实例化一次的吗?

    因为onCreate走了多次,说明创建了多个
    那这个问题答案应该明朗了,在某种情况下,Application不唯一了

    那这种情况是什么情况呢?
    答案是:多进程
    一般我们开发可能极少,除非一些特别的APP,可能我们都不会指定多进程
    那为啥集成了第三方SDK会出现这种情况呢
    是因为有些SDK指定了组件运行在特别的进程

    那为啥第三方SDK会使用多进程?多进程带来的好处是什么?又有什么坏处呢?

    进程

    Android系统是底层是由Linux改造而来的
    进程系统也是一致的,进程,就是程序的具体实现
    当程序第一次启动,Android会启动一个Linux进程(具体由Zygote fork出来)和一个主线程
    默认的情况下,所有组件都将运行在该进程内
    同一个应用由系统分配一个独立的Linux账户,应用的产生的所有进程,都会是这同一个Linux账户

    多进程Application会创建多个

    很明显带来的问题就是Application的onCreate方法会执行多次
    如果在onCreate方法中,做了初始化的操作,将会导致多次初始化操作
    如果是启动的时候就执行的,将会导致启动时间延长

    将组件指定在单独的进程

    指定多进程是在AndroidManifest.xml里面配置
    Android四大组件activity,service,provider, receiver
    可以通过android:process属性来指定运行所在的进程
    不指定默认就是运行在系统分配的进程
    也可以修改默认进程 设置Application的android:process属性,来设置所有组件的默认进程
    进程名分两种:

    • 如果以小写字母开头,比如":com.gaode.map"
      这种情况下,是该APP私有的,进程名是APP包名+冒号+后面的名字
    <activity android:name=".TestActivity"
                android:process=":com.gaode.map"/>
    
    • 如果名字以:开头 比如"com.baidu.map"
      该组件将运行在以这个名字命名的进程中
      这种方式就可以让不同应用中的组件可以共享一个进程
    <activity android:name=".TestActivity"
                android:process="com.baidu.map"/>
    

    示例:

    <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
    
            <activity android:name=".TestActivity"
                android:process="com.baidu.map"/>
    

    Application 完整代码

    public class BaseApplication extends Application {
    
        private static final String APP_NAME = "com.qingguoguo.baseapp";
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("BaseApplication", "onCreate");
            Log.e("BaseApplication", getProcessNameByPID(getApplicationContext(), android.os.Process.myPid()));
            DBConfig.initGreenDao(this);
        }
    
        /**
         * 判断是否是主进程
         * @return
         */
        public boolean isAppMainProcess() {
            try {
                int pid = android.os.Process.myPid();
                String process = getProcessNameByPID(getApplicationContext(), pid);
                return TextUtils.isEmpty(process) || APP_NAME.equalsIgnoreCase(process);
            } catch (Exception e) {
                return true;
            }
        }
    
        /**
         * 根据 pid 获取进程名
         * @param context
         * @param pid
         * @return
         */
        public String getProcessNameByPID(Context context, int pid) {
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            if (manager == null) {
                return "";
            }
            for (android.app.ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
                if (processInfo == null) {
                    continue;
                }
                if (processInfo.pid == pid) {
                    return processInfo.processName;
                }
            }
            return "";
        }
    }
    
    

    在ManinActivity中的点击 事件跳转到TestActivity,将会导致Application再次初始化

    onCreate执行多次

    底层原理参考:http://gityuan.com/2016/03/26/app-process-create/

    如何避免Application多次初始化

    上面的代码已经给出了解决方法,就是判断当前进程是不是主进程
    若不是可以跳过主进程的初始化
    可以参考文章:https://blog.csdn.net/sz_chrome/article/details/72911392

    多进程的好处

    常驻后台任务应用

    核心后台服务模块和其他UI模块进行分离,保证应用能更稳定的提供服务
    从而提升用户体验

    解决OOM问题

    吃内存的大图,WebView等另开进程,避免主进程OOM

    多模块开发

    诸如下载服务,监控服务等等另开进程

    多进程带来的问题

    静态变量和单例模式完全失效

    因为进程之间,内存是相互独立的,所以VM方法区的静态变量
    也都是独立的,单例模式基于静态变量,所以单例也会失效
    在两个不同进程访问一个相同类的静态变量,值未必相同

    线程同步机制完全失效

    Java的同步机制是VM来进行调度的,两个进程拥有两个不同的VM
    所以,同步也会在多进程环境下失效,Synchronized,volatile关键字
    等都是基于VM级别的同步,所以不要跨进程去使用线程同步,比如
    主进程有个生产者,子进程的消费者是无法正常使用消费功能的,
    只能通过跨进程通信,让主进程的消费者去消费,然后再回调

    Application会多次创建

    每个新进程在创建的时候,都会新建一个Application,所以多进程还
    会导致Application多次创建的问题,onCreate方法会多次调用,一般
    我们都会在onCreate里初始化操作,那么会多次初始化,最好也不要在
    Application中设置过多的静态变量,导致内存增加

    文件读写并发访问的问题

    文件指的泛指所有需要并发访问的文件,例如:本地文件,数据库文件,
    sharepreference等。由于Java中,文件锁、队列机制都是VM级别的,
    所以不同进程访问同一个文件锁是不管用的。(通过C++可以实现多进程
    文件锁机制,不过不在文本讨论范围内。)所以在实际开发过程中,还是
    避免多进程同时访问统一文件,多利用Android中IPC的C/S思想,提供服务,
    接口调用,避免直接去访问对方进程的文件或者数据库,提升设计美感,
    同时也能提升代码的稳定性

    相关文章

      网友评论

        本文标题:Android Application是单例,正确吗?

        本文链接:https://www.haomeiwen.com/subject/vqkvzftx.html