之前上一篇讲解到本地服务,本地服务只能在自身APP中Activity访问Service,调用Service里面到方法等操作

如果想A应用访问B应用里面的方法,属于跨进程调用,如果Android不特供这种跨进程间通讯等API,是不能实现的

Google Android 为了解决 A应用--->B应用 跨进程访问通讯,提供了一种机制,就是IBInder,这种IBinder机制是Google工程师加入进去的,以前的Linux里是没有这个机制的

bindService 会返回IBinder接口,IBinder就是Google工程师在为了解决这种跨应用跨进程通讯,IBinder还需要结合Aidl才能实现远程服务(跨进程跨应用通讯)

 

注意:⚠️以前本地服务是通过显示意图去绑定,现在远程服务由于无法拿到另外应用的字节码,只能隐士意图去绑定

 

Service2,作为服务端,需要把MyService暴露出去

    <!--
                 代表在应用程序里,当需要该service时,会自动创建新的进程。
                 android:process=":remote"

                 是否可以被系统实例化
                 android:enabled="true"

                 代表是否能被其他应用隐式调用
                 android:exported="true"
        -->
        <service android:name=".service.MyService3">

            <intent-filter>

                <!-- 激活 MyService2 唯一name,不能重名-->
                <action android:name="liudeli.service2.service.MyService3" />

            </intent-filter>
            
        </service>

 

Service2,传递的对象Student,需要实现 Parcelable 接口

⚠️注意:Student.java  Student.aidl 包名必须一致

 

Service2,传递的对象Student,需要实现 Parcelable 接口

package liudeli.service2;

import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {

    public int id;
    public String name;
    public int age;
    public String hobby;

    /**
     * 注意:构造方法的读取一定要和writeToParcel(写入)顺序一致
     * @param in
     */
    protected Student(Parcel in) {
        id  = in.readInt();
        name = in.readString();
        age = in.readInt();
        hobby = in.readString();
    }

    public Student(){

    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

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

    /**
     * 注意:构造方法的读取一定要和writeToParcel(写入)顺序一致
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeString(hobby);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}

 

Service2,传递的对象Student,还需要在.aidl文件中声明是Parcelable

// Student.aidl
package liudeli.service2;

// Declare any non-default types here with import statements

parcelable Student;

 

Service2,把逻辑都写在MyService

package liudeli.service2.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import liudeli.service2.IQueryStudent;
import liudeli.service2.Student;

public class MyService3 extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return new IQueryStudent.Stub() {
            @Override
            public Student queryStudentByID(int id) throws RemoteException {
                Student student = null;
                switch (id) {
                    case 1:
                        student = new Student();
                        student.id = 1;
                        student.name = "张三";
                        student.age = 20;
                        student.hobby = "喜欢画画";
                        break;
                    case 2:
                        student = new Student();
                        student.id = 2;
                        student.name = "李连杰";
                        student.age = 46;
                        student.hobby = "套路扎实,功夫厉害";
                        break;
                    default:
                        student = new Student();
                        student.id = 3;
                        student.name = "梁小龙";
                        student.age = 54;
                        student.hobby = "腿功厉害";
                        break;
                }
                return student;
            }
        };
    }
}

 

Service2,现在远程服务的AIdl语言接口的定义:

// IQueryStudent.aidl
package liudeli.service2;

// Declare any non-default types here with import statements

import liudeli.service2.Student;

interface IQueryStudent {

    /**
     * ID查询学生详情(学生对象)
     */
    Student queryStudentByID(int id);

}

 

注意:要把这些aidl文件,Student.java 文件 全部copy 到 -->Service1应用,要保证这些文件的命名 包名 两个工程 一模一样

 

 

----------------------------- 分割线 下面代码是 Service1 访问者相关的了  

 Service1,⚠️要把Service2 aidl 带包一起copy到Service1,保持aidl相关一模一样

 

Service1,绑定远程服务相关代码

package liudeli.service1;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Toast;

import liudeli.service2.IQueryStudent;
import liudeli.service2.Student;

public class RemoteActivity2 extends Activity {

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

        bindServiceAction();
    }

    /**
     * Activity初始化的时候绑定服务
     */
    private void bindServiceAction() {
        Intent intent = new Intent();
        intent.setAction("liudeli.service2.service.MyService3");
        // android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Component
        intent.setPackage("liudeli.service2");
        bindService(intent, connection, BIND_AUTO_CREATE);
    }

    private IQueryStudent iQueryStudent;

    /**
     * 定义服务连接对象,用于连接远程服务
     */
    private ServiceConnection connection = new ServiceConnection() {
        /**
         * 服务连接成功
         * @param name 可以获取到服务到完整信息
         * @param service 服务那边返回到IBinder接口
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 这里和本地服务不同,本地服务是强制类型转换,远程服务直接使用代理完成
            iQueryStudent = IQueryStudent.Stub.asInterface(service);
        }

        /**
         * 服务断开连接
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 查询远程服务里面的数据
     * @param view
     */
    public void queryRemoteService(View view) {
        if (null == iQueryStudent) {
            Toast.makeText(this, "绑定远程服务失败,无法查询", Toast.LENGTH_LONG).show();
            return;
        }
        try {
            Student student = iQueryStudent.queryStudentByID(2);
            Toast.makeText(this, "查询成功:" + student.toString(), Toast.LENGTH_LONG).show();
        } catch (RemoteException e) {
            e.printStackTrace();
            Toast.makeText(this, "远程服务失败 远程异常", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Activity结束时,记得一定要解绑服务,否则会报连接资源异常
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}

 

 Layout布局相关:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="queryRemoteService"
        android:text="查询远程服务"
        />

</LinearLayout>

 

操作: