基于RePlugin实现Android项目插件化(二)

上一篇介绍了宿主工程和插件工程接入RePlugin的过程,这一篇主要讲解下插件工程间以及插件与宿主工程间如何进行消息传递的。

由于插件化后,插件与宿主相当于两个不同apk,它们之间的通信不能依赖传统的方法,如EventBus,只能使用广播BroadcastReceiver或者AIDL来做。广播的用法跟普通app开发一样,唯一要注意的就是数据的传递,比如我们要传递对象,就需要将其转为如json字符串的形式再发送。接下来主要讲解的是如何使用AIDL来通信。

这一篇的demo逻辑跟上一篇稍有不同,LoadingActivity到HomeActivity,然后HomeActivity中点击登录跳转到LoginActivity,点击LoginActivity中的登录按钮返回到HomeActivity中,再点击登录按钮会根据是否AIDL中获取的User对象来Toast展示。

首先看下lib_base的目录结构,将AIDL接口定义在此module下。

ILoginService.aidl

// ILoginService.aidl
package com.example.lib_base.service.login;
import com.example.lib_base.service.login.user.User;
// Declare any non-default types here with import statements

interface ILoginService {
    boolean hasLogin();

    User getUserInfo();
}

User.java

package com.example.lib_base.service.login.user;

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

public class User implements Parcelable {
    private String name;
    private int age;

    public User() {}

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

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

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

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

User.aidl

// User.aidl
//这里的包名需要跟User.java包名一致
package com.example.lib_base.service.login.user;

parcelable User;

接着就是在ft_login中实现aidl接口

LoginServiceImpl.java

package com.example.ft_login.service.aidl;

import com.example.ft_login.utils.UserManager;
import com.example.lib_base.service.login.ILoginService;
import com.example.lib_base.service.login.user.User;

public class LoginServiceImpl extends ILoginService.Stub {
    @Override
    public boolean hasLogin() {
        return UserManager.getInstance().hasLogin();
    }

    @Override
    public User getUserInfo() {
        return UserManager.getInstance().getUser();
    }
}

然后在application中注册aidl

package com.example.ft_login;

import android.app.Application;

import com.example.ft_login.service.aidl.LoginServiceImpl;
import com.qihoo360.replugin.RePlugin;

public class LoginApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        //注册AIDL的具体实现
        RePlugin.registerPluginBinder(getPackageName() + ".interface", new LoginServiceImpl());
    }
}

最后看下HomeAcitivty中是如何启动LoginActivity并实现AIDL通信的。

package com.example.test;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;

import com.example.lib_base.service.login.ILoginService;
import com.example.lib_base.service.login.user.User;
import com.qihoo360.replugin.RePlugin;

public class HomeActivity extends FragmentActivity {

    private Button mLoginBtn;

    public static void start(Context context) {
        Intent intent = new Intent(context, HomeActivity.class);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home_layout);

        mLoginBtn = findViewById(R.id.loginBtn);

        mLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                IBinder binder = RePlugin.fetchBinder("ft_login", getPackageName() + ".interface");
                if (binder == null) return;
                ILoginService loginService =  ILoginService.Stub.asInterface(binder);

                //判断是否登录了
                try {
                    if (!loginService.hasLogin()) {
                        Intent intent = RePlugin.createIntent("ft_login", "com.example.ft_login.LoginActivity");
                        //相当于在宿主app主启动另外一个app中的activity,需要加这个参数
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        RePlugin.startActivity(HomeActivity.this, intent);
                    } else {
                        User user = loginService.getUserInfo();
                        Toast.makeText(HomeActivity.this, "已经登录了--->" + user.getName(), Toast.LENGTH_LONG).show();
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

这样就完成了宿主和插件工程之间的通信了。

demo逻辑很简单,主要是AIDL接口的定义,大家一定要注意包名,别搞错了。还有就是AIDL接口的注册和使用。

我们在实际项目开发中,可以遵循上面的开发思路来实现其他业务模块的插件化。