zoukankan      html  css  js  c++  java
  • android IPC 机制 (开发艺术探索)

    一、IPC 机制介绍

      IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。那么什么是进程,什么是线程,进程和线程是两个截然不同的概念。在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。

    1.1  Android中为什么要开启多进程

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

      当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。比如在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程等。

    (2)防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。

    1.2 如何开启多进程

      四大组件在AndroidManifest文件中注册的时候,有个属性android:process这里可以指定组件的所处的进程。默认情况下的进程名就是包名。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。

      获取进程id和进程名:

         String processName = "";
            ActivityManager manager = (ActivityManager) getApplicationContext()
                    .getSystemService(Context.ACTIVITY_SERVICE);
            for (ActivityManager.RunningAppProcessInfo process: manager.getRunningAppProcesses()) {
                if(process.pid == Process.myPid())
                {
                    processName = process.processName;
                }
            }
            Log.d(TAG, "Main:   "+ Process.myTid()+"    "+processName);

    需要注意的是 运行在不同进程中的组件是属于不同的虚拟机和application。当某个应用有三个进程时则它的Application的oncreate会执行三次。

    开启多进程如下:


    进程名以“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。而进程名不以“:”开头的进程属于全局进程,其他应用可以通过某些方式和它跑在同一个进程中。

    1.3 Android中开启多进程有哪些弊端

    (1)多占了系统的内存空间,很容易沾满而导致卡顿,同时也消耗用户的电量。同时在启动单独进程时,进程的创建会影响继承Application的实例,onCreate()会再次执行一遍。

    (2)不同进程之间内存不能共享,最大的弊端是他们之间通信麻烦,不能将公用数据放在Application中,堆栈信息、文件操作也是独立的,如果他们之间传递的数据不大并且是可序列化的,可以考虑通过Bundle传递, 如果数据量较大,则需要通过AIDL或者文件操作来实现。

      一般在安卓中使用多进程需要注意以下问题:

    • 静态成员和单例模式完全失效
    • 线程同步机制完全失效
    • SharedPreferences的可靠性下降
    • Application会多次创建

      为了解决这个问题,系统提供了很多跨进程通信方法,虽然说不能直接地共享内存,但是通过跨进程通信我们还是可以实现数据交互。实现跨进程通信的方式有很多,比如通过Intent来传递数据,共享文件SharedPreference,基于Binder的Messenger和AIDL以及Socket等。

    二、IPC 基础概念

      主要介绍IPC中的基础概念,主要包括三个方面,Serializable 及 Parcelable 接口以及Binder。

      2.1 Serializable 及 Parcelable 接口

        参考:http://www.cnblogs.com/renqingping/archive/2012/10/25/Parcelable.html

       2.2 IDAL( Android Interface definition language)

        IDAL  是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口

      2.3 Binder


    三、Android中的IPC方式

    AIDL 方式实现进程间通信


    在server端和client端都需要添加aidl文件作为进程通信的协议。

    image

    距离每个文件里面的代码如下:

    package com.lypeer.ipcclient;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    /**
     * Created by lypeer on 2016/7/16.
     */
    public class Book implements Parcelable{
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getPrice() {
            return price;
        }
    
        public void setPrice(int price) {
            this.price = price;
        }
    
        private String name;
    
        private int price;
    
        public Book(){}
    
        public Book(Parcel in) {
            name = in.readString();
            price = in.readInt();
        }
    
        public static final Creator<Book> CREATOR = new Creator<Book>() {
            @Override
            public Book createFromParcel(Parcel in) {
                return new Book(in);
            }
    
            @Override
            public Book[] newArray(int size) {
                return new Book[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(price);
        }
    
        /**
         * 参数是一个Parcel,用它来存储与传输数据
         * @param dest
         */
        public void readFromParcel(Parcel dest) {
            //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
            name = dest.readString();
            price = dest.readInt();
        }
    
        //方便打印数据
        @Override
        public String toString() {
            return "name : " + name + " , price : " + price;
        }
    }
    
    Book
    // Book.aidl
    package com.lypeer.ipcclient;
    
    parcelable Book;
    Book.aidl
    package com.lypeer.ipcclient;
    import com.lypeer.ipcclient.Book;
    
    interface BookManager {
    
        List<Book> getBooks();
        void addBook(inout Book book);
    }
    
    BookManager.aidl

    server端

    定义并注册AIDLServcie,并实现onBind() 接口,具体实现如下:

    package com.lypeer.ipcserver.service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    import com.lypeer.ipcclient.Book;
    import com.lypeer.ipcclient.BookManager;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 服务端的AIDLService.java
     * <p/>
     * Created by lypeer on 2016/7/17.
     */
    public class AIDLService extends Service {
    
        public final String TAG = this.getClass().getSimpleName();
    
        //包含Book对象的list
        private List<Book> mBooks = new ArrayList<>();
    
        //由AIDL文件生成的BookManager
        private final BookManager.Stub mBookManager = new BookManager.Stub() {
            @Override
            public List<Book> getBooks() throws RemoteException {
                synchronized (this) {
                    Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());
                    if (mBooks != null) {
                        return mBooks;
                    }
                    return new ArrayList<>();
                }
            }
    
    
            @Override
            public void addBook(Book book) throws RemoteException {
                synchronized (this) {
                    if (mBooks == null) {
                        mBooks = new ArrayList<>();
                    }
                    if (book == null) {
                        Log.e(TAG, "Book is null in In");
                        book = new Book();
                    }
                    //尝试修改book的参数,主要是为了观察其到客户端的反馈
                    book.setPrice(2333);
                    if (!mBooks.contains(book)) {
                        mBooks.add(book);
                    }
                    //打印mBooks列表,观察客户端传过来的值
                    Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
                }
            }
        };
    
        @Override
        public void onCreate() {
            Book book = new Book();
            book.setName("Android开发艺术探索");
            book.setPrice(28);
            mBooks.add(book);
            Log.d(TAG, "AIDLService onCreate: ");
            super.onCreate();
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return mBookManager;
        }
    }
    
    

    当Client端调用bindService绑定该服务时,AIDLService会被创建。

    client端

    client端定义一个Activity,并在Activity的onstart()方法中绑定AIDLService,即可以获取到AIDLService中定义的BookManager对象。通过一个按钮调用addBook方法就能调用Service端的addBook接口

    package com.lypeer.ipcclient;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    
    import java.util.List;
    
    /**
     * 客户端的AIDLActivity.java
     * 由于测试机的无用debug信息太多,故log都是用的e
     * <p/>
     * Created by lypeer on 2016/7/17.
     */
    public class AIDLActivity extends AppCompatActivity {
    
        //由AIDL文件生成的Java类
        private BookManager mBookManager = null;
    
        //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中
        private boolean mBound = false;
    
        //包含Book对象的list
        private List<Book> mBooks;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_aidl);
        }
    
        /**
         * 按钮的点击事件,点击之后调用服务端的addBookIn方法
         * @param view
         */
        public void addBook(View view) {
            //如果与服务端的连接处于未连接状态,则尝试连接
            if (!mBound) {
                attemptToBindService();
                Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
                return;
            }
            if (mBookManager == null) return;
    
            Book book = new Book();
            book.setName("APP研发录In");
            book.setPrice(30);
            try {
                mBookManager.addBook(book);
                Log.e(getLocalClassName(), book.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 尝试与服务端建立连接
         */
        private void attemptToBindService() {
            Intent intent = new Intent();
            intent.setAction("com.lypeer.aidl");
            intent.setPackage("com.lypeer.ipcserver");
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (!mBound) {
                attemptToBindService();
            }
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            if (mBound) {
                unbindService(mServiceConnection);
                mBound = false;
            }
        }
    
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(getLocalClassName(), "service connected");
                mBookManager = BookManager.Stub.asInterface(service);
                mBound = true;
    
                if (mBookManager != null) {
                    try {
                        mBooks = mBookManager.getBooks();
                        Log.e(getLocalClassName(), mBooks.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(getLocalClassName(), "service disconnected");
                mBound = false;
            }
        };
    }
    

    注: 1. 当AIDLActivity onStart()函数调用时会绑定服务,此时AIDLService的onCreate函数会被调用,同时mServiceConnection 的onServiceConnected接口会被调用,会解析出BookManager对象。

    2. 当addBook被调用(按钮点击事件)时,AIDLService里面的addBook方法会被调用。从而实现进程间的通信。

    AIDL为什么能实现进程间通信?

    实际使用场景:

    1. 智能设备中,有多个服务负责不同的职责:定位服务/配置服务/主功能服务/日志服务 等这些服务都是独立的app,通过aidl接口对外提供能力。

    其它app如果希望获取位置信息/配置信息/执行某个功能/记一条日志等,都可以通过aidl调用相应服务。


    注意事项:

    1. 绑定服务的过程可能存在失败(对于十分重要的接口可以采用重试机制,但是会一定程度上增加逻辑复杂性)

    2. 业务需要处理绑定失败的场景(onServiceDisconnected/onBindingDied)

    3. 可能会存在绑定的ServiceConnection没有任何回调,可以通过定时器保证有结果(超时没有回调则回调绑定失败)

    参考:

    https://blog.csdn.net/lmj623565791/article/details/38461079

    https://blog.csdn.net/luoyanglizi/article/details/51980630

    通过守护进程service相互拉起(AIDL):  https://blog.csdn.net/returnnull0/article/details/53750483

    Android_常驻进程(杀不死的进程):https://blog.csdn.net/two_water/article/details/52126855

  • 相关阅读:
    win8应用的编码UI测试
    什么是Peer Review
    Android开发环境的搭建
    运用int.parse()判断闰年代码实现
    等价类划分方法的应用之EditBox(二)
    等价类划分方法的应用之EditBox
    集成测试
    数据可视化简介
    关于processing
    白盒测试VS黑盒测试
  • 原文地址:https://www.cnblogs.com/NeilZhang/p/7617334.html
Copyright © 2011-2022 走看看