├── .gitignore ├── AndroidManifest.xml ├── LICENSE ├── README.md ├── binrary └── CJFrame_v1.11.jar └── cjframe ├── .classpath ├── .gitignore ├── .project ├── AndroidManifest.xml ├── proguard-project.txt ├── project.properties └── src └── org └── kymjs └── cjframe ├── CJActivityUtils.java ├── CJConfig.java ├── CJProxyActivity.java ├── CJProxyService.java ├── CJTool.java ├── I_LoadPlugin.java ├── NativeLibUnpacker.java ├── api ├── FileTools.java ├── LPluginBugListener.java ├── LPluginLoadListener.java ├── LPluginSearcher.java └── PluginException.java ├── bean ├── ActivityPlugin.java ├── AndroidPackage.java ├── PluginInfo.java └── ServicePlugin.java ├── control ├── I_CJActivity.java ├── PluginActivityControl.java └── PluginInstrument.java ├── manager ├── ApkManager.java ├── CJClassLoader.java ├── LCallbackManager.java └── LPluginBugManager.java └── reflect ├── Reflect.java └── ReflectException.java /.gitignore: -------------------------------------------------------------------------------- 1 | tomsuite.xml 2 | **pom.xml.releaseBackup 3 | release.properties 4 | gen 5 | */seed.txt 6 | notes 7 | logs 8 | gen-external-apklibs 9 | .idea 10 | *.iml 11 | .DS_Store 12 | *.swp 13 | out 14 | .gradle 15 | /local.properties 16 | /build 17 | 18 | ###OSX### 19 | 20 | .DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must ends with two \r. 25 | Icon 26 | 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear on external disk 32 | .Spotlight-V100 33 | .Trashes 34 | 35 | 36 | ###Linux### 37 | 38 | *~ 39 | 40 | # KDE directory preferences 41 | .directory 42 | 43 | 44 | ###Android### 45 | 46 | # Built application files 47 | *.apk 48 | *.ap_ 49 | 50 | # Files for ART and Dalvik VM 51 | *.dex 52 | 53 | # Java class files 54 | *.class 55 | 56 | # Generated files 57 | bin/ 58 | gen/ 59 | 60 | # Gradle files 61 | .gradle/ 62 | .gradletasknamecache 63 | build/ 64 | 65 | # Local configuration file (sdk path, etc) 66 | local.properties 67 | 68 | # Proguard folder generated by Eclipse 69 | proguard/ 70 | 71 | # Lint 72 | lint-report.html 73 | lint-report_files/ 74 | lint_result.txt 75 | 76 | # Mobile Tools for Java (J2ME) 77 | .mtj.tmp/ 78 | 79 | # Package Files # 80 | *.war 81 | *.ear 82 | 83 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 84 | hs_err_pid* 85 | 86 | 87 | ###IntelliJ### 88 | 89 | *.iml 90 | *.ipr 91 | *.iws 92 | .idea/ 93 | 94 | 95 | ###Eclipse### 96 | 97 | *.pydevproject 98 | .metadata 99 | tmp/ 100 | *.tmp 101 | *.bak 102 | *.swp 103 | *~.nib 104 | .settings/ 105 | .loadpath 106 | 107 | # External tool builders 108 | .externalToolBuilders/ 109 | 110 | # Locally stored "Eclipse launch configurations" 111 | *.launch 112 | 113 | # CDT-specific 114 | .cproject 115 | 116 | # PDT-specific 117 | .buildpath 118 | 119 | # sbteclipse plugin 120 | .target 121 | 122 | # TeXlipse plugin 123 | .texlipseXml version="1.0" encoding="UTF-8"?>captures 124 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![OSL](https://kymjs.com/qiniu/image/logo3.png)](https://kymjs.com/) 2 | CJFrameForAndroid简介 3 | 4 | --- 5 | 6 | ## 原理描述 7 | CJFrameForAndroid的实现原理是通过类加载器,动态加载存在于SD卡上的apk包中的Activity。通过使用一个托管所,插件Activity全部事务(包括声明周期与交互事件)将交由托管所来处理,间接实现插件的运行。更多介绍:[CJFrameForAndroid原理介绍](http://blog.kymjs.com/code/2014/10/15/01/)
8 | 一句话概括:CJFrameForAndroid中的托管所,复制了插件中的Activity,来替代插件中的Activity与用户交互。
9 | 10 | ## 框架使用 11 | 1. 需要注意的是,插件中所涉及的权限,都需要在宿主中加以声明。宿主Manifest文件写法请参考:[AndroidManifest.xml](https://github.com/kymjs/CJFrameForAndroid/blob/master/AndroidManifest.xml) 12 | 2. 你只需要在你项目想要启动插件的任意位置(UI线程中),例如Button的Onclick事件中加入如下代码即可。 13 | 14 | ```java 15 | /** 16 | * @param context 上下文对象 17 | * @param path 插件所在的绝对路径 18 | */ 19 | CJActivityUtils.startPlugin(context,path); 20 | ``` 21 | 22 | 23 | ## 许可 24 | Copyright (c) 2014, Zhang Tao. 25 | 26 | Licensed under the Apache License, Version 2.0 (the "License"); 27 | you may not use this file except in compliance with the License. 28 | You may obtain a copy of the License at 29 | 30 | http://www.apache.org/licenses/LICENSE-2.0 31 | 32 | Unless required by applicable law or agreed to in writing, software 33 | distributed under the License is distributed on an "AS IS" BASIS, 34 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | See the License for the specific language governing permissions and 36 | limitations under the License. 37 | -------------------------------------------------------------------------------- /binrary/CJFrame_v1.11.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/CJFrameForAndroid/21632794918105969bd43718974b583d5c414738/binrary/CJFrame_v1.11.jar -------------------------------------------------------------------------------- /cjframe/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /cjframe/.gitignore: -------------------------------------------------------------------------------- 1 | tomsuite.xml 2 | **pom.xml.releaseBackup 3 | release.properties 4 | gen 5 | */seed.txt 6 | notes 7 | logs 8 | gen-external-apklibs 9 | .idea 10 | *.iml 11 | .DS_Store 12 | *.swp 13 | out 14 | .gradle 15 | /local.properties 16 | /build 17 | 18 | ###OSX### 19 | 20 | .DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must ends with two \r. 25 | Icon 26 | 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear on external disk 32 | .Spotlight-V100 33 | .Trashes 34 | 35 | 36 | ###Linux### 37 | 38 | *~ 39 | 40 | # KDE directory preferences 41 | .directory 42 | 43 | 44 | ###Android### 45 | 46 | # Built application files 47 | *.apk 48 | *.ap_ 49 | 50 | # Files for ART and Dalvik VM 51 | *.dex 52 | 53 | # Java class files 54 | *.class 55 | 56 | # Generated files 57 | bin/ 58 | gen/ 59 | 60 | # Gradle files 61 | .gradle/ 62 | .gradletasknamecache 63 | build/ 64 | 65 | # Local configuration file (sdk path, etc) 66 | local.properties 67 | 68 | # Proguard folder generated by Eclipse 69 | proguard/ 70 | 71 | # Lint 72 | lint-report.html 73 | lint-report_files/ 74 | lint_result.txt 75 | 76 | # Mobile Tools for Java (J2ME) 77 | .mtj.tmp/ 78 | 79 | # Package Files # 80 | *.war 81 | *.ear 82 | 83 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 84 | hs_err_pid* 85 | 86 | 87 | ###IntelliJ### 88 | 89 | *.iml 90 | *.ipr 91 | *.iws 92 | .idea/ 93 | 94 | 95 | ###Eclipse### 96 | 97 | *.pydevproject 98 | .metadata 99 | tmp/ 100 | *.tmp 101 | *.bak 102 | *.swp 103 | *~.nib 104 | .settings/ 105 | .loadpath 106 | 107 | # External tool builders 108 | .externalToolBuilders/ 109 | 110 | # Locally stored "Eclipse launch configurations" 111 | *.launch 112 | 113 | # CDT-specific 114 | .cproject 115 | 116 | # PDT-specific 117 | .buildpath 118 | 119 | # sbteclipse plugin 120 | .target 121 | 122 | # TeXlipse plugin 123 | .texlipseXml version="1.0" encoding="UTF-8"?>captures 124 | -------------------------------------------------------------------------------- /cjframe/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | cjframe 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /cjframe/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /cjframe/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /cjframe/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-21 15 | android.library=true 16 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/CJActivityUtils.java: -------------------------------------------------------------------------------- 1 | package org.kymjs.cjframe; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | /** 8 | * Created by lody on 2015/3/24. 9 | */ 10 | public class CJActivityUtils { 11 | 12 | /** 13 | * 直接启动一个apk 14 | * 15 | * @param context 16 | * 当前上下文 17 | * @param pluginPath 18 | * 插件路径 19 | */ 20 | public static void startPlugin(Context context, String pluginPath) { 21 | Intent i = new Intent(context, CJProxyActivity.class); 22 | Bundle bundle = new Bundle(); 23 | bundle.putString(CJConfig.KEY_PLUGIN_DEX_PATH, pluginPath); 24 | i.putExtras(bundle); 25 | context.startActivity(i); 26 | } 27 | 28 | /** 29 | * 直接启动一个apk 30 | * 31 | * @param context 32 | * 当前上下文 33 | * @param pluginPath 34 | * 插件路径 35 | * @param args 36 | * 携带数据 37 | */ 38 | public static void startPlugin(Context context, String pluginPath, 39 | Bundle args) { 40 | Intent i = new Intent(context, CJActivityUtils.class); 41 | args.putString(CJConfig.KEY_PLUGIN_DEX_PATH, pluginPath); 42 | i.putExtras(args); 43 | context.startActivity(i); 44 | } 45 | 46 | /** 47 | * 启动插件中的指定activity 48 | * 49 | * @param context 50 | * @param pluginPath 51 | * @param activityName 52 | * 要启动的插件的activity名 53 | */ 54 | public static void startActivity(Context context, String pluginPath, 55 | String activityName) { 56 | Intent i = new Intent(context, CJProxyActivity.class); 57 | Bundle bundle = new Bundle(); 58 | bundle.putString(CJConfig.KEY_PLUGIN_DEX_PATH, pluginPath); 59 | bundle.putString(CJConfig.DEF_PLUGIN_CLASS_NAME, activityName); 60 | i.putExtras(bundle); 61 | context.startActivity(i); 62 | } 63 | 64 | /** 65 | * 启动插件中的指定activity 66 | * 67 | * @param context 68 | * @param pluginPath 69 | * @param activityName 70 | * 要启动的插件的activity名 71 | * @param args 72 | * 携带数据 73 | */ 74 | public static void startActivity(Context context, String pluginPath, 75 | String activityName, Bundle args) { 76 | Intent i = new Intent(context, CJProxyActivity.class); 77 | args.putString(CJConfig.KEY_PLUGIN_DEX_PATH, pluginPath); 78 | args.putString(CJConfig.DEF_PLUGIN_CLASS_NAME, activityName); 79 | i.putExtras(args); 80 | context.startActivity(i); 81 | } 82 | 83 | /** 84 | * 启动插件中的指定service 85 | * 86 | * @param context 87 | * @param pluginPath 88 | * @param serviceName 89 | * 要启动的插件的activity名 90 | */ 91 | public static void startService(Context context, String pluginPath, 92 | String serviceName) { 93 | Intent i = new Intent(context, CJProxyService.class); 94 | CJProxyService.SERVICE_APK_PATH = pluginPath; 95 | CJProxyService.SERVICE_CLASS_NAME = serviceName; 96 | context.startService(i); 97 | } 98 | 99 | /** 100 | * 启动插件中的指定service 101 | * 102 | * @param context 103 | * @param pluginPath 104 | * @param serviceName 105 | * 要启动的插件的activity名 106 | * @param args 107 | * 携带数据 108 | */ 109 | public static void startService(Context context, String pluginPath, 110 | String serviceName, Bundle args) { 111 | Intent i = new Intent(context, CJProxyService.class); 112 | i.putExtras(args); 113 | CJProxyService.SERVICE_APK_PATH = pluginPath; 114 | CJProxyService.SERVICE_CLASS_NAME = serviceName; 115 | context.startService(i); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/CJConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe; 17 | 18 | /** 19 | * 保存插件的配置信息 20 | */ 21 | public class CJConfig { 22 | /** 23 | * 当没有代理Activity时的默认值 24 | */ 25 | public static final String DEF_PLUGIN_CLASS_NAME = "no_activity_proxy_now"; 26 | /** 27 | * 当没有插件Dex路径时的默认值 28 | */ 29 | public static final String DEF_PLUGIN_DEX_PATH = "no_dex_path"; 30 | 31 | /** 32 | * 获取插件activity类名的Key 33 | */ 34 | public static final String KEY_PLUGIN_ACT_NAME = "plugin_act_name"; 35 | /** 36 | * 获取插件dex的key 37 | */ 38 | public static final String KEY_PLUGIN_DEX_PATH = "plugin_dex_path"; 39 | /** 40 | * 插件Activity的索引值(AndroidManifest.xml里的第几个Activity?) 41 | */ 42 | public static final String KEY_PLUGIN_INDEX = "activity_index"; 43 | 44 | /** 45 | * 是否使用插件的标题 46 | */ 47 | public static boolean usePluginTitle = true; 48 | /** 49 | * ProxyService需要托管新的Service 50 | */ 51 | public static boolean FLAG_NEW_SERVICE; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/CJProxyActivity.java: -------------------------------------------------------------------------------- 1 | package org.kymjs.cjframe; 2 | 3 | import java.io.File; 4 | import java.io.FileDescriptor; 5 | import java.io.PrintWriter; 6 | 7 | import org.kymjs.cjframe.api.PluginException; 8 | import org.kymjs.cjframe.bean.ActivityPlugin; 9 | import org.kymjs.cjframe.control.I_CJActivity; 10 | import org.kymjs.cjframe.control.PluginActivityControl; 11 | import org.kymjs.cjframe.manager.ApkManager; 12 | import org.kymjs.cjframe.manager.LCallbackManager; 13 | import org.kymjs.cjframe.manager.LPluginBugManager; 14 | 15 | import android.app.Activity; 16 | import android.app.Fragment; 17 | import android.content.ComponentName; 18 | import android.content.Context; 19 | import android.content.Intent; 20 | import android.content.pm.ActivityInfo; 21 | import android.content.pm.PackageInfo; 22 | import android.content.pm.PackageManager; 23 | import android.content.res.AssetManager; 24 | import android.content.res.Configuration; 25 | import android.content.res.Resources; 26 | import android.content.res.Resources.Theme; 27 | import android.graphics.Bitmap; 28 | import android.graphics.Canvas; 29 | import android.os.Build; 30 | import android.os.Bundle; 31 | import android.util.AttributeSet; 32 | import android.util.Log; 33 | import android.view.KeyEvent; 34 | import android.view.Menu; 35 | import android.view.MotionEvent; 36 | import android.view.View; 37 | 38 | /** 39 | * Created by lody on 2015/3/27. 40 | */ 41 | public class CJProxyActivity extends Activity implements I_LoadPlugin { 42 | 43 | private static final String TAG = CJProxyActivity.class.getSimpleName(); 44 | private ActivityPlugin remotePlugin; 45 | 46 | @Override 47 | public ActivityPlugin loadPlugin(Activity ctx, String apkPath) { 48 | // 插件必须要确认有没有经过初始化,不然只是空壳 49 | remotePlugin = new ActivityPlugin(ctx, apkPath); 50 | return remotePlugin; 51 | } 52 | 53 | @Override 54 | public ActivityPlugin loadPlugin(Activity ctx, String apkPath, 55 | String activityName) { 56 | ActivityPlugin plugin = loadPlugin(ctx, apkPath); 57 | plugin.setTopActivityName(activityName); 58 | fillPlugin(plugin); 59 | return plugin; 60 | } 61 | 62 | /** 63 | * 装载插件 64 | * 65 | * @param plugin 66 | */ 67 | @Override 68 | public void fillPlugin(ActivityPlugin plugin) { 69 | if (plugin == null) { 70 | throw new RuntimeException("Plugin is null!"); 71 | } 72 | String apkPath = plugin.getPluginPath(); 73 | File apk = new File(apkPath); 74 | if (!apk.exists()) 75 | throw new RuntimeException(apkPath); 76 | 77 | if (!this.remotePlugin.from().canUse()) { 78 | Log.i(TAG, "Plugin is not been init,init it now!"); 79 | ApkManager.initApk(plugin.from(), this); 80 | // remotePlugin.from().debug(); 81 | 82 | } else { 83 | Log.i(TAG, "Plugin have been init."); 84 | } 85 | fillPluginTheme(plugin); 86 | fillPluginActivity(plugin); 87 | 88 | } 89 | 90 | private void fillPluginTheme(ActivityPlugin plugin) { 91 | 92 | Theme pluginTheme = plugin.from().pluginRes.newTheme(); 93 | pluginTheme.setTo(super.getTheme()); 94 | plugin.setTheme(pluginTheme); 95 | 96 | PackageInfo packageInfo = plugin.from().pluginPkgInfo; 97 | String mClass = plugin.getTopActivityName(); 98 | 99 | Log.i(TAG, "Before fill Plugin 's Theme,We check the plugin:info = " 100 | + packageInfo + "topActivityName = " + mClass); 101 | 102 | int defaultTheme = packageInfo.applicationInfo.theme; 103 | ActivityInfo curActivityInfo = null; 104 | for (ActivityInfo a : packageInfo.activities) { 105 | if (a.name.equals(mClass)) { 106 | curActivityInfo = a; 107 | if (a.theme != 0) { 108 | defaultTheme = a.theme; 109 | } else if (defaultTheme != 0) { 110 | // ignore 111 | } else { 112 | // 支持不同系统的默认Theme 113 | if (Build.VERSION.SDK_INT >= 14) { 114 | defaultTheme = android.R.style.Theme_DeviceDefault; 115 | } else { 116 | defaultTheme = android.R.style.Theme; 117 | } 118 | } 119 | break; 120 | } 121 | } 122 | 123 | pluginTheme.applyStyle(defaultTheme, true); 124 | 125 | setTheme(defaultTheme); 126 | if (curActivityInfo != null) { 127 | getWindow().setSoftInputMode(curActivityInfo.softInputMode); 128 | } 129 | 130 | if (CJConfig.usePluginTitle) { 131 | CharSequence title = null; 132 | try { 133 | title = CJTool.getAppName(this, plugin.getPluginPath()); 134 | } catch (PackageManager.NameNotFoundException e) { 135 | e.printStackTrace(); 136 | } 137 | if (title != null) 138 | setTitle(title); 139 | } 140 | 141 | } 142 | 143 | /** 144 | * 装载插件的Activity 145 | * 146 | * @param plugin 147 | */ 148 | private void fillPluginActivity(ActivityPlugin plugin) { 149 | try { 150 | String top = plugin.getTopActivityName(); 151 | if (top == null) { 152 | top = plugin.from().pluginPkgInfo.activities[0].name; 153 | plugin.setTopActivityName(top); 154 | } 155 | Activity myPlugin = (Activity) plugin.from().pluginLoader 156 | .loadClass(plugin.getTopActivityName()).newInstance(); 157 | plugin.setCurrentPluginActivity(myPlugin); 158 | 159 | } catch (Exception e) { 160 | throw new RuntimeException(e.getMessage()); 161 | } 162 | } 163 | 164 | @Override 165 | protected void onCreate(Bundle savedInstanceState) { 166 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 167 | @Override 168 | public void uncaughtException(Thread thread, final Throwable ex) { 169 | PluginException bug = new PluginException(); 170 | bug.error = ex; 171 | bug.errorTime = System.currentTimeMillis(); 172 | bug.errorThread = thread; 173 | bug.errorPlugin = remotePlugin; 174 | bug.processId = android.os.Process.myPid(); 175 | LPluginBugManager.callAllBugListener(bug); 176 | } 177 | }); 178 | super.onCreate(savedInstanceState); 179 | final Bundle pluginMessage = getIntent().getExtras(); 180 | 181 | String pluginActivityName; 182 | String pluginDexPath; 183 | if (pluginMessage != null) { 184 | pluginActivityName = pluginMessage.getString( 185 | CJConfig.KEY_PLUGIN_ACT_NAME, 186 | CJConfig.DEF_PLUGIN_CLASS_NAME); 187 | pluginDexPath = pluginMessage.getString( 188 | CJConfig.KEY_PLUGIN_DEX_PATH, CJConfig.DEF_PLUGIN_DEX_PATH); 189 | } else { 190 | throw new RuntimeException("Please put the Plugin Path!"); 191 | } 192 | if (pluginDexPath == CJConfig.DEF_PLUGIN_DEX_PATH) { 193 | throw new RuntimeException("Please put the Plugin Path!"); 194 | } 195 | 196 | remotePlugin = loadPlugin(CJProxyActivity.this, pluginDexPath); 197 | 198 | if (pluginActivityName != CJConfig.DEF_PLUGIN_CLASS_NAME) { 199 | remotePlugin.setTopActivityName(pluginActivityName); 200 | } 201 | 202 | fillPlugin(remotePlugin); 203 | 204 | PluginActivityControl control = new PluginActivityControl( 205 | CJProxyActivity.this, remotePlugin.getCurrentPluginActivity(), 206 | remotePlugin.from().pluginApplication); 207 | remotePlugin.setControl(control); 208 | control.dispatchProxyToPlugin(); 209 | try { 210 | control.callOnCreate(savedInstanceState); 211 | LCallbackManager.callAllOnCreate(savedInstanceState); 212 | } catch (Exception e) { 213 | processError(e); 214 | } 215 | 216 | } 217 | 218 | private void processError(Exception e) { 219 | e.printStackTrace(); 220 | } 221 | 222 | @Override 223 | public Resources getResources() { 224 | if (remotePlugin == null) 225 | return super.getResources(); 226 | return remotePlugin.from().pluginRes == null ? super.getResources() 227 | : remotePlugin.from().pluginRes; 228 | } 229 | 230 | @Override 231 | public Theme getTheme() { 232 | if (remotePlugin == null) 233 | return super.getTheme(); 234 | return remotePlugin.getTheme() == null ? super.getTheme() 235 | : remotePlugin.getTheme(); 236 | } 237 | 238 | @Override 239 | public AssetManager getAssets() { 240 | if (remotePlugin == null) 241 | return super.getAssets(); 242 | return remotePlugin.from().pluginAssets == null ? super.getAssets() 243 | : remotePlugin.from().pluginAssets; 244 | } 245 | 246 | @Override 247 | public ClassLoader getClassLoader() { 248 | if (remotePlugin == null) { 249 | return super.getClassLoader(); 250 | } 251 | if (remotePlugin.from().canUse()) { 252 | return remotePlugin.from().pluginLoader; 253 | } 254 | return super.getClassLoader(); 255 | } 256 | 257 | @Override 258 | protected void onResume() { 259 | super.onResume(); 260 | if (remotePlugin == null) { 261 | return; 262 | } 263 | I_CJActivity caller = remotePlugin.getControl(); 264 | if (caller != null) { 265 | caller.callOnResume(); 266 | LCallbackManager.callAllOnResume(); 267 | } 268 | } 269 | 270 | @Override 271 | protected void onStart() { 272 | super.onStart(); 273 | if (remotePlugin == null) { 274 | return; 275 | } 276 | 277 | I_CJActivity caller = remotePlugin.getControl(); 278 | if (caller != null) { 279 | try { 280 | caller.callOnStop(); 281 | LCallbackManager.callAllOnStop(); 282 | } catch (Exception e) { 283 | processError(e); 284 | 285 | } 286 | } 287 | } 288 | 289 | @Override 290 | protected void onDestroy() { 291 | super.onDestroy(); 292 | if (remotePlugin == null) { 293 | return; 294 | } 295 | I_CJActivity caller = remotePlugin.getControl(); 296 | if (caller != null) { 297 | 298 | { 299 | try { 300 | caller.callOnDestroy(); 301 | LCallbackManager.callAllOnDestroy(); 302 | } catch (Exception e) { 303 | processError(e); 304 | } 305 | } 306 | } 307 | 308 | } 309 | 310 | @Override 311 | protected void onPause() { 312 | super.onPause(); 313 | if (remotePlugin == null) { 314 | return; 315 | } 316 | I_CJActivity caller = remotePlugin.getControl(); 317 | if (caller != null) { 318 | 319 | try { 320 | caller.callOnPause(); 321 | LCallbackManager.callAllOnPause(); 322 | } catch (Exception e) { 323 | processError(e); 324 | } 325 | } 326 | } 327 | 328 | @Override 329 | public void onBackPressed() { 330 | 331 | if (remotePlugin == null) { 332 | super.onBackPressed(); 333 | } 334 | 335 | I_CJActivity caller = remotePlugin.getControl(); 336 | if (caller != null) { 337 | try { 338 | caller.callOnBackPressed(); 339 | LCallbackManager.callAllOnBackPressed(); 340 | } catch (Exception e) { 341 | processError(e); 342 | } 343 | 344 | } 345 | } 346 | 347 | @Override 348 | protected void onStop() { 349 | super.onStop(); 350 | if (remotePlugin == null) { 351 | return; 352 | } 353 | I_CJActivity caller = remotePlugin.getControl(); 354 | if (caller != null) { 355 | 356 | { 357 | try { 358 | caller.callOnStop(); 359 | LCallbackManager.callAllOnStop(); 360 | } catch (Exception e) { 361 | processError(e); 362 | } 363 | 364 | } 365 | } 366 | } 367 | 368 | @Override 369 | protected void onRestart() { 370 | super.onRestart(); 371 | if (remotePlugin == null) { 372 | return; 373 | } 374 | 375 | I_CJActivity caller = remotePlugin.getControl(); 376 | if (caller != null) { 377 | try { 378 | caller.callOnRestart(); 379 | LCallbackManager.callAllOnRestart(); 380 | } catch (Exception e) { 381 | processError(e); 382 | } 383 | } 384 | } 385 | 386 | @Override 387 | public boolean onKeyDown(int keyCode, KeyEvent event) { 388 | if (remotePlugin == null) { 389 | return super.onKeyDown(keyCode, event); 390 | } 391 | I_CJActivity caller = remotePlugin.getControl(); 392 | if (caller != null) { 393 | 394 | LCallbackManager.callAllOnKeyDown(keyCode, event); 395 | return caller.callOnKeyDown(keyCode, event); 396 | 397 | } 398 | 399 | return super.onKeyDown(keyCode, event); 400 | } 401 | 402 | @Override 403 | public ComponentName startService(Intent service) { 404 | // 转移Service跳转目标 405 | CJProxyService.SERVICE_CLASS_NAME = service.getComponent() 406 | .getClassName(); 407 | service.setClass(this, CJProxyService.class); 408 | return super.startService(service); 409 | } 410 | 411 | @Override 412 | public void dump(String prefix, FileDescriptor fd, PrintWriter writer, 413 | String[] args) { 414 | super.dump(prefix, fd, writer, args); 415 | if (remotePlugin == null) { 416 | return; 417 | } 418 | I_CJActivity caller = remotePlugin.getControl(); 419 | if (caller != null) { 420 | caller.callDump(prefix, fd, writer, args); 421 | } 422 | } 423 | 424 | @Override 425 | public void onConfigurationChanged(Configuration newConfig) { 426 | super.onConfigurationChanged(newConfig); 427 | if (remotePlugin == null) { 428 | return; 429 | } 430 | I_CJActivity caller = remotePlugin.getControl(); 431 | if (caller != null) { 432 | caller.callOnConfigurationChanged(newConfig); 433 | } 434 | } 435 | 436 | @Override 437 | protected void onPostResume() { 438 | super.onPostResume(); 439 | if (remotePlugin == null) { 440 | return; 441 | } 442 | I_CJActivity caller = remotePlugin.getControl(); 443 | if (caller != null) { 444 | caller.callOnPostResume(); 445 | } 446 | } 447 | 448 | @Override 449 | public void onDetachedFromWindow() { 450 | if (remotePlugin == null) { 451 | return; 452 | } 453 | I_CJActivity caller = remotePlugin.getControl(); 454 | if (caller != null) { 455 | caller.callOnDetachedFromWindow(); 456 | } 457 | super.onDetachedFromWindow(); 458 | } 459 | 460 | @Override 461 | public View onCreateView(String name, Context context, AttributeSet attrs) { 462 | if (remotePlugin == null) { 463 | return super.onCreateView(name, context, attrs); 464 | } 465 | I_CJActivity caller = remotePlugin.getControl(); 466 | if (caller != null) { 467 | return caller.callOnCreateView(name, context, attrs); 468 | } 469 | return super.onCreateView(name, context, attrs); 470 | } 471 | 472 | @Override 473 | public View onCreateView(View parent, String name, Context context, 474 | AttributeSet attrs) { 475 | if (remotePlugin == null) { 476 | return super.onCreateView(parent, name, context, attrs); 477 | } 478 | I_CJActivity caller = remotePlugin.getControl(); 479 | if (caller != null) { 480 | return caller.callOnCreateView(parent, name, context, attrs); 481 | } 482 | return super.onCreateView(parent, name, context, attrs); 483 | } 484 | 485 | @Override 486 | protected void onNewIntent(Intent intent) { 487 | super.onNewIntent(intent); 488 | if (remotePlugin == null) { 489 | return; 490 | } 491 | I_CJActivity caller = remotePlugin.getControl(); 492 | if (caller != null) { 493 | caller.callOnNewIntent(intent); 494 | } 495 | } 496 | 497 | @Override 498 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 499 | super.onActivityResult(requestCode, resultCode, data); 500 | if (remotePlugin == null) { 501 | return; 502 | } 503 | remotePlugin.getControl().getPluginRef() 504 | .call("onActivityResult", requestCode, resultCode, data); 505 | } 506 | 507 | @Override 508 | public void onAttachFragment(Fragment fragment) { 509 | 510 | super.onAttachFragment(fragment); 511 | if (remotePlugin == null) { 512 | return; 513 | } 514 | remotePlugin.getCurrentPluginActivity().onAttachFragment(fragment); 515 | } 516 | 517 | @Override 518 | public View onCreatePanelView(int featureId) { 519 | 520 | if (remotePlugin == null) 521 | return super.onCreatePanelView(featureId); 522 | return remotePlugin.getCurrentPluginActivity().onCreatePanelView( 523 | featureId); 524 | } 525 | 526 | @Override 527 | public void onOptionsMenuClosed(Menu menu) { 528 | 529 | super.onOptionsMenuClosed(menu); 530 | if (remotePlugin == null) { 531 | return; 532 | } 533 | remotePlugin.getCurrentPluginActivity().onOptionsMenuClosed(menu); 534 | } 535 | 536 | @Override 537 | public void onPanelClosed(int featureId, Menu menu) { 538 | 539 | super.onPanelClosed(featureId, menu); 540 | if (remotePlugin == null) { 541 | return; 542 | } 543 | remotePlugin.getCurrentPluginActivity().onPanelClosed(featureId, menu); 544 | } 545 | 546 | @Override 547 | public boolean onKeyUp(int keyCode, KeyEvent event) { 548 | 549 | if (remotePlugin == null) { 550 | return super.onKeyUp(keyCode, event); 551 | } 552 | return remotePlugin.getCurrentPluginActivity().onKeyUp(keyCode, event); 553 | } 554 | 555 | @Override 556 | public void onAttachedToWindow() { 557 | 558 | super.onAttachedToWindow(); 559 | if (remotePlugin == null) { 560 | return; 561 | } 562 | remotePlugin.getCurrentPluginActivity().onAttachedToWindow(); 563 | } 564 | 565 | @Override 566 | public CharSequence onCreateDescription() { 567 | 568 | if (remotePlugin == null) 569 | return super.onCreateDescription(); 570 | 571 | return remotePlugin.getCurrentPluginActivity().onCreateDescription(); 572 | } 573 | 574 | @Override 575 | public boolean onGenericMotionEvent(MotionEvent event) { 576 | if (remotePlugin == null) 577 | return super.onGenericMotionEvent(event); 578 | 579 | return remotePlugin.getCurrentPluginActivity().onGenericMotionEvent( 580 | event); 581 | } 582 | 583 | @Override 584 | public void onContentChanged() { 585 | 586 | super.onContentChanged(); 587 | if (remotePlugin == null) { 588 | return; 589 | } 590 | remotePlugin.getCurrentPluginActivity().onContentChanged(); 591 | } 592 | 593 | @Override 594 | public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) { 595 | 596 | if (remotePlugin == null) { 597 | return super.onCreateThumbnail(outBitmap, canvas); 598 | } 599 | return remotePlugin.getCurrentPluginActivity().onCreateThumbnail( 600 | outBitmap, canvas); 601 | } 602 | 603 | } 604 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/CJProxyService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe; 17 | 18 | import org.kymjs.cjframe.bean.ServicePlugin; 19 | import org.kymjs.cjframe.manager.ApkManager; 20 | import org.kymjs.cjframe.manager.CJClassLoader; 21 | import org.kymjs.cjframe.reflect.Reflect; 22 | 23 | import android.app.Service; 24 | import android.content.ComponentName; 25 | import android.content.Intent; 26 | import android.content.res.AssetManager; 27 | import android.content.res.Configuration; 28 | import android.content.res.Resources; 29 | import android.os.IBinder; 30 | 31 | 32 | public class CJProxyService extends Service { 33 | 34 | ServicePlugin remote; 35 | public static String SERVICE_CLASS_NAME = Service.class.getName(); 36 | public static String SERVICE_APK_PATH = CJClassLoader.finalApkPath; 37 | 38 | @Override 39 | public IBinder onBind(Intent i) { 40 | return remote.getCurrentPluginService().onBind(i); 41 | } 42 | 43 | @Override 44 | public int onStartCommand(Intent intent, int flags, int startId) { 45 | if (!remote.getCurrentPluginService().getClass().getName() 46 | .equals(SERVICE_CLASS_NAME)) { 47 | fillService(); 48 | remote.getCurrentPluginService().onCreate(); 49 | } 50 | return remote.getCurrentPluginService().onStartCommand(intent, flags, 51 | startId); 52 | } 53 | 54 | @Override 55 | public void onCreate() { 56 | super.onCreate(); 57 | 58 | fillService(); 59 | remote.getCurrentPluginService().onCreate(); 60 | } 61 | 62 | private void fillService() { 63 | remote = new ServicePlugin(this, SERVICE_APK_PATH); 64 | remote.setTopServiceName(SERVICE_CLASS_NAME); 65 | remote.from().debug(); 66 | if (!remote.from().canUse()) { 67 | ApkManager.initApk(remote.from(), this); 68 | } 69 | 70 | try { 71 | Service plugin = (Service) remote.from().pluginLoader.loadClass( 72 | remote.getTopServiceName()).newInstance(); 73 | remote.setCurrentPluginService(plugin); 74 | Reflect thiz = Reflect.on(this); 75 | Reflect.on(plugin).call("attach", this, thiz.get("mThread"), 76 | getClass().getName(), thiz.get("mToken"), getApplication(), 77 | thiz.get("mActivityManager")); 78 | 79 | } catch (IllegalAccessException e) { 80 | } catch (ClassNotFoundException e) { 81 | } catch (InstantiationException e) { 82 | } 83 | } 84 | 85 | @Override 86 | public void onStart(Intent intent, int startId) { 87 | super.onStart(intent, startId); 88 | remote.getCurrentPluginService().onStart(intent, startId); 89 | } 90 | 91 | @Override 92 | public void onConfigurationChanged(Configuration newConfig) { 93 | 94 | super.onConfigurationChanged(newConfig); 95 | remote.getCurrentPluginService().onConfigurationChanged(newConfig); 96 | } 97 | 98 | @Override 99 | public boolean onUnbind(Intent intent) { 100 | 101 | return remote.getCurrentPluginService().onUnbind(intent); 102 | 103 | } 104 | 105 | @Override 106 | public void onDestroy() { 107 | 108 | super.onDestroy(); 109 | remote.getCurrentPluginService().onDestroy(); 110 | } 111 | 112 | @Override 113 | public void onRebind(Intent intent) { 114 | 115 | super.onRebind(intent); 116 | remote.getCurrentPluginService().onRebind(intent); 117 | } 118 | 119 | @Override 120 | public void onTrimMemory(int level) { 121 | 122 | super.onTrimMemory(level); 123 | remote.getCurrentPluginService().onTrimMemory(level); 124 | } 125 | 126 | @Override 127 | public void onLowMemory() { 128 | 129 | super.onLowMemory(); 130 | remote.getCurrentPluginService().onLowMemory(); 131 | } 132 | 133 | @Override 134 | public void onTaskRemoved(Intent rootIntent) { 135 | 136 | super.onTaskRemoved(rootIntent); 137 | remote.getCurrentPluginService().onTaskRemoved(rootIntent); 138 | } 139 | 140 | @Override 141 | public Resources getResources() { 142 | if (remote == null) 143 | return super.getResources(); 144 | return remote.from().pluginRes == null ? super.getResources() : remote 145 | .from().pluginRes; 146 | } 147 | 148 | @Override 149 | public AssetManager getAssets() { 150 | if (remote == null) 151 | return super.getAssets(); 152 | return remote.from().pluginAssets == null ? super.getAssets() : remote 153 | .from().pluginAssets; 154 | } 155 | 156 | @Override 157 | public ClassLoader getClassLoader() { 158 | if (remote == null) { 159 | return super.getClassLoader(); 160 | } 161 | if (remote.from().canUse()) { 162 | return remote.from().pluginLoader; 163 | } 164 | return super.getClassLoader(); 165 | } 166 | 167 | @Override 168 | public ComponentName startService(Intent service) { 169 | CJProxyService.SERVICE_CLASS_NAME = service.getComponent() 170 | .getClassName(); 171 | service.setClass(this, CJProxyService.class); 172 | return super.startService(service); 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/CJTool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe; 17 | 18 | import android.content.Context; 19 | import android.content.pm.ActivityInfo; 20 | import android.content.pm.ApplicationInfo; 21 | import android.content.pm.PackageInfo; 22 | import android.content.pm.PackageManager; 23 | import android.graphics.drawable.Drawable; 24 | import android.os.Build; 25 | 26 | public class CJTool { 27 | /** 28 | * 获取一个apk的信息 29 | * 30 | * @param cxt 31 | * 应用上下文 32 | * @param apkPath 33 | * apk所在绝对路径 34 | * @return 35 | */ 36 | public static PackageInfo getAppInfo(Context cxt, String apkPath) 37 | throws PackageManager.NameNotFoundException { 38 | PackageManager pm = cxt.getPackageManager(); 39 | PackageInfo pkgInfo = null; 40 | pkgInfo = pm.getPackageArchiveInfo(apkPath, 41 | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES); 42 | return pkgInfo; 43 | } 44 | 45 | /** 46 | * 获取一个apk中,在Manifest.xml中声明的第一个Activity的信息 47 | * 48 | * @param cxt 49 | * 应用上下文 50 | * @param apkPath 51 | * apk所在绝对路径 52 | * @return 53 | */ 54 | public static ActivityInfo getActivityInfo(Context cxt, String apkPath) 55 | throws PackageManager.NameNotFoundException { 56 | return getActivityInfo(getAppInfo(cxt, apkPath), 0); 57 | } 58 | 59 | /** 60 | * 获取一个apk中,在Manifest.xml中声明的第index个Activity的信息
61 | * 注意index的大小不正确可能会报ArrayIndexOutOfBoundsException 62 | * 63 | * @param cxt 64 | * 应用上下文 65 | * @param apkPath 66 | * apk所在绝对路径 67 | * @param index 68 | * 要获取的Activity在Manifest.xml中声明的序列(从0开始) 69 | * @return 70 | * @throws ArrayIndexOutOfBoundsException 71 | * index超出范围会报 72 | */ 73 | public static ActivityInfo getActivityInfo(Context cxt, String apkPath, 74 | int index) throws PackageManager.NameNotFoundException { 75 | return getActivityInfo(getAppInfo(cxt, apkPath), index); 76 | } 77 | 78 | /** 79 | * 获取一个apk中,在Manifest.xml中声明的第index个Activity的信息
80 | * 注意index的大小不正确可能会报ArrayIndexOutOfBoundsException 81 | * 82 | * @param pkgInfo 83 | * Activity所在应用的PackageInfo 84 | * @param index 85 | * Activity在插件Manifest.xml中的序列(从0开始) 86 | * @return 87 | * @throws ArrayIndexOutOfBoundsException 88 | * index超出范围会报 89 | */ 90 | public static ActivityInfo getActivityInfo(PackageInfo pkgInfo, int index) { 91 | return pkgInfo.activities[index]; 92 | } 93 | 94 | /** 95 | * 获取应用图标 96 | * 97 | * @param cxt 98 | * 应用上下文 99 | * @param apkPath 100 | * apk所在绝对路径 101 | * @return 102 | */ 103 | public static Drawable getAppIcon(Context cxt, String apkPath) 104 | throws PackageManager.NameNotFoundException { 105 | PackageManager pm = cxt.getPackageManager(); 106 | PackageInfo pkgInfo = getAppInfo(cxt, apkPath); 107 | if (pkgInfo == null) { 108 | return null; 109 | } else { 110 | ApplicationInfo appInfo = pkgInfo.applicationInfo; 111 | if (Build.VERSION.SDK_INT >= 8) { 112 | appInfo.sourceDir = apkPath; 113 | appInfo.publicSourceDir = apkPath; 114 | } 115 | return pm.getApplicationIcon(appInfo); 116 | } 117 | } 118 | 119 | /** 120 | * 获取指定APK应用名 121 | * 122 | * @param cxt 123 | * 应用上下文 124 | * @param apkPath 125 | * apk所在绝对路径 126 | * @return 127 | */ 128 | public static CharSequence getAppName(Context cxt, String apkPath) 129 | throws PackageManager.NameNotFoundException { 130 | PackageManager pm = cxt.getPackageManager(); 131 | PackageInfo pkgInfo = getAppInfo(cxt, apkPath); 132 | if (pkgInfo == null) { 133 | return null; 134 | } else { 135 | ApplicationInfo appInfo = pkgInfo.applicationInfo; 136 | if (Build.VERSION.SDK_INT >= 8) { 137 | appInfo.sourceDir = apkPath; 138 | appInfo.publicSourceDir = apkPath; 139 | } 140 | return pm.getApplicationLabel(appInfo); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/I_LoadPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe; 17 | 18 | import org.kymjs.cjframe.bean.ActivityPlugin; 19 | 20 | import android.app.Activity; 21 | 22 | 23 | public interface I_LoadPlugin { 24 | /** 25 | * 根据apk路径加载一个插件 26 | * 27 | * @param proxyParent 28 | * 代理activity 29 | * @param apkPath 30 | * apk路径 31 | * @return 插件的bean 32 | */ 33 | ActivityPlugin loadPlugin(Activity proxyParent, String apkPath); 34 | 35 | /** 36 | * 根据apk路径加载一个插件 37 | * 38 | * @param proxyParent 39 | * 代理activity 40 | * @param apkPath 41 | * apk路径 42 | * @param activityName 43 | * 启动的activity名 44 | * @return 45 | */ 46 | ActivityPlugin loadPlugin(Activity proxyParent, String apkPath, 47 | String activityName); 48 | 49 | /** 50 | * 装载一个插件
51 | * 一个刚刚初始化的插件只有apk路径信息, 本操作能够使其变得完整(装载资源,类加载器等)。 52 | * 53 | * @param plugin 54 | * 需要装载的插件 55 | */ 56 | void fillPlugin(ActivityPlugin plugin); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/NativeLibUnpacker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.util.Enumeration; 21 | import java.util.HashMap; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.zip.ZipEntry; 26 | import java.util.zip.ZipException; 27 | import java.util.zip.ZipFile; 28 | 29 | import org.kymjs.cjframe.api.FileTools; 30 | 31 | import android.util.Log; 32 | 33 | 34 | /** 35 | * 根据CPU型号解压apk内的相应的.so文件 36 | */ 37 | public class NativeLibUnpacker { 38 | 39 | public static final String TAG = NativeLibUnpacker.class.getSimpleName(); 40 | public static final String DEF_ARCH_1 = "armeabi"; 41 | public static final String DEF_ARCH_2 = "armeabi-v7a"; 42 | public static String ARCH = System.getProperty("os.arch"); 43 | 44 | public static void unPackSOFromApk(String apkPath, String toPath) { 45 | 46 | Log.i(TAG, "CPU is " + ARCH); 47 | 48 | try { 49 | ZipFile apk = new ZipFile(new File(apkPath)); 50 | boolean hasLib = extractLibFile(apk, new File(toPath)); 51 | if (hasLib) { 52 | Log.i(TAG, "The plugin is contains .so files."); 53 | } else { 54 | Log.i(TAG, "The plugin isn't contain any .so files."); 55 | } 56 | 57 | } catch (Exception e) { 58 | Log.e(TAG, e.getMessage()); 59 | } 60 | 61 | } 62 | 63 | private static boolean extractLibFile(ZipFile zip, File to) 64 | throws ZipException, IOException { 65 | 66 | Map> archLibEntries = new HashMap>(); 67 | for (Enumeration e = zip.entries(); e 68 | .hasMoreElements();) { 69 | ZipEntry entry = e.nextElement(); 70 | String name = entry.getName(); 71 | // Log.i(TAG,"found file :" + name); 72 | if (name.startsWith("/")) { 73 | name = name.substring(1); 74 | } 75 | if (name.startsWith("lib/")) { 76 | if (entry.isDirectory()) { 77 | continue; 78 | } 79 | int sp = name.indexOf('/', 4); 80 | String en2add; 81 | if (sp > 0) { 82 | String osArch = name.substring(4, sp); 83 | en2add = osArch.toLowerCase(); 84 | } else { 85 | en2add = DEF_ARCH_1; 86 | } 87 | List zipEntries = archLibEntries.get(en2add); 88 | if (zipEntries == null) { 89 | zipEntries = new LinkedList(); 90 | archLibEntries.put(en2add, zipEntries); 91 | } 92 | zipEntries.add(entry); 93 | } 94 | } 95 | /* 96 | * new EasyFor>(archLibEntries.values()) { 97 | * 98 | * @Override public void onNewElement(List element) { new 99 | * EasyFor(element){ 100 | * 101 | * @Override public void onNewElement(ZipEntry element) { 102 | * Log.i(TAG,element.getName()); } }; } }; 103 | */ 104 | 105 | List libEntries = archLibEntries.get(ARCH.toLowerCase()); 106 | if (libEntries == null) { 107 | libEntries = archLibEntries.get(DEF_ARCH_1); 108 | if (libEntries == null) { 109 | libEntries = archLibEntries.get(DEF_ARCH_2); 110 | } 111 | } 112 | boolean hasLib = false;// 是否包含so 113 | if (libEntries != null) { 114 | hasLib = true; 115 | if (!to.exists()) { 116 | to.mkdirs(); 117 | } 118 | for (ZipEntry libEntry : libEntries) { 119 | String name = libEntry.getName(); 120 | String pureName = name.substring(name.lastIndexOf('/') + 1); 121 | File target = new File(to, pureName); 122 | FileTools.writeToFile(zip.getInputStream(libEntry), target); 123 | } 124 | } 125 | 126 | return hasLib; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/api/FileTools.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.api; 17 | 18 | import java.io.BufferedOutputStream; 19 | import java.io.ByteArrayInputStream; 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.nio.channels.Channels; 26 | import java.nio.channels.FileChannel; 27 | import java.nio.channels.ReadableByteChannel; 28 | 29 | /** 30 | * 文件工具类 31 | */ 32 | public class FileTools { 33 | 34 | /** 35 | * 将流写入文件 36 | * 37 | * @param dataIns 38 | * @param target 39 | * @throws java.io.IOException 40 | */ 41 | public static void writeToFile(InputStream dataIns, File target) 42 | throws IOException { 43 | final int BUFFER = 1024; 44 | BufferedOutputStream bos = new BufferedOutputStream( 45 | new FileOutputStream(target)); 46 | int count; 47 | byte data[] = new byte[BUFFER]; 48 | while ((count = dataIns.read(data, 0, BUFFER)) != -1) { 49 | bos.write(data, 0, count); 50 | } 51 | bos.close(); 52 | } 53 | 54 | /** 55 | * 从字节数组中写入文件 56 | * 57 | * @param data 58 | * @param target 59 | * @throws java.io.IOException 60 | */ 61 | public static void writeToFile(byte[] data, File target) throws IOException { 62 | FileOutputStream fo = null; 63 | ReadableByteChannel src = null; 64 | FileChannel out = null; 65 | try { 66 | src = Channels.newChannel(new ByteArrayInputStream(data)); 67 | fo = new FileOutputStream(target); 68 | out = fo.getChannel(); 69 | out.transferFrom(src, 0, data.length); 70 | } finally { 71 | if (fo != null) { 72 | fo.close(); 73 | } 74 | if (src != null) { 75 | src.close(); 76 | } 77 | if (out != null) { 78 | out.close(); 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * 复制文件 85 | * 86 | * @param source 87 | * - 源文件 88 | * @param target 89 | * - 目标文件 90 | */ 91 | public static void copyFile(File source, File target) { 92 | 93 | FileInputStream fi = null; 94 | FileOutputStream fo = null; 95 | 96 | FileChannel in = null; 97 | 98 | FileChannel out = null; 99 | 100 | try { 101 | fi = new FileInputStream(source); 102 | 103 | fo = new FileOutputStream(target); 104 | 105 | in = fi.getChannel();// 得到对应的文件通道 106 | 107 | out = fo.getChannel();// 得到对应的文件通道 108 | 109 | in.transferTo(0, in.size(), out);// 连接两个通道,并且从in通道读取,然后写入out通道 110 | 111 | } catch (IOException e) { 112 | e.printStackTrace(); 113 | } finally { 114 | try { 115 | fi.close(); 116 | 117 | in.close(); 118 | 119 | fo.close(); 120 | 121 | out.close(); 122 | 123 | } catch (IOException e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/api/LPluginBugListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.api; 17 | 18 | /** 19 | * Created by lody on 2015/4/3. 20 | */ 21 | public interface LPluginBugListener { 22 | void OnError(PluginException bug); 23 | } 24 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/api/LPluginLoadListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.api; 17 | 18 | /** 19 | * Created by lody on 2015/4/3. 20 | */ 21 | public interface LPluginLoadListener { 22 | void onLoadStart(); 23 | 24 | void onLoading(); 25 | 26 | void onLoadFinish(); 27 | } 28 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/api/LPluginSearcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.api; 17 | 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | 23 | import org.kymjs.cjframe.CJTool; 24 | import org.kymjs.cjframe.bean.PluginInfo; 25 | 26 | import android.content.Context; 27 | import android.content.pm.PackageManager; 28 | import android.graphics.drawable.Drawable; 29 | 30 | 31 | /** 32 | * Created by lody on 2015/4/4. 33 | */ 34 | public class LPluginSearcher { 35 | 36 | public static List listAllPluginFromDirectory( 37 | final Context context, String directory) { 38 | File dir = new File(directory); 39 | if (!dir.isDirectory() || !dir.exists()) { 40 | return null; 41 | } 42 | 43 | File[] files = dir.listFiles(); 44 | if (files.length == 0) { 45 | return null; 46 | } 47 | final List apks = filterApk(files); 48 | if (apks.size() == 0) { 49 | return null; 50 | } 51 | final List infos = new ArrayList(); 52 | 53 | Iterator iterator = apks.iterator(); 54 | while (iterator.hasNext()) { 55 | onNewElement(context, iterator.next(), infos); 56 | } 57 | 58 | return infos; 59 | 60 | } 61 | 62 | public static void onNewElement(Context context, File element, 63 | List infos) { 64 | String filePath = element.getAbsolutePath(); 65 | Drawable drawable = null; 66 | try { 67 | drawable = CJTool.getAppIcon(context, filePath); 68 | 69 | } catch (PackageManager.NameNotFoundException e) { 70 | e.printStackTrace(); 71 | 72 | } 73 | PluginInfo info = new PluginInfo(); 74 | info.setApkIcon(drawable); 75 | info.setApkPath(filePath); 76 | infos.add(info); 77 | 78 | } 79 | 80 | public static List filterApk(File[] files) { 81 | final List apks = new ArrayList(); 82 | for (File file : files) { 83 | // 根据后缀判断是否是一个apk 84 | if (!file.getName().toLowerCase().endsWith(".apk")) { 85 | continue; 86 | } 87 | apks.add(file); 88 | } 89 | return apks; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/api/PluginException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.api; 17 | 18 | import org.kymjs.cjframe.bean.ActivityPlugin; 19 | 20 | /** 21 | * Created by lody on 2015/4/3. 22 | */ 23 | public class PluginException { 24 | public Throwable error; 25 | public long errorTime; 26 | public Thread errorThread; 27 | public ActivityPlugin errorPlugin; 28 | public int processId; 29 | } 30 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/bean/ActivityPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.bean; 17 | 18 | import org.kymjs.cjframe.control.PluginActivityControl; 19 | import org.kymjs.cjframe.manager.ApkManager; 20 | 21 | import android.app.Activity; 22 | import android.content.res.Resources; 23 | 24 | /** 25 | * 插件的一个Activity的bean,包含了这个Activity所需要的全部信息 26 | */ 27 | public class ActivityPlugin { 28 | /** 29 | * 插件所属apk 30 | */ 31 | AndroidPackage from; 32 | /** 33 | * 插件主题 34 | */ 35 | private Resources.Theme currentPluginTheme; 36 | /** 37 | * 插件代理器 38 | */ 39 | private Activity proxyParent; 40 | /** 41 | * 插件实体Activity 42 | */ 43 | private Activity CurrentPluginActivity; 44 | /** 45 | * 插件的第一个Activity名 46 | */ 47 | private String topActivityName = null; 48 | /** 49 | * 插件控制器 50 | */ 51 | private PluginActivityControl control; 52 | 53 | /** 54 | * 得到处于顶部的插件Activity名 55 | * 56 | * @return 处于顶部的插件Activity名 57 | */ 58 | public String getTopActivityName() { 59 | return topActivityName; 60 | } 61 | 62 | /** 63 | * 设置处于顶部的插件Activity名 64 | * 65 | * @param topActivityName 66 | * 处于顶部的插件Activity名 67 | */ 68 | public void setTopActivityName(String topActivityName) { 69 | this.topActivityName = topActivityName; 70 | } 71 | 72 | /** 73 | * 74 | * @return 插件控制器 75 | */ 76 | public PluginActivityControl getControl() { 77 | return control; 78 | } 79 | 80 | /** 81 | * 设置插件控制器 82 | * 83 | * @param control 84 | */ 85 | public void setControl(PluginActivityControl control) { 86 | this.control = control; 87 | } 88 | 89 | /** 90 | * 得到代理的实体 91 | * 92 | * @return 93 | */ 94 | public Activity getProxyParent() { 95 | return proxyParent; 96 | } 97 | 98 | /** 99 | * 设置代理的实体 100 | * 101 | * @param proxyParent 102 | */ 103 | public void setProxyParent(Activity proxyParent) { 104 | this.proxyParent = proxyParent; 105 | } 106 | 107 | /** 108 | * 109 | * @return 插件所在路径 110 | */ 111 | public String getPluginPath() { 112 | return from.pluginPath; 113 | } 114 | 115 | /** 116 | * 得到当前主题 117 | * 118 | * @return 当前主题 119 | */ 120 | public Resources.Theme getTheme() { 121 | return currentPluginTheme; 122 | } 123 | 124 | /** 125 | * 设置当前主题 126 | * 127 | * @param currentPluginTheme 128 | * 当前主题 129 | */ 130 | public void setTheme(Resources.Theme currentPluginTheme) { 131 | this.currentPluginTheme = currentPluginTheme; 132 | } 133 | 134 | /** 135 | * @return 当前的插件Activity 136 | */ 137 | public Activity getCurrentPluginActivity() { 138 | return CurrentPluginActivity; 139 | } 140 | 141 | public void setCurrentPluginActivity(Activity currentPluginActivity) { 142 | CurrentPluginActivity = currentPluginActivity; 143 | } 144 | 145 | /** 146 | * 插件是否已经可以正常使用? 147 | * 148 | * @return 149 | */ 150 | public boolean canUse() { 151 | return proxyParent != null && getCurrentPluginActivity() != null; 152 | } 153 | 154 | public AndroidPackage from() { 155 | return from; 156 | } 157 | 158 | public ActivityPlugin(Activity proxyParent, String apkPath) { 159 | this.proxyParent = proxyParent; 160 | from = ApkManager.get(apkPath); 161 | from.bindDexLoader(proxyParent); 162 | // NOTE:此时from这个对象可能没有初始化,也可能已经初始化了。 163 | // 可以通过from.canUse()来判断。 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/bean/AndroidPackage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.bean; 17 | 18 | import org.kymjs.cjframe.manager.CJClassLoader; 19 | 20 | import android.app.Application; 21 | import android.content.Context; 22 | import android.content.pm.PackageInfo; 23 | import android.content.res.AssetManager; 24 | import android.content.res.Resources; 25 | import android.util.Log; 26 | 27 | 28 | import dalvik.system.DexClassLoader; 29 | 30 | /** 31 | * Created by lody on 2015/4/6. 32 | */ 33 | public class AndroidPackage { 34 | 35 | public static final String TAG = AndroidPackage.class.getSimpleName(); 36 | 37 | /** 38 | * 插件的Application名 39 | */ 40 | public String applicationName; 41 | /** 42 | * 插件的Application 43 | */ 44 | public Application pluginApplication; 45 | /** 46 | * 插件路径 47 | */ 48 | public String pluginPath; 49 | /** 50 | * 插件资源管理器 51 | */ 52 | public AssetManager pluginAssets; 53 | /** 54 | * 插件资源 55 | */ 56 | public Resources pluginRes; 57 | /** 58 | * 插件包信息 59 | */ 60 | public PackageInfo pluginPkgInfo; 61 | /** 62 | * 插件加载器 63 | */ 64 | public DexClassLoader pluginLoader; 65 | 66 | /** 67 | * 绑定apk路径 68 | * 69 | * @param apkPath 70 | */ 71 | public void attach(String apkPath) { 72 | pluginPath = apkPath; 73 | } 74 | 75 | // ///////////////////////////////////////////////////////////////////////////////////////////// 76 | // GETTER && SETTER start // 77 | // ///////////////////////////////////////////////////////////////////////////////////////////// 78 | public void setPluginPkgInfo(PackageInfo pluginPkgInfo) { 79 | this.pluginPkgInfo = pluginPkgInfo; 80 | } 81 | 82 | public void setPluginRes(Resources pluginRes) { 83 | this.pluginRes = pluginRes; 84 | } 85 | 86 | public void setPluginAssets(AssetManager pluginAssets) { 87 | this.pluginAssets = pluginAssets; 88 | } 89 | 90 | public void setPluginPath(String pluginPath) { 91 | this.pluginPath = pluginPath; 92 | } 93 | 94 | public void setPluginApplication(Application pluginApplication) { 95 | this.pluginApplication = pluginApplication; 96 | } 97 | 98 | public void setApplicationName(String applicationName) { 99 | this.applicationName = applicationName; 100 | } 101 | 102 | // ///////////////////////////////////////////////////////////////////////////////////////////// 103 | // GETTER && SETTER end // 104 | // ///////////////////////////////////////////////////////////////////////////////////////////// 105 | /** 106 | * 是否能够使用? 107 | * 108 | * @return 109 | */ 110 | public boolean canUse() { 111 | return pluginPkgInfo != null && pluginLoader != null 112 | && pluginPath != null; 113 | } 114 | 115 | /** 116 | * 仅供测试使用 117 | */ 118 | public void debug() { 119 | Log.i(TAG, "Plugin Path = " + pluginPath); 120 | Log.i(TAG, "Plugin Resources = " + pluginRes); 121 | Log.i(TAG, "Plugin Assets = " + pluginAssets); 122 | Log.i(TAG, "Plugin Loader = " + pluginLoader); 123 | Log.i(TAG, "Plugin PackageInfo = " + pluginPkgInfo); 124 | Log.i(TAG, "Plugin Application name = " + applicationName); 125 | Log.i(TAG, "Plugin Application = " + pluginApplication); 126 | 127 | } 128 | 129 | /** 130 | * 与一个类加载器绑定 131 | * 132 | * @param ctx 133 | */ 134 | public void bindDexLoader(Context ctx) { 135 | pluginLoader = CJClassLoader.getClassLoader(pluginPath, ctx, 136 | CJClassLoader.getSystemLoader() == null ? ctx.getClassLoader() 137 | : CJClassLoader.getSystemLoader()); 138 | } 139 | 140 | /** 141 | * 擦除所有保留的信息 NOTE:不要在其它地方保留本类中对象的引用 142 | */ 143 | public void recycle() { 144 | applicationName = null; 145 | pluginPkgInfo = null; 146 | pluginPath = null; 147 | pluginLoader = null; 148 | pluginAssets = null; 149 | pluginApplication = null; 150 | pluginRes = null; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/bean/PluginInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.bean; 17 | 18 | import android.graphics.drawable.Drawable; 19 | 20 | /** 21 | * Created by lody on 2015/4/4. 22 | */ 23 | public class PluginInfo { 24 | 25 | public String getApkPath() { 26 | return apkPath; 27 | } 28 | 29 | public void setApkPath(String apkPath) { 30 | this.apkPath = apkPath; 31 | } 32 | 33 | public Drawable getApkIcon() { 34 | return apkIcon; 35 | } 36 | 37 | public void setApkIcon(Drawable apkIcon) { 38 | this.apkIcon = apkIcon; 39 | } 40 | 41 | /** 42 | * apk路径 43 | */ 44 | String apkPath; 45 | /** 46 | * apk图标 47 | */ 48 | Drawable apkIcon; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/bean/ServicePlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.bean; 17 | 18 | import org.kymjs.cjframe.manager.ApkManager; 19 | 20 | import android.app.Service; 21 | 22 | 23 | public class ServicePlugin { 24 | /** 25 | * 插件所属apk 26 | */ 27 | AndroidPackage from; 28 | /** 29 | * 插件代理器 30 | */ 31 | private Service proxyParent; 32 | /** 33 | * 插件实体Service 34 | */ 35 | private Service CurrentPluginService; 36 | /** 37 | * 插件的第一个Service 38 | */ 39 | private String topServiceName = null; 40 | 41 | public ServicePlugin(Service proxyParent, String apkPath) { 42 | this.proxyParent = proxyParent; 43 | from = ApkManager.get(apkPath); 44 | from.bindDexLoader(proxyParent); 45 | // NOTE:此时from这个对象可能没有初始化,也可能已经初始化了,为了保证效率,需要判断。 46 | // 可以通过from.canUse()来判断。 47 | } 48 | 49 | public AndroidPackage from() { 50 | return from; 51 | } 52 | 53 | /** 54 | * 设置代理的实体 55 | * 56 | * @param proxyParent 57 | */ 58 | public void setProxyParent(Service proxyParent) { 59 | this.proxyParent = proxyParent; 60 | } 61 | 62 | /** 63 | * 得到代理的实体 64 | * 65 | * @return 66 | */ 67 | public Service getProxyParent() { 68 | return proxyParent; 69 | } 70 | 71 | public void setCurrentPluginService(Service currentPluginService) { 72 | CurrentPluginService = currentPluginService; 73 | } 74 | 75 | /** 76 | * @return 当前的插件Service 77 | */ 78 | public Service getCurrentPluginService() { 79 | return CurrentPluginService; 80 | } 81 | 82 | /** 83 | * 设置当前的插件Service 84 | * 85 | */ 86 | public void setTopServiceName(String topServiceName) { 87 | this.topServiceName = topServiceName; 88 | } 89 | 90 | public String getTopServiceName() { 91 | return topServiceName; 92 | } 93 | 94 | /** 95 | * 插件是否已经可以正常使用? 96 | * 97 | * @return 98 | */ 99 | public boolean canUse() { 100 | return proxyParent != null && getCurrentPluginService() != null; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/control/I_CJActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.control; 17 | 18 | import java.io.FileDescriptor; 19 | import java.io.PrintWriter; 20 | 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.res.Configuration; 24 | import android.os.Bundle; 25 | import android.util.AttributeSet; 26 | import android.view.KeyEvent; 27 | import android.view.View; 28 | 29 | /** 30 | * Created by lody on 2015/3/26. 31 | */ 32 | public interface I_CJActivity { 33 | 34 | void callOnCreate(Bundle saveInstance); 35 | 36 | void callOnStart(); 37 | 38 | void callOnResume(); 39 | 40 | void callOnDestroy(); 41 | 42 | void callOnStop(); 43 | 44 | void callOnRestart(); 45 | 46 | void callOnSaveInstanceState(Bundle outState); 47 | 48 | void callOnRestoreInstanceState(Bundle savedInstanceState); 49 | 50 | void callOnPause(); 51 | 52 | void callOnBackPressed(); 53 | 54 | boolean callOnKeyDown(int keyCode, KeyEvent event); 55 | 56 | void callDump(String prefix, FileDescriptor fd, PrintWriter writer, 57 | String[] args); 58 | 59 | void callOnPostResume(); 60 | 61 | void callOnDetachedFromWindow(); 62 | 63 | View callOnCreateView(String name, Context context, AttributeSet attrs); 64 | 65 | View callOnCreateView(View parent, String name, Context context, 66 | AttributeSet attrs); 67 | 68 | void callOnNewIntent(Intent intent); 69 | 70 | void callOnConfigurationChanged(Configuration newConfig); 71 | } 72 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/control/PluginActivityControl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.control; 17 | 18 | import java.io.FileDescriptor; 19 | import java.io.PrintWriter; 20 | 21 | import org.kymjs.cjframe.reflect.Reflect; 22 | import org.kymjs.cjframe.reflect.ReflectException; 23 | 24 | import android.app.Activity; 25 | import android.app.Application; 26 | import android.app.Instrumentation; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.content.res.Configuration; 30 | import android.os.Bundle; 31 | import android.util.AttributeSet; 32 | import android.view.KeyEvent; 33 | import android.view.View; 34 | 35 | 36 | /** 37 | * Created by lody on 2015/3/26. 38 | *

39 | * 插件的控制器
40 | * 派发插件事件和控制插件生命周期 41 | */ 42 | public class PluginActivityControl implements I_CJActivity { 43 | 44 | Activity proxy;// 代理Activity 45 | Activity plugin;// 插件Activity 46 | Reflect proxyRef;// 指向代理Activity的反射工具类 47 | Reflect pluginRef;// 指向插件Activity的反射工具类 48 | Application app;// 分派给插件的Application 49 | Instrumentation i; 50 | 51 | /** 52 | * 53 | * @param proxy 54 | * 代理Activity 55 | * @param plugin 56 | * 插件Activity 57 | */ 58 | public PluginActivityControl(Activity proxy, Activity plugin) { 59 | this(proxy, plugin, null); 60 | 61 | } 62 | 63 | /** 64 | * 65 | * @param proxy 66 | * 代理Activity 67 | * @param plugin 68 | * 插件Activity 69 | * @param app 70 | * 分派给插件的Application 71 | */ 72 | public PluginActivityControl(Activity proxy, Activity plugin, 73 | Application app) { 74 | this.proxy = proxy; 75 | this.plugin = plugin; 76 | this.app = app; 77 | 78 | i = new Instrumentation(); 79 | 80 | // 使反射工具类指向相应的对象 81 | proxyRef = Reflect.on(proxy); 82 | pluginRef = Reflect.on(plugin); 83 | } 84 | 85 | public void dispatchProxyToPlugin() { 86 | 87 | if (plugin.getBaseContext() != null) 88 | return; 89 | try { 90 | // Finals 修改以前的注入方式,采用原生的方式 91 | Instrumentation instrumentation = proxyRef.get("mInstrumentation"); 92 | pluginRef.call( 93 | // 方法名 94 | "attach", 95 | // Context context 96 | proxy, 97 | // ActivityThread aThread 98 | proxyRef.get("mMainThread"), 99 | // Instrumentation instr 100 | new PluginInstrument(instrumentation), 101 | // IBinder token 102 | proxyRef.get("mToken"), 103 | // int ident 104 | proxyRef.get("mEmbeddedID") == null ? 0 : proxyRef 105 | .get("mEmbeddedID"), 106 | // Application application 107 | app == null ? proxy.getApplication() : app, 108 | // Intent intent 109 | proxy.getIntent(), 110 | // ActivityInfo info 111 | proxyRef.get("mActivityInfo"), 112 | // CharSequence title 113 | proxy.getTitle(), 114 | // Activity parent 115 | proxy.getParent(), 116 | // String id 117 | proxyRef.get("mEmbeddedID"), 118 | // NonConfigurationInstances lastNonConfigurationInstances 119 | proxy.getLastNonConfigurationInstance(), 120 | // Configuration config 121 | proxyRef.get("mCurrentConfig")); 122 | 123 | pluginRef.set("mWindow", proxy.getWindow()); 124 | plugin.getWindow().setCallback(plugin); 125 | Reflect.on(proxy.getBaseContext()).call("setOuterContext", plugin); 126 | 127 | } catch (ReflectException e) { 128 | e.printStackTrace(); 129 | } 130 | 131 | } 132 | 133 | /** 134 | * @return 插件的Activity 135 | */ 136 | public Activity getPlugin() { 137 | return plugin; 138 | } 139 | 140 | /** 141 | * 设置插件的Activity 142 | * 143 | * @param plugin 144 | */ 145 | public void setPlugin(Activity plugin) { 146 | this.plugin = plugin; 147 | proxyRef = Reflect.on(plugin); 148 | } 149 | 150 | /** 151 | * 得到代理的Activity 152 | * 153 | * @return 154 | */ 155 | public Activity getProxy() { 156 | return proxy; 157 | } 158 | 159 | /** 160 | * 设置代理的Activity 161 | * 162 | * @param proxy 163 | */ 164 | public void setProxy(Activity proxy) { 165 | this.proxy = proxy; 166 | proxyRef = Reflect.on(proxy); 167 | } 168 | 169 | /** 170 | * @return 代理Activity的反射工具类 171 | */ 172 | public Reflect getProxyRef() { 173 | return proxyRef; 174 | } 175 | 176 | /** 177 | * 178 | * @return 插件Activity的反射工具类 179 | */ 180 | public Reflect getPluginRef() { 181 | return pluginRef; 182 | } 183 | 184 | /** 185 | * 执行插件的onCreate方法 186 | * 187 | * @see android.app.Activity#onCreate(android.os.Bundle) 188 | * @param saveInstance 189 | */ 190 | @Override 191 | public void callOnCreate(Bundle saveInstance) { 192 | // getPluginRef().call("onCreate", saveInstance); 193 | i.callActivityOnCreate(plugin, saveInstance); 194 | } 195 | 196 | /** 197 | * 执行插件的onStart方法 198 | * 199 | * @see android.app.Activity#onStart() 200 | */ 201 | @Override 202 | public void callOnStart() { 203 | // getPluginRef().call("onStart"); 204 | i.callActivityOnStop(plugin); 205 | } 206 | 207 | /** 208 | * 执行插件的onResume方法 209 | * 210 | * @see android.app.Activity#onResume() 211 | */ 212 | @Override 213 | public void callOnResume() { 214 | // getPluginRef().call("onResume"); 215 | i.callActivityOnResume(plugin); 216 | } 217 | 218 | /** 219 | * 执行插件的onDestroy方法 220 | * 221 | * @see android.app.Activity#onDestroy() 222 | */ 223 | @Override 224 | public void callOnDestroy() { 225 | // getPluginRef().call("onDestroy"); 226 | i.callActivityOnDestroy(plugin); 227 | } 228 | 229 | /** 230 | * 执行插件的onStop方法 231 | * 232 | * @see android.app.Activity#onStop() 233 | */ 234 | @Override 235 | public void callOnStop() { 236 | // getPluginRef().call("onStop"); 237 | i.callActivityOnStop(plugin); 238 | } 239 | 240 | /** 241 | * 执行插件的onRestart方法 242 | * 243 | * @see android.app.Activity#onRestart() 244 | */ 245 | @Override 246 | public void callOnRestart() { 247 | // getPluginRef().call("onRestart"); 248 | i.callActivityOnRestart(plugin); 249 | } 250 | 251 | /** 252 | * 执行插件的onSaveInstanceState方法 253 | * 254 | * @see android.app.Activity#onSaveInstanceState(android.os.Bundle) 255 | * @param outState 256 | */ 257 | @Override 258 | public void callOnSaveInstanceState(Bundle outState) { 259 | getPluginRef().call("onSaveInstanceState", outState); 260 | } 261 | 262 | /** 263 | * 执行插件的onRestoreInstanceState方法 264 | * 265 | * @see android.app.Activity#onRestoreInstanceState(android.os.Bundle) 266 | * @param savedInstanceState 267 | */ 268 | @Override 269 | public void callOnRestoreInstanceState(Bundle savedInstanceState) { 270 | getPluginRef().call("onRestoreInstanceState", savedInstanceState); 271 | } 272 | 273 | /** 274 | * 执行插件的onStop方法 275 | * 276 | * @see android.app.Activity#onStop() 277 | */ 278 | @Override 279 | public void callOnPause() { 280 | // getPluginRef().call("onStop"); 281 | i.callActivityOnPause(plugin); 282 | } 283 | 284 | /** 285 | * 执行插件的onBackPressed方法 286 | * 287 | * @see android.app.Activity#onBackPressed() 288 | */ 289 | @Override 290 | public void callOnBackPressed() { 291 | // getPluginRef().call("onBackPressed"); 292 | plugin.onBackPressed(); 293 | } 294 | 295 | /** 296 | * 执行插件的onKeyDown方法 297 | * 298 | * @see android.app.Activity#onKeyDown(int, android.view.KeyEvent) 299 | * @param keyCode 300 | * @param event 301 | * @return 302 | */ 303 | @Override 304 | public boolean callOnKeyDown(int keyCode, KeyEvent event) { 305 | return plugin.onKeyDown(keyCode, event); 306 | // return (Boolean)getPluginRef().call("onKeyDown", keyCode, 307 | // event).get(); 308 | } 309 | 310 | // Finals ADD 修复Fragment BUG 311 | @Override 312 | public void callDump(String prefix, FileDescriptor fd, PrintWriter writer, 313 | String[] args) { 314 | // getPluginRef().call("dump", prefix, fd, writer, args); 315 | plugin.dump(prefix, fd, writer, args); 316 | } 317 | 318 | @Override 319 | public void callOnConfigurationChanged(Configuration newConfig) { 320 | // getPluginRef().call("onConfigurationChanged", newConfig); 321 | plugin.onConfigurationChanged(newConfig); 322 | } 323 | 324 | @Override 325 | public void callOnPostResume() { 326 | getPluginRef().call("onPostResume"); 327 | 328 | } 329 | 330 | @Override 331 | public void callOnDetachedFromWindow() { 332 | // getPluginRef().call("onDetachedFromWindow"); 333 | plugin.onDetachedFromWindow(); 334 | } 335 | 336 | @Override 337 | public View callOnCreateView(String name, Context context, 338 | AttributeSet attrs) { 339 | return plugin.onCreateView(name, context, attrs); 340 | // return getPluginRef().call("onCreateView", name, context, 341 | // attrs).get(); 342 | } 343 | 344 | @Override 345 | public View callOnCreateView(View parent, String name, Context context, 346 | AttributeSet attrs) { 347 | return plugin.onCreateView(parent, name, context, attrs); 348 | // return getPluginRef().call("onCreateView", parent, name, context, 349 | // attrs).get(); 350 | } 351 | 352 | @Override 353 | public void callOnNewIntent(Intent intent) { 354 | // getPluginRef().call("onNewIntent"); 355 | i.callActivityOnNewIntent(plugin, intent); 356 | } 357 | 358 | } 359 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/control/PluginInstrument.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.control; 17 | 18 | import org.kymjs.cjframe.CJActivityUtils; 19 | import org.kymjs.cjframe.CJConfig; 20 | import org.kymjs.cjframe.manager.CJClassLoader; 21 | import org.kymjs.cjframe.reflect.Reflect; 22 | 23 | import android.app.Activity; 24 | import android.app.Application; 25 | import android.app.Instrumentation; 26 | import android.content.ComponentName; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | import android.os.IBinder; 31 | import android.util.Log; 32 | 33 | 34 | /** 35 | * Created by lody on 2015/3/27. 36 | * 37 | * @author Lody 38 | * 39 | * 负责转移插件的跳转目标
40 | * @see android.app.Activity#startActivity(android.content.Intent) 41 | */ 42 | public class PluginInstrument extends Instrumentation { 43 | 44 | private static final String TAG = PluginInstrument.class.getSimpleName(); 45 | Instrumentation pluginIn; 46 | Reflect instrumentRef; 47 | 48 | public PluginInstrument(Instrumentation pluginIn) { 49 | this.pluginIn = pluginIn; 50 | instrumentRef = Reflect.on(pluginIn); 51 | } 52 | 53 | /** @Override */ 54 | public ActivityResult execStartActivity(Context who, IBinder contextThread, 55 | IBinder token, Activity target, Intent intent, int requestCode, 56 | Bundle options) { 57 | 58 | ComponentName componentName = intent.getComponent(); 59 | if (componentName == null) { 60 | return instrumentRef.call("execStartActivity", who, contextThread, 61 | token, target, intent, requestCode, options).get(); 62 | } 63 | String className = componentName.getClassName(); 64 | intent.setClass(who, CJActivityUtils.class); 65 | 66 | Log.i(TAG, "Jump to " + className + "[" + CJClassLoader.finalApkPath 67 | + "]"); 68 | 69 | intent.putExtra(CJConfig.KEY_PLUGIN_DEX_PATH, 70 | CJClassLoader.finalApkPath); 71 | intent.putExtra(CJConfig.KEY_PLUGIN_ACT_NAME, className); 72 | 73 | return instrumentRef.call("execStartActivity", who, contextThread, 74 | token, target, intent, requestCode, options).get(); 75 | 76 | } 77 | 78 | /** @Override */ 79 | public ActivityResult execStartActivity(Context who, IBinder contextThread, 80 | IBinder token, Activity target, Intent intent, int requestCode) { 81 | 82 | ComponentName componentName = intent.getComponent(); 83 | if (componentName == null) { 84 | return instrumentRef.call("execStartActivity", who, contextThread, 85 | token, target, intent, requestCode).get(); 86 | } 87 | String className = componentName.getClassName(); 88 | intent.setClass(who, CJActivityUtils.class); 89 | 90 | Log.i(TAG, "Jump to " + className + "[" + CJClassLoader.finalApkPath 91 | + "]"); 92 | 93 | intent.putExtra(CJConfig.KEY_PLUGIN_DEX_PATH, 94 | CJClassLoader.finalApkPath); 95 | intent.putExtra(CJConfig.KEY_PLUGIN_ACT_NAME, className); 96 | 97 | return instrumentRef.call("execStartActivity", who, contextThread, 98 | token, target, intent, requestCode).get(); 99 | 100 | } 101 | 102 | @Override 103 | public void onStart() { 104 | pluginIn.onStart(); 105 | } 106 | 107 | @Override 108 | public void onCreate(Bundle arguments) { 109 | pluginIn.onCreate(arguments); 110 | } 111 | 112 | @Override 113 | public void onDestroy() { 114 | pluginIn.onDestroy(); 115 | } 116 | 117 | @Override 118 | public boolean onException(Object obj, Throwable e) { 119 | return pluginIn.onException(obj, e); 120 | } 121 | 122 | @Override 123 | public void callActivityOnCreate(Activity activity, Bundle icicle) { 124 | pluginIn.callActivityOnCreate(activity, icicle); 125 | } 126 | 127 | @Override 128 | public void callActivityOnNewIntent(Activity activity, Intent intent) { 129 | pluginIn.callActivityOnNewIntent(activity, intent); 130 | } 131 | 132 | @Override 133 | public void callApplicationOnCreate(Application app) { 134 | pluginIn.callApplicationOnCreate(app); 135 | } 136 | 137 | @Override 138 | public void callActivityOnDestroy(Activity activity) { 139 | pluginIn.callActivityOnDestroy(activity); 140 | } 141 | 142 | @Override 143 | public void callActivityOnPause(Activity activity) { 144 | pluginIn.callActivityOnDestroy(activity); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/manager/ApkManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.manager; 17 | 18 | import java.io.File; 19 | import java.io.FileNotFoundException; 20 | import java.util.Map; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | import org.kymjs.cjframe.CJTool; 24 | import org.kymjs.cjframe.bean.AndroidPackage; 25 | import org.kymjs.cjframe.reflect.Reflect; 26 | 27 | import android.app.Application; 28 | import android.content.Context; 29 | import android.content.pm.PackageInfo; 30 | import android.content.pm.PackageManager; 31 | import android.content.res.AssetManager; 32 | import android.content.res.Resources; 33 | import android.util.Log; 34 | 35 | 36 | /** 37 | * Created by lody on 2015/4/6. 38 | */ 39 | public final class ApkManager { 40 | 41 | private static final Map apks = new ConcurrentHashMap(); 42 | private static final String TAG = ApkManager.class.getSimpleName(); 43 | 44 | public static AndroidPackage get(String apkPath) { 45 | AndroidPackage apk; 46 | apk = apks.get(apkPath); 47 | if (apk == null) { 48 | apk = new AndroidPackage(); 49 | apk.attach(apkPath); 50 | apks.put(apkPath, apk); 51 | } 52 | return apk; 53 | } 54 | 55 | public static void initApk(AndroidPackage apk, Context ctx) { 56 | String apkPath = apk.pluginPath; 57 | File file = new File(apkPath); 58 | if (!file.exists()) 59 | try { 60 | throw new FileNotFoundException(apkPath); 61 | } catch (FileNotFoundException e) { 62 | } 63 | Log.i(TAG, "Init a plugin on" + apkPath); 64 | if (!apk.canUse()) { 65 | Log.i(TAG, "Plugin is not been init,init it now!"); 66 | fillPluginInfo(apk, ctx); 67 | fillPluginRes(apk, ctx); 68 | fillPluginApplication(apk, ctx); 69 | } else { 70 | Log.i(TAG, "Plugin have been init."); 71 | } 72 | 73 | } 74 | 75 | private static void fillPluginInfo(AndroidPackage apk, Context ctx) { 76 | PackageInfo info = null; 77 | try { 78 | info = CJTool.getAppInfo(ctx, apk.pluginPath); 79 | } catch (PackageManager.NameNotFoundException e) { 80 | throw new RuntimeException("file not found" + apk.pluginPath); 81 | } 82 | if (info == null) { 83 | throw new RuntimeException("Can't create Plugin from :" 84 | + apk.pluginPath); 85 | } 86 | apk.setPluginPkgInfo(info); 87 | apk.setApplicationName(info.applicationInfo.className); 88 | } 89 | 90 | private static void fillPluginRes(AndroidPackage apk, Context ctx) { 91 | try { 92 | AssetManager assetManager = AssetManager.class.newInstance(); 93 | Reflect assetRef = Reflect.on(assetManager); 94 | assetRef.call("addAssetPath", apk.pluginPath); 95 | Log.i(TAG, "Assets = " + assetManager); 96 | apk.setPluginAssets(assetManager); 97 | 98 | Resources pluginRes = new Resources(assetManager, ctx 99 | .getResources().getDisplayMetrics(), ctx.getResources() 100 | .getConfiguration()); 101 | Log.i(TAG, "Res = " + pluginRes); 102 | apk.setPluginRes(pluginRes); 103 | 104 | } catch (Exception e) { 105 | e.printStackTrace(); 106 | } 107 | } 108 | 109 | private static void fillPluginApplication(AndroidPackage apk, Context ctx) { 110 | String applicationName = apk.applicationName; 111 | if (applicationName == null) 112 | return; 113 | if (applicationName.isEmpty()) 114 | return; 115 | 116 | ClassLoader loader = apk.pluginLoader; 117 | if (loader == null) 118 | throw new RuntimeException("Not found ClassLoader in plugin!"); 119 | try { 120 | Application pluginApp = (Application) loader.loadClass( 121 | applicationName).newInstance(); 122 | Reflect.on(pluginApp).call("attachBaseContext", 123 | ctx.getApplicationContext()); 124 | apk.pluginApplication = pluginApp; 125 | pluginApp.onCreate(); 126 | 127 | } catch (InstantiationException e) { 128 | // throw new PluginCreateFailedException(e.getMessage()); 129 | } catch (IllegalAccessException e) { 130 | // throw new PluginCreateFailedException(e.getMessage()); 131 | } catch (ClassNotFoundException e) { 132 | // throw new PluginCreateFailedException(e.getMessage()); 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/manager/CJClassLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.manager; 17 | 18 | import java.util.Map; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | import org.kymjs.cjframe.NativeLibUnpacker; 22 | import org.kymjs.cjframe.reflect.Reflect; 23 | 24 | import android.content.Context; 25 | 26 | 27 | import dalvik.system.DexClassLoader; 28 | 29 | /** 30 | * Created by lody on 2015/3/24. 插件的核心加载器
31 | * 已支持Native 32 | */ 33 | public class CJClassLoader extends DexClassLoader { 34 | 35 | private static final Map pluginLoader = new ConcurrentHashMap(); 36 | public static String finalApkPath; 37 | 38 | protected CJClassLoader(String dexPath, String optimizedDirectory, 39 | String libraryPath, ClassLoader parent) { 40 | super(dexPath, optimizedDirectory, libraryPath, parent); 41 | finalApkPath = dexPath; 42 | NativeLibUnpacker.unPackSOFromApk(dexPath, libraryPath); 43 | } 44 | 45 | /** 46 | * 返回apk对应的加载器,不会重复加载同样的资源 47 | */ 48 | public static CJClassLoader getClassLoader(String dexPath, Context cxt, 49 | ClassLoader parent) { 50 | CJClassLoader pluginDexLoader = pluginLoader.get(dexPath); 51 | if (pluginDexLoader == null) { 52 | // 获取到app的启动路径 53 | final String dexOutputPath = cxt.getDir("plugin", 54 | Context.MODE_PRIVATE).getAbsolutePath(); 55 | final String libOutputPath = cxt.getDir("plugin_lib", 56 | Context.MODE_PRIVATE).getAbsolutePath(); 57 | 58 | pluginDexLoader = new CJClassLoader(dexPath, dexOutputPath, 59 | libOutputPath, parent); 60 | pluginLoader.put(dexPath, pluginDexLoader); 61 | } 62 | return pluginDexLoader; 63 | } 64 | 65 | public static ClassLoader getSystemLoader() { 66 | Context context = Reflect.on("android.app.ActivityThread") 67 | .call("currentActivityThread").call("getSystemContext").get(); 68 | return context == null ? null : context.getClassLoader(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/manager/LCallbackManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.manager; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | 21 | import org.kymjs.cjframe.control.I_CJActivity; 22 | 23 | import android.os.Bundle; 24 | import android.view.KeyEvent; 25 | 26 | 27 | /** 28 | * Created by lody on 2015/3/31. 29 | */ 30 | public class LCallbackManager { 31 | private static final ArrayList pluginsMapForPath = new ArrayList(); 32 | 33 | public static void addActivityCallback(I_CJActivity callback) { 34 | pluginsMapForPath.add(callback); 35 | } 36 | 37 | public static void removeActivityCallback(I_CJActivity callback) { 38 | pluginsMapForPath.remove(callback); 39 | } 40 | 41 | public static void callAllOnCreate(final Bundle args) { 42 | Iterator iterator = pluginsMapForPath.iterator(); 43 | while (iterator.hasNext()) { 44 | iterator.next().callOnCreate(args); 45 | } 46 | } 47 | 48 | public static void callAllOnStart() { 49 | Iterator iterator = pluginsMapForPath.iterator(); 50 | while (iterator.hasNext()) { 51 | iterator.next().callOnStart(); 52 | } 53 | } 54 | 55 | public static void callAllOnStop() { 56 | Iterator iterator = pluginsMapForPath.iterator(); 57 | while (iterator.hasNext()) { 58 | iterator.next().callOnStop(); 59 | } 60 | } 61 | 62 | public static void callAllOnResume() { 63 | Iterator iterator = pluginsMapForPath.iterator(); 64 | while (iterator.hasNext()) { 65 | iterator.next().callOnResume(); 66 | } 67 | } 68 | 69 | public static void callAllOnRestart() { 70 | Iterator iterator = pluginsMapForPath.iterator(); 71 | while (iterator.hasNext()) { 72 | iterator.next().callOnRestart(); 73 | } 74 | } 75 | 76 | public static void callAllOnBackPressed() { 77 | Iterator iterator = pluginsMapForPath.iterator(); 78 | while (iterator.hasNext()) { 79 | iterator.next().callOnBackPressed(); 80 | } 81 | } 82 | 83 | public static void callAllOnDestroy() { 84 | Iterator iterator = pluginsMapForPath.iterator(); 85 | while (iterator.hasNext()) { 86 | iterator.next().callOnDestroy(); 87 | } 88 | } 89 | 90 | public static void callAllOnSaveInstanceState(final Bundle out) { 91 | Iterator iterator = pluginsMapForPath.iterator(); 92 | while (iterator.hasNext()) { 93 | iterator.next().callOnSaveInstanceState(out); 94 | } 95 | } 96 | 97 | public static void callAllOnRestoreInstanceState( 98 | final Bundle savedInstanceState) { 99 | Iterator iterator = pluginsMapForPath.iterator(); 100 | while (iterator.hasNext()) { 101 | iterator.next().callOnRestoreInstanceState(savedInstanceState); 102 | } 103 | } 104 | 105 | public static void callAllOnKeyDown(final int keyCode, final KeyEvent event) { 106 | Iterator iterator = pluginsMapForPath.iterator(); 107 | while (iterator.hasNext()) { 108 | iterator.next().callOnKeyDown(keyCode, event); 109 | } 110 | } 111 | 112 | public static void callAllOnPause() { 113 | Iterator iterator = pluginsMapForPath.iterator(); 114 | while (iterator.hasNext()) { 115 | iterator.next().callOnPause(); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/manager/LPluginBugManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.manager; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | 22 | import org.kymjs.cjframe.api.LPluginBugListener; 23 | import org.kymjs.cjframe.api.PluginException; 24 | 25 | 26 | /** 27 | * Created by lody on 2015/4/3. 28 | */ 29 | public class LPluginBugManager { 30 | private static final List errorCollection = new ArrayList(); 31 | 32 | /** 33 | * 加入一个插件异常监听器 34 | * 35 | * @param lPluginBugListener 36 | */ 37 | public static void addBugListener(LPluginBugListener lPluginBugListener) { 38 | errorCollection.add(lPluginBugListener); 39 | } 40 | 41 | /** 42 | * 移除一个插件异常监听器 43 | * 44 | * @param lPluginBugListener 45 | */ 46 | public static void removeBugListener(LPluginBugListener lPluginBugListener) { 47 | errorCollection.remove(lPluginBugListener); 48 | } 49 | 50 | public static void callAllBugListener(final PluginException error) { 51 | if (errorCollection.size() == 0) 52 | return; 53 | Iterator iterator = errorCollection.iterator(); 54 | while (iterator.hasNext()) { 55 | iterator.next().OnError(error); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/reflect/Reflect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.reflect; 17 | 18 | import java.lang.reflect.AccessibleObject; 19 | import java.lang.reflect.Constructor; 20 | import java.lang.reflect.Field; 21 | import java.lang.reflect.InvocationHandler; 22 | import java.lang.reflect.Member; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Modifier; 25 | import java.lang.reflect.Proxy; 26 | import java.util.Arrays; 27 | import java.util.LinkedHashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * 概述
32 | * 一套新颖的反射工具类, 能够轻松实现反射,并使代码具有强可读性。 33 | * 34 | * @author lody 35 | */ 36 | public class Reflect { 37 | 38 | /** 39 | * 封装Class.forName(name) 40 | *

41 | * 可以这样调用: on(Class.forName(name)) 42 | * 43 | * @param name 44 | * 完整类名 45 | * @return 工具类自身 46 | * @throws ReflectException 47 | * 反射时发生的异常 48 | * @see #on(Class) 49 | */ 50 | public static Reflect on(String name) throws ReflectException { 51 | return on(forName(name)); 52 | } 53 | 54 | /** 55 | * 封装Class.forName(name) 56 | *

57 | * 可以这样调用: on(Xxx.class) 58 | * 59 | * @param clazz 60 | * 类 61 | * @return 工具类自身 62 | * @throws ReflectException 63 | * 反射时发生的异常 64 | * @see #on(Class) 65 | */ 66 | public static Reflect on(Class clazz) { 67 | return new Reflect(clazz); 68 | } 69 | 70 | /** 71 | * 包装起一个对象 72 | *

73 | * 当你需要访问实例的字段和方法时可以使用此方法 {@link Object} 74 | * 75 | * @param object 76 | * 需要被包装的对象 77 | * @return 工具类自身 78 | */ 79 | public static Reflect on(Object object) { 80 | return new Reflect(object); 81 | } 82 | 83 | /** 84 | * 使受访问权限限制的对象转为不受限制。 一般情况下, 一个类的私有字段和方法是无法获取和调用的, 原因在于调用前Java会检查是否具有可访问权限, 85 | * 当调用此方法后, 访问权限检查机制将被关闭。 86 | * 87 | * @param accessible 88 | * 受访问限制的对象 89 | * @return 不受访问限制的对象 90 | */ 91 | public static T accessible(T accessible) { 92 | if (accessible == null) { 93 | return null; 94 | } 95 | 96 | if (accessible instanceof Member) { 97 | Member member = (Member) accessible; 98 | 99 | if (Modifier.isPublic(member.getModifiers()) 100 | && Modifier.isPublic(member.getDeclaringClass() 101 | .getModifiers())) { 102 | 103 | return accessible; 104 | } 105 | } 106 | 107 | // 默认为false,即反射时检查访问权限, 108 | // 设为true时不检查访问权限,可以访问private字段和方法 109 | if (!accessible.isAccessible()) { 110 | accessible.setAccessible(true); 111 | } 112 | 113 | return accessible; 114 | } 115 | 116 | // --------------------------------------------------------------------- 117 | // 成员 118 | // --------------------------------------------------------------------- 119 | 120 | /** 121 | * 被包装的对象 122 | */ 123 | private final Object object; 124 | 125 | /** 126 | * 反射的是一个Class还是一个Object实例? 127 | */ 128 | private final boolean isClass; 129 | 130 | // --------------------------------------------------------------------- 131 | // 构造器 132 | // --------------------------------------------------------------------- 133 | 134 | private Reflect(Class type) { 135 | this.object = type; 136 | this.isClass = true; 137 | } 138 | 139 | private Reflect(Object object) { 140 | this.object = object; 141 | this.isClass = false; 142 | } 143 | 144 | // --------------------------------------------------------------------- 145 | // 洗尽铅华的卓越API :) 146 | // --------------------------------------------------------------------- 147 | 148 | /** 149 | * 得到当前包装的对象 150 | */ 151 | public T get() { 152 | // 泛型的好处瞬间就体现出来了 153 | return (T) object; 154 | } 155 | 156 | /** 157 | * 修改一个字段的值 158 | *

159 | * 等价于 {@link java.lang.reflect.Field#set(Object, Object)}. 如果包装的对象是一个 160 | * {@link Class}, 那么修改的将是一个静态字段, 如果包装的对象是一个{@link Object}, 那么修改的就是一个实例字段。 161 | * 162 | * @param name 163 | * 字段名 164 | * @param value 165 | * 字段的值 166 | * @return 完事后的工具类 167 | * @throws ReflectException 168 | */ 169 | public Reflect set(String name, Object value) throws ReflectException { 170 | try { 171 | Field field = field0(name); 172 | field.set(object, unwrap(value)); 173 | return this; 174 | } catch (Exception e) { 175 | throw new ReflectException(e); 176 | } 177 | } 178 | 179 | /** 180 | * 得到字段对值 181 | * 182 | * @param name 183 | * 字段名 184 | * @return The field value 185 | * @throws ReflectException 186 | * @see #field(String) 187 | */ 188 | public T get(String name) throws ReflectException { 189 | return field(name). get(); 190 | } 191 | 192 | /** 193 | * 取得字段 194 | * 195 | * @param name 196 | * 字段名 197 | * @return 字段 198 | * @throws ReflectException 199 | */ 200 | public Reflect field(String name) throws ReflectException { 201 | try { 202 | Field field = field0(name); 203 | return on(field.get(object)); 204 | } catch (Exception e) { 205 | throw new ReflectException(e); 206 | } 207 | } 208 | 209 | private Field field0(String name) throws ReflectException { 210 | Class type = type(); 211 | 212 | // 尝试作为公有字段处理 213 | try { 214 | return type.getField(name); 215 | } 216 | 217 | // 尝试以私有方式处理 218 | catch (NoSuchFieldException e) { 219 | do { 220 | try { 221 | return accessible(type.getDeclaredField(name)); 222 | } catch (NoSuchFieldException ignore) { 223 | } 224 | 225 | type = type.getSuperclass(); 226 | } while (type != null); 227 | 228 | throw new ReflectException(e); 229 | } 230 | } 231 | 232 | /** 233 | * 将一个对象的所有对象映射到一个Map中,key为字段名。 234 | * 235 | * @return 包含所有字段的map 236 | */ 237 | public Map fields() { 238 | Map result = new LinkedHashMap(); 239 | Class type = type(); 240 | 241 | do { 242 | for (Field field : type.getDeclaredFields()) { 243 | if (!isClass ^ Modifier.isStatic(field.getModifiers())) { 244 | String name = field.getName(); 245 | 246 | if (!result.containsKey(name)) 247 | result.put(name, field(name)); 248 | } 249 | } 250 | 251 | type = type.getSuperclass(); 252 | } while (type != null); 253 | 254 | return result; 255 | } 256 | 257 | /** 258 | * 给定方法名称,调用无参方法 259 | *

260 | * 等价于 call(name, new Object[0]) 261 | * 262 | * @param name 263 | * 方法名 264 | * @return 工具类自身 265 | * @throws ReflectException 266 | * @see #call(String, Object...) 267 | */ 268 | public Reflect call(String name) throws ReflectException { 269 | return call(name, new Object[0]); 270 | } 271 | 272 | /** 273 | * 给定方法名和参数,调用一个方法。 274 | *

275 | * 封装自 {@link java.lang.reflect.Method#invoke(Object, Object...)}, 可以接受基本类型 276 | * 277 | * @param name 278 | * 方法名 279 | * @param args 280 | * 方法参数 281 | * @return 工具类自身 282 | * @throws ReflectException 283 | */ 284 | public Reflect call(String name, Object... args) throws ReflectException { 285 | Class[] types = types(args); 286 | 287 | // 尝试调用方法 288 | try { 289 | Method method = exactMethod(name, types); 290 | return on(method, object, args); 291 | } 292 | 293 | // 如果没有符合参数的方法, 294 | // 则匹配一个与方法名最接近的方法。 295 | catch (NoSuchMethodException e) { 296 | try { 297 | Method method = similarMethod(name, types); 298 | return on(method, object, args); 299 | } catch (NoSuchMethodException e1) { 300 | 301 | throw new ReflectException(e1); 302 | } 303 | } 304 | } 305 | 306 | /** 307 | * 根据方法名和方法参数得到该方法。 308 | */ 309 | private Method exactMethod(String name, Class[] types) 310 | throws NoSuchMethodException { 311 | Class type = type(); 312 | 313 | // 先尝试直接调用 314 | try { 315 | return type.getMethod(name, types); 316 | } 317 | 318 | // 也许这是一个私有方法 319 | catch (NoSuchMethodException e) { 320 | do { 321 | try { 322 | return type.getDeclaredMethod(name, types); 323 | } catch (NoSuchMethodException ignore) { 324 | } 325 | 326 | type = type.getSuperclass(); 327 | } while (type != null); 328 | 329 | throw new NoSuchMethodException(); 330 | } 331 | } 332 | 333 | /** 334 | * 给定方法名和参数,匹配一个最接近的方法 335 | */ 336 | private Method similarMethod(String name, Class[] types) 337 | throws NoSuchMethodException { 338 | Class type = type(); 339 | 340 | // 对于公有方法: 341 | for (Method method : type.getMethods()) { 342 | if (isSimilarSignature(method, name, types)) { 343 | return method; 344 | } 345 | } 346 | 347 | // 对于私有方法: 348 | do { 349 | for (Method method : type.getDeclaredMethods()) { 350 | if (isSimilarSignature(method, name, types)) { 351 | return method; 352 | } 353 | } 354 | 355 | type = type.getSuperclass(); 356 | } while (type != null); 357 | 358 | throw new NoSuchMethodException("No similar method " + name 359 | + " with params " + Arrays.toString(types) 360 | + " could be found on type " + type() + "."); 361 | } 362 | 363 | /** 364 | * 再次确认方法签名与实际是否匹配, 将基本类型转换成对应的对象类型, 如int转换成Int 365 | */ 366 | private boolean isSimilarSignature(Method possiblyMatchingMethod, 367 | String desiredMethodName, Class[] desiredParamTypes) { 368 | return possiblyMatchingMethod.getName().equals(desiredMethodName) 369 | && match(possiblyMatchingMethod.getParameterTypes(), 370 | desiredParamTypes); 371 | } 372 | 373 | /** 374 | * 调用一个无参构造器 375 | *

376 | * 等价于 create(new Object[0]) 377 | * 378 | * @return 工具类自身 379 | * @throws ReflectException 380 | * @see #create(Object...) 381 | */ 382 | public Reflect create() throws ReflectException { 383 | return create(new Object[0]); 384 | } 385 | 386 | /** 387 | * 调用一个有参构造器 388 | * 389 | * @param args 390 | * 构造器参数 391 | * @return 工具类自身 392 | * @throws ReflectException 393 | */ 394 | public Reflect create(Object... args) throws ReflectException { 395 | Class[] types = types(args); 396 | 397 | try { 398 | Constructor constructor = type().getDeclaredConstructor(types); 399 | return on(constructor, args); 400 | } 401 | 402 | // 这种情况下,构造器往往是私有的,多用于工厂方法,刻意的隐藏了构造器。 403 | catch (NoSuchMethodException e) { 404 | // private阻止不了反射的脚步:) 405 | for (Constructor constructor : type().getDeclaredConstructors()) { 406 | if (match(constructor.getParameterTypes(), types)) { 407 | return on(constructor, args); 408 | } 409 | } 410 | 411 | throw new ReflectException(e); 412 | } 413 | } 414 | 415 | /** 416 | * 为包装的对象创建一个代理。 417 | * 418 | * @param proxyType 419 | * 代理类型 420 | * @return 包装对象的代理者。 421 | */ 422 | @SuppressWarnings("unchecked") 423 | public

P as(Class

proxyType) { 424 | final boolean isMap = (object instanceof Map); 425 | final InvocationHandler handler = new InvocationHandler() { 426 | @SuppressWarnings("null") 427 | @Override 428 | public Object invoke(Object proxy, Method method, Object[] args) 429 | throws Throwable { 430 | String name = method.getName(); 431 | 432 | try { 433 | return on(object).call(name, args).get(); 434 | } catch (ReflectException e) { 435 | if (isMap) { 436 | Map map = (Map) object; 437 | int length = (args == null ? 0 : args.length); 438 | 439 | if (length == 0 && name.startsWith("get")) { 440 | return map.get(property(name.substring(3))); 441 | } else if (length == 0 && name.startsWith("is")) { 442 | return map.get(property(name.substring(2))); 443 | } else if (length == 1 && name.startsWith("set")) { 444 | map.put(property(name.substring(3)), args[0]); 445 | return null; 446 | } 447 | } 448 | 449 | throw e; 450 | } 451 | } 452 | }; 453 | 454 | return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), 455 | new Class[] { proxyType }, handler); 456 | } 457 | 458 | private static String property(String string) { 459 | int length = string.length(); 460 | 461 | if (length == 0) { 462 | return ""; 463 | } else if (length == 1) { 464 | return string.toLowerCase(); 465 | } else { 466 | return string.substring(0, 1).toLowerCase() + string.substring(1); 467 | } 468 | } 469 | 470 | // --------------------------------------------------------------------- 471 | // 对象API 472 | // --------------------------------------------------------------------- 473 | 474 | private boolean match(Class[] declaredTypes, Class[] actualTypes) { 475 | if (declaredTypes.length == actualTypes.length) { 476 | for (int i = 0; i < actualTypes.length; i++) { 477 | if (actualTypes[i] == NULL.class) 478 | continue; 479 | 480 | if (wrapper(declaredTypes[i]).isAssignableFrom( 481 | wrapper(actualTypes[i]))) 482 | continue; 483 | 484 | return false; 485 | } 486 | 487 | return true; 488 | } else { 489 | return false; 490 | } 491 | } 492 | 493 | @Override 494 | public int hashCode() { 495 | return object.hashCode(); 496 | } 497 | 498 | @Override 499 | public boolean equals(Object obj) { 500 | if (obj instanceof Reflect) { 501 | return object.equals(((Reflect) obj).get()); 502 | } 503 | 504 | return false; 505 | } 506 | 507 | @Override 508 | public String toString() { 509 | return object.toString(); 510 | } 511 | 512 | // --------------------------------------------------------------------- 513 | // 内部工具方法 514 | // --------------------------------------------------------------------- 515 | 516 | private static Reflect on(Constructor constructor, Object... args) 517 | throws ReflectException { 518 | try { 519 | return on(accessible(constructor).newInstance(args)); 520 | } catch (Exception e) { 521 | throw new ReflectException(e); 522 | } 523 | } 524 | 525 | private static Reflect on(Method method, Object object, Object... args) 526 | throws ReflectException { 527 | try { 528 | accessible(method); 529 | 530 | if (method.getReturnType() == void.class) { 531 | method.invoke(object, args); 532 | return on(object); 533 | } else { 534 | return on(method.invoke(object, args)); 535 | } 536 | } catch (Exception e) { 537 | throw new ReflectException(e); 538 | } 539 | } 540 | 541 | /** 542 | * 内部类,使一个对象脱离包装 543 | */ 544 | private static Object unwrap(Object object) { 545 | if (object instanceof Reflect) { 546 | return ((Reflect) object).get(); 547 | } 548 | 549 | return object; 550 | } 551 | 552 | /** 553 | * 内部类, 给定一系列参数,返回它们的类型 554 | * 555 | * @see Object#getClass() 556 | */ 557 | private static Class[] types(Object... values) { 558 | if (values == null) { 559 | // 空 560 | return new Class[0]; 561 | } 562 | 563 | Class[] result = new Class[values.length]; 564 | 565 | for (int i = 0; i < values.length; i++) { 566 | Object value = values[i]; 567 | result[i] = value == null ? NULL.class : value.getClass(); 568 | } 569 | 570 | return result; 571 | } 572 | 573 | /** 574 | * 加载一个类 575 | * 576 | * @see Class#forName(String) 577 | */ 578 | private static Class forName(String name) throws ReflectException { 579 | try { 580 | return Class.forName(name); 581 | } catch (Exception e) { 582 | throw new ReflectException(e); 583 | } 584 | } 585 | 586 | /** 587 | * 获取包装的对象的类型 588 | * 589 | * @see Object#getClass() 590 | */ 591 | public Class type() { 592 | if (isClass) { 593 | return (Class) object; 594 | } else { 595 | return object.getClass(); 596 | } 597 | } 598 | 599 | /** 600 | * 得到包装的对象的类型, 如果是基本类型,像int,float,boolean这种, 那么将被转换成相应的对象类型。 601 | */ 602 | public static Class wrapper(Class type) { 603 | if (type == null) { 604 | return null; 605 | } else if (type.isPrimitive()) { 606 | if (boolean.class == type) { 607 | return Boolean.class; 608 | } else if (int.class == type) { 609 | return Integer.class; 610 | } else if (long.class == type) { 611 | return Long.class; 612 | } else if (short.class == type) { 613 | return Short.class; 614 | } else if (byte.class == type) { 615 | return Byte.class; 616 | } else if (double.class == type) { 617 | return Double.class; 618 | } else if (float.class == type) { 619 | return Float.class; 620 | } else if (char.class == type) { 621 | return Character.class; 622 | } else if (void.class == type) { 623 | return Void.class; 624 | } 625 | } 626 | 627 | return type; 628 | } 629 | 630 | /** 631 | * 定义了一个null类型 632 | */ 633 | private static class NULL { 634 | } 635 | } 636 | -------------------------------------------------------------------------------- /cjframe/src/org/kymjs/cjframe/reflect/ReflectException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, 张涛, lody. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.kymjs.cjframe.reflect; 17 | 18 | /** 19 | * 反射发生错误时抛出异常 20 | */ 21 | public class ReflectException extends RuntimeException { 22 | 23 | private static final long serialVersionUID = -2243843843843438438L; 24 | 25 | public ReflectException(String message) { 26 | super(message); 27 | } 28 | 29 | public ReflectException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | public ReflectException() { 34 | super(); 35 | } 36 | 37 | public ReflectException(Throwable cause) { 38 | super(cause); 39 | } 40 | } 41 | --------------------------------------------------------------------------------