Android开发高级进阶——多进程间通信

摘要:
I什么是多处理?多进程意味着多个进程。什么是流程?当应用程序开始运行时,系统将为其创建一个进程。默认情况下,应用程序只有一个进程,此进程(主进程)的名称是应用程序的包名称。进程特性:进程是系统资源和分配的基本单位,而线程是调度的基本单位。每个进程都有自己的独立资源和内存空间。其他进程无法随意访问当前进程的内存和资源。系统分配给每个进程的内存是有限的。根据上述介绍和工艺特点
一. 什么是多进程?

多进程就是多个进程的意思,那么什么是进程呢?

当一个应用在开始运行时,系统会为它创建一个进程,一个应用默认只有一个进程,这个进程(主进程)的名称就是应用的包名。

进程的特点:

  • 进程是系统资源和分配的基本单位,而线程是调度的基本单位。
  • 每个进程都有自己独立的资源和内存空间
  • 其它进程不能任意访问当前进程的内存和资源
  • 系统给每个进程分配的内存会有限制

根据上边的引言和进程的特点可以看出,使用多进程的场景为:需要使apk所使用的内存限制扩大。

二. 进程的等级

按优先级可以分为五类,优先级从高到低排列:

Android开发高级进阶——多进程间通信第1张
Android进程分类
  1. 前台进程:该进程包含正在与用户进行交互的界面组件,比如一个Activity。在接收关键生命周期方法时会让一个进程临时提升为前台进程,包括任何服务的生命周期方法onCreate()和onDestroy()和任何广播接收器onReceive()方法。这样做确保了这些组件的操作是有效的原子操作,每个组件都能执行完成而不被杀掉。
  2. 可见进程:该进程中的组件虽然没有和用户交互,但是仍然可以被看到。activity可见的时候不一定在前台。一个简单的例子是前台的 activity 使用对话框启动了一个新的 activity 或者一个透明 activity 。另一个例子是当调用运行时权限对话框时(事实上它就是一个 activity!)。
  3. 服务进程:该进程包含在执行后台操作的服务组件,比如播放音乐的Service。对于许多在后台做处理(如加载数据)而没有立即成为前台服务的应用都属于这种情况。
    请特别注意从onStartCommand()返回的常量,如果服务由于内存压力被杀掉,它表示控制什么发生什么:
    START_STICKY表示希望系统可用的时候自动重启服务,但不关心是否能获得最后一次的 Intent (例如,可以重建自己的状态或者控制自己的 start/stop 生命周期)。
    START_REDELIVER_INTENT是为那些在被杀死之后重启时重新获得 Intent 的服务的,直到用传递给 onStartCommand() 方法的 startId 参数调用stopSelf()为止。这里会使用 Intent 和 startId 作为队列完成工作。
    START_NOT_STICKY用于那些杀掉也没关系的服务。这适合那些管理周期性任务的服务,它们只是等待下一个时间窗口工作。
  4. 后台进程:该进程包含的组件没有与用户交互,用户也看不到 Service。在一般操作场景下,设备上的许多内存就是用在这上面的,使可以重新回到之前打开过的某个 activity 。
  5. 空进程:没有任何界面组件、服务组件,或触发器组件,只是出于缓存的目的而被保留(为了更加有效地使用内存而不是完全释放掉),只要 Android 需要可以随时杀掉它们。
三. 多进程的创建

Android多进程创建很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可。命名之后,就成了一个单独的进程。

process分私有进程和全局进程:

  • 私有进程的名称前面有冒号,例如:
<service android:name=".MusicService"   
           android:process=":musicservice"/>
  • 全局进程的名称前面没有冒号,例如:
<service android:name=".MusicService"   
           android:process="com.trampcr.musicdemo.service"/>

为了节省系统内存,在退出该Activity的时候可以将其杀掉(如果没有人为杀掉该进程,在程序完全退出时该进程会被系统杀掉)。

多进程被创建好了,应用运行时就会对进程进行初始化,如果一个application中有多个进程,在进行全局初始化时,多进程会被初始化多次。

解决办法:判断当前进程,然后做相应的初始化操作。

四. 多进程间的通信IPC

IPC:InterProcess Communication,即进程间通信。

我们知道,同一个进程的多个线程是共享该进程的所有资源,但多个进程间内存是不可见的,也就是说多个进程间内存是不共享的。那么进程间是如何进行通信的呢?

Android中提供了三种方法:

  • 系统实现。
  • AIDL(Android Interface Definition Language,Android接口定义语言):大部分应用程序不应该使用AIDL去创建一个绑定服务,因为它需要多线程能力,并可能导致一个更复杂的实现。
  • Messenger:利用Handler实现。(适用于多进程、单线程,不需要考虑线程安全),其底层基于AIDL。

使用Messenger

如需让服务与远程进程通信,则可使用Messenger为服务提供接口。
定义一个MessengerService继承自Service,并在AndroidManifest.xml中声明并给一个进程名,使该服务成为一个单独的进程。代码如下:

MessengerService.java

public class MessengerService extends Service{

    class IncomingHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0:
                    Toast.makeText(getApplicationContext(), "hello, trampcr", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }

    Messenger mMessenger = new Messenger(new IncomingHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

AndroidManifest.xml文件的配置如下:

<service android:name=".MessengerService"  
         android:process="com.trampcr.messenger.service"/>

MessengerActivity.java

public class MessengerActivity extends Activity{

    private boolean mBound;
    private Messenger mMessenger;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMessenger = null;
            mBound = false;
        }
    };

    public void sayHello(View v){
        if(!mBound){
            return;
        }
        Message msg = Message.obtain(null, 0 , 0, 0);
        try {
            mMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(mBound){
            unbindService(mServiceConnection);
            mBound = false;
        }
    }
}

通过以上代码,可以看到Messenger的使用方法:

