Skip to content

Commit 87d7219

Browse files
committed
test: 在sample演示一种宿主加载插件View的方法
宿主在插件进程的HostAddPluginViewActivity调用宿主在主进程的MainProcessManagerReceiver。 主进程的MainProcessManagerReceiver调用主进程的manager对象。 主进程的manager对象调用PluginLoader接口的startPluginService方法。 插件中的HostAddPluginViewService通过共享宿主中的类HostAddPluginViewContainerHolder获取 宿主中同进程的HostAddPluginViewActivity对象,将自己构造的View设置到其中。 插件View由插件代码构造,此时View对象只能直接被同进程的宿主代码复用。 所以宿主中的HostAddPluginViewActivity要设置在插件进程。 由于Intent不能直接传递对象,所以通过HostAddPluginViewContainerHolder传递HostAddPluginViewActivity对象 到插件代码中,插件代码以HostAddPluginViewContainer接口操作该对象。 由于HostAddPluginViewActivity位于插件进程,而插件是由主进程中的manager对象 启动的,所以通过一个MainProcessManagerReceiver在主进程转调manager对象。 需要注意这并不是唯一的实现方案。方案的关键在于View构造时有自己的Context,构造后可以将对象添加到任意ViewGroup中。 #702
1 parent 7b02667 commit 87d7219

File tree

12 files changed

+198
-7
lines changed

12 files changed

+198
-7
lines changed

projects/sample/source/sample-constant/src/main/java/com/tencent/shadow/sample/constant/Constant.java

