diff --git a/README.md b/README.md index 323d8430..a6946dd8 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@

[![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/Qihoo360/RePlugin/blob/master/LICENSE) -[![Release Version](https://img.shields.io/badge/release-2.2.4-brightgreen.svg)](https://github.com/Qihoo360/RePlugin/releases) - +[![Release Version](https://img.shields.io/badge/release-2.3.1-brightgreen.svg)](https://github.com/Qihoo360/RePlugin/releases) +## 活动通知 +移动技术最新活动通知:9月1号360移动技术开放日 http://t.cn/RDiNru9 ## RePlugin —— A flexible, stable, easy-to-use Android Plug-in Framework @@ -55,6 +56,9 @@ At present, almost **all Apps with hundreds of millions users from 360, and many ## Our Vision Make RePlugin be used in all kinds of ordinary Apps; and provide stable, flexible, liberal plug-ins which adopt for both large and small projects. +## Latest features +Solved the Android P (Android 9.0) related adaptation issues, fully support the official version of Android P (Android 9.0). + ## RePlugin Architecture

diff --git a/README_CN.md b/README_CN.md index 63bc3517..85b84bc4 100644 --- a/README_CN.md +++ b/README_CN.md @@ -6,7 +6,7 @@ [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/Qihoo360/RePlugin/blob/master/LICENSE) -[![Release Version](https://img.shields.io/badge/release-2.2.4-brightgreen.svg)](https://github.com/Qihoo360/RePlugin/releases) +[![Release Version](https://img.shields.io/badge/release-2.3.1-brightgreen.svg)](https://github.com/Qihoo360/RePlugin/releases) @@ -57,6 +57,10 @@ RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件 让插件化能**飞入寻常应用家**,做到稳定、灵活、自由,大小项目兼用。 +## 最新特性 + +解决了Android P(Android 9.0)相关适配问题,全面支持Android P(Android 9.0)正式版。 + ## RePlugin 架构图

diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..0f37f936 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +export RP_BASE_DIR=$(cd "$(dirname "$0")"; pwd) + +export TARGET_PROJECTS=( +replugin-host-gradle +replugin-host-library +replugin-plugin-gradle +replugin-plugin-library +) + +__gradle_exec(){ if [[ -x gradlew ]];then ./gradlew ${@}; else gradle ${@}; fi; } + +__rp_deploy_project(){ + [[ ! -d ${1} ]] && echo ">>> INVALID ${1}!!! <<<" && return + # execute deploying + echo ">>> ${1} <<<" && __gradle_exec -p ${1} clean bintrayUpload + # revert changed files + git checkout ${1} +} + +rp_deploy(){ + local current=`pwd` && cd ${RP_BASE_DIR} + # saving all changes: git stash save "saving stash for deploying!!!" + # deploy + for p in ${TARGET_PROJECTS}; do __rp_deploy_project ${RP_BASE_DIR}/${p}; done + # revert local changes: git revert --hard HEAD; git stash pop + local current=`pwd` +} + +rp_test(){ + local projects=( + # replugin-sample/host/app + replugin-sample/host + # replugin-sample/plugin/plugin-demo1/app + replugin-sample/plugin/plugin-demo1 + # replugin-sample/plugin/plugin-demo2/app + replugin-sample/plugin/plugin-demo2 + # replugin-sample/plugin/plugin-demo3-kotlin/app + replugin-sample/plugin/plugin-demo3-kotlin + # replugin-sample/plugin/plugin-webview/app + replugin-sample/plugin/plugin-webview + # replugin-sample-extra/fresco/FrescoHost/app + replugin-sample-extra/fresco/FrescoHost + # replugin-sample-extra/fresco/FrescoPlugin/app + replugin-sample-extra/fresco/FrescoPlugin + ) + local log=${RP_BASE_DIR}/build/rp_test.log && [[ -f $log ]] && rm -f $log + local current=`pwd` + for p in ${projects}; do + echo -e ">>> BUILDING ${p}" + p=${RP_BASE_DIR}/${p} && __gradle_exec -p ${p} clean asDebug 2>/dev/null >> ${log} && echo "SUCCEED"; + ls -l ${p}/app/build/outputs/apk + done + cd ${current} +} + +# grep --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,build,.gradle} -inr '2\.3\.0' . \ No newline at end of file diff --git a/replugin-host-gradle/build.gradle b/replugin-host-gradle/build.gradle index d698fbc2..6f08f2e8 100644 --- a/replugin-host-gradle/build.gradle +++ b/replugin-host-gradle/build.gradle @@ -30,6 +30,7 @@ buildscript { jcenter() mavenCentral() } + dependencies { // classpath 'com.android.tools.build:gradle:2.1.0' // 将项目发布到JCenter 所需要的jar 添加依赖 @@ -38,14 +39,6 @@ buildscript { } } -group = 'com.qihoo360.replugin' // 组名 -String classPath = ".src.main.groovy.com.qihoo360.replugin.gradle.host.AppConstant".replace(".", java.io.File.separator) -String verPath = "${project.projectDir}" + classPath + ".groovy" -String verLine = new File(verPath).filterLine { it =~ /def static final VER =/ } -version = "${verLine.split("\"")[1]}" // 版本 -//红色醒目打印显示版本号 -java.lang.System.err.println "version=${version}" - dependencies { compile 'com.android.tools.build:gradle:2.1.3' compile 'org.json:json:20160212' @@ -57,60 +50,7 @@ dependencies { compile 'com.google.gradle:osdetector-gradle-plugin:1.2.1' compile 'net.dongliu:apk-parser:2.2.0' - -} - - - -if (project.hasProperty("android")) { // Android libraries - task sourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.srcDirs - } - - task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } -} else { // Java libraries - task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource - } -} - -// 强制 Java/JavaDoc 等的编码为 UTF-8 -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" } -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - - - -// add javadoc/source jar tasks as artifacts -artifacts { - archives sourcesJar, javadocJar -} - -publishing { - publications { - mavenJava(MavenPublication) { - if (plugins.hasPlugin('war')) { - from components.web - } else { - from components.java - } - - artifact sourcesJar - artifact javadocJar - } - } -} - -apply from: 'bintray.gradle' - - +project.ext.RP_ARTIFACT_ID = 'replugin-host-gradle' +apply from: '../rp-publish.gradle' \ No newline at end of file diff --git a/replugin-host-gradle/src/main/groovy/com/qihoo360/replugin/gradle/host/AppConstant.groovy b/replugin-host-gradle/src/main/groovy/com/qihoo360/replugin/gradle/host/AppConstant.groovy index d0c1dada..a671b1af 100644 --- a/replugin-host-gradle/src/main/groovy/com/qihoo360/replugin/gradle/host/AppConstant.groovy +++ b/replugin-host-gradle/src/main/groovy/com/qihoo360/replugin/gradle/host/AppConstant.groovy @@ -23,7 +23,7 @@ package com.qihoo360.replugin.gradle.host class AppConstant { /** 版本号 */ - def static final VER = "2.3.0" + def static final VER = "${RP_VERSION}" /** 打印信息时候的前缀 */ def static final TAG = "< replugin-host-v${VER} >" diff --git a/replugin-host-library/replugin-host-lib/build.gradle b/replugin-host-library/replugin-host-lib/build.gradle index fbfa6db4..1280694a 100644 --- a/replugin-host-library/replugin-host-lib/build.gradle +++ b/replugin-host-library/replugin-host-lib/build.gradle @@ -19,11 +19,6 @@ */ apply plugin: 'com.android.library' - -version = "2.3.0" - -group = 'com.qihoo360.replugin' // 组名 - android { compileSdkVersion 25 buildToolsVersion '25.0.2' @@ -54,4 +49,5 @@ dependencies { provided 'com.android.support:support-v4:25.2.0' } -apply from: 'bintray.gradle' +project.ext.RP_ARTIFACT_ID = 'replugin-host-lib' +apply from: '../../rp-publish.gradle' diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/Loader.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/Loader.java index 518bc8e7..aa054aa3 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/Loader.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/Loader.java @@ -17,6 +17,7 @@ package com.qihoo360.loader2; import android.content.Context; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -382,12 +383,18 @@ final boolean loadDex(ClassLoader parent, int load) { private void regReceivers() throws android.os.RemoteException { String plugin = mPluginObj.mInfo.getName(); + Map> map = ManifestParser.INS.getReceiverFilterMap(plugin); + + if (map == null || map.size() == 0) { + return; + } + if (mPluginHost == null) { mPluginHost = getPluginHost(); } if (mPluginHost != null) { - mPluginHost.regReceiver(plugin, ManifestParser.INS.getReceiverFilterMap(plugin)); + mPluginHost.regReceiver(plugin, map); } } diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PluginProcessMain.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PluginProcessMain.java index 4751e567..c1ade55c 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PluginProcessMain.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PluginProcessMain.java @@ -16,17 +16,15 @@ package com.qihoo360.loader2; -import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; +import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import com.qihoo360.i.IPluginManager; -import com.qihoo360.mobilesafe.api.Tasks; -import com.qihoo360.replugin.RePluginInternal; -import com.qihoo360.replugin.base.AMSUtils; import com.qihoo360.replugin.base.IPC; import com.qihoo360.replugin.component.process.PluginProcessHost; import com.qihoo360.replugin.helper.LogDebug; @@ -44,6 +42,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.qihoo360.replugin.helper.LogDebug.LOG; import static com.qihoo360.replugin.helper.LogDebug.MAIN_TAG; @@ -51,179 +50,66 @@ import static com.qihoo360.replugin.helper.LogRelease.LOGR; /** + * 进程管理类 * @author RePlugin Team */ public class PluginProcessMain { - private static final int STATE_UNUSED = 0; - - private static final int STATE_ALLOCATED = 1; - - private static final int STATE_RUNNING = 2; - -// private static final int STATE_MARKED = 3; - - private static final int STATE_STOPED = 4; - + public static final String TAG = PluginProcessMain.class.getSimpleName(); /** - * + * 常驻进程使用,非常驻进程为null buyuntao */ private static IPluginHost sPluginHostLocal; /** - * + * 非常驻进程使用,常驻进程为null,用于非常驻进程连接常驻进程 buyuntao */ private static IPluginHost sPluginHostRemote; - /** - * + * 提供binder保存功能,无其他逻辑 */ static HashMap sBinders = new HashMap(); - /** - * TODO 待重构 PROCESSES & ALL - */ - private static final ProcessRecord PROCESSES[] = new ProcessRecord[Constant.STUB_PROCESS_COUNT]; - - /** - * TODO 待重构 PROCESSES & ALL - * processName -> ProcessClientRecord + * 当前运行的所有进程的列表(常驻进程除外) */ private static final Map ALL = new HashMap(); - - static { - for (int i = 0; i < Constant.STUB_PROCESS_COUNT; i++) { - ProcessRecord r = new ProcessRecord(i, STATE_UNUSED); - PROCESSES[i] = r; - } - } - /** - * + * ALL的读写锁,用于并发时的性能提升 */ + private static final ReentrantReadWriteLock PROCESS_CLIENT_LOCK = new ReentrantReadWriteLock(); private static final Object COOKIE_LOCK = new Object(); - - /** - * - */ private static boolean sPersisistCookieInitialized; - /** * 常驻进程cookie,用来控制卫士进程组是否需要退出等 */ private static long sPersisistCookie; - static final int CHECK_STAGE1_DELAY = 17 * 1000; - - private static final int CHECK_STAGE2_DELAY = 11 * 1000; - - private static final int CHECK_STAGE3_DELAY = 3 * 1000; - - private static final Runnable CHECK = new Runnable() { - - @Override - public void run() { - doPluginProcessLoop(); - } - }; - - private static final class ProcessRecord { - - final int index; - - int state; - - long mobified; - - String plugin; - - int pid; - - IBinder binder; - - IPluginClient client; - - int activities; - - int services; - - int binders; - - ProcessRecord(int index, int state) { - this.index = index; - this.state = state; - } - - void allocate(String plugin) { - this.state = STATE_ALLOCATED; - this.mobified = System.currentTimeMillis(); - this.plugin = plugin; - this.pid = 0; - this.binder = null; - this.client = null; - this.activities = 0; - this.services = 0; - this.binders = 0; - } - - void setRunning(int pid) { - this.state = STATE_RUNNING; - this.pid = pid; - } - - void setClient(IBinder binder, IPluginClient client) { - this.binder = binder; - this.client = client; - } - -// void setMarked() { -// this.state = STATE_MARKED; -// } - - void setStoped() { - this.state = STATE_STOPED; - this.pid = 0; - this.binder = null; - this.client = null; - } - - @Override - public String toString() { - if (LOG) { - return super.toString() + " {index=" + index + " state=" + state + " mobified=" + mobified + " plugin=" + plugin + " pid=" + pid + " binder=" + binder + " client=" + client - + " activities=" + activities + " services=" + services + " binders=" + binders + "}"; - } - return super.toString(); - } - } - /** - * 常驻进程使用 + * 进程记录,用于进程及进程列表管理 buyuntao */ private static final class ProcessClientRecord implements IBinder.DeathRecipient { - String name; - + String name; //进程名称 String plugin; - int pid; - int index; - IBinder binder; - IPluginClient client; + PluginManagerServer pluginManager; //单个进程的插件管理类 - private final PluginManagerServer mManagerServer; - - // FIXME 不建议这么传递,但为了在死亡周期中使用,不得已而为之 - public ProcessClientRecord(PluginManagerServer pms) { - mManagerServer = pms; + public ProcessClientRecord(String process, String plugin, int pid, int index, IBinder binder, IPluginClient client, PluginManagerServer pms) { + this.name = process; + this.plugin = plugin; + this.pid = pid; + this.index = index; + this.binder = binder; + this.client = client; + this.pluginManager = pms; } @Override public void binderDied() { - handleBinderDied(this, mManagerServer); + handleBinderDied(this); } @Override @@ -238,18 +124,6 @@ public IPluginClient getClient() { return client; } } - - static final void reportStatus() { - for (ProcessRecord r : PROCESSES) { - if (r.binder == null) { - continue; - } - if (LOG) { - LogDebug.i(PLUGIN_TAG, "i=" + r.index + " p=" + r.plugin + " a=" + r.activities + " s=" + r.services + " b=" + r.binders); - } - } - } - static final String dump() { // 1.dump Activity映射表, service列表 @@ -324,10 +198,7 @@ static final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println(r); } writer.println(); - writer.println("--- PROCESSES.length = " + PROCESSES.length + " ---"); - for (ProcessRecord r : PROCESSES) { - writer.println(r); - } + StubProcessManager.dump(writer); writer.println(); // writer.println("--- USED_PLUGINS.size = " + USED_PLUGINS.size() + " ---"); // for (ProcessPluginInfo r : USED_PLUGINS.values()) { @@ -360,8 +231,6 @@ static final void installHost(IPluginHost host) { */ static final void connectToHostSvc() { Context context = PMF.getApplicationContext(); - - // IBinder binder = PluginProviderStub.proxyFetchHostBinder(context); if (LOG) { LogDebug.d(PLUGIN_TAG, "host binder = " + binder); @@ -373,11 +242,8 @@ static final void connectToHostSvc() { } System.exit(1); } - - // try { binder.linkToDeath(new IBinder.DeathRecipient() { - @Override public void binderDied() { if (LOGR) { @@ -430,8 +296,11 @@ public void binderDied() { // 注册该进程信息到“插件管理进程”中 PMF.sPluginMgr.attach(); } - - // @hide 内部框架使用 + /** + * sPluginHostLocal 常驻进程使用,非常驻进程为null buyuntao + * sPluginHostRemote 非常驻进程使用,常驻进程为null,用于非常驻进程连接常驻进程 buyuntao + * @hide 内部框架使用 + */ public static final IPluginHost getPluginHost() { if (sPluginHostLocal != null) { return sPluginHostLocal; @@ -471,36 +340,39 @@ static final long getPersistentCookie() { * @param info * @return */ - static final IPluginClient probePluginClient(String plugin, int process, PluginBinderInfo info) { - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (process == IPluginManager.PROCESS_UI) { - if (!TextUtils.equals(r.plugin, Constant.PLUGIN_NAME_UI)) { - continue; - } + static final IPluginClient probePluginClient(final String plugin, final int process, final PluginBinderInfo info) { + return readProcessClientLock(new Action() { + @Override + public IPluginClient call() { + for (ProcessClientRecord r : ALL.values()) { + if (process == IPluginManager.PROCESS_UI) { + if (!TextUtils.equals(r.plugin, Constant.PLUGIN_NAME_UI)) { + continue; + } - /* 是否是用户自定义进程 */ - } else if (PluginProcessHost.isCustomPluginProcess(process)) { - if (!TextUtils.equals(r.plugin, getProcessStringByIndex(process))) { - continue; + /* 是否是用户自定义进程 */ + } else if (PluginProcessHost.isCustomPluginProcess(process)) { + if (!TextUtils.equals(r.plugin, getProcessStringByIndex(process))) { + continue; + } + } else { + if (!TextUtils.equals(r.plugin, plugin)) { + continue; + } } - } else { - if (!TextUtils.equals(r.plugin, plugin)) { - continue; + if (!isBinderAlive(r)) { + return null; } + if (!r.binder.pingBinder()) { + return null; + } + info.pid = r.pid; + info.index = r.index; + return r.client; } - if (!isBinderAlive(r)) { - return null; - } - if (!r.binder.pingBinder()) { - return null; - } - info.pid = r.pid; - info.index = r.index; - return r.client; + return null; } - } - return null; + }); } /** @@ -518,153 +390,160 @@ private static String getProcessStringByIndex(int index) { * @param info * @return */ - static final IPluginClient probePluginClientByPid(int pid, PluginBinderInfo info) { - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (r.pid != pid) { - continue; - } - if (!isBinderAlive(r)) { - return null; - } - if (!r.binder.pingBinder()) { - return null; + static final IPluginClient probePluginClientByPid(final int pid, final PluginBinderInfo info) { + return readProcessClientLock(new Action() { + @Override + public IPluginClient call() { + for (ProcessClientRecord r : ALL.values()) { + if (r.pid != pid) { + continue; + } + if (!isBinderAlive(r)) { + return null; + } + if (!r.binder.pingBinder()) { + return null; + } + info.pid = r.pid; + info.index = r.index; + return r.client; } - info.pid = r.pid; - info.index = r.index; - return r.client; + return null; } - } - return null; + }); } /** + * 发送intent给进程 buyuntao * @param target * @param intent */ - static final void sendIntent2Process(String target, Intent intent, boolean sync) { - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (target == null || target.length() <= 0) { - // 所有 - } else if (TextUtils.equals(r.name, target)) { - // 特定目标 - } else { - continue; - } - if (!isBinderAlive(r)) { - continue; - } - if (LOG) { - LogDebug.d(PLUGIN_TAG, "sendIntent2Process name=" + r.name); - } - try { - if (sync) { - r.client.sendIntentSync(intent); + static final void sendIntent2Process(final String target, Intent intent, boolean sync) { + final Map map = readProcessClientLock(new Action>() { + @Override + public Map call() { + Map map = new HashMap<>(); + for (ProcessClientRecord r : ALL.values()) { + if (TextUtils.isEmpty(target)) { + // 所有 + } else if (TextUtils.equals(r.name, target)) { + // 特定目标 } else { - r.client.sendIntent(intent); - } - } catch (Throwable e) { - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "s.i2pr e: n=" + r.name + ": " + e.getMessage(), e); + continue; } + map.put(r.name, r); } + return map; } - } + }); + sendIntent2Client(map, intent, sync); } /** + * 发送intent给指定插件 buyuntao * @param target * @param intent */ - static final void sendIntent2Plugin(String target, Intent intent, boolean sync) { - if (target == null || target.length() <= 0) { + static final void sendIntent2Plugin(final String target, Intent intent, boolean sync) { + if (TextUtils.isEmpty(target)) { return; } - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (TextUtils.equals(r.plugin, target)) { - // 特定目标 - } else { - continue; - } - if (!isBinderAlive(r)) { - continue; - } - try { - if (sync) { - r.client.sendIntentSync(intent); + final Map map = readProcessClientLock(new Action>() { + @Override + public Map call() { + final Map map = new HashMap<>(1 << 4); + for (ProcessClientRecord r : ALL.values()) { + if (TextUtils.equals(r.plugin, target)) { + // 特定目标 } else { - r.client.sendIntent(intent); - } - } catch (Throwable e) { - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "s.i2pl e: " + e.getMessage(), e); + continue; } + map.put(r.name, r); + } + return map; + } + }); + sendIntent2Client(map, intent, sync); + } + /** + * 发送intent给进程Client buyuntao + * @param map + * @param intent + */ + private static void sendIntent2Client(Map map, Intent intent, boolean sync){ + for (ProcessClientRecord r : map.values()) { + if (!isBinderAlive(r)) { + continue; + } + try { + if (sync) { + r.client.sendIntentSync(intent); + } else { + r.client.sendIntent(intent); + } + } catch (Throwable e) { + if (LOGR) { + LogRelease.e(PLUGIN_TAG, "p.p sic e: " + e.getMessage(), e); } } } } /** + * 判断进程是否存活 buyuntao * @param name * @return */ - static final boolean isProcessAlive(String name) { - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (!TextUtils.equals(r.name, name)) { - continue; - } + static final boolean isProcessAlive(final String name) { + if (TextUtils.isEmpty(name)){ + return false; + } + return readProcessClientLock(new Action() { + @Override + public Boolean call() { + ProcessClientRecord r = ALL.get(name); return isBinderAlive(r); } - } - return false; + }); } private static boolean isBinderAlive(ProcessClientRecord r) { - if (r == null) { - return false; - } - if (r.binder == null || r.client == null) { - return false; - } - if (!r.binder.isBinderAlive()) { - return false; - } - return true; + return r != null && r.binder != null && r.client != null && r.binder.isBinderAlive(); } static final int sumActivities() { - int sum = 0; - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (!isBinderAlive(r)) { - continue; - } - int rc = 0; - try { - rc = r.client.sumActivities(); - if (rc == -1) { - return -1; + return readProcessClientLock(new Action() { + @Override + public Integer call() { + int sum = 0; + for (ProcessClientRecord r : ALL.values()) { + if (!isBinderAlive(r)) { + continue; } - sum += rc; - } catch (Throwable e) { - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "ppm.sa e: " + e.getMessage(), e); + int rc = 0; + try { + rc = r.client.sumActivities(); + if (rc == -1) { + return -1; + } + sum += rc; + } catch (Throwable e) { + if (LOGR) { + LogRelease.e(PLUGIN_TAG, "ppm.sa e: " + e.getMessage(), e); + } } } + return sum; } - } - return sum; + }); } /** - * @deprecated 待优化 - * 插件进程调度 * @param plugin * @param process * @return + * @deprecated 待优化 + * 插件进程调度 */ @Deprecated static final int allocProcess(String plugin, int process) { @@ -683,43 +562,37 @@ static final int allocProcess(String plugin, int process) { } return IPluginManager.PROCESS_AUTO; } + return StubProcessManager.allocProcess(plugin); - synchronized (PROCESSES) { - return allocProcessLocked(plugin); - } } /** - * 常驻进程调用 + * 常驻进程调用,添加进程信息到进程管理列表 * @param pid * @param process * @param index * @param binder * @param client - * @return + * @return 进程的默认插件名称(非框架内的进程返回null) */ static final String attachProcess(int pid, String process, int index, IBinder binder, IPluginClient client, String def, PluginManagerServer pms) { - synchronized (PROCESSES) { - String plugin = attachProcessLocked(pid, process, index, binder, client, def); - - ProcessClientRecord pr = new ProcessClientRecord(pms); - pr.name = process; - pr.plugin = plugin; - pr.pid = pid; - pr.index = index; - pr.binder = binder; - pr.client = client; - ALL.put(process, pr); - try { - pr.binder.linkToDeath(pr, 0); - } catch (Throwable e) { - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "ap l2d: " + e.getMessage(), e); - } + final String plugin = getDefaultPluginName(pid, index, binder, client, def); + final ProcessClientRecord pr = new ProcessClientRecord(process, plugin, pid, index, binder, client, pms); + try { + pr.binder.linkToDeath(pr, 0); + } catch (Throwable e) { + if (LOGR) { + LogRelease.e(PLUGIN_TAG, "ap l2d: " + e.getMessage(), e); } - - return plugin; } + writeProcessClientLock(new Action() { + @Override + public Void call() { + ALL.put(pr.name, pr); + return null; + } + }); + return plugin; } /** @@ -731,9 +604,7 @@ static final String attachProcess(int pid, String process, int index, IBinder bi * @return */ static final boolean attachActivity(int pid, int index, String plugin, String activity, String container) { - synchronized (PROCESSES) { - return regActivityLocked(pid, index, plugin, activity, container); - } + return StubProcessManager.attachActivity(pid, index, plugin, activity, container); } /** @@ -745,9 +616,7 @@ static final boolean attachActivity(int pid, int index, String plugin, String ac * @return */ static final boolean detachActivity(int pid, int index, String plugin, String activity, String container) { - synchronized (PROCESSES) { - return unregActivityLocked(pid, index, plugin, activity, container); - } + return StubProcessManager.detachActivity(pid, index, plugin, activity, container); } /** @@ -758,9 +627,7 @@ static final boolean detachActivity(int pid, int index, String plugin, String ac * @return */ static final boolean attachService(int pid, int index, String plugin, String service) { - synchronized (PROCESSES) { - return regServiceLocked(pid, index, plugin, service); - } + return StubProcessManager.attachService(pid, index, plugin, service); } /** @@ -771,667 +638,159 @@ static final boolean attachService(int pid, int index, String plugin, String ser * @return */ static final boolean detachService(int pid, int index, String plugin, String service) { - synchronized (PROCESSES) { - return unregServiceLocked(pid, index, plugin, service); - } + return StubProcessManager.detachService(pid, index, plugin, service); } static final void attachBinder(int pid, IBinder binder) { - synchronized (PROCESSES) { - regBinderLocked(pid, binder); - } + StubProcessManager.attachBinder(pid, binder); } static final void detachBinder(int pid, IBinder binder) { - synchronized (PROCESSES) { - unregBinderLocked(pid, binder); - } + StubProcessManager.detachBinder(pid, binder); } static final int sumBinders(int index) { - synchronized (PROCESSES) { - return sumBindersLocked(index); - } + return StubProcessManager.sumBinders(index); } - static final void schedulePluginProcessLoop(long delayMillis) { - if (Constant.SIMPLE_QUIT_CONTROLLER) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "schedule plugin process quit check: delay=" + (delayMillis / 1000)); - } - Tasks.cancelThreadTask(CHECK); - Tasks.postDelayed2Thread(CHECK, delayMillis); + //change by buyuntao + static final int getPidByProcessName(final String processName) { + if (TextUtils.isEmpty(processName)){ + return -1; } - } - - static final void cancelPluginProcessLoop() { - if (Constant.SIMPLE_QUIT_CONTROLLER) { - Tasks.cancelThreadTask(CHECK); - } - } - - // Added by Jiongxuan Zhang - static final int getPidByProcessName(String processName) { // 获取的是常驻进程自己?直接返回 if (TextUtils.equals(processName, IPC.getCurrentProcessName())) { return IPC.getCurrentProcessId(); } - // 在“进程列表”中寻找“线索” - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (!TextUtils.equals(r.name, processName)) { - continue; + return readProcessClientLock(new Action() { + @Override + public Integer call() { + ProcessClientRecord r = ALL.get(processName); + if (r != null && isBinderAlive(r)) { + return r.pid; } - if (!isBinderAlive(r)) { - continue; - } - return r.pid; + return -1; } - } - return -1; + }); } // Added by Jiongxuan Zhang - static final String getProcessNameByPid(int pid) { + static final String getProcessNameByPid(final int pid) { // 获取的是常驻进程自己?直接返回 if (pid == IPC.getCurrentProcessId()) { return IPC.getCurrentProcessName(); } - - synchronized (PROCESSES) { - for (ProcessClientRecord r : ALL.values()) { - if (r.pid != pid) { - continue; - } - if (!isBinderAlive(r)) { - continue; + return readProcessClientLock(new Action() { + @Override + public String call() { + for (ProcessClientRecord r : ALL.values()) { + if (r.pid != pid) { + continue; + } + if (!isBinderAlive(r)) { + continue; + } + return r.name; } - return r.name; + return null; } - } - return null; + }); } - private static final void handleBinderDied(ProcessClientRecord p, PluginManagerServer pms) { + private static final void handleBinderDied(ProcessClientRecord p) { if (LOG) { LogDebug.d(PLUGIN_TAG, "plugin process has died: plugin=" + p.plugin + " index=" + p.index + " pid=" + p.pid); } - synchronized (PROCESSES) { - handleBinderDiedLocked(p, pms); - } + handleBinderDiedLocked(p); } /** - * @deprecated 待优化 - * @param plugin + * 获取进程的默认插件名 + * @param pid + * @param index + * @param binder + * @param client + * @param def * @return */ - @Deprecated - private static final int allocProcessLocked(String plugin) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin=" + plugin); - } - - // 取运行列表 - List processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext()); - - // 取运行列表失败,则直接返回失败 - if (processes == null || processes.isEmpty()) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: get running processes is empty"); - LogDebug.i(PLUGIN_TAG, "get list exception p=" + plugin); - } - return IPluginManager.PROCESS_AUTO; - } - - updateListLocked(processes); - - // 找一个插件可能用过的进程 - for (ProcessRecord r : PROCESSES) { - if (TextUtils.equals(plugin, r.plugin)) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: found saved plugin process: index=" + r.index + " p=" + plugin); - } - // 标记为分配状态 - if (r.state == STATE_UNUSED || r.state == STATE_STOPED) { - r.allocate(plugin); - // 确保进程为空 - int pid = lookupPluginProcess(processes, r.index); - if (pid > 0) { - if (LOGR) { - LogRelease.i(PLUGIN_TAG, "ppr k i: " + pid); - } - android.os.Process.killProcess(pid); - waitKilled(pid); - } - } - if (LOG) { - LogDebug.i(PLUGIN_TAG, "used st=" + r.state + " i=" + r.index + " p=" + plugin); - } - return r.index; - } - } - - // 没有找到,则找一个空闲的 - for (ProcessRecord r : PROCESSES) { - if (r.state == STATE_UNUSED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: found unused plugin process: index=" + r.index); - LogDebug.i(PLUGIN_TAG, "free st=" + r.state + " i=" + r.index + " p=" + plugin + " orig.p=" + r.plugin); - } - // 标记为分配状态 - r.allocate(plugin); - // 确保进程为空 - int pid = lookupPluginProcess(processes, r.index); - if (pid > 0) { - if (LOGR) { - LogRelease.i(PLUGIN_TAG, "ppr k i: " + pid); - } - android.os.Process.killProcess(pid); - waitKilled(pid); - } - return r.index; - } - } - - // 没有找到,则找一个停止的 - for (ProcessRecord r : PROCESSES) { - if (r.state == STATE_STOPED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: found stoped plugin process: index=" + r.index); - LogDebug.i(PLUGIN_TAG, "stoped st=" + r.state + " i=" + r.index + " orig.p=" + r.plugin); - } - // 标记为分配状态 - r.allocate(plugin); - // 确保进程为空 - int pid = lookupPluginProcess(processes, r.index); - if (pid > 0) { - if (LOGR) { - LogRelease.i(PLUGIN_TAG, "ppr k i: " + pid); - } - android.os.Process.killProcess(pid); - waitKilled(pid); - } - return r.index; - } - } - - // 分配之后,一定时间如果没被使用,则需要回收 - // 没有找到,则找一个最早分配中的,且分配了很久的 - { - int i = -1; - long mod = Long.MAX_VALUE; - for (ProcessRecord r : PROCESSES) { - if (r.state != STATE_ALLOCATED) { - continue; - } - if (r.mobified < mod) { - i = r.index; - mod = r.mobified; - } - } - if (i >= 0 && (System.currentTimeMillis() - mod > 10 * 1000)) { - ProcessRecord r = PROCESSES[i]; - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin processes maybe busy, reuse process which allocating and expired: index=" + r.index); - LogDebug.i(PLUGIN_TAG, "force maybe st=" + r.state + " i=" + r.index + " orig.p=" + r.plugin); - } - // - r.setStoped(); - // - r.allocate(plugin); - // 确保进程为空 - int pid = lookupPluginProcess(processes, r.index); - if (pid > 0) { - if (LOGR) { - LogRelease.i(PLUGIN_TAG, "ppr k i: " + pid); - } - android.os.Process.killProcess(pid); - waitKilled(pid); - } - return r.index; - } - } - - // 没有找到,则找一个最先分配的组件为空的 - { - int i = -1; - long mod = Long.MAX_VALUE; - for (ProcessRecord r : PROCESSES) { - if (r.activities > 0) { - continue; - } - if (r.services > 0) { - continue; - } - if (r.binders > 0) { - continue; - } - if (r.mobified < mod) { - i = r.index; - mod = r.mobified; - } - } - if (i >= 0) { - ProcessRecord r = PROCESSES[i]; - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin processes busy, reuse process which components is empty: index=" + r.index); - } - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "ppr r & k i: " + r.pid); - LogRelease.i(PLUGIN_TAG, "force empty st=" + r.state + " i=" + r.index + " orig.p=" + r.plugin); - } - // - android.os.Process.killProcess(r.pid); - waitKilled(r.pid); - // - r.setStoped(); - // - r.allocate(plugin); - return r.index; - } - } - - // 还没有找到,则强制终止一个最先分配的 - { - int i = 0; - long mod = Long.MAX_VALUE; - for (ProcessRecord r : PROCESSES) { - if (r.mobified < mod) { - i = r.index; - mod = r.mobified; - } - } - // - ProcessRecord r = PROCESSES[i]; - if (LOG) { - LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin processes busy, reuse process which earliest allocated: index=" + r.index); - } - if (LOGR) { - LogRelease.e(PLUGIN_TAG, "ppr r & k i: " + r.pid); - LogRelease.i(PLUGIN_TAG, "force earliest st=" + r.state + " i=" + r.index + " orig.p=" + r.plugin); - } - // - android.os.Process.killProcess(r.pid); - waitKilled(r.pid); - // - r.setStoped(); - // - r.allocate(plugin); - return r.index; - } - } - - private static final String attachProcessLocked(int pid, String process, int index, IBinder binder, IPluginClient client, String def) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "attach process: pid=" + pid + " index=" + index + " binder=" + client); - } - + private static final String getDefaultPluginName(int pid, int index, IBinder binder, IPluginClient client, String def) { if (index == IPluginManager.PROCESS_UI) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "attach process: ui"); - } return Constant.PLUGIN_NAME_UI; } - /* 是否是用户自定义进程 */ if (PluginProcessHost.isCustomPluginProcess(index)) { return getProcessStringByIndex(index); } - - if (!PluginManager.isPluginProcess(index)) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "attach process: invalid index=" + index); - } - return null; - } - - // 检测状态是否一致 - ProcessRecord r = PROCESSES[index]; - if (!TextUtils.isEmpty(def)) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "attach process: allocate now"); - } - r.allocate(def); + if (PluginManager.isPluginProcess(index)) { + return StubProcessManager.attachStubProcess(pid, index, binder, client, def); } - - if (r.state != STATE_ALLOCATED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "attach process: state not allocated: state=" + r.state); - } - return null; - } - - r.setRunning(pid); - r.setClient(binder, client); - - return r.plugin; - } - - private static final boolean regActivityLocked(int pid, int index, String plugin, String activity, String container) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "reg activity: pid=" + pid + " index=" + index + " plugin=" + plugin + " activity=" + activity + " container=" + container); - } - - if (index < 0 || index >= PROCESSES.length) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "reg activity: invalid index=" + index); - } - return false; - } - - ProcessRecord r = PROCESSES[index]; - r.activities++; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); - } - - cancelPluginProcessLoop(); - - return true; - } - - private static final boolean unregActivityLocked(int pid, int index, String plugin, String activity, String container) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "unreg activity: pid=" + pid + " index=" + index + " plugin=" + plugin + " activity=" + activity + " container=" + container); - } - - if (index < 0 || index >= PROCESSES.length) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "unreg activity: invalid index=" + index); - } - return false; - } - - ProcessRecord r = PROCESSES[index]; - r.activities--; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); - } - - schedulePluginProcessLoop(CHECK_STAGE2_DELAY); - - return true; - } - - private static final boolean regServiceLocked(int pid, int index, String plugin, String service) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "reg service: pid=" + pid + " index=" + index + " plugin=" + plugin + " service=" + service); - } - - if (index < 0 || index >= PROCESSES.length) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "reg service: invalid index=" + index); - } - return false; - } - - ProcessRecord r = PROCESSES[index]; - r.services++; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); - } - - cancelPluginProcessLoop(); - - return true; - } - - private static final boolean unregServiceLocked(int pid, int index, String plugin, String service) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "unreg service: pid=" + pid + " index=" + index + " plugin=" + plugin + " service=" + service); - } - - if (index < 0 || index >= PROCESSES.length) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "unreg service: invalid index=" + index); - } - return false; - } - - ProcessRecord r = PROCESSES[index]; - r.services--; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); - } - - schedulePluginProcessLoop(CHECK_STAGE2_DELAY); - - return true; + return null; } - private static final boolean regBinderLocked(int pid, IBinder binder) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "reg binder: pid=" + pid + " binder=" + binder); - } - -// // TODO 优化 -// for (ProcessClientRecord r : ALL.values()) { -//// if (r.xx == xx) { -//// break; -//// } -// } - - // TODO 优化 - for (ProcessRecord r : PROCESSES) { - if (r.pid == pid) { - r.binders++; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); - } - break; - } - } - - cancelPluginProcessLoop(); - return true; - } - - private static final boolean unregBinderLocked(int pid, IBinder binder) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "unreg binder: pid=" + pid + " binder=" + binder); + /** + * 进程结束后执行操作 buyuntao + * @param p + */ + private static final void handleBinderDiedLocked(final ProcessClientRecord p) { + if (p == null){ + return; } - -// // TODO 优化 -// for (ProcessClientRecord r : ALL.values()) { -//// if (r.xx == xx) { -//// break; -//// } -// } - - // TODO 优化 - for (ProcessRecord r : PROCESSES) { - if (r.pid == pid) { - r.binders--; - r.mobified = System.currentTimeMillis(); - if (LOG) { - LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + writeProcessClientLock(new Action() { + @Override + public Void call() { + ProcessClientRecord r = ALL.get(p.name); + if (r == p){ //防止进程重启误判 + ALL.remove(r.name); } - break; + return null; } - } - - schedulePluginProcessLoop(CHECK_STAGE2_DELAY); - - return true; - } + }); - private static final int sumBindersLocked(int index) { - for (ProcessRecord r : PROCESSES) { - if (r.index == index) { - return r.binders; - } - } - return -1; - } + StubProcessManager.setProcessStop(p.binder); - private static final void doPluginProcessLoop() { - if (Constant.SIMPLE_QUIT_CONTROLLER) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "do plugin process quit check"); - } - synchronized (PROCESSES) { - for (ProcessRecord r : PROCESSES) { - if (r.state != STATE_RUNNING) { - continue; - } - if (r.activities > 0) { - continue; - } - if (r.services > 0) { - continue; - } - if (r.binders > 0) { - continue; - } - if (LOGR) { - // terminate empty process - LogRelease.i(PLUGIN_TAG, "t e p " + r.pid); - } - // - android.os.Process.killProcess(r.pid); - waitKilled(r.pid); - r.setStoped(); - // - schedulePluginProcessLoop(CHECK_STAGE3_DELAY); - return; - } - } - } + // 通知 PluginManagerServer 客户端进程链接已断开 + p.pluginManager.onClientProcessKilled(p.name); } - private static final void updateListLocked(List processes) { - /* TODO 待完善 - // 标记所有 - for (ProcessRecord r : PROCESSES) { - if (r.state == STATE_RUNNING) { - r.setMarked(); - } - } - for (ProcessPluginInfo r : USED_PLUGINS.values()) { - if (r.state == STATE_RUNNING) { - r.setMarked(); - } - } + /// - // 遍历设置状态 - for (RunningAppProcessInfo info : processes) { - if (info.uid != PluginManager.sUid) { - continue; - } - // - ProcessPluginInfo pi = USED_PLUGINS.get(info.processName); - if (pi != null && pi.pid == info.pid) { - pi.setRunning(); - } - // - int index = PluginManager.evalPluginProcess(info.processName); - if (!PluginManager.isPluginProcess(index)) { - continue; - } - // - ProcessRecord r = PROCESSES[index]; - // 状态分析 - if (r.state == STATE_ALLOCATED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "update plugin process list: plugin process started, index=" + index); - } - r.setRunning(info.pid); - } else if (r.state == STATE_MARKED) { - if (info.pid == r.pid) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "update plugin process list: plugin process running index=" + index); - } - r.setRunning(info.pid); - } - } else { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "update plugin process list: plugin process unknown: state=" + r.state + " index=" + index); - } - } - } - - // 标记所有 - for (ProcessRecord r : PROCESSES) { - if (r.state == STATE_MARKED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "update plugin process list: plugin process died, index=" + r.index); - } - r.setStoped(); - } - } - for (ProcessPluginInfo r : USED_PLUGINS.values()) { - if (r.state == STATE_MARKED) { - if (LOG) { - LogDebug.d(PLUGIN_TAG, "update plugin process list: used plugin process died, processName=" + r.processName); - } - r.setStoped(); - } - } */ - } - - private static final void waitKilled(int pid) { - for (int i = 0; i < 10; i++) { - try { - Thread.sleep(100, 0); - } catch (Throwable e) { - // - } - // - List processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext()); - if (processes == null || processes.isEmpty()) { - continue; - } - boolean found = false; - for (RunningAppProcessInfo info : processes) { - if (info.pid == pid) { - found = true; - } + private static T writeProcessClientLock(@NonNull final Action action) { + final long start = System.currentTimeMillis(); +// final String stack = OptUtil.stack2Str(Thread.currentThread().getStackTrace()[3]); + try { + PROCESS_CLIENT_LOCK.writeLock().lock(); + if (LogDebug.LOG) { + Log.d(TAG, String.format("%s(%sms@%s) WRITING", Thread.currentThread().getStackTrace()[3], System.currentTimeMillis() - start, Thread.currentThread())); } - if (!found) { - return; + return action.call(); + } finally { + PROCESS_CLIENT_LOCK.writeLock().unlock(); + if (LogDebug.LOG) { + Log.d(TAG, String.format("%s(%sms@%s) WRITING DONE", Thread.currentThread().getStackTrace()[3], System.currentTimeMillis() - start, Thread.currentThread())); } } } - private static final int lookupPluginProcess(List processes, int index) { - for (RunningAppProcessInfo pi : processes) { - if (pi.uid != PluginManager.sUid) { - continue; + private static T readProcessClientLock(@NonNull final Action action) { + final long start = System.currentTimeMillis(); +// final String stack = OptUtil.stack2Str(Thread.currentThread().getStackTrace()[3]); + try { + PROCESS_CLIENT_LOCK.readLock().lock(); + if (LogDebug.LOG) { + Log.d(TAG, String.format("%s(%sms@%s) READING", Thread.currentThread().getStackTrace()[3], System.currentTimeMillis() - start, Thread.currentThread())); } - int i = PluginManager.evalPluginProcess(pi.processName); - if (i == index) { - return pi.pid; + return action.call(); + } finally { + PROCESS_CLIENT_LOCK.readLock().unlock(); + if (LogDebug.LOG) { + Log.d(TAG, String.format("%s(%sms@%s) READING DONE", Thread.currentThread().getStackTrace()[3], System.currentTimeMillis() - start, Thread.currentThread())); } } - return -1; } - private static final void handleBinderDiedLocked(ProcessClientRecord p, PluginManagerServer pms) { - // TODO 优化 - for (ProcessClientRecord r : ALL.values()) { - if (r == p) { - ALL.remove(r.name); - break; - } - } - - // TODO 优化 - for (ProcessRecord r : PROCESSES) { - if (r.binder == p.binder) { - r.setStoped(); - break; - } - } - - // 通知 PluginManagerServer 客户端进程链接已断开 - pms.onClientProcessKilled(p.name); + private interface Action { + T call(); } } diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java index d5041940..bcb0b0ce 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java @@ -173,6 +173,34 @@ class PmBase { */ private static final byte[] LOCKER = new byte[0]; + /** + * 广播接收器,声明为成员变量以避免重复创建 + */ + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (TextUtils.isEmpty(action)) { + return; + } + + if (action.equals(intent.getAction())) { + PluginInfo info = intent.getParcelableExtra("obj"); + if (info != null) { + switch (action) { + case ACTION_NEW_PLUGIN: + // 非常驻进程上下文 + newPluginFound(info, intent.getBooleanExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, false)); + break; + case ACTION_UNINSTALL_PLUGIN: + pluginUninstalled(info); + break; + } + } + } + } + }; + /** * 类映射 */ @@ -280,7 +308,7 @@ private final void initForServer() { mHostSvc = new PmHostSvc(mContext, this); PluginProcessMain.installHost(mHostSvc); - PluginProcessMain.schedulePluginProcessLoop(PluginProcessMain.CHECK_STAGE1_DELAY); + StubProcessManager.schedulePluginProcessLoop(StubProcessManager.CHECK_STAGE1_DELAY); // 兼容即将废弃的p-n方案 by Jiongxuan Zhang mAll = new Builder.PxAll(); @@ -619,36 +647,17 @@ final void callAppCreate() { if (!IPC.isPersistentProcess()) { // 由于常驻进程已经在内部做了相关的处理,此处仅需要在UI进程注册并更新即可 - registerReceiverAction(ACTION_NEW_PLUGIN); - registerReceiverAction(ACTION_UNINSTALL_PLUGIN); - } - } - - /** - * @param action - */ - private final void registerReceiverAction(final String action) { - IntentFilter filter = new IntentFilter(action); - LocalBroadcastManager.getInstance(mContext).registerReceiver(new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - if (action.equals(intent.getAction())) { - PluginInfo info = intent.getParcelableExtra("obj"); - if (info != null) { - switch (action) { - case ACTION_NEW_PLUGIN: - // 非常驻进程上下文 - newPluginFound(info, intent.getBooleanExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, false)); - break; - case ACTION_UNINSTALL_PLUGIN: - pluginUninstalled(info); - break; - } - } + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_NEW_PLUGIN); + intentFilter.addAction(ACTION_UNINSTALL_PLUGIN); + try { + LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, intentFilter); + } catch (Exception e) { + if (LOGR) { + LogRelease.e(PLUGIN_TAG, "p m hlc a r e: " + e.getMessage(), e); } } - }, filter); + } } /** @@ -1215,7 +1224,7 @@ final IPluginClient startPluginProcessLocked(String plugin, int process, PluginB } // - PluginProcessMain.schedulePluginProcessLoop(PluginProcessMain.CHECK_STAGE1_DELAY); + StubProcessManager.schedulePluginProcessLoop(StubProcessManager.CHECK_STAGE1_DELAY); // 获取 IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info); @@ -1238,7 +1247,7 @@ final IPluginClient startPluginProcessLocked(String plugin, int process, PluginB LogRelease.e(PLUGIN_TAG, "a.p.p: " + e.getMessage(), e); } } - // 分配的坑位不属于UI、和自定义进程,就返回。 + // 分配的坑位不属于UI、自定义进程或Stub坑位进程,就返回。(没找到有效进程) if (!(index == IPluginManager.PROCESS_UI || PluginProcessHost.isCustomPluginProcess(index) || PluginManager.isPluginProcess(index))) { diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java index 1e5e0654..f3fae174 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java @@ -177,9 +177,7 @@ public long fetchPersistentCookie() throws RemoteException { @Override public IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException { - synchronized (this) { - return mPluginMgr.startPluginProcessLocked(plugin, process, info); - } + return mPluginMgr.startPluginProcessLocked(plugin, process, info); } @Override @@ -340,7 +338,6 @@ public PluginInfo pluginDownloaded(String path) throws RemoteException { syncInstalledPluginInfo2All(pi); } - return pi; } @@ -484,28 +481,27 @@ private void sendIntent2Process(String target, Intent intent, boolean sync) thro if (LOG) { LogDebug.d(PLUGIN_TAG, "sendIntent2Process target=" + target + " intent=" + intent); } - if (TextUtils.equals(target, IPC.getPluginHostProcessName())) { - intent.setExtrasClassLoader(getClass().getClassLoader()); - if (sync) { - LocalBroadcastHelper.sendBroadcastSyncUi(mContext, intent); - } else { - LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); - } + sendIntent2PluginHostProcess(intent, sync); return; } if (TextUtils.isEmpty(target)) { - intent.setExtrasClassLoader(getClass().getClassLoader()); - if (sync) { - LocalBroadcastHelper.sendBroadcastSyncUi(mContext, intent); - } else { - LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); - } + sendIntent2PluginHostProcess(intent, sync); } + PluginProcessMain.sendIntent2Process(target, intent, sync); } + private void sendIntent2PluginHostProcess(Intent intent, boolean sync) { + intent.setExtrasClassLoader(getClass().getClassLoader()); + if (sync) { + LocalBroadcastHelper.sendBroadcastSyncUi(mContext, intent); + } else { + LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); + } + } + @Override public void sendIntent2Plugin(String target, Intent intent) throws RemoteException { sendIntent2Plugin(target, intent, false); diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/StubProcessManager.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/StubProcessManager.java new file mode 100644 index 00000000..4a56e1a4 --- /dev/null +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/StubProcessManager.java @@ -0,0 +1,544 @@ +package com.qihoo360.loader2; + +import android.app.ActivityManager; +import android.os.IBinder; +import android.text.TextUtils; + +import com.qihoo360.i.IPluginManager; +import com.qihoo360.mobilesafe.api.Tasks; +import com.qihoo360.replugin.RePluginInternal; +import com.qihoo360.replugin.base.AMSUtils; +import com.qihoo360.replugin.helper.LogDebug; +import com.qihoo360.replugin.helper.LogRelease; + +import java.io.PrintWriter; +import java.util.List; + +import static com.qihoo360.replugin.helper.LogDebug.LOG; +import static com.qihoo360.replugin.helper.LogDebug.PLUGIN_TAG; +import static com.qihoo360.replugin.helper.LogRelease.LOGR; + +/** + * @author RePlugin Team + * dec: 坑位进程管理 buyuntao + */ +public class StubProcessManager { + /** + * 坑位进程列表 + */ + static final ProcessRecord STUB_PROCESSES[] = new ProcessRecord[Constant.STUB_PROCESS_COUNT]; + static final int CHECK_STAGE1_DELAY = 17 * 1000; + private static final int CHECK_STAGE2_DELAY = 11 * 1000; + private static final int CHECK_STAGE3_DELAY = 3 * 1000; + private static final Runnable CHECK = new Runnable() { + @Override + public void run() { + doPluginProcessLoop(); + } + }; + + static { + for (int i = 0; i < Constant.STUB_PROCESS_COUNT; i++) { + ProcessRecord r = new ProcessRecord(i, StubProcessState.STATE_UNUSED); + STUB_PROCESSES[i] = r; + } + } + + /** + * 分配坑位进程 buyuntao(外部调用端已经加锁) + * @param plugin + * @return 进程index值 + */ + static final int allocProcess(String plugin) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin=" + plugin); + } + // 取运行列表 + List processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext()); + // 取运行列表失败,则直接返回失败 + if (processes == null || processes.isEmpty()) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "alloc plugin process: get running processes is empty"); + LogDebug.i(PLUGIN_TAG, "get list exception p=" + plugin); + } + return IPluginManager.PROCESS_AUTO; + } + //根据优先级分配坑位进程 + int prevMatchPriority = -1; //临时变量,保存上一个ProcessRecord的进程分配优先级 + ProcessRecord selectRecord = null; //被选中的坑位进程 + for (ProcessRecord r : STUB_PROCESSES) { + synchronized (r) { + if (r.calculateMatchPriority(plugin) > prevMatchPriority) { + prevMatchPriority = r.calculateMatchPriority(plugin); + selectRecord = r; + } else if (r.calculateMatchPriority(plugin) == prevMatchPriority) { + if (r.mobified < selectRecord.mobified) { + selectRecord = r; + } + } + } + } + if (selectRecord == null) { //不应该出现 + return IPluginManager.PROCESS_AUTO; + } + synchronized (selectRecord){ + //插件已在分配进程中运行,直接返回 + if (selectRecord.calculateMatchPriority(plugin) == Integer.MAX_VALUE && (selectRecord.state == StubProcessState.STATE_ALLOCATED || selectRecord.state == StubProcessState.STATE_RUNNING)) + { + return selectRecord.index; + } + selectRecord.resetAllocate(plugin, processes); + return selectRecord.index; + } + } + + private static final int lookupPluginProcess(List processes, int index) { + for (ActivityManager.RunningAppProcessInfo pi : processes) { + if (pi.uid != PluginManager.sUid) { + continue; + } + int i = PluginManager.evalPluginProcess(pi.processName); + if (i == index) { + return pi.pid; + } + } + return -1; + } + + private static final void waitKilled(int pid) { + for (int i = 0; i < 10; i++) { + try { + Thread.sleep(100, 0); + } catch (Throwable e) { + // + } + // + List processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext()); + if (processes == null || processes.isEmpty()) { + continue; + } + boolean found = false; + for (ActivityManager.RunningAppProcessInfo info : processes) { + if (info.pid == pid) { + found = true; + } + } + if (!found) { + return; + } + } + } + + static final void cancelPluginProcessLoop() { + if (Constant.SIMPLE_QUIT_CONTROLLER) { + Tasks.cancelThreadTask(CHECK); + } + } + + /** + * @param pid + * @param index + * @param plugin + * @param activity + * @param container + * @return + */ + static final boolean attachActivity(int pid, int index, String plugin, String activity, String container) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "reg activity: pid=" + pid + " index=" + index + " plugin=" + plugin + " activity=" + activity + " container=" + container); + } + + if (index < 0 || index >= STUB_PROCESSES.length) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "reg activity: invalid index=" + index); + } + return false; + } + + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r){ + r.activities++; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + cancelPluginProcessLoop(); + + return true; + } + + /** + * @param pid + * @param index + * @param plugin + * @param activity + * @param container + * @return + */ + static final boolean detachActivity(int pid, int index, String plugin, String activity, String container) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "unreg activity: pid=" + pid + " index=" + index + " plugin=" + plugin + " activity=" + activity + " container=" + container); + } + + if (index < 0 || index >= STUB_PROCESSES.length) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "unreg activity: invalid index=" + index); + } + return false; + } + + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r){ + r.activities--; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + schedulePluginProcessLoop(CHECK_STAGE2_DELAY); + + return true; + } + + /** + * @param pid + * @param index + * @param plugin + * @param service + * @return + */ + static final boolean attachService(int pid, int index, String plugin, String service) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "reg service: pid=" + pid + " index=" + index + " plugin=" + plugin + " service=" + service); + } + + if (index < 0 || index >= STUB_PROCESSES.length) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "reg service: invalid index=" + index); + } + return false; + } + + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r) { + r.services++; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + cancelPluginProcessLoop(); + + return true; + } + + /** + * @param pid + * @param index + * @param plugin + * @param service + * @return + */ + static final boolean detachService(int pid, int index, String plugin, String service) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "unreg service: pid=" + pid + " index=" + index + " plugin=" + plugin + " service=" + service); + } + + if (index < 0 || index >= STUB_PROCESSES.length) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "unreg service: invalid index=" + index); + } + return false; + } + + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r){ + r.services--; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + schedulePluginProcessLoop(CHECK_STAGE2_DELAY); + + return true; + } + + static final void attachBinder(int pid, IBinder binder) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "reg binder: pid=" + pid + " binder=" + binder); + } + for (ProcessRecord r : STUB_PROCESSES) { + if (r.pid == pid) { + synchronized (r) { + r.binders++; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + break; + } + } + + cancelPluginProcessLoop(); + } + + static final void detachBinder(int pid, IBinder binder) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "unreg binder: pid=" + pid + " binder=" + binder); + } + for (ProcessRecord r : STUB_PROCESSES) { + if (r.pid == pid) { + synchronized (r){ + r.binders--; + r.mobified = System.currentTimeMillis(); + if (LOG) { + LogDebug.d(PLUGIN_TAG, "activities=" + r.activities + " services=" + r.services + " binders=" + r.binders); + } + } + break; + } + } + + schedulePluginProcessLoop(CHECK_STAGE2_DELAY); + } + + static final int sumBinders(int index) { + if (index >=0 && index < STUB_PROCESSES.length){ + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r) { + return STUB_PROCESSES[index].binders; + } + } + return -1; + } + + /** + * attach坑位进程,设置坑位进程为运行状态,并返回正在使用坑位进程的插件名称 buyuntao + * + * @param pid + * @param index + * @param binder + * @param client + * @param def + * @return + */ + static final String attachStubProcess(int pid, int index, IBinder binder, IPluginClient client, String def) { + // 检测状态是否一致 + ProcessRecord r = STUB_PROCESSES[index]; + synchronized (r) { + if (!TextUtils.isEmpty(def)) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "attach process: allocate now"); + } + r.allocate(def); + } + if (r.state != StubProcessState.STATE_ALLOCATED) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "attach process: state not allocated: state=" + r.state); + } + return null; + } + + r.setRunning(pid); + r.setClient(binder, client); + return r.plugin; + } + } + + static final void setProcessStop(final IBinder binder) { + for (ProcessRecord r : STUB_PROCESSES) { + synchronized (r) { + if (r.binder == binder) { + r.setStoped(); + break; + } + } + } + } + + private static final void doPluginProcessLoop() { + if (Constant.SIMPLE_QUIT_CONTROLLER) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "do plugin process quit check"); + } + for (ProcessRecord r : STUB_PROCESSES) { + synchronized (r) { + if (r.state != StubProcessState.STATE_RUNNING) { + continue; + } + if (r.activities > 0) { + continue; + } + if (r.services > 0) { + continue; + } + if (r.binders > 0) { + continue; + } + if (LOGR) { + // terminate empty process + LogRelease.i(PLUGIN_TAG, "t e p " + r.pid); + } + // + android.os.Process.killProcess(r.pid); + waitKilled(r.pid); + r.setStoped(); + // + schedulePluginProcessLoop(CHECK_STAGE3_DELAY); + return; + } + } + } + } + + static final void schedulePluginProcessLoop(long delayMillis) { + if (Constant.SIMPLE_QUIT_CONTROLLER) { + if (LOG) { + LogDebug.d(PLUGIN_TAG, "schedule plugin process quit check: delay=" + (delayMillis / 1000)); + } + Tasks.cancelThreadTask(CHECK); + Tasks.postDelayed2Thread(CHECK, delayMillis); + } + } + + static final void dump(PrintWriter writer) { + writer.println("--- STUB_PROCESSES.length = " + STUB_PROCESSES.length + " ---"); + for (ProcessRecord r : STUB_PROCESSES) { + synchronized (r){ + writer.println(r); + } + } + } + + /** + * 坑位进程的状态 buyuntao + */ + public class StubProcessState { + public static final int STATE_UNUSED = 0; + + public static final int STATE_ALLOCATED = 1; + + public static final int STATE_RUNNING = 2; + + public static final int STATE_STOPED = 4; + } + + private static final class ProcessRecord { + + final int index; + + int state; + + long mobified; + + String plugin; + + int pid; + + IBinder binder; + + IPluginClient client; + + int activities; + + int services; + + int binders; + + ProcessRecord(int index, int state) { + this.index = index; + this.state = state; + } + + void allocate(String plugin) { + this.state = StubProcessState.STATE_ALLOCATED; + this.mobified = System.currentTimeMillis(); + this.plugin = plugin; + this.pid = 0; + this.binder = null; + this.client = null; + this.activities = 0; + this.services = 0; + this.binders = 0; + } + + void setRunning(int pid) { + this.state = StubProcessState.STATE_RUNNING; + this.pid = pid; + } + + void setClient(IBinder binder, IPluginClient client) { + this.binder = binder; + this.client = client; + } + + void setStoped() { + this.state = StubProcessState.STATE_STOPED; + this.pid = 0; + this.binder = null; + this.client = null; + } + + /** + * 当前坑位的选择优先级(值越大被选中的概率越高) + * + * @param newPluginName + * @return 坑位的选择优先级 + */ + int calculateMatchPriority(String newPluginName) { + int priority = Integer.MAX_VALUE; + if (TextUtils.equals(newPluginName, plugin)) { //插件可能用过的进程 + return priority; + } + if (state == StubProcessState.STATE_UNUSED) { //空闲的进程 + priority = Integer.MAX_VALUE - 1; + return priority; + } + if (state == StubProcessState.STATE_STOPED) { //已停止的进程 + priority = Integer.MAX_VALUE - 2; + return priority; + } + if ((System.currentTimeMillis() - mobified) > 10 * 1000) { //分配时间超过10秒的 + priority = Integer.MAX_VALUE - 3; + return priority; + } + if ((activities <= 0) && (services <= 0) && (binders <= 0)) { //组件为空的 + priority = Integer.MAX_VALUE - 4; + return priority; + } + priority = 0; //默认值 + return priority; + } + + void resetAllocate(String plugin, List processes) { + killProcess(processes); + allocate(plugin); + } + + private void killProcess(List processes) { + // 确保进程为空 + int pid = lookupPluginProcess(processes, index); + if (pid > 0) { + if (LOGR) { + LogRelease.i(PLUGIN_TAG, "ppr k i: " + pid); + } + android.os.Process.killProcess(pid); + waitKilled(pid); + } + } + + @Override + public String toString() { + if (LOG) { + return super.toString() + " {index=" + index + " state=" + state + " mobified=" + mobified + " plugin=" + plugin + " pid=" + pid + " binder=" + binder + " client=" + client + + " activities=" + activities + " services=" + services + " binders=" + binders + "}"; + } + return super.toString(); + } + } + + +} diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/RePlugin.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/RePlugin.java index 888421dd..721449e7 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/RePlugin.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/RePlugin.java @@ -66,6 +66,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import static com.qihoo360.replugin.helper.LogDebug.LOG; @@ -915,6 +916,8 @@ public static class App { static boolean sAttached; + static AtomicBoolean sCreated = new AtomicBoolean(false); + /** * 当Application的attachBaseContext调用时需调用此方法

* 使用插件框架默认的方案 @@ -997,6 +1000,10 @@ public static void onCreate() { throw new IllegalStateException(); } + if (!sCreated.compareAndSet(false, true)) { + return; + } + Tasks.init(); PMF.callAppCreate(); diff --git a/replugin-plugin-gradle/build.gradle b/replugin-plugin-gradle/build.gradle index fd50c208..7b3321cb 100644 --- a/replugin-plugin-gradle/build.gradle +++ b/replugin-plugin-gradle/build.gradle @@ -112,4 +112,5 @@ publishing { } } -apply from: 'bintray.gradle' \ No newline at end of file +project.ext.RP_ARTIFACT_ID = 'replugin-plugin-gradle' +apply from: '../rp-publish.gradle' \ No newline at end of file diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/AppConstant.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/AppConstant.groovy index 01d38ae7..7e87db05 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/AppConstant.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/AppConstant.groovy @@ -23,8 +23,7 @@ package com.qihoo360.replugin.gradle.plugin class AppConstant { /** 版本号 */ - def static final VER = "2.3.0" - + def static final VER = "${RP_VERSION}" /** 打印信息时候的前缀 */ def static final TAG = "< replugin-plugin-v${VER} >" diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy index 2f257de6..be576dff 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy @@ -240,6 +240,10 @@ public class ReClassTransform extends Transform { jar = new File(jarPath) } + if(!jar.exists()){ + return + } + String destName = input.name def hexName = DigestUtils.md5Hex(jar.absolutePath) if (destName.endsWith('.jar')) { diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy index ce7d506b..d833a68c 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy @@ -128,7 +128,12 @@ public class Util { * 压缩 dirPath 到 zipFilePath */ def static zipDir(String dirPath, String zipFilePath) { - new AntBuilder().zip(destfile: zipFilePath, basedir: dirPath) + File dir = new File(dirPath) + if(dir.exists()){ + new AntBuilder().zip(destfile: zipFilePath, basedir: dirPath) + }else{ + println ">>> Zip file is empty! Ignore" + } } /** diff --git a/replugin-plugin-library/replugin-plugin-lib/build.gradle b/replugin-plugin-library/replugin-plugin-lib/build.gradle index 93a9f6c5..8adbbf94 100644 --- a/replugin-plugin-library/replugin-plugin-lib/build.gradle +++ b/replugin-plugin-library/replugin-plugin-lib/build.gradle @@ -16,9 +16,6 @@ apply plugin: 'com.android.library' -version = "2.3.0" -group = 'com.qihoo360.replugin' // 组名 - android { compileSdkVersion 25 buildToolsVersion '25.0.2' @@ -46,4 +43,6 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) provided 'com.android.support:appcompat-v7:23.4.0' } -apply from: 'bintray.gradle' \ No newline at end of file + +project.ext.RP_ARTIFACT_ID = 'replugin-plugin-lib' +apply from: '../../rp-publish.gradle' \ No newline at end of file diff --git a/replugin-sample-extra/fresco/FrescoHost/app/build.gradle b/replugin-sample-extra/fresco/FrescoHost/app/build.gradle index 7a5bdf3e..e6427bc2 100644 --- a/replugin-sample-extra/fresco/FrescoHost/app/build.gradle +++ b/replugin-sample-extra/fresco/FrescoHost/app/build.gradle @@ -45,7 +45,7 @@ repluginHostConfig { } dependencies { - compile 'com.qihoo360.replugin:replugin-host-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-host-lib:${RP_VERSION}" compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { diff --git a/replugin-sample-extra/fresco/FrescoHost/build.gradle b/replugin-sample-extra/fresco/FrescoHost/build.gradle index dce24ee7..3b9afc07 100644 --- a/replugin-sample-extra/fresco/FrescoHost/build.gradle +++ b/replugin-sample-extra/fresco/FrescoHost/build.gradle @@ -1,13 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.0' - classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-host-gradle:${RP_VERSION}" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/replugin-sample-extra/fresco/FrescoHost/gradlew b/replugin-sample-extra/fresco/FrescoHost/gradlew old mode 100644 new mode 100755 diff --git a/replugin-sample-extra/fresco/FrescoPlugin/app/build.gradle b/replugin-sample-extra/fresco/FrescoPlugin/app/build.gradle index 5a3b109d..a2d017d6 100644 --- a/replugin-sample-extra/fresco/FrescoPlugin/app/build.gradle +++ b/replugin-sample-extra/fresco/FrescoPlugin/app/build.gradle @@ -29,7 +29,7 @@ android { apply plugin: 'replugin-plugin-gradle' dependencies { - compile 'com.qihoo360.replugin:replugin-plugin-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-plugin-lib:${RP_VERSION}" compile 'com.android.support:appcompat-v7:26.0.0-alpha1' diff --git a/replugin-sample-extra/fresco/FrescoPlugin/build.gradle b/replugin-sample-extra/fresco/FrescoPlugin/build.gradle index 30b1ed98..93474514 100644 --- a/replugin-sample-extra/fresco/FrescoPlugin/build.gradle +++ b/replugin-sample-extra/fresco/FrescoPlugin/build.gradle @@ -1,13 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.0' - classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-plugin-gradle:${RP_VERSION}" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/replugin-sample-extra/fresco/FrescoPlugin/gradlew b/replugin-sample-extra/fresco/FrescoPlugin/gradlew old mode 100644 new mode 100755 diff --git a/replugin-sample/host/app/build.gradle b/replugin-sample/host/app/build.gradle index e32f9df0..6b97f1d8 100644 --- a/replugin-sample/host/app/build.gradle +++ b/replugin-sample/host/app/build.gradle @@ -45,5 +45,5 @@ repluginHostConfig { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.qihoo360.replugin:replugin-host-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-host-lib:${RP_VERSION}" } diff --git a/replugin-sample/host/build.gradle b/replugin-sample/host/build.gradle index 25113a2c..b879ef4c 100644 --- a/replugin-sample/host/build.gradle +++ b/replugin-sample/host/build.gradle @@ -14,13 +14,14 @@ * the License. */ buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-host-gradle:${RP_VERSION}" } } diff --git a/replugin-sample/plugin/plugin-demo1/app/build.gradle b/replugin-sample/plugin/plugin-demo1/app/build.gradle index 2110a51e..9b987064 100644 --- a/replugin-sample/plugin/plugin-demo1/app/build.gradle +++ b/replugin-sample/plugin/plugin-demo1/app/build.gradle @@ -66,7 +66,7 @@ repluginPluginConfig { } dependencies { - compile 'com.qihoo360.replugin:replugin-plugin-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-plugin-lib:${RP_VERSION}" provided files('libs/fragment.jar')//这个jar就是从Support-fragment中提取出来的并非特制包目的是为了骗过编译期 provided files('libs/common-utils-lib-1.0.0.jar')//这个jar就是从Host的utils中编译生成的,其目的是为了骗过编译期 compile(name: 'plugin-library', ext: 'aar')//sample:compile aar diff --git a/replugin-sample/plugin/plugin-demo1/build.gradle b/replugin-sample/plugin/plugin-demo1/build.gradle index 4b276bf9..8f2deea3 100644 --- a/replugin-sample/plugin/plugin-demo1/build.gradle +++ b/replugin-sample/plugin/plugin-demo1/build.gradle @@ -14,13 +14,14 @@ * the License. */ buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-plugin-gradle:${RP_VERSION}" } } diff --git a/replugin-sample/plugin/plugin-demo2/app/build.gradle b/replugin-sample/plugin/plugin-demo2/app/build.gradle index 78a9c6b1..00560a3d 100644 --- a/replugin-sample/plugin/plugin-demo2/app/build.gradle +++ b/replugin-sample/plugin/plugin-demo2/app/build.gradle @@ -66,7 +66,7 @@ repluginPluginConfig { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.qihoo360.replugin:replugin-plugin-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-plugin-lib:${RP_VERSION}" } if (android.defaultConfig.multiDexEnabled) { diff --git a/replugin-sample/plugin/plugin-demo2/build.gradle b/replugin-sample/plugin/plugin-demo2/build.gradle index 4b276bf9..8f2deea3 100644 --- a/replugin-sample/plugin/plugin-demo2/build.gradle +++ b/replugin-sample/plugin/plugin-demo2/build.gradle @@ -14,13 +14,14 @@ * the License. */ buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-plugin-gradle:${RP_VERSION}" } } diff --git a/replugin-sample/plugin/plugin-demo3-kotlin/app/build.gradle b/replugin-sample/plugin/plugin-demo3-kotlin/app/build.gradle index fe18624f..09562afe 100644 --- a/replugin-sample/plugin/plugin-demo3-kotlin/app/build.gradle +++ b/replugin-sample/plugin/plugin-demo3-kotlin/app/build.gradle @@ -54,7 +54,7 @@ android { dependencies { - compile 'com.qihoo360.replugin:replugin-plugin-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-plugin-lib:${RP_VERSION}" provided files('libs/fragment.jar') compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" //这个jar就是从Support-fragment中提取出来的并非特制包目的是为了骗过编译期 diff --git a/replugin-sample/plugin/plugin-demo3-kotlin/build.gradle b/replugin-sample/plugin/plugin-demo3-kotlin/build.gradle index c18c077b..8538d4e4 100644 --- a/replugin-sample/plugin/plugin-demo3-kotlin/build.gradle +++ b/replugin-sample/plugin/plugin-demo3-kotlin/build.gradle @@ -14,6 +14,7 @@ * the License. */ buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) ext.kotlin_version = '1.1.3' repositories { mavenLocal() @@ -21,7 +22,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-plugin-gradle:${RP_VERSION}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/replugin-sample/plugin/plugin-webview/app/build.gradle b/replugin-sample/plugin/plugin-webview/app/build.gradle index ff7c89e5..3528d971 100644 --- a/replugin-sample/plugin/plugin-webview/app/build.gradle +++ b/replugin-sample/plugin/plugin-webview/app/build.gradle @@ -62,5 +62,5 @@ repluginPluginConfig { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.qihoo360.replugin:replugin-plugin-lib:2.3.0' + compile "com.qihoo360.replugin:replugin-plugin-lib:${RP_VERSION}" } \ No newline at end of file diff --git a/replugin-sample/plugin/plugin-webview/build.gradle b/replugin-sample/plugin/plugin-webview/build.gradle index 4b276bf9..8f2deea3 100644 --- a/replugin-sample/plugin/plugin-webview/build.gradle +++ b/replugin-sample/plugin/plugin-webview/build.gradle @@ -14,13 +14,14 @@ * the License. */ buildscript { + { p, cfg = "rp-config.gradle" -> if (new File(p, cfg).exists()) apply from: "${p}/${cfg}" else if (p.exists()) call(p.parentFile) }(buildscript.sourceFile.parentFile) repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.0' + classpath "com.qihoo360.replugin:replugin-plugin-gradle:${RP_VERSION}" } } diff --git a/replugin-sample/plugin/plugin-webview/gradle/wrapper/gradle-wrapper.jar b/replugin-sample/plugin/plugin-webview/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..13372aef Binary files /dev/null and b/replugin-sample/plugin/plugin-webview/gradle/wrapper/gradle-wrapper.jar differ diff --git a/rp-config.gradle b/rp-config.gradle new file mode 100644 index 00000000..39e7e57f --- /dev/null +++ b/rp-config.gradle @@ -0,0 +1,14 @@ +project.ext{ + RP_USER = 'qihoo360' + RP_REPO = 'replugin' + // + RP_SITE = 'https://github.com/Qihoo360/Replugin' + RP_DESC = 'RePlugin - A flexible, stable, easy-to-use Android Plug-in Framework' + RP_EMAIL = 'replugin@gmail.com' + RP_GIT_URL = 'https://github.com/Qihoo360/RePlugin' + RP_LICENSES_URL = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + RP_LICENSES_NAME = 'Apache-2.0' + // + RP_GROUP = 'com.qihoo360.replugin' + RP_VERSION = '2.3.1' +} \ No newline at end of file diff --git a/rp-publish.gradle b/rp-publish.gradle new file mode 100644 index 00000000..6cff4872 --- /dev/null +++ b/rp-publish.gradle @@ -0,0 +1,194 @@ +// utilities + +def static replaceVariables(File file, String key, String value) { + if(!file.exists()) return + def regex = '\\$\\{' + key + '\\}' + file.write((file.text =~ /${regex}/).replaceAll(value)) +} + +def isAndroid(){ return project.hasProperty("android"); } + +def getPropertySafe(name, defaultValue = ''){ + if(hasProperty(name) && getProperty(name).trim() != '') return getProperty(name) + return defaultValue +} + +def getPropertyExt(name, defaultValue = ''){ + if ( project.ext.has(name) && project.ext[name] != null ) return project.ext[name] + return getPropertySafe(name, defaultValue) +} + +// def pbHelp(){ +// println(">>> Please make sure MUST-HVAE Properties defined in 'project.ext' or gradle.properties." +// + "\ne.g.: " +// + "\n PB_USER = hyongbai" +// + "\n PB_ARTIFACT_ID = HelloWorld" +// ) +// } + +def pbUpdateVariables(){ + [ + "src/main/groovy/com/qihoo360/replugin/gradle/host/AppConstant.groovy", + "src/main/groovy/com/qihoo360/replugin/gradle/plugin/AppConstant.groovy" + ].forEach { replaceVariables(project.file(it), "RP_VERSION", version) } +} + +// pbHelp() + +///// CONFIG + +apply from: "${buildscript.sourceFile.parent}/rp-config.gradle" + +group = getPropertyExt('RP_GROUP') + +version = getPropertyExt('RP_VERSION') + +def pbArtifactId = getPropertyExt('RP_ARTIFACT_ID') + +pbUpdateVariables() + +// + +def pbUser = getPropertyExt('RP_USER') + +def pbRepo = getPropertyExt('RP_REPO', 'maven') + +def pbEmail = getPropertyExt('RP_EMAIL', pbUser) + +def pbUserId = getPropertyExt('RP_USER_ID', pbUser) + +def pbBintayKey = getPropertyExt('RP_BINTRAY_KEY', System.getenv('RP_BINTRAY_KEY')) + +// + +def pbDesc = getPropertyExt('RP_DESC', 'DONT BE EVIL') + +def pbLicense = getPropertyExt('RP_LICENSES_NAME', 'The Apache Software License, Version 2.0') + +def pbLicenseUrl = getPropertyExt('RP_LICENSES_URL', 'http://www.apache.org/licenses/LICENSE-2.0.txt') + +// + +def pbSiteUrl = getPropertyExt('RP_SITE', "https://github.com") + +def pbGitUrl = getPropertyExt('RP_GIT_URL', pbSiteUrl) + +def pbIssueUrl = getPropertyExt('RP_ISSUE_URL', pbSiteUrl) + +println( + ">>>「publish.gradle」 CONFIGURATIONS:" + + "\n ${group}:${pbArtifactId}:${version}" + + "\n ${pbDesc}" + + "\n Android = ${project.hasProperty("android")}" + + "\n pbUser = ${pbUser}:${pbEmail}" + + "\n pbSite = ${pbSiteUrl}" + + "\n pbGitUrl = ${pbGitUrl}" + + "\n pbIssueUrl = ${pbIssueUrl}" + + "\n License = ${pbLicense}(${pbLicenseUrl})" + + "\n pbBintayKey = ${pbBintayKey}" +) + +//// task + +if (isAndroid()) { // Android libraries + task sourcesJar(overwrite: true, type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs + } + task javadoc(overwrite: true, type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + } +} else { // Java libraries + task sourcesJar(overwrite: true, type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } +} + +task javadocJar(overwrite: true, type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + // archives javadocJar + archives sourcesJar +} + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +/////// publish to BINTRAY + +apply plugin: 'com.jfrog.bintray' +if (isAndroid()) { + apply plugin: 'com.github.dcendents.android-maven' +} + +install { + repositories.mavenInstaller { + pom { + project { + packaging 'aar' + name pbArtifactId + url pbSiteUrl + licenses { + license { + name pbLicense + url pbLicenseUrl + } + } + developers { + developer { + id pbUser + name pbUserId + email pbEmail + } + } + scm { + connection pbGitUrl + developerConnection pbGitUrl + url pbSiteUrl + } + } + } + } +} + +// config bintray + +if ( pbBintayKey == null ){ + System.err.println("bintray access key is EMPTY!!!") +} else { + bintray { + user = pbUser + key = pbBintayKey + configurations = ['archives'] + pkg { + desc = pbDesc + repo = pbRepo + name = pbArtifactId + vcsUrl = pbGitUrl + userOrg = 'qihoo360' + websiteUrl = pbSiteUrl + issueTrackerUrl = pbIssueUrl + licenses = [pbLicense] + publish = true + } + } +} + +/////// publish to LOCAL + +// apply plugin: 'maven' + +// uploadArchives { +// repositories.mavenDeployer { +// repository(url: "file://${System.getenv('HOME')}/.m2") +// pom.groupId = group +// pom.version = version +// pom.artifactId = pbArtifactId +// } +// } \ No newline at end of file