精华内容
下载资源
问答
  • android多进程通信二

    2016-09-08 11:50:19
    android多进程通信
  • Android多进程通讯AIDL实现客户端回调
  • 主要为大家简单分析Android开发中多进程共享数据,怎么做才能让这两边共享数据,感兴趣的小伙伴们可以参考一下
  • Android多进程的实现

    千次阅读 2019-05-20 11:39:01
    一般,一个app只有一个进程,但会有个线程,比如UI主线程,各种网络请求的子线程。 但是,一些大型的app,比如QQ,会有进程!刚通过top命令,看了一下QQ运行时的进程: 10644 u0_a130 20 0 12% S 57 1425816K ...

    前言

    一般,一个app只有一个进程,但会有多个线程,比如UI主线程,各种网络请求的子线程。
    但是,一些大型的app,比如QQ,会有多个进程!刚通过top命令,看了一下QQ运行时的进程:

    10644 u0_a130  20   0  12% S    57 1425816K  86924K  bg com.tencent.mobileqq:tool
    10371 u0_a130  16  -4   5% S   107 1765092K 176420K  bg com.tencent.mobileqq:mini
     3195 u0_a130  20   0   1% S    49 1401268K  53152K  fg com.tencent.mobileqq:MSF
     3271 u0_a130  10 -10   0% S   130 1744152K 161772K  ta com.tencent.mobileqq
    10451 u0_a130  20   0   0% S    38 1382680K  66268K  bg com.tencent.mobileqq:mini3
    29008 u0_a130  20   0   0% S    31 1367512K  38376K  fg com.tencent.mobileqq:TMAssistantDownloadSDKService
    

    艾玛,太过分了,竟然有6个进程!我们知道,在Android中,系统会为每个应用或进程分配独立的虚拟机和内存空间,看来,QQ内存欲很强呀,通过增加进程这种方式,满足内存需要呀^^

    使用多进程的场景

    一般有2种情况,需要使用多进程。

    1. 内存不够,扩大内存。
    2. 一些业务,希望在单独进程运行。
      比如,QQ的一些插件功能(微视),希望在一个独立进程运行,当它遇到崩溃时,不会影响QQ退出(毕竟进程不同)

    开启多进程

    很简单,在AndroidManifest.xml中注册Service、Activity、Receiver、ContentProvider时指定android:process属性。

    <service
        android:name=".RemoteService"
        android:process=":remote">
    </service>
    
    <activity
        android:name=".RemoteActivity"
        android:process="com.chenxf.ipc.remote">
    </activity>
    

    有两种声明方式,一个加冒号,一个完整的名字,区别如下:
    :remote: 以冒号开头是一种简写,系统会在当前进程名前附件当前包名,完整的进程名为:com.chenxf.ipc:remote,同时以冒号开头的进程属于当前应用的私有进程,其它应用的组件不能和它跑在同一进程。

    com.chenxf.ipc.remote:这是完整的命名方式,不会附加包名,其它应用如果和该进程的ShareUID、签名相同,则可以和它跑在同一个进程,实现数据共享。(一般极少这样用,除非是同一公司开发的app,且2个app关联很大,才会签名也一样)

    多进程的优缺点

    优点

    其实就是上面的场景,算是它的优点。

    1. 增加内存。
    2. 业务隔离。一些子业务,放子进程,如果崩溃了,不会影响主app退出。

    缺点

    1. 静态成员和单例模式失效
    2. 线程同步机制失效
    3. SharedPreferences 可靠性降低
    4. Application 被多次创建

    1, 2 很容易理解,每个应用或进程分配独立的虚拟机,不同的虚拟机自然占有不同的内存地址空间。可以认为,每个进程,都有独立的静态成员和单例模式的对象,所以进程之间,千万不能通过这些通信,因为它们属于不同的时空喔。
    3嘛,如果一个读,一个写,还好,要是同时去写,就可能出问题了,A进程刚写1,B进程又写2,A一脸懵逼,为啥变成2了,你说可靠不可靠。
    4很重要,指的是,Application会被重复创建。比如,如果有3个进程,Application会初始化3次。如果希望不同进程做不同的初始化,则可以参考如下的实现:

    package com.chenxf.processtest;
    
    import android.app.ActivityManager;
    import android.app.Application;
    import android.content.Context;
    import android.text.TextUtils;
    import android.util.Log;
    
    public class MyApplication extends Application {
        private static final String TAG = "MyApplication";
        private static final String DOWNLOADER_PROCESS = ":downloader";
        private static final String PLUGIN_PROCESS = ":plugin";
    
        private BaseApplication mProxy;
    
        @Override
        public void onCreate() {
            super.onCreate();
            String processName = getMyProcessName();
            Log.i(TAG, "onCreate " + processName);
            initProxyApplication(processName);
        }
    
        private void initProxyApplication(String processName) {
            String mPackageName = getPackageName();
    
            if (TextUtils.equals(mPackageName, processName)) {
                //主进程
                Log.i(TAG, "init process " + mPackageName);
                mProxy = new MainApplication(processName);
            } else if (TextUtils.equals(processName, mPackageName + PLUGIN_PROCESS)) {
                //插件安装进程
                Log.i(TAG, "init process " + PLUGIN_PROCESS);
                mProxy = new PluginApplication(processName);
            } else if (TextUtils.equals(processName, mPackageName + DOWNLOADER_PROCESS)) {
                //下载进程
                Log.i(TAG, "init process " + DOWNLOADER_PROCESS);
                mProxy = new DownloaderApplication(processName);
            } else {
                mProxy = new BaseApplication(processName);
            }
        }
    
        /**
         * 获取进程的名称
         *
         * @return
         */
        public String getMyProcessName() {
            if (mProxy != null) {
                return mProxy.getProcessName();
            } else {
                return initCurrentProcessName(this);
            }
        }
    
        private String initCurrentProcessName(Context context) {
            int pid = android.os.Process.myPid();
            ActivityManager manager =
                    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
                if (process.pid == pid) {
                    return process.processName;
                }
            }
            return null;
        }
    }
    

    多进程的通信

    有多种通信方式,包括:
    AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。
    Messenger:支持一对多的串行实时通信, AIDL 的简化版本,不需要写AIDL文件,只能支持一次处理一个调用。
    ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。
    BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。
    文件共享:在非高并发情况下共享简单的数据。
    Socket:通过网络传输数据。

    AIDL最为复杂,网上也有很多文章介绍,这里根据多进程的情况下,来写一个AIDL实现多进程通信吧。

    AIDL实现多进程通信

    服务端实现

    服务端,咱单独建一个模块,如downloader。模块的Manifest声明一个service在独立进程。
    service加一个action,方便其他模块启动service。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.chenxf.downloader">
    
        <application>
            <service
                android:name=".DownloadService"
                android:enabled="true"
                android:exported="true"
                android:process=":downloader">
                <intent-filter>
                    <action android:name="com.chenxf.downloader.action.START_SERVICE" />
                </intent-filter>
            </service>
        </application>
    
    </manifest>
    

    选中模块,右键,New -> AIDL -> AIDL File。
    创建一个文件,IDownloadAidl。

    // IDownloadAidl.aidl
    package com.chenxf.downloader;
    import com.chenxf.downloader.DownloadBean;
    // Declare any non-default types here with import statements
    
    interface IDownloadAidl {
        void sendMessage(in DownloadBean url);//注意加in,不然编不过
        DownloadBean getMessage(in DownloadBean param);
    }
    

    以上代码的DownloadBean是传输数据用的,需要也写一个AIDL文件才能编译过:

    // DownloadBean.aidl
    package com.chenxf.downloader;
    
    // Declare any non-default types here with import statements
    
    parcelable DownloadBean;
    

    这个文件,注意写法和上面不一样,不是interface,是parcelable,待会我们还得实现一个包名一致的类,DownloadBean,继承Parcelable。

    接着,执行Build-> Make Project,编译downloader模块,将会生成一个文件。
    在这里插入图片描述
    这个文件声明了一些类,看起来乱七八糟,其实跟我们有关系的,只有一个内部类:IDownloadAidl.Stub。服务端,需要继承IDownloadAidl.Stub,来实现对应的函数。

    package com.chenxf.downloader;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    public class DownloadService extends Service {
        private DownloadServiceStub downloadServiceStub;
        public DownloadService() {
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            downloadServiceStub = new DownloadServiceStub();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            return downloadServiceStub;
        }
    
        public class DownloadServiceStub extends IDownloadAidl.Stub {
    
    
            @Override
            public void sendMessage(DownloadBean url) throws RemoteException {
    
            }
    
            @Override
            public DownloadBean getMessage(DownloadBean param) throws RemoteException {
                DownloadBean result = new DownloadBean();
                result.setDownloadResult("/sdcard/xx.mp4");
                return result;
            }
        }
    }
    
    

    很简单吧,对了,DownloadBean 还要实现一下:

    package com.chenxf.downloader;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class DownloadBean implements Parcelable {
        String url;
    
        String downloadResult;
    
        public DownloadBean() {
        }
    
        public DownloadBean(String url) {
            this.url = url;
        }
    
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getDownloadResult() {
            return downloadResult;
        }
    
        public void setDownloadResult(String downloadResult) {
            this.downloadResult = downloadResult;
        }
    
    
        protected DownloadBean(Parcel in) {
            url = in.readString();
            downloadResult = in.readString();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(url);
            dest.writeString(downloadResult);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        public static final Creator<DownloadBean> CREATOR = new Creator<DownloadBean>() {
            @Override
            public DownloadBean createFromParcel(Parcel in) {
                return new DownloadBean(in);
            }
    
            @Override
            public DownloadBean[] newArray(int size) {
                return new DownloadBean[size];
            }
        };
    }
    
    

    好了,服务端写好啦。

    接着是客户端调用。也很简单。

    客户端实现

    客户端就写在app模块吧,gradle得依赖downloader模块,这样,DownloadBean才可以用。我们让一个activity绑定远程Service,然后通信。
    ps:记得activity退出时,unbindService。

    package com.chenxf.processtest;
    
    import android.app.Service;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    import com.chenxf.downloader.DownloadBean;
    import com.chenxf.downloader.DownloadService;
    import com.chenxf.downloader.IDownloadAidl;
    
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        private IDownloadAidl downloadAidl;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            findViewById(R.id.start_service).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    handleClick();
                }
            });
        }
    
        private void handleClick() {
            if(downloadAidl == null) {
                //点击时,才开启进程,没有start的话,系统不会有downloder进程
                startService();
            } else {
                try {
                    DownloadBean result = downloadAidl.getMessage(new DownloadBean("http://xx.mp4"));
                    Toast.makeText(MainActivity.this, "result " + result.getDownloadResult(), Toast.LENGTH_LONG).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            if(downloadAidl != null) {
                unbindService(conn);
            }
        }
    
        private void startService() {
        // 创建所需要绑定的Service的Intent
    //        Intent intent = new Intent();
    //        intent.setAction("com.chenxf.downloader.action.START_SERVICE");
    //        intent.setPackage("com.chenxf.downloader");
    //        // 绑定远程的服务
    //        bindService(intent, conn, Service.BIND_AUTO_CREATE);
            Intent intent = new Intent(this, DownloadService.class);
            bindService(intent, conn, Service.BIND_AUTO_CREATE);
        }
    
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.i(TAG, "onServiceDisconnected " +name);
    
                downloadAidl = null;
            }
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // 获取远程Service的onBinder方法返回的对象代理
                downloadAidl = IDownloadAidl.Stub.asInterface(service);
                Log.i(TAG, "onServiceConnected " +name +" downloadAidl" +downloadAidl );
            }
        };
    
    
    }
    
    

    需要强调的是,虽然我们声明了DownloadService是在独立进程,但只有这个service启动了,进程才会被创建,否则不会喔!

    完整源码

    https://github.com/newchenxf/MultiProcess

    参考文献

    Android 多进程通信

    展开全文
  • 前言 正常情况下,一个apk启动后只会运行在一个进程中,其进程名...但是如果需要将某些组件(如Service,Activity等)运行在单独的进程中,就需要用到android:process属性了。我们可以给android的组件设置android:p...

    前言

    正常情况下,一个apk启动后只会运行在一个进程中,其进程名为apk的包名,所有的组件都会在这个进程中运行,以下为DDMS的进程截屏:

    这里写图片描述

    com.biyou.multiprocess为进程名,也是apk的包名, 


    但是如果需要将某些组件(如Service,Activity等)运行在单独的进程中,就需要用到android:process属性了。我们可以给android的组件设置android:process属性来使其运行在指定的进程中。

    • AndroidMantifest.xml中的activity、service、receiver和provider均支持android:process属性
    • 设置该属性可以使每个组件均在各自的进程中运行,或者使一些组件共享一个进程
    • AndroidMantifest.xml中的application元素也支持android:process属性,可以修改应用程序的默认进程名(默认值为包名)

    为何要使用多进程

     

    1.分散内存的占用



    我们知道Android系统对每个应用进程的内存占用是有限制的,而且占用内存越大的进程,通常被系统杀死的可能性越大。让一个组件运行在单独的进程中,可以减少主进程所占用的内存,避免OOM问题,降低被系统杀死的概率,

    2.实现多模块



    比如我做的应用大而全,里面肯定会有很多模块,假如有地图模块、大图浏览、自定义WebView等等(这些都是吃内存大户),还会有一些诸如下载服务,监控服务等等,一个成熟的应用一定是多模块化的。

    当我们的应用开发越来越大,模块越来越多,团队规模也越来越大,协作开发也是个很麻烦的事情。项目解耦,模块化,是这阶段的目标。通过模块解耦,开辟新的进程,独立的JVM,来达到数据解耦目的。模块之间互不干预,团队并行开发,责任分工也明确。

    3.子进程奔溃,主进程可以继续工作

    如果子进程因为某种原因崩溃了,不会直接导致主程序的崩溃,可以降低我们程序的崩溃率。 
     

    4.主进程退出,子进程可以继续工作

    即使主进程退出了,我们的子进程仍然可以继续工作,假设子进程是推送服务,在主进程退出的情况下,仍然能够保证用户可以收到推送消息。 
     

    5.实现守护进程



    如果主线程中的服务要从开机起持续运行,若由于内存等原因被系统kill掉,守护进程可以重新启动主线程的服务。

    通过JNI利用C/C++,调用fork()方法来生成子进程,一般开发者会利用这种方法来做一些daemon(守护进程)进程,来实现防杀保活等效果。

    另外:

    还能通过监控进程,将这个错误上报给系统,告知他在什么机型、环境下、产生了什么样的Bug,提升用户体验。

    实现

    1 . 如果android:process的值以冒号开头的话,那么该进程就是私有进程,如下:

    配置:

    <application
       ……
       <service android:name=".ProcessTestService" android:process=":secondProcess"/>
       ……
    </application>

    进程:

    这里写图片描述

    2 . 以小写字母开头(如com.secondProcess),那么就是公有进程,android:process值一定要有个点号:

    不能以数字开头,并且要符合命名规范,必须要有.否则将会出现这种错误: Invalid process name simon in package com.wind.check: must have at least one ‘.’ 
    配置:

    <application
       ……
       <service android:name=".LocalService" android:process="com.secondProcess"/>
       ……
    </application>

    进程:

    这里写图片描述

    3 . 私有进程和公有进程的区别: 
    android:process=":remote",以冒号开头,冒号后面的字符串原则上是可以随意指定的。如果我们的包名为“com.biyou.multiprocess”,则实际的进程名 
    为“com.biyou.multiprocess:remote”。这种设置形式表示该进程为当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。 
    全局进程 
    进程名称不以“:”开头的进程都可以叫全局进程,如android:process="com.secondProcess",以小写字母开头,表示运行在一个以这个名字命名的全局进程中,其他应用通过设置相同的ShareUID可以和它跑在同一个进程

    ps:ShareUID : 
    ShareUserId,在Android里面每个app都有一个唯一的linux user ID,则这样权限就被设置成该应用程序的文件只对该用户可见,只对该应用程序自身可见,而我们可以使他们对其他的应用程序可见,这会使我们用到SharedUserId,也就是让两个apk使用相同的userID,这样它们就可以看到对方的文件。为了节省资源,具有相同ID的apk也可以在相同的linux进程中进行(注意,并不是一定要在一个进程里面运行),共享一个虚拟机。 
    ShareUserId的作用,数据共享、调用其他程序资源。

    进程生命周期与优先级

    Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。

    重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程):

    1.前台进程:(foreground process) 

    用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程: 
    托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法) 
    托管某个 Service,后者绑定到用户正在交互的 Activity 
    托管正在“前台”运行的 Service(服务已调用 startForeground()) 
    托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy()) 
    托管正执行其 onReceive() 方法的 BroadcastReceiver 
    通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

    2.可见进程

    没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程: 
    托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。 
    托管绑定到可见(或前台)Activity 的 Service。 
    可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

    3.服务进程 
    正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

    4.后台进程 
    包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅 Activity文档。

    5.空进程 
    不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 
    根据进程中当前活动组件的重要程度,Android 会将进程评定为它可能达到的最高级别。例如,如果某进程托管着服务和可见 Activity,则会将此进程评定为可见进程,而不是服务进程。

    此外,一个进程的级别可能会因其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。 例如,如果进程 A 中的内容提供程序为进程 B 中的客户端提供服务,或者如果进程 A 中的服务绑定到进程 B 中的组件,则进程 A 始终被视为至少与进程 B 同样重要。

    由于运行服务的进程其级别高于托管后台 Activity 的进程,因此启动长时间运行操作的 Activity 最好为该操作启动服务,而不是简单地创建工作线程,当操作有可能比 Activity 更加持久时尤要如此。例如,正在将图片上传到网站的 Activity 应该启动服务来执行上传,这样一来,即使用户退出 Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论 Activity 发生什么情况,该操作至少具备“服务进程”优先级。 同理,广播接收器也应使用服务,而不是简单地将耗时冗长的操作放入线程中。

    注意的地方

    有一点一定要记住:进程间的内存空间是不可见的。从而,开启多进程后,我们需要面临这样几个问题:

    1. Application的多次重建。

    Manifest文件如上面提到的,定义了两个类:ProcessTestActivity和ProcessTestService,我们只是在Activity的onCreate方法中直接启动了该Service,同时,我们自定义了自己的Application类。代码如下:

    public class MyApplication extends Application {
        public static final String TAG = "viclee";
        @Override
        public void onCreate() {
            super.onCreate();
            int pid = android.os.Process.myPid();
            Log.d(TAG, "MyApplication onCreate");
            Log.d(TAG, "MyApplication pid is " + pid);
        }
    }
    public class ProcessTestActivity extends Activity {
        public final static String TAG = "viclee";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_process_test);
    
            Log.i(TAG, "ProcessTestActivity onCreate");
            this.startService(new Intent(this, ProcessTestService.class));
        }
    }
    public class ProcessTestService extends Service {
        public static final String TAG = "viclee";
    
        @Override
        public void onCreate() {
            Log.i(TAG, "ProcessTestService onCreate");
        }
    
        @Override
        public IBinder onBind(Intent arg0) {
            return null;
        }
    
    }


     

    这里写图片描述 
     

     

      我们发现MyApplication的onCreate方法调用了两次,分别是在启动ProcessTestActivity和ProcessTestService的时候,而且我们发现打印出来的pid也不相同。由于通常会在Application的onCreate方法中做一些全局的初始化操作,它被初始化多次是完全没有必要的。出现这种情况,是由于即使是通过指定process属性启动新进程的情况下,系统也会新建一个独立的虚拟机,自然需要重新初始化一遍Application。那么怎么来解决这个问题呢? 
    下面给出解决方案:

    思路:判断是否为主进程,只有主进程的时候才执行下面的操作

    String processName = this.getProcessName();
    
    //判断进程名,保证只有主进程运行
    if (!TextUtils.isEmpty(processName) &&processName.equals(this.getPackageName())) {
        //在这里进行主进程初始化逻辑操作                          
        Log.i(">>>>>>","oncreate");
    }

    获取进程名的方法,这个方法是效率最好的:

     public static String getProcessName() {
            try {
                File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
                BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
                String processName = mBufferedReader.readLine().trim();
                mBufferedReader.close();
                return processName;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

    2. 静态成员的失效。

    将之前定义的Activity和Service的代码进行简单的修改,代码如下:

    public class ProcessTestActivity extends Activity {
        public final static String TAG = "viclee";
        public static boolean processFlag = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_process_test);
    
            processFlag = true;
            Log.i(TAG, "ProcessTestActivity onCreate");
            this.startService(new Intent(this, ProcessTestService.class));
        }
    }
    public class ProcessTestService extends Service {
        public static final String TAG = "viclee";
    
        @Override
        public void onCreate() {
            Log.i(TAG, "ProcessTestService onCreate");
            Log.i(TAG, "ProcessTestActivity.processFlag is " + ProcessTestActivity.processFlag);
        }
    
        @Override
        public IBinder onBind(Intent arg0) {
            return null;
        }
    
    }
    

    重新执行代码,打印Log :
     

    这里写图片描述

     

      从上面的代码和执行结果看,我们在Activity中定义了一个标志processFlag并在onCreate中修改了它的值为true,然后启动Service,但是在Service中读到这个值却为false。按照正常的逻辑,静态变量是可以在应用的所有地方共享的,但是设置了process属性后,产生了两个隔离的内存空间,一个内存空间里值的修改并不会影响到另外一个内存空间。

    3. 文件共享问题。

      多进程情况下会出现两个进程在同一时刻访问同一个数据库文件的情况。这就可能造成资源的竞争访问,导致诸如数据库损坏、数据丢失等。在多线程的情况下我们有锁机制控制资源的共享,但是在多进程中比较难,虽然有文件锁、排队等机制,但是在Android里很难实现。解决办法就是多进程的时候不并发访问同一个文件,比如子进程涉及到操作数据库,就可以考虑调用主进程进行数据库的操作。

    参考: 
    1. 这可能是最全的Android:Process (进程)讲解了 
    2. Android应用内多进程分析和研究

    展开全文
  • Android多进程保活

    千次阅读 2018-06-27 16:08:20
    1 简介    在Android 4.4及以后的系统中,应用能否常驻内存,一直以来都是相当头疼的事情,尤其移动端IM、...  此次将对Android进程保活方案进行调研,避免android程序因系统资源紧张或用户主动去清理应...

    1 简介

       在Android 4.4及以后的系统中,应用能否常驻内存,一直以来都是相当头疼的事情,尤其移动端IM、消息推送这类应用,为了保证“全时在线”的概念,真是费尽了心思。虽然APP常驻内存对于用户来说比较”恶心”,但是在诸如IM和消息推送这类场景来说,APP的常驻内存却尤其重要。
      此次将对Android的进程保活方案进行调研,避免android程序因系统资源紧张或用户主动去清理应用而导致的程序被系统杀死,导致“行啊”无法实时的接收最新的消息推送和通知。通过调研,了解系统为什么会杀掉进程,杀的为什么是我的进程,这是按照什么标准来选择的,是一次性干掉多个进程,还是一个接着一个杀,保活套路一堆,如何进行进程保活才是比较恰当以及实现思路。


    2 Android进程

    2.1 概述

      Android应用启动后至少对应一个进程,有的是多个进程,而且主流应用中多个进程的应用比例较大。

    2.2 进程分类

    2.2.1 前台进程

      用户正在使用的程序,一般系统是不会杀死前台进程的,除非用户强制停止应用或者系统内存不足等极端情况会杀死。

    场景
    • 某个进程持有一个正在与用户交互的Activity并且该Activity正处于resume的状态。
    • 某个进程持有一个Service,并且该Service与用户正在交互的Activity绑定。
    • 某个进程持有一个Service,并且该Service调用startForeground()方法使之位于前台运行。
    • 某个进程持有一个Service,并且该Service正在执行它的某个生命周期回调方法,比如onCreate()、 onStart()或onDestroy()。
    • 某个进程持有一个BroadcastReceiver,并且该BroadcastReceiver正在执行其onReceive()方法。

    2.2.2 可见进程

    • 我们的Activity处在onPause()(没有进入onStop())
    • 绑定到前台Activity的Service。

    2.2.3 服务进程

      简单的startService()启动。

    2.2.4 后台进程

    • 对用户没有直接影响的进程—-Activity出于onStop()的时候。
    • android:process=”:xxx”

    2.2.5 空进程

      不含有任何的活动的组件。(android设计的,为了第二次启动更快,采取的一个权衡)

    2.3 内存阈值

      系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app, 这套杀进程回收内存的机制就叫 Low Memory Killer。那这个不足怎么来规定呢,那就是内存阈值。内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程.

    2.4 进程被杀死的优先级

      现在要杀后台进程,但是手机中后台进程很多,难道要一次性全部都清理掉?当然不是的,进程是有它的优先级的,这个优先级通过进程的adj值来反映,它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。
      adj越大,占用物理内存越多会被最先kill掉。那么现在对于进程保活这个问题就转化成如何降低adj的值,以及如何使得我们应用占的内存最少。

    3 进程保活方案

    当前业界的Android进程保活手段主要分为 黑、白、灰 三种,其大致的实现思路如下
    • 黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
    • 白色保活:启动前台Service
    • 灰色保活:利用系统的漏洞启动前台Service

    3.1 黑色保活

      所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒。

    常见的应用场景如下
    • 监控系统的广播,如开机,网络切换等,唤醒进程
    • 利用App自身,发出广播或者启动服务。比如打开阿里系的任意一款APP,都可能会激活其他阿里系App的进程

      针对第一种方案,谷歌已经做了部分处理,禁止监控系统级别的某些广播,比如网络切换;第二种方案的使用范围相对更广,更实用。
    黑色保活介绍

    3.2 白色保活

      白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。常见的音乐播放器都是采用该方案。
      粘性服务这个是系统自带的,onStartCommand方法必须具有一个整形的返回值,这个整形的返回值用来告诉系统在服务启动完毕后。Service的onStartCommand方法里返回 STATR_STICK,onDestory中start自启(准确的将算不上进程拉活,只能算service自启,force_stop后不能正常拉活)。
    白色保活介绍

    3.3 灰色保活

      灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路如下:

    • API < 18,启动前台Service时直接传入new Notification();
    • API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;

      当某一天 API >= 18 的方案也失效的时候,我们就又要另谋出路了。需要注意的是,使用灰色保活并不代表着你的Service就永生不死了,只能说是提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。

      另外一种灰色保活方案是开启一个像素Activity。在锁屏的时候在本进程开启一个Activity,为了欺骗用户,让这个Activity的大小是1像素,并且透明无切换动画,在开屏幕的时候,把这个Activity关闭掉,所以这个就需要监听系统锁屏广播。我们的应用就始终和前台进程是一样的优先级了,为了省电,系统检测到锁屏事件后一段时间内会杀死后台进程,如果采取这种方案,就可以避免了这个问题,但是还是有被杀掉的可能。
    灰色保活介绍

    4 APP进程不死的秘密

      许多大厂的APP,比如微信,QQ,支付宝等,你在使用的时候会发觉,他们的进程是不会被系统杀死,而且在启动一系列内存清理工具进行强制清理,也只是暂时杀死进程或者根本不能杀死。那么他们拥有什么秘密武器,可以保证进程不死呢?答案就是白名单。有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。

    5 总结

      从保活这一点来说,黑色保活和白色保活方案相对较好;灰色保活方案可行性虽然很高,可以但是对用户来说太流氓了,不推荐。 对于有硬性需求的,可以引导用户加入白名单。至于推送, 可以尝试集成多个推送方案,小米,华为等都有推送sdk,在对应手机上可以确保收到消息, 然后像百度这种是多app公用通道的,也就是手机中有一个使用百度推送的app被允许后台启动,就能让其他app收到推送。随着Android版本的不断更新及国内厂商对ROM的不断优化,如何最大可能的对进程保活,是Android一道需要长期钻研的学问,也是Android开发者不得不面对的问题。进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的伪命题!

    6 参考

    https://www.jianshu.com/p/1c353edf73ba
    https://segmentfault.com/a/1190000006251859
    https://www.jianshu.com/p/63aafe3c12af

    展开全文
  • Android 多进程的基础使用及优缺点

    千次阅读 2017-04-08 19:20:58
    前言说起进程,不得不说说进程和线程之前的关系。在操作系统角度描述,线程是CPU调度的最小...我们可以使用多进程分担主进程压力以免因资源消耗过大被crash掉,另外多进程相互监听可以唤醒,使应用程序长期驻守后...

    目录

    前言

    什么是IPC?

    什么是AIDL?

    为什么用多进程?

    正文

    一、多进程的使用及优化

    二、多进程解决问题

    三、多进程的坑

    四、多进程优化


     

        说起进程,不得不说说进程和线程之前的关系。在操作系统角度描述,线程是CPU调度的最小单位且是有限的系统资源。而进程是值一个执行单位,例如一个程序或应用,一个进程包含多个线程,默认的一个线程是主线程,即UI线程,可以操作Ui界面元素,但不能做耗时任务,否则会ARN程序无响应。我们可以使用多进程分担主进程压力以免因资源消耗过大被crash掉,另外多进程相互监听可以唤醒,使应用程序长期驻守后台接受即时消息和通知,防止应用被系统回收。

     

     

    什么是IPC

    IPC(Inter-Process Communication)即进程间通信或者跨进程通信,指两个进程之间数据交换的过程。

     

    什么是AIDL

    AIDL(Android Interface Definition Language)即Android接口定义语言,指Android 提供的一种进程间通信 (IPC) 机制。

    Android Binder机制AIDL实例介绍

    为什么用多进程

    下面说一下微信采用多进程开发的客户端架构,看看微信是怎么做的?

    微信在v2.x+版本时,对NetWork层做轻重进程分离,独立到一个单独的进程中(:push中),而上面两个层级依然跑在微信主进程中(:workder中)。对于有内存泄漏的webview或者其他不频繁使用的功能,再把其单独分离到工具进程中(:tools中)。

    通过分离进程,微信第一次重构解决了系统因为微信资源消耗,主动干掉微信服务的困境。

     

    一、多进程的使用及优化

    1、我们可以通过指定四大组件在AndroidManifest文件中的属性android:process所处的进程包名即可。启动方式看起来很简单,但是远远没有我们想的那么简单,有时候我们使用多进程带来的好处,不及弥补代码层面的负面影响。

    2、默认情况下,应用拥有一个主进程。组件被指定新进程之后,系统在启动这个组件的时候,是先创建这个进程,再创建该组件。打印出进程名称办法:重载Application类的onCreate方法即可。

    3、设置android:process属性时,要注意:如果是android:process=”:deamon”,以:开头的名字,表示这是一个应用程序的私有进程,否则它是一个全局进程。私有进程的进程名称是会在冒号前自动加上包名,而全局进程则不会。一般我们都是有私有进程,很少使用全局进程。例如:

     

     

    <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=".SecondActivity"
                android:process=":remote" />
            <activity
                android:name=".ThirdActivity"
                android:process="com.example.mac.processdemo.remote" />
        </application>

     

    二、多进程解决问题

    1、分担主进程的内存压力。

    当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现

    2、使应用常驻后台,防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。

    Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。坏处:消耗用户的电量,多占用了系统的空间,若所有应用都这样占用,系统内存很容易占满而导致卡顿,应用程序架构会变得复杂,因为要处理多进程之间的通信。这里又是另外一个问题了。
     

    三、多进程的坑

    1、静态成员和单例模式完全失效 。
    Android 为每一个进程分配一个独立的虚拟机,不同虚拟机在内存分配上有不同地址空间,这就导致多进程下访问同一个类的对象会产生多分副本。所以在一个进程中修改某个值,只会在当前进程有效,对其他进程不会造成任何影响。

    2、线程同步机制完全失效。
    因为多进程的内存地址空间不同,锁的不是同一个对象,所以不管是锁对象还是锁全局对象都无法保证线程同步。

    3、SharedPreferences 的可靠性下降。
    因为SharedPreferences 底层通过读写XML实现,并发读写显然是不安全的操作,甚至会出现数据错乱。

    4、Application 会多次创建。
    当一个组件指定一个新进场启动时,由于系统在创建新进程的同时要分配独立虚拟机,而这是一个启动应用的过程。因此,相当于系统又把应用的重新启动了一遍,既然重新启动了,那么自然要创建新的Application。
     

    四、多进程优化

    1、针对Application的多次重建:
    在Application的onCreate中获取进程Id来判断不同进程,然后做不同的事情。

    public class MyApplication extends Application {
    	@Override
    	public void onCreate() {
    		super.onCreate();
    		//获取进程Id
    		int pid = android.os.Process.myPid();
    		Log.e("m_tag", "MyApplication onCreate pid is " + pid); //根据进程id获取进程名称
    		String pName = getProcessName(this,pid);
    		if("com.xyy.processtest".equals(pName)){
    		//处理该进程的业务
    		}
    	}
    }
    public String getProcessName(Context cxt, int pid) {
    	ActivityManager am = (ActivityManager)
    	cxt.getSystemService(Context.ACTIVITY_SERVICE);
    	List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
    	if (runningApps == null) {
    		return null;
    	}
    	for (RunningAppProcessInfo procInfo : runningApps) {
    		if (procInfo.pid == pid) {
    			return procInfo.processName;
    		}
    	}
    	return null;	
    }

    2、针对静态成员的失效

    使用Intent或者aidl等进程通讯方式传递内容,不能用静态或单例模式。

    3、针对文件共享问题

    多进程情况下会出现两个进程在同一时刻访问同一个数据库文件的情况这就可能造成资源的竞争访问导致诸如数据库损坏数据丢失等在多线程的情况下我们有锁机制控制资源的共享但是在多进程中比较难虽然有文件锁排队等机制但是在Android里很难实现解决办法就是多进程的时候不并发访问同一个文件比如子进程涉及到操作数据库就可以考虑调用主进程进行数据库的操作

     

    束。

     

    展开全文
  • Android 多进程的利弊分析

    千次阅读 2018-03-26 23:26:41
    多进程的使用方法:Android多进程概念:一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程...
  • Android 多进程调试技巧

    千次阅读 2019-07-16 13:54:07
    之前开发多进程的应用,想要调试Application 里面的代码,一直调试不了代码。 因为是多进程,Application 会执行两次。 原因: 比如我们有两个进程,一个叫进程a, 一个叫进程b. 因为我们android debug 的时候,选择...
  • Android多进程实现及常见问题

    千次阅读 2016-12-28 14:23:07
    Android多进程实现及常见问题   1、为什么需要多个进程? 默认情况下,一个Android应用中所有的组件都会运行在以包名为进程名的单个进程中,但是由于Android自身平台的一些限制或者多进程固有的一些好处,导致很多...
  • 背景 一般情况下,一个应用程序就一个进程,这个进程的名称就是...一个进程情况下,Application 的 onCreate 方法只会执行一次,但如果应用中采用多进程方式,onCreate 方法会执行多次。 解决方案 获取当前运行进程...
  • android多进程操作sp问题

    千次阅读 2020-03-03 23:29:40
    假如你的应用拥有进程,比如有两个进程,分别叫进程A和进程B,你定义了一个sp文件叫apkInfo用于存储应用的一些信息。A进程和B进程都需要存取应用信息,所以你在A进程和B进程都会操作apkInfo这个sp文件,这样会...
  • 解决身份证扫描停不下来,而采用Activity独立进程,通过开启和关闭该进程来实现开启和关闭扫描,问题:反应慢,卡顿  解决办法: 1、使用独立的App,将该App独立出来,通过调用该App来实现,因为这个独立的app...
  • 就我个人开发实践中就多次使用了Android多进程机制,如项目中的推送业务开发,提出要求如下: - 不能影响主业务的代码稳定运行 - 不能占用主业务的进程内存 - 不受主业务进程生命周期影响,独立存在和运行 要...
  • Android 多进程使用场景

    千次阅读 2017-01-12 14:30:30
    本文章原作者已授权转载 原文地址...在上一篇《Android 线程介绍》中,我们大概了解了一下Android中的进程的概念以及其生命周期,另外还有Low Memory Kill...
  • 自己写了一个音视频通讯的SDK,由于做了双进程保活,造成很...原来是多进程的缘故,导致了Application创建了两次,每个进程都会有一个Application,于是就找到以下解决方法:  主要就是:android:process这个属性,
  • Android多进程使用及其带来的问题

    千次阅读 2018-08-08 22:21:31
    本文介绍Android程序如何使用多进程,为什么要使用多进程,并说明了使用多进程可能会出现的一些问题。 1. Android程序如何使用多进程 根据Processes and Threads,默认情况下,一个Android程序的所有组件都是在一...
  • 不过,单进程开发并不是Android应用的全部,今天我们就来说说Android中的多进程开发以及多进程的使用场景。 1. 进程 我们都知道Android系统是基于Linux改造而来的,进程系统也是一脉相承,进程其实就是程序的...
  • android 多进程下的坑

    千次阅读 2017-03-07 18:00:20
    android 多进程下的坑 一个进程的启动会分配一个android虚拟机,同时申请自己的堆栈空间,这样导致就可能存在异常的坑点。常见的案例即为android启动一个独立进程的service进行后台事件监听,如推送等功能。多进程...
  • 本文将试图对于Android中应用多进程做一些整理总结。 android:process 应用实现多进程需要依赖于android:process这个属性 适用元素:Application, Activity, BroadcastReceiver, Service, ContentProvider。 通常...
  • Android多进程实现,一个APP多个进程

    千次阅读 2019-07-30 10:03:06
    相关文章: Android IPC机制(一)开启多进程(文章1:刘望舒大神的...Android多进程实现,一个APP多个进程(文章3:1w阅读量,5赞) Android中,默认一个APK包就对应一个进程。 Android平台对每个进程有内存限...
  • 后来冷静下来分析一下,才发现有一些第三方组件,比如百度推送之类的,它们是单独开了一个进程,那么每个进程会自己初始化自己的Application,那自然onCreate方法会次执行。准确的说就是你的...
  • 在不同应用程序之间交互数据(跨进程通讯),在Android SDK中提供了4种用于跨进程通讯的方式。 这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。其中Activity...
  • Android多进程实现,一个APP多个进程
  • android 多进程操作sp问题

    千次阅读 2017-09-23 10:36:48
    假如你的应用拥有进程,比如有两个进程,分别叫进程A和进程B,你定义了一个sp文件叫apkInfo用于存储应用的一些信息。A进程和B进程都需要存取应用信息,所以你在A进程和B进程都会操作apkInfo这个sp文件,这样会...
  • android多进程深入分析

    千次阅读 2017-02-21 18:24:00
    android一个应用程序,可以存在多个进程, 在某些场景,比如希望获得更多的内存、希望功能模块间不相互影响,或者彻底解决webview内存泄漏等, ...看似好用且强大的多进程,也是一把双刃剑,今天就深入分析分析多进程
  • 最近遇到一个bug,当应用加了多进程后,比如总共进程数为N,会出现在startService()时onStartCommand()方法会被重复调用(N-1)次的奇怪现象。祸起 最近遇到两个模块互不相干却受到影响的奇怪问题,一个push模块...
  • Android 多进程会多次初始化 Application

    千次阅读 2016-08-21 22:12:04
    集成某 SDK 后 APP 了一个进程,打 log 的时候偶然发现Application 初始化了两次,虽然对 APP 本身没什么影响,但初始化一次也没什么用 - - 解决方法就是判断当前进程是否是 APP 默认进程,是才进行初始化操作,...
  • 相信很多同学在实际开发中,基本都不会去给app划分进程,而且,在Android中使用多进程,还可能需要编写额外的进程通讯代码,还可能带来额外的Bug,这无疑加大了开发的工作量,在很多创业公司中工期也不允许,这导致...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 166,780
精华内容 66,712
关键字:

android多进程