(二)Android 占位式/插桩式 插件化之`Service`

496 阅读4分钟

源码 github.com/itang01/jia…

与上一篇 (一)Android 占位式/插桩式 插件化之Activity 类似,由于插件中的Service没有运行环境,无法直接启动,需要依托Service代理类,通过插件的DexClassloader,去加载插件中的Service,然后,调用插件中的接口方法。

流程图:

第一步,加载插件

关键点

  1. DexClassloader (插件的DexClassloader,new 创建);
  2. AssetManager(反射创建,AssetManager 被 final 修饰);
  3. Resources (插件的Resources,new 创建);
  4. PackageManager、PackageInfo 和 ActivityInfo;
  5. 了解系统源码,反射的使用等等;

具体步骤

MainActivity

/**
 * 1、 加载插件
 * @param view
 */
public void loadPlugin(View view) {
    PluginManager.getInstance(this).loadPlugin();
}

PluginManager

/**
 * 加载插件 class and resources
 */
public void loadPlugin() {
    try {
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
        if (!file.exists()) {
            Log.d(TAG, "插件包 不存在...");
            return;
        }
        String pluginPaht = file.getAbsolutePath();
        // >>>> 下面是加载插件里面的 class
        // dexClassLoader需要一个缓存目录   /data/data/当前应用的包名/pDir
        File fileDir = context.getDir("pDir", Context.MODE_PRIVATE);
        // Activity class
        dexClassLoader = new DexClassLoader(pluginPaht, fileDir.getAbsolutePath(), null, context.getClassLoader());
        // >>>> 下面是加载插件里面的layout
        // 加载资源
        AssetManager assetManager = AssetManager.class.newInstance();
        // 我们要执行此方法,为了把插件包的路径 添加进去
        // public final int addAssetPath(String path)
        Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class); // 他是类类型 Class
        addAssetPathMethod.invoke(assetManager, pluginPaht); // 插件包的路径   pluginPaht
        Resources r = context.getResources(); // 宿主的资源配置信息
        // 特殊的 Resources,加载插件里面的资源的 Resources
        resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration()); // 参数2 3  资源配置信息
    } catch (Exception e) {
        e.printStackTrace();
    }
}

第二步,启动插件里面的Activity

MainActivity

/**
 * 2. 启动插件里面的Activity
 */
public void startPluginActivity(View view) {
    File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
    String path = file.getAbsolutePath();
    // 获取插件包 里面的 Activity
    PackageManager packageManager = getPackageManager();
    PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
    ActivityInfo activityInfo = packageInfo.activities[0];
    // 占位  代理Activity
    Intent intent = new Intent(this, ProxyActivity.class);
    intent.putExtra("className", activityInfo.name);
    startActivity(intent);
}

ProxyActivity

/**
 * 代理的Activity 代理/占位 插件里面的 Activity
 */
public class ProxyActivity extends Activity {

    @Override
    public Resources getResources() {
        return PluginManager.getInstance(this).getResources();
    }

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance(this).getClassLoader();
    }

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

        // 真正的加载 插件里面的 Activity
        String className = getIntent().getStringExtra("className");

        try {
            Class mPluginActivityClass = getClassLoader().loadClass(className);
            // 实例化 插件包里面的 Activity
            Constructor constructor = mPluginActivityClass.getConstructor(new Class[]{});
            Object mPluginActivity = constructor.newInstance(new Object[]{});

            ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;

            // 注入
            activityInterface.insertAppContext(this);

            Bundle bundle = new Bundle();
            bundle.putString("appName", "我是宿主传递过来的信息");

            // 执行插件里面的onCreate方法
            activityInterface.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");

        Intent proxyIntent = new Intent(this, ProxyActivity.class);
        proxyIntent.putExtra("className", className); // 包名+TestActivity

        //让 Activity 进栈(本例子中的 TestActivity )
        super.startActivity(proxyIntent);
    }
    
    @Override
    public ComponentName startService(Intent service) {
        String className = service.getStringExtra("className");

        Intent intent = new Intent(this, ProxyService.class);
        intent.putExtra("className", className);
        return super.startService(intent);
    }
}

ProxyService

public class ProxyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String className = intent.getStringExtra("className");

        // com.netease.plugin_package.TestService

        try {
            Class mTestServiceClass = PluginManager.getInstance(this).getClassLoader().loadClass(className);
            Object mTestService = mTestServiceClass.newInstance();

            ServiceInterface serviceInterface = (ServiceInterface) mTestService;

            // 注入 组件环境
            serviceInterface.insertAppContext(this);

            serviceInterface.onStartCommand(intent, flags, startId);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

第三步,启动插件里面的Service

PluginActivity

public class PluginActivity extends BaseActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.plugin_main);
                // this 会报错,因为插件没有安装,也没有组件的环境,所以必须使用宿主环境
        Toast.makeText(appActivity, "我是插件", Toast.LENGTH_SHORT).show();
        findViewById(R.id.bt_start_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(appActivity, TestActivity.class));
            }
        });
        
        findViewById(R.id.bt_start_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(new Intent(appActivity, TestService.class));
            }
        });
    }

}

BaseActivity

public class BaseActivity extends Activity implements ActivityInterface {

    public Activity appActivity; // 宿主的环境

    @Override
    public void insertAppContext(Activity appActivity) {
        this.appActivity = appActivity;
    }

    public void setContentView(int resId) {
        appActivity.setContentView(resId);
    }

    public View findViewById(int layoutId) {
        return appActivity.findViewById(layoutId);
    }

    @Override
    public void startActivity(Intent intent) {
        Intent intentNew = new Intent();
        intentNew.putExtra("className", intent.getComponent().getClassName());
        appActivity.startActivity(intentNew);
    }
     
    @Override
    public ComponentName startService(Intent service) {
        Intent intentNew = new Intent();
        intentNew.putExtra("className", service.getComponent().getClassName()); // TestService 全类名
        return appActivity.startService(intentNew);
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() {

    }
}

ActivityInterface

/**
 * 标准
 */
public interface ActivityInterface {

    /**
     * 把宿主(app)的环境 给 插件
     * @param appActivity
     */
    void insertAppContext(Activity appActivity);

    // 生命周期方法
    void onCreate(Bundle savedInstanceState);

    void onStart();

    void onResume();

    void onDestroy();

}

TestService

public class TestService extends BaseService {

    private static final String TAG= TestService.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        // 开启子线程,执行耗时任务
        new Thread(new Runnable(){

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        Log.d(TAG, "插件里面的服务 正在执行中....");
                    }
                }
            }

        }).start();

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

BaseService

public class BaseService extends Service implements ServiceInterface {

    public Service appService;

    /**
     * 把宿主(app)的环境  给  插件
     * @param appService
     */
    public void insertAppContext(Service appService){
        this.appService = appService;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {

    }

    @SuppressLint("WrongConstant")
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return 0;
    }

    @Override
    public void onDestroy() {

    }
}

ServiceInterface

public interface ServiceInterface {

    /**
     * 把宿主(app)的环境  给  插件
     * @param appService
     */
    void insertAppContext(Service appService);

    public void onCreate();

    public int onStartCommand(Intent intent, int flags, int startId);

    public void onDestroy();

}

项目结构