  1. 服务实现一个Handler,由其接收来自客户端的每个调用的回调。
  2. Handler用于创建Messenger对象(对Handler的引用)。
  3. Messenger创建一个IBinder,服务通过onBind()使其返回客户端。
  4. 客户端使用IBinder将Messenger(引用服务的Handler)实例化,然后使用后者将Message对象发送给服务。
  5. 服务在其Handler中(具体地讲,是在handleMessage()方法中)接收每个Message。

这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(Message对象)是服务在其Handler中接收的。

以上代码实现的应用,刚打开会弹出一个binding,binding表示打开应用Activity就通过Messenger连接了一个服务进程,然后点击say hello会弹出hello,trampcr,这表示了Activity通过Messenger将Message发送给了服务进程。如下图:

Android开发高级进阶——多进程间通信第2张
MessengerService进程与MessengerActivity之间的通信

使用AIDL

AIDL是一种接口描述语言,通常用于进程间通信。

使用AIDL的步骤:

  1. 创建AIDL,在main下新建一个文件夹aidl,然后在aidl下新建AIDL文件,这时系统会自动为该文件创建一个包名。
    aidl文件中会有一个默认的basicType方法,我们为它增加一个getName方法。代码如下:
interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String getName(String nickName);
}

以上是我们自己创建的aidl文件,系统还会自动生成aidl代码,所在位置为:build/generated/source/aidl下debug和release,但是此时debug下没有任何东西,可以rebuild或运行一下程序,再次打开debug,发现生成了一个包和一个aidl文件。

  1. 在java下新建一个类AIDLService继承自Service。代码如下:
public class AIDLService extends Service {

    IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String getName(String nickName) throws RemoteException {
            return "aidl " + nickName;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }
}
  1. 在AndroidManifest.xml中注册,并给一个进程名,是该服务成为一个独立的进程。
<service android:name=".AIDLService"   
            android:process="com.aidl.test.service"/>
  1. 在MainActivity中进行与AIDLService之间的进程间通信。代码如下:
public class MainActivity extends AppCompatActivity {

    private Button mBtnAidl;
    private IMyAidlInterface mIMyAidlInterface;

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnAidl = (Button) findViewById(R.id.btn_aidl);

        bindService(new Intent(MainActivity.this, AIDLService.class), mServiceConnection, BIND_AUTO_CREATE);

        mBtnAidl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mIMyAidlInterface != null){
                    try {
                        String name = mIMyAidlInterface.getName("I'm nick");
                        Toast.makeText(MainActivity.this, "name = " + name, Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}

在Activity中利用bindService与AIDLService进行连接,通过IMyAidlInterface实例与AIDLService进程进行通信,如下图所示:

Android开发高级进阶——多进程间通信第3张
AIDLService进程与MainActivity之间的通信.gif
五.序列化插件

Parcelable code generate:自动生成实现了Parcelable接口的对象。



作者:trampcr
链接:https://www.jianshu.com/p/ce1e35c84134
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

免责声明:文章转载自《Android开发高级进阶——多进程间通信》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇获取 wx.getUserInfo 接口后续将不再出现授权弹窗,请注意升级(微信小程序开发)vs2012应用程序的打包和图标设置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

WFLoader、Module和RemoteObject失效原因

近日做项目碰到的问题: 主界面里用的是SWFLoader,分别调用不同的swf文件(通过application创建的) 现在调用都可以显示,单个swf也正常,与后台java通讯也正常,但通过SWFLoader加载后,第一个加载的swf文件与后台可以通讯,切换成别的swf文件后与后台通讯就断开了,但这个swf文件单独运行与后台通讯正常   通过网上...

手把手教你uiautomator_android自动化测试第一个示范

手把手教你uiautomator_android自动化测试第一个示例 android的自动化测试方案,弄了好久了。 Google在sdk4.0以后提供了一个自动化解决方案uiautomator: 优点:可以跨应用了;这可是亲生的; 缺点:必须sdk4.0以上版本;要想实现的好,最好有开发配合;java项目编译为jar后需要push到手机才能运行,也就是说必...

[原]零基础学习SDL开发之在Android使用SDL2.0渲染PNG图片

在上一篇文章我们知道了如何在android使用SDL2.0来渲染显示一张bmp图,但是如果是一张png或者一张jpg的图,那么还能显示成功么?答案是否定的 我们需要移植SDL_image库来支持除bmp之外的图片格式。 一、移植SDL_image库: 使用如下命令,从SDLMercurial获取SDL_image的源码: hg clone https://...

android的activity被杀死后如何重启

最近公司的大屏展示机器人上的程序运行时间长了,比如五天,十天会出现偶尔的崩溃,查日志可能是内存溢出或者是ndk层的错误,这种错误一时也不太好查找,但是产品那边有个要求就是程序退出了一定要能重启,能抓日志最好抓日志,然而ndk层的崩溃是无法重启的。但是我们去查看日志的时候发现了一个规律。 虽然应用程序所在的程序被kill process了,但是不到两秒的时间...

9款Android经常使用的高速开发框架

1、Afinal框架 项目地址:https://github.com/yangfuhai/afinal 项目地址:http://www.oschina.net/p/afinal 主要有四大模块: (1) 数据库模块:android中的orm框架,使用了线程池对sqlite进行操作。 (2) 注解模块:android中的ioc框架,全然注解方式就能够进行...

android学习和广告平台赚钱

[原创] android学习和广告平台赚钱 - Java,Android,Android学习,Android赚钱,广告平台 - mobile - ITeye论坛 前言: 2011年11月份我开始学习android软件开发(本人有良好的java基础、web开发基础、c++基础),2011年12月份开始开发第一款手机软件(软件名字就不透露了),2012年1...