+1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ final public class Constant {
3030
public static final int FROM_ID_NOOP = 1000;
3131
public static final int FROM_ID_START_ACTIVITY = 1002;
3232
public static final int FROM_ID_CLOSE = 1003;
33+
public static final int FROM_ID_LOAD_VIEW_TO_HOST = 1004;
3334
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.tencent.shadow.sample.host.lib;
2+
3+
import android.view.View;
4+
5+
public interface HostAddPluginViewContainer {
6+
void addView(View view);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.tencent.shadow.sample.host.lib;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class HostAddPluginViewContainerHolder {
7+
public final static Map<Integer, HostAddPluginViewContainer> instances = new HashMap<>();
8+
}

projects/sample/source/sample-host/src/main/AndroidManifest.xml

+15-7
Original file line numberDiff line numberDiff line change
@@ -75,23 +75,26 @@
7575
android:theme="@android:style/Theme.DeviceDefault"
7676
android:usesCleartextTraffic="true"
7777
tools:ignore="GoogleAppIndexingWarning">
78-
<activity android:name="com.tencent.shadow.sample.host.MainActivity"
79-
>
78+
<activity android:name="com.tencent.shadow.sample.host.MainActivity">
8079
<intent-filter>
8180
<action android:name="android.intent.action.MAIN" />
8281

8382
<category android:name="android.intent.category.LAUNCHER" />
8483
</intent-filter>
8584
</activity>
85+
<activity
86+
android:name=".plugin_view.HostAddPluginViewActivity"
87+
android:process=":plugin" />
88+
8689
<provider
8790
android:authorities="${applicationId}.contentprovider.authority.dynamic"
8891
android:name="com.tencent.shadow.core.runtime.container.PluginContainerContentProvider"
8992
android:grantUriPermissions="true"
90-
android:process=":plugin"
91-
/>
92-
<service android:name="com.tencent.shadow.sample.host.PluginProcessPPS"
93-
android:process=":plugin"
94-
/>
93+
android:process=":plugin" />
94+
95+
<service
96+
android:name="com.tencent.shadow.sample.host.PluginProcessPPS"
97+
android:process=":plugin" />
9598
<service
9699
android:name="com.tencent.shadow.sample.host.Plugin2ProcessPPS"
97100
android:process=":plugin2" />
@@ -136,6 +139,11 @@
136139

137140
/>
138141
<!--dynamic activity注册 end -->
142+
<receiver android:name=".plugin_view.MainProcessManagerReceiver">
143+
<intent-filter>
144+
<action android:name="sample_host.manager.startPluginService" />
145+
</intent-filter>
146+
</receiver>
139147
</application>
140148

141149
</manifest>

projects/sample/source/sample-host/src/main/java/com/tencent/shadow/sample/host/MainActivity.java

+9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import android.widget.TextView;
3535

3636
import com.tencent.shadow.sample.constant.Constant;
37+
import com.tencent.shadow.sample.host.plugin_view.HostAddPluginViewActivity;
3738

3839

3940
public class MainActivity extends Activity {
@@ -91,6 +92,14 @@ public void onClick(View v) {
9192
});
9293
rootView.addView(startPluginButton);
9394

95+
Button startHostAddPluginViewActivityButton = new Button(this);
96+
startHostAddPluginViewActivityButton.setText("宿主添加插件View");
97+
startHostAddPluginViewActivityButton.setOnClickListener(v -> {
98+
Intent intent = new Intent(this, HostAddPluginViewActivity.class);
99+
startActivity(intent);
100+
});
101+
rootView.addView(startHostAddPluginViewActivityButton);
102+
94103
setContentView(rootView);
95104

96105
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.tencent.shadow.sample.host.plugin_view;
2+
3+
import android.app.Activity;
4+
import android.content.Intent;
5+
import android.os.Bundle;
6+
import android.view.View;
7+
import android.view.ViewGroup;
8+
import android.widget.Button;
9+
import android.widget.LinearLayout;
10+
import android.widget.TextView;
11+
12+
import com.tencent.shadow.sample.host.lib.HostAddPluginViewContainer;
13+
import com.tencent.shadow.sample.host.lib.HostAddPluginViewContainerHolder;
14+
15+
public class HostAddPluginViewActivity extends Activity implements HostAddPluginViewContainer {
16+
private ViewGroup mPluginViewContainer;
17+
18+
@Override
19+
protected void onCreate(Bundle savedInstanceState) {
20+
super.onCreate(savedInstanceState);
21+
22+
LinearLayout activityContentView = new LinearLayout(this);
23+
activityContentView.setOrientation(LinearLayout.VERTICAL);
24+
25+
LinearLayout.LayoutParams wrapContent = new LinearLayout.LayoutParams(
26+
ViewGroup.LayoutParams.WRAP_CONTENT,
27+
ViewGroup.LayoutParams.WRAP_CONTENT
28+
);
29+
30+
TextView note = new TextView(this);
31+
note.setLayoutParams(wrapContent);
32+
note.setText("需要先启动插件sample-plugin-app后,才能点下面的加载插件View");
33+
34+
Button loadButton = new Button(this);
35+
loadButton.setText("加载插件View");
36+
loadButton.setOnClickListener(this::loadPluginView);
37+
loadButton.setLayoutParams(wrapContent);
38+
39+
ViewGroup pluginViewContainer = new LinearLayout(this);
40+
pluginViewContainer.setLayoutParams(wrapContent);
41+
mPluginViewContainer = pluginViewContainer;
42+
43+
View[] views = {
44+
note,
45+
loadButton,
46+
pluginViewContainer
47+
};
48+
for (View view : views) {
49+
activityContentView.addView(view);
50+
}
51+
setContentView(activityContentView);
52+
}
53+
54+
private void loadPluginView(View view) {
55+
//简化逻辑,只允许点一次
56+
view.setEnabled(false);
57+
58+
//因为当前Activity和插件都在:plugin进程,不能直接操作主进程的manager对象,所以通过一个广播调用manager。
59+
Intent intent = new Intent();
60+
intent.setPackage(getPackageName());
61+
intent.setAction("sample_host.manager.startPluginService");
62+
63+
final int id = System.identityHashCode(this);
64+
HostAddPluginViewContainerHolder.instances.put(id, this);
65+
intent.putExtra("id", id);
66+
67+
sendBroadcast(intent);
68+
}
69+
70+
@Override
71+
public void addView(View view) {
72+
mPluginViewContainer.addView(view);
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.tencent.shadow.sample.host.plugin_view;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
7+
import com.tencent.shadow.sample.constant.Constant;
8+
import com.tencent.shadow.sample.host.HostApplication;
9+
10+
public class MainProcessManagerReceiver extends BroadcastReceiver {
11+
@Override
12+
public void onReceive(Context context, Intent intent) {
13+
HostApplication.getApp().getPluginManager()
14+
.enter(context, Constant.FROM_ID_LOAD_VIEW_TO_HOST, intent.getExtras(), null);
15+
}
16+
}

projects/sample/source/sample-manager/src/main/java/com/tencent/shadow/sample/manager/SamplePluginManager.java

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.content.Context;
2626
import android.content.Intent;
2727
import android.os.Bundle;
28+
import android.os.RemoteException;
2829
import android.view.LayoutInflater;
2930
import android.view.View;
3031

@@ -80,11 +81,27 @@ public void enter(final Context context, long fromId, Bundle bundle, final Enter
8081
onStartActivity(context, bundle, callback);
8182
} else if (fromId == Constant.FROM_ID_CLOSE) {
8283
close();
84+
} else if (fromId == Constant.FROM_ID_LOAD_VIEW_TO_HOST) {
85+
loadViewToHost(context, bundle);
8386
} else {
8487
throw new IllegalArgumentException("不认识的fromId==" + fromId);
8588
}
8689
}
8790

91+
private void loadViewToHost(final Context context, Bundle bundle) {
92+
Intent pluginIntent = new Intent();
93+
pluginIntent.setClassName(
94+
context.getPackageName(),
95+
"com.tencent.shadow.sample.plugin.app.lib.usecases.service.HostAddPluginViewService"
96+
);
97+
pluginIntent.putExtras(bundle);
98+
try {
99+
mPluginLoader.startPluginService(pluginIntent);
100+
} catch (RemoteException e) {
101+
throw new RuntimeException(e);
102+
}
103+
}
104+
88105
private void onStartActivity(final Context context, Bundle bundle, final EnterCallback callback) {
89106
final String pluginZipPath = bundle.getString(Constant.KEY_PLUGIN_ZIP_PATH);
90107
final String partKey = bundle.getString(Constant.KEY_PLUGIN_PART_KEY);

projects/sample/source/sample-plugin/sample-app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
android:authorities="${applicationId}.provider.test"
6868
android:name="com.tencent.shadow.sample.plugin.app.lib.usecases.provider.TestProvider" />
6969

70+
<service android:name=".usecases.service.HostAddPluginViewService" />
7071
</application>
7172

7273
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.tencent.shadow.sample.plugin.app.lib.usecases.service;
2+
3+
import android.app.IntentService;
4+
import android.content.Intent;
5+
import android.os.Handler;
6+
import android.os.Looper;
7+
import android.support.annotation.Nullable;
8+
import android.view.LayoutInflater;
9+
import android.view.View;
10+
11+
import com.tencent.shadow.sample.host.lib.HostAddPluginViewContainer;
12+
import com.tencent.shadow.sample.host.lib.HostAddPluginViewContainerHolder;
13+
import com.tencent.shadow.sample.plugin.app.lib.R;
14+
15+
public class HostAddPluginViewService extends IntentService {
16+
private final Handler uiHandler = new Handler(Looper.getMainLooper());
17+
18+
public HostAddPluginViewService() {
19+
super("HostAddPluginViewService");
20+
}
21+
22+
@Override
23+
protected void onHandleIntent(@Nullable Intent intent) {
24+
int id = intent.getIntExtra("id", 0);
25+
HostAddPluginViewContainer viewContainer
26+
= HostAddPluginViewContainerHolder.instances.remove(id);
27+
28+
uiHandler.post(() -> {
29+
View view = LayoutInflater.from(this).inflate(
30+
R.layout.layout_host_add_plugin_view, null, false);
31+
viewContainer.addView(view);
32+
});
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:layout_width="match_parent"
4+
android:layout_height="match_parent">
5+
6+
<TextView
7+
android:layout_width="wrap_content"
8+
android:layout_height="wrap_content"
9+
android:text="@string/host_add_plugin_view" />
10+
11+
<ImageView
12+
android:layout_width="wrap_content"
13+
android:layout_height="wrap_content"
14+
android:src="@drawable/collapse" />
15+
</LinearLayout>

projects/sample/source/sample-plugin/sample-app/src/main/res/values/strings.xml

+1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
<resources>
2020
<!-- Simple strings. -->
2121
<string name="app_name">Shadow主测试用例集合</string>
22+
<string name="host_add_plugin_view">这是插件中的string资源</string>
2223
</resources>
2324

0 commit comments

Comments
 (0)