Android跨进程启动Service流程及常见问题

最近学习Android跨进程通信,使用到AIDL,参考着开发艺术探索,但是实践过程中也遇到一些问题,特记下流程和常见问题,使用工具Android Studio

1.服务端编写AIDL文件
Android跨进程启动Service流程及常见问题_第1张图片
点击new,创建AIDL接口文件
文件结构
生成了IMyAidlInterface.aidl文件,文件名是可以自己修改的

package com.example.android_7_test;

// Declare any non-default types here with import statements
import com.example.android_7_test.Book;

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * 在接口文件中定义 服务端提供的方法  供客户端调用 ,下面的setBook是我测试的方法
     *
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    /**
    1.定义的方法不能有修饰符
    2.不能有方法体
    3.除了基本数据类型,要加上方向参数  in 输入性(作为参数传入)  out 输出型(返回值) inout
    */
    boolean setBook(in Book book);
}

这里面有一个测试用的实体类Book(实体类要实现parcelable接口),同时在相同包下创建Book.aidl,自定义实体类都需要创建同名的aidl文件,可以点击new -》file -》 相应的文件名,后缀为.aidl,参考上方的图

package com.example.android_7_test;
/**
注明 parcelable + 自定义类名
*/
parcelable Book;

Note: 这三个类的包名必须一致,有可能自动生成的包名带有.aidl.xxx,有可能出错,可以都去掉,而且在IMyAidlInterface接口中要明确引入 相关实体类,参考上方的代码
实体类不一定要和这两个aidl文件放在同一个包下,但是为了客户端方便移植,建议放同一个包下,但是这样会造成java目录下的类无法访问这个实体类,会出现无法找到类引用,这时候需要在build.gradle添加配置

android{
sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }
}

2.同步项目,生成AIDL的java文件
这里写图片描述
点击同步项目,完成后在Project目录结构下的build-》source-》aidl下可以看到生成的java文件
Android跨进程启动Service流程及常见问题_第2张图片
3.服务端实现接口

1.实现接口
2.重写onBind,返回binder对象
/**
这个是kotlin代码,可以自己改成java代码
*/
class MyService : Service() {
    //实现接口,这里实现的是接口的内部stub类
    val interfa = object : IMyAidlInterface.Stub() {


        override fun setBook(book: Book?): Boolean {
            //实现定义的方法,这里是发送一个toast
            Handler(Looper.getMainLooper()).post({ Toast.makeText(applicationContext, "收到设置", Toast.LENGTH_SHORT).show() })
            return true
        }

        override fun basicTypes(anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String?) {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }
    }

    override fun onBind(intent: Intent): IBinder {
        //返回binder对象,不返回的话绑定服务无法成功
        return interfa.asBinder()
    }
}

4.在Manifest文件注册Service


<service     
            android:name=".MyService"
            android:process=":remote"
            android:exported="true"
           >service>

5.客户端复制创建的文件到相同包下
可以和服务端一样,创建aidl文件,生成aidl目录,但是这样生成的包名会不一致,可以在该目录下新建包,和服务端文件包名一致
Android跨进程启动Service流程及常见问题_第3张图片
①同样,这样java目录下的类文件也无法访问实体类Book,还是要在build.gradle中进行上述的配置!!!

②包名必须和服务端的包名一致!!!,否则会出现Binder invocation to an incorrect interface

6.客户端绑定启动service,调用相关方法

/**
获取返回的binder对象
*/
IMyAidlInterface iMyAidlInterface = null;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Intent intent = new Intent();
        /**
        跨进程,无法使用Intent(this,xx.class)方法,可以使用下面的,通过包名和完整路径类名创建Intent
        */
        intent.setClassName("com.example.android_7_test","com.example.android_7_test.MyService");
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }

后面就可以用IMyAidlInterface对象调用服务端的定义的方法实现通信,需要注意上面提到的容易出错的点。

感谢阅读,如有错误,欢迎指正。

你可能感兴趣的:(Android,跨进程)