Android IPC机制(2)

前言

本章主要讲解Android中IPC的通信方式。。。

1.Bundle

Bundle实现了Parcelable接口,且四大组件中Activity、Service、Receiver都支持在Intent中传递Bundle。传输的数据必须能够被序列化,如基本类型、实现了了Parcelable以及Serializable接口的对象。

接下来使用Bundle在A和B之间进行跨进程数据传递:

A的AndroidManifest.xml配置文件:

 <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

B的AndroidManifest.xml配置文件:

<activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
android:exported="true" 总体来说它的主要作用是:是否支持其它应用调用当前组件。 
默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。

A和B中添加实现序列化Parcelable接口的实体类User.kt:(必须位于同名包下)

package com.wdl.model

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import java.io.Serializable
/**
 * author:   wdl
 * time: 2018/10/15 14:58
 * des:    TODO
 */

@Parcelize
data class User constructor(var userName: String, var age: Int) : Parcelable,Serializable

A的MainActivity携带Bundle参数跳转至B的MainActivity:

package com.wdl.ipcone

import android.content.ComponentName
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.wdl.model.User
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //第一种方式  通过Intent指定component跳转
        mGoIpcTwo.setOnClickListener {
            val intent = Intent(Intent.ACTION_MAIN)
            intent.addCategory(Intent.CATEGORY_DEFAULT)
            val componentName = ComponentName("com.wdl.ipctwo", "com.wdl.ipctwo.MainActivity")
            intent.component = componentName
            val user = User("wdl",23)
            val bundle = Bundle()
            bundle.putParcelable("parcelable",user)
            intent.putExtra("bundle",bundle)
            intent.putExtra("msg","我是来自第一个APP的值")
            startActivity(intent)
        }
    }
}

B的MainActivity:

package com.wdl.ipctwo

import android.annotation.SuppressLint
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.wdl.model.User
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        intent?.let {
            val content = it.getStringExtra("msg")
            val bundle = it.getBundleExtra("bundle")
            bundle?.classLoader = User::class.java.classLoader
            val user = bundle?.getParcelable<User>("parcelable")
            mBundle.text = "通过A传来的String值:$content 序列化传来的对象:${user?.toString()}"
            Log.e("wdl", "收到来自IPCOne的数据:$content")
        }

    }
}

启动并测试:

Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.wdl.model.User
        at android.os.Parcel.readParcelableCreator(Parcel.java:2535)
        at android.os.Parcel.readParcelable(Parcel.java:2461)
        at android.os.Parcel.readValue(Parcel.java:2364)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:2717)
        at android.os.BaseBundle.unparcel(BaseBundle.java:269)
        at android.os.Bundle.getParcelable(Bundle.java:840)
        at com.wdl.ipctwo.MainActivity.onCreate(MainActivity.kt:19)
        at android.app.Activity.performCreate(Activity.java:6662)

造成上述的原因是:两个User.kt实体类不是位于同一包名下,因此要想正常解析数据必须把实体类放置在包名一样的包中

 Caused by: java.lang.ClassNotFoundException: Didn't find class "com.wdl.model.User" on path: DexPathList[[zip file "/data/app/com.wdl.ipctwo-2/base.apk"],nativeLibraryDirectories=[/data/app/com.wdl.ipctwo-2/lib/x86, /system/lib, /vendor/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at java.lang.Class.classForName(Native Method) 
        at java.lang.Class.forName(Class.java:400) 
        at android.os.Parcel.readParcelableCreator(Parcel.java:2507) 
        at android.os.Parcel.readParcelable(Parcel.java:2461) 
        at android.os.Parcel.readValue(Parcel.java:2364) 
        at android.os.Parcel.readArrayMapInternal(Parcel.java:2717) 
        at android.os.BaseBundle.unparcel(BaseBundle.java:269) 
        at android.os.Bundle.getParcelable(Bundle.java:840) 
        at com.wdl.ipctwo.MainActivity.onCreate(MainActivity.kt:19) 
        at android.app.Activity.performCreate(Activity.java:6662) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6077) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) 

导致上述原因是:

Android有两种不同的classloaders:framework classloader和apk classloader,其中framework classloader知道怎么加载android classes,apk classloader知道怎么加载你的代码,即可以知道你自定义的类,apk classloader继承自framework classloader,所以也知道怎么加载android classes。在应用刚启动时,默认class loader是apk classloader,但在系统内存不足应用被系统回收会再次启动,这个默认class loader会变为framework classloader了,所以对于自己的类会报ClassNotFoundException。


解决方法:设置相应的classloader

bundle?.classLoader = User::class.java.classLoader

效果:
在这里插入图片描述

2. 使用文件共享

两个进程通过读写同一个文件夹来交换数据

在A中MainActivity中的onResume中序列化一个对象到sd卡的一个文件上:

override fun onResume() {
        super.onResume()
        persistToFile()
    }

    private fun persistToFile() {
        Thread(Runnable {
            val user = User("wdl",23)
            val dir = File(MyConstants.CHAPTER_2_PATH)
            if (!dir.exists())dir.mkdirs()
            val fileCache = File(MyConstants.CACHE_FILE_PATH)
            val objectOutputStream = ObjectOutputStream(FileOutputStream(fileCache))
            objectOutputStream.writeObject(user)
            objectOutputStream.close()
        }).start()
    }

在B中MainActivity中的onResume中反序列化sd卡的一个文件到User对象:

override fun onResume() {
        super.onResume()
        recoverFromFile()
    }

    private fun recoverFromFile() {
        Thread(Runnable {
            val cachedFile = File(MyConstants.CACHE_FILE_PATH)
            if (!cachedFile.exists()) {
                cachedFile.createNewFile()
            }
            val objectInputStream = ObjectInputStream(FileInputStream(cachedFile))
            val user = objectInputStream.readObject() as User
            objectInputStream.close()
            mBundle.text = "$user"
            Log.e("wdl", user.toString())

        }).start()
    }

效果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34341338/article/details/83096679