九、Android 中的 IPC 方式(1) --- 使用 Bundle

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yz_cfm/article/details/90320850

    在 Android 四大组件中的其中三个(Activity、Service、BroadcastReceiver)都是支持在 Intent 中传递 Bundle 数据的。我们通过 Intent 源码看到,通过 Intent 中的 putExtra() 方法携带 Bundle 数据,然后在另一个组件再通过 getBundleExtra() 方法取出 Bundle 数据:

public Intent putExtra (String name, Bundle value)
public Bundle getBundleExtra (String name)

    由于 Bundle 实现了 Parcelable 接口,所以它可以很方便的在不同进程间进行传输:

public final class Bundle extends BaseBundle implements Cloneable, Parcelable { ... }

    所以通过上面的介绍我们就可以知道,当我们在一个进程中启动了另一个进程的 Activity 、Service 、 BroadcastReceiver,我们就可以在 Bundle 中附加我们需要传输给远程进程的信息并通过 Intent 对象发送出去。当然,我们传输的数据必须能够被序列化,比如基本类型、实现了 Parcelable 接口的对象、实现了 Serializable 接口的对象以及一些 Android 支持的特殊对象。

下面我们就演示一个 Demo,通过 Bundle 传输一个 int 型数据以及一个实现了 Serializable 接口的对象和一个实现了 Parcelable 接口的对象:

Book.java:

package com.cfm.bundletest;

public class Book implements Parcelable {
    private String mName;
    private int mPrice;

    public Book(String name, int price) {
        mName = name;
        mPrice = price;
    }

    protected Book(Parcel in) {
        mName = in.readString();
        mPrice = 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];
        }
    };

    public String getName() {
        return mName;
    }

    public int getPrice() {
        return mPrice;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mName);
        dest.writeInt(mPrice);
    }
}

User.java:

package com.cfm.bundletest;

public class User implements Serializable {
    public static final long serialVersionUID = 1L;

    private String mName;
    private String mDream;

    public User(String name, String dream) {
        mName = name;
        mDream = dream;
    }

    public String getName() {
        return mName;
    }

    public String getDream() {
        return mDream;
    }
}

Tools.java:

package com.cfm.bundletest;

public class Tools{
    // 获取进程名方法
    public static String getProcessName(Context context) {
        int pid = android.os.Process.myPid();
        ActivityManager am = (ActivityManager) context.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;
    }
}

FirstActivity.java:

package com.cfm.bundletest;

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("cfmtest", "FirstActivity 所在进程名: " + Tools.getProcessName(this));

        Intent intent = new Intent(this, SecondActivity.class);
        Bundle bundle = new Bundle();
        bundle.putInt("mId", 1);
        bundle.putSerializable("mUser", new User("cfm", "open Source"));
        bundle.putParcelable("mBook", new Book("If Have a Day", 888));
        intent.putExtra("mBundle", bundle);
        startActivity(intent);
    }
}

SecondActivity.java:

package com.cfm.bundletest;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Log.d("cfmtest", "SecondActivity 所在进程名: " + Tools.getProcessName(this));

        Intent intent = getIntent();
        Bundle bundle = intent.getBundleExtra("mBundle");
        int id = bundle.getInt("mId");
        Log.d("cfmtest", "id: " + id);
        User user = (User) bundle.getSerializable("mUser");
        Log.d("cfmtest", "userName: " + user.getName() + "userDream: " + user.getDream());
        Book book = bundle.getParcelable("mBook");
        Log.d("cfmtest", "bookName: " + book.getName() + "bookPrice: " + book.getPrice());
    }
}

AndroidManifest.xml:

...
<activity android:name=".SecondActivity"
    android:process=":remote">
</activity>
...

Log 打印信息:

com.cfm.bundletest D/cfmtest: FirstActivity 所在进程名: com.cfm.bundletest
com.cfm.bundletest:remote D/cfmtest: SecondActivity 所在进程名: com.cfm.bundletest:remote
com.cfm.bundletest:remote D/cfmtest: id: 1
com.cfm.bundletest:remote D/cfmtest: userName: cfmuserDream: open Source
com.cfm.bundletest:remote D/cfmtest: bookName: If Have a DaybookPrice: 888

    上面我们通过对能通过 Bundle 传输的数据进行了测试,假设有一个 A 进程正在进行一个计算,计算完成后它要启动 B 进程的一个组件并把计算结果传递给 B 进程,但是该结果并不支持放入 Bundle 中,所以也就无法通过 intent 来传输,这时如果使用其它 IPC 方式就又略显复杂,那我们该怎么办?

    我们可以在 A 进程中通过 Intent 启动 B 进程的一个 Service 组件(比如 IntentService),让 B 进程中的 Service 在后台进行计算后再启动 B 进程中真正要启动的目标组件,由于 Service 也运行在 B 进程中,所以目标组件就可以直接获取了计算结果。所以这种方式的核心思想就是将原本需要在 A 进程中计算的任务转移到了 B 进程的后台 Service 中去执行,这样就成功的避免了进程间通信的问题了,而且只用了很小的代价。

    总体来讲,使用 Bundle 完成 IPC,操作起来很简单,缺点就是只能传输 Bundle 支持的数据类型。这种方式适用于不同进程(应用)四大组件间的通信。

猜你喜欢

转载自blog.csdn.net/yz_cfm/article/details/90320850