Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nnjun committed Mar 17, 2024
0 parents commit 0f75106
Show file tree
Hide file tree
Showing 143 changed files with 9,217 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
/.idea/*
.DS_Store*
/build
/captures
.externalNativeBuild
.cxx
local.properties
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2024, Milk
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# BlackShadow
接入shadow需要大量的二次开发工作,其实一般小型项目其实并不想关心太多的逻辑和管理,只想开袋即食,奈何Shadow也并没有提供这方面的能力,所有开发者接入都需要二次开发才可以使用,包括本次我自己使用也是,所以花了点时间在Shadow的基础上包装了一层,几乎不需要任何二次开发,即可通过几个简单的接口使用与管理Shadow,屏蔽了Shadow所有的技术细节。

## 依赖安装
```
git clone https://github.com/Tencent/Shadow.git
或者
git clone https://github.com/nnjun/Shadow.git (建议使用这个)
```

拉下仓库后,进入仓库目录,将Shadow发布到本地maven仓库。
```
./gradlew publish
```


## 使用方法
在Application#attachBaseContext中初始化
```
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
BlackShadow.get().init(this);
}
```

安装与启动
```
InstallResult installResult = BlackShadow.get().installPlugin("plugin-key", new File(pluginAPk));
if (installResult.isSuccess()) {
Intent intent = new Intent();
intent.xxxxxxxxxxxxx
BlackShadow.get().launchPlugin("plugin-key", intent);
}
```

其余接口
```
// 仅启动application
public boolean callApplication(String pluginKey)
// 获取所有已安装的plugin
public List<InstalledPlugin> getInstalledPlugins()
// 获取某个已安装的plugin
public InstalledPlugin getInstalledPlugin(String pluginKey);
// 卸载某个plugin
public void uninstallPlugin(String pluginKey)
// 停止某个plugin
public void stopPlugin(String pluginKey)
// 停止所有plugin
public void stopAllPlugin()
// 获取正在运行的plugin
public List<RunningPlugin> getRunningPlugins()
```

## 基于Shadow的技术方案
BlackShadow使用的是非动态方案,支持同时最多10个插件运行,分别都是各自单独的进程。install与launch都有boolean返回值,可反馈出插件是否安装/启动成功。

## 插件包名与宿主包名不相同的需求
由于Shadow内核要求,plugin与宿主的包名必须一致,否则会出现问题,然而我方产品可能会存在不同的渠道包不同的包名,但是插件没有必要分开很多份,所以BlackShadow是支持插件与宿主不同的包名,处理的方法是在install时如果不一样,BlackShaodw会自动将插件的包名改成与宿主相同,不需要额外开发,直接进行install即可,BlackShadow会自动处理该问题。

假如你也有这个需求,则需要自行修改Shaodw内核,将这个检测去除,或者安装我这个内核。其余内容都是与官方保持一致。

https://github.com/nnjun/Shadow/commit/4f769afdd4e86814fa09d1ef9b19d6ea68f175fd

### 不是修改了包名了吗?为什么还需要去除检测?
因为Shadow的包名基准是由Shadow编译时生成的com.tencent.shadow.core.manifest_parser.PluginManifest文件来确定,BlackShadow只会修改Manifest中的包名,并不会修改PluginManifest.class内的硬编码包名,所以需要去除检测,否则无法运行。

如果你没有以上的场景,那么请无视上面这一段内容,直接使用即可。
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
35 changes: 35 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
plugins {
id 'com.android.application'
}
android {
namespace rootProject.ext.hostPackageName
compileSdk rootProject.ext.compileSdkVersion

defaultConfig {
applicationId rootProject.ext.hostPackageName
minSdk rootProject.ext.minSdkVersion
targetSdk rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.4.0'

implementation project(":black-shadow")
}
28 changes: 28 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class top.niunaijun.shadow.common.** { *; }

-keep class top.niunaijun.shadow.BlackShadow {
public *;
}
-keep class top.niunaijun.shadow.container.** { *; }
-keep class com.tencent.shadow.** { *; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package top.niunaijun.blackshadow;

import android.content.Context;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("top.niunaijun.blackshadow", appContext.getPackageName());
}
}
30 changes: 30 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:name="top.niunaijun.blackshadow.BlackShadowApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BlackShadow"
tools:targetApi="31">

<activity
android:name="top.niunaijun.blackshadow.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BlackShadow">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Binary file added app/src/main/assets/plugin.apk
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package top.niunaijun.blackshadow;

import android.app.Application;
import android.content.Context;

import top.niunaijun.blackshadow.utils.FileUtils;
import top.niunaijun.shadow.BlackShadow;

/**
* Created by Milk on 2024/3/17.
* * ∧_∧
* (`・ω・∥
* 丶 つ0
* しーJ
* 此处无Bug
*/
public class BlackShadowApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
BlackShadow.get().init(this);
}

@Override
public void onCreate() {
super.onCreate();
}
}
70 changes: 70 additions & 0 deletions app/src/main/java/top/niunaijun/blackshadow/MainActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package top.niunaijun.blackshadow;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.io.File;
import java.io.IOException;

import top.niunaijun.blackshadow.utils.FileUtils;
import top.niunaijun.shadow.BlackShadow;
import top.niunaijun.shadow.common.InstallResult;
import top.niunaijun.shadow.common.InstalledPlugin;
import top.niunaijun.shadow.host.R;

public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";

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

findViewById(R.id.btn_start).setOnClickListener(v -> {
Toast.makeText(this, "启动中", Toast.LENGTH_SHORT).show();
new Thread(this::handlePlugin).start();
});
}

private void handlePlugin() {
File plugin = new File(getFilesDir(), "plugin.apk");
copyAssetsPlugin(plugin);

// 允许访问宿主的白名单类
String[] hostWhiteList = new String[]{
"com.tencent.*",
"okhttp3",
"okhttp3.*",
"okhttp3.**",
"com.google.**"
};

Intent launcher = new Intent();
launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launcher.setClassName("top.niunaijun.shadow.host", "top.niunaijun.shadow.plugin.MainActivity");

String key = "app-plugin";
InstalledPlugin installedPlugin = BlackShadow.get().getInstalledPlugin(key);
if (installedPlugin == null) {
InstallResult installResult = BlackShadow.get().installPlugin(key, plugin, hostWhiteList, launcher);
Log.d(TAG, "installPlugin: " + installResult);
installedPlugin = BlackShadow.get().getInstalledPlugin(key);
}
BlackShadow.get().launchPlugin(installedPlugin.pluginKey, installedPlugin.launcher);
}

private void copyAssetsPlugin(File target) {
try {
if (target.exists()) {
return;
}
FileUtils.copyFile(this.getAssets().open("plugin.apk"), target);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
Loading

0 comments on commit 0f75106

Please sign in to comment.