如何从插件apk中启动sviservice.apk

插件托管服务(4)
&结合上节&&,本节下载并按照云端的插件。
实现项目demo&
实现代码 :com.apkplugdemo.adapter.SearchBundleAdapter
一,插件下载安装服务
&&&&插件下载安装服务也是以OSGI服务形式提供的,获取服务的模板代码为:&&&&&&&
ServiceReference
reference=context.getServiceReference(AppDownload.class.getName());
if(null!=reference){
&&&&&&&&service=(AppDownload)
context.getService(reference);
context.ungetService(reference);
二,AppDownload服务接口说明&&
public&interface&AppDownload
插件托管服务接口
@param appBean ab&&& 包含插件基本信息
@param mContext&&&&& android上下文
@param callback&&&&& 服务回调接口
@throws IOException
&&&&public&void&download(appBean
ab,Context mContext,AppDownloadCallBack callback)throws&IOE
三,调用插件安装服务接口下载插件
&&&&1)首先应该获取appBean 目前有两种方式可以获取到
&&&&&&&&1.通过插件搜索服务&&&&&&&&&& 即上一节提到的
&&&&&&& 2.通过插件版本状态服务&&&&
&& 2)编写回调接口AppDownloadCallBack
public&interface&AppDownloadCallBack
&&&&public&final&int&suc_install=0;
&&&&public&final&int&fail_install=-1;
&&&&public&final&int&suc_download=1;
&&&&public&final&int&fail_download=-2;
获取url成功
&&&&public&final&int&suc_url=2;
获取url失败
&&&&public&final&int&fail_url=-2;
插件安装成功后
&&&&public&void&onInstallSuccess(int&stutas,Bundle
&&&&public&void&onDownLoadSuccess(String
插件下载中
@param bytesWritten& 已下载数据
@param totalSize&&&& 总数据大小
@param Speed&&&&&&&& 下载速度
@param bean
&&&&public&void&onProgress(int&bytesWritten,&int&totalSize,String
@param bean
@param errorNo
@param strMsg
&&&&public&void&onFailure(int&errorNo
,String strMsg);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:95617次
积分:1415
积分:1415
排名:千里之外
原创:42篇
评论:61条
(1)(1)(1)(22)(1)(2)(5)(4)(1)(4)1104人阅读
Android 动态部署(5)
github地址:
经过前面几篇文章的分析,我们了解到了Google原生是如何拆分apk的,并且我们自己可以通过解析manifest文件,通过创建插件ClassLoader,Resources对象来启动插件APK中的Activity,上一篇关于资源的问题,有一点遗漏,在插件中开发者有可能通过如下代码获取资源Id
getIdentifier("xxx", "layout", getPackageName());
此时调用getPackageName()方法返回的是宿主apk的包名,所以我们需要在DynamicContextImpl类中重写getPackageName()方法,返回从插件apk的manifest中解析出来的的包名,接下来我们通过分析Service启动流程来看看宿主apk如何启动Android四大组件之Service。
Service启动流程
startService(new Intent(this, TargetService.class));
在Activity中,很简单的一行代码,就可以启动TargetService了,下图就是调用这行代码后的时序图:
带着成功启动插件Activity的经验,我们继续通过分析Service启动流程,试图从中找到hook点从而将我们对插件Service的扩展操作,通过类似的重写DynamicInstrumentation类,替换进ActivityThread中。
在时序图中我们发现在调用startService方法后,最终都会调到ContextImpl中的startService。
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
private ComponentName startServiceCommon(Intent service, UserHandle user) {
validateServiceIntent(service);
service.prepareToLeaveProcess();
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
在看了源码之后我们发现,这个方法的功能,其实跟Activity启动流程中Instrumentation类中的execStartActivity方法类似,看到这就感觉启动插件Service已经十拿九稳了,我们已经跨出了很大的一步,我们继续来深入研究Service的启动流程的源码:
ActiveServices.java
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting) throws TransactionTooLargeException {
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
if (!whileRestarting && r.restartDelay & 0) {
return null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);
if (mRestartingServices.remove(r)) {
r.resetRestartCounter();
clearRestartingIfNeededLocked(r);
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
if (mAm.mStartedUsers.get(r.userId) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processN
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
app = r.isolatedP
if (app == null) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
if (isolated) {
r.isolatedProc =
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
if (r.delayedStop) {
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (in bring up): " + r);
stopServiceLocked(r);
return null;
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
while (r.pendingStarts.size() & 0) {
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (Exception e) {
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
mAm.updateOomAdjLocked();
boolean created = false;
if (LOG_SERVICE_START_STOP) {
String nameT
int lastPeriod = r.shortName.lastIndexOf('.');
nameTerm = lastPeriod &= 0 ? r.shortName.substring(lastPeriod) : r.shortN
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
patibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
mAm.appDiedLocked(app);
} finally {
if (!created) {
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
if (newService) {
app.services.remove(r);
r.app = null;
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null));
sendServiceArgsLocked(r, execInFg, true);
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
if (r.delayedStop) {
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (from start): " + r);
stopServiceLocked(r);
ActivityThread.java
public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
int flags ,Intent args) {
sendMessage(H.SERVICE_ARGS, s);
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
sendMessage(H.CREATE_SERVICE, s);
public void handleMessage(Message msg) {
switch (msg.what) {
case CREATE_SERVICE: {
handleCreateService((CreateServiceData)msg.obj);
case SERVICE_ARGS: {
handleServiceArgs((ServiceArgsData)msg.obj);
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
.applicationInfo, patInfo);
Service service = null;
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl..name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + .name
+ ": " + e.toString(), e);
if (localLOGV) Slog.v(TAG, "Creating service " + .name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, .name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + .name
+ ": " + e.toString(), e);
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
QueuedWork.waitToFinish();
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
ensureJitEnabled();
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
看到这里,原生Service也启动起来了,我们发现Service的启动流程和Activity的类似,但又不完全一样,正是因为这些许差别,让我们的开发工作陷入了困境,遇到了下面这些“坑”:
1. Service不像Activity的标准模式,可以一直实例化,当某个Service启动后,从上面的代码可以看到,再次调用startService方法,源码中并不会去重新创建Service,调用onCreate,而是直接调用onStartCommand方法,所以我们不能通过StubService的方式来启动插件Service。
2. Service的handleCreateService方法不像在Activity启动流程中的performLaunchActivity方法中获取类名后,通过Instrumentation类的newActivity方法实例化,通过callActivityOnCreate方法间接调用Activity的onCreate,这样我们有机会通过重写DynamicInstrumentation类来扩展插件功能,而Service却直接在ActivityThread类中实例化,并且在attach方法结束后直接调用onCreate方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityI
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
Activity activity =
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
if (activity != null) {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
mInstrumentation.callActivityOnCreate(activity, r.state);
} catch (Exception e) {
上面那两个坑,恰恰是我们在实现从插件apk中启动Activity时所关注的点,似乎这些点在这里一个都用不上,前面跨出的一大步,感觉也是被打了回去,在接下来的几天里,我反复阅读源码,发现了其中的一个关键点,插件中的Service是使用插件的ClassLoader通过类名来加载的,那我们可以在ClassLoader上做一些“手脚”。
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
.applicationInfo, patInfo);
Service service =
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl..name).newInstance();
} catch (Exception e) {
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true, false);
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
synchronized (mResourcesManager) {
WeakReference&LoadedApk&
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
ref = mPackages.get(aInfo.packageName);//宿主apk的LoadApk保存在mPackages中
ref = mResourcePackages.get(aInfo.packageName);
LoadedApk packageInfo = ref != null ? ref.get() :
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
if (mSystemThread && "android".equals(aInfo.packageName)) {
packageInfo.installSystemApplicationInfo(aInfo,
getSystemContext().mPackageInfo.getClassLoader());
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference&LoadedApk&(packageInfo));
mResourcePackages.put(aInfo.packageName,
new WeakReference&LoadedApk&(packageInfo));
return packageI
看到这个点之后,感觉像是抓住一个救命稻草那样兴奋,我们可以在每次安装一个插件apk的同时将插件apk的ClassLoader安装到mPackages.get(“host package name”)中,这样就可以不做其他的修改,根据插件apk中的Service类名就可以加载TargetService了,同时细心的读者也会发现,其实Activity,BroadcastReceiver,ContentProvider也是通过这样的方式加载的,真是一劳永逸呢。
DynamicClassLoaderWrapper.java
package com.ximsfei.dynamic.
import java.util.ArrayL
* Created by pengfenx on 3/15/2016.
public class DynamicClassLoaderWrapper extends ClassLoader {
private final ClassLoader mB
private final ArrayList&ClassLoader& mDynamicLoaders = new ArrayList&&();
protected DynamicClassLoaderWrapper(ClassLoader base) {
public void addClassLoader(ClassLoader cl) {
if (!mDynamicLoaders.contains(cl)) {
mDynamicLoaders.add(cl);
protected Class&?& findClass(String className) throws ClassNotFoundException {
return mBase.loadClass(className);
} catch (ClassNotFoundException e) {
int N = mDynamicLoaders.size();
for (int i=0; i&N; i++) {
return mDynamicLoaders.get(i).loadClass(className);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException(className);
安装ClassLoader:
public synchronized void installClassLoader(ClassLoader classLoader) {
Object loadedApk = ((WeakReference) getPackages().get(getHostPackageName())).get();
ClassLoader cl = Reflect.create(loadedApk.getClass())
.setMethod("getClassLoader").invoke(loadedApk);
if (!(cl instanceof DynamicClassLoaderWrapper)) {
DynamicClassLoaderWrapper dclw = new DynamicClassLoaderWrapper(cl);
dclw.addClassLoader(classLoader);
Reflect.create(loadedApk.getClass()).setField("mClassLoader")
.set(loadedApk, dclw);
((DynamicClassLoaderWrapper) cl).addClassLoader(classLoader);
} catch (Exception e) {
private synchronized Map getPackages() {
if (mPackages == null) {
mPackages = mActivityThreadReflect.setField("mPackages").get(currentActivityThread());
} catch (Exception e) {
stopService, bindService, unbindService的流程与startService流程类似,并且不需要做过多的修改,在这里就不再分析了,有兴趣的读者可以自己去看一下源码,分析一下。
第一个坑中,Service只能启动一次,所以我们不能通过伪装成StubService的方式,来“骗过”AndroidManifest的检测,我也没有想到更好的方法来实现,暂时只能将要使用的Service类名注册到宿主apk的AndroidManifest中来实现,如果读者有什么好的方法,可以分享出来一起学习一下。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6400次
排名:千里之外
原创:15篇热更新原理 如何做到上线后更新 插件apk是放在服务器吗_百度知道
热更新原理 如何做到上线后更新 插件apk是放在服务器吗
我有更好的答案
在Java中,要实现热部署,首先,你得明白,Java中类的加载方式。每一个应用程序的类都会被ClassLoader加载,所以,要实现一个支持热部署的应用,我们可以对每一个用户自定义的应用程序使用一个单独的ClassLoader进行加载。然后,当某个用户自定义的应用程序发生变化的时候,我们首先销毁原来的应用,然后使用一个新的ClassLoader来加载改变之后的应用。而所有其他的应用程序不会受到一点干扰。
热更新的思路:
定义一个用户自定义应用程序的接口,这是因为,我们需要在容器应用中去加载用户自定义的应用程序。
我们还需要一个配置文件,让用户去配置他们的应用程序。
应用启动的时候,加载所有已有的用户自定义应用程序。
为了支持热部署,我们需要一个监听器,来监听应用发布目录中每个文件的变动。这样,当某...
其他类似问题
为您推荐:
apk的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁android(20)
插件化(1)
Android大型项目中为了减小apk的体积,可以采用插件化的方法,即一些不常用的功能独立成插件,当用户需要的使用的时候再从服务器上下载回来,动态加载。这样就避免了为了满足所有用户需求而把功能全部打包到apk,导致apk体积的膨胀。所谓的插件,其实也是一个apk,但是一般都依赖正式对外发布的app,也叫宿主。本篇不讨论插件化的原理和实现难点,只介绍怎么使用以及优缺点。
Android插件化常用实现方案有两种:
(1) DynamicLoadApk
(2) DroidPlugin
这两个是目前比较主流的Android插件化实现方案,在Github的星星数很高,两者的Github地址如下:
DynamicLoadApk的Github地址:
DroidPlugin的Github地址:
(1) DynamicLoadApk是由团队维护的,但是目前已经很长时间没有更新了,途牛用的就是这个插件化框架
插件不依赖宿主,对宿主开发者透明,提供三种依赖方式
宿主和插件可以频繁交互,启动时间短
插件apk必须实现DLBasePluginActivity,属于侵入式的,以及不支持service
宿主调用插件和插件内部的相互调用都要使用DL提供的方法,而不能使用Android原生的api,例如:启动Activity
插件开发有一套规定,因此造成插件开发门槛高,学习成本高
(2) DroidPlugin是360公司开源的一个框架,已经在360手机助手上使用
宿主和插件完全隔离,插件不依赖宿主,可以独立安装运行
低入侵设计,插件不需要继承任何类
插件apk和普通apk一样的,所以插件开发没有门槛
有大公司维护,有360手机助手这样的商用app在使用
插件启动速度太慢,而且宿主只能调用插件的LaunchMode的Activity,不能调用其他Activity
3 使用方法
(1) 导入Dynamic-load-apk中的lib。
下载Dynamic-load-apk后解压,在Android Studio中新建工程DLTest(自己命名) –& new –& import module –&选择lib所在的目录:dynamic-load-apk-master\DynamicLoadApk\lib
(2) 新建插件模块plugin,宿主模块host,这两个模块都是application, 最后都要生成apk的。项目目录如下:
编译lib模块,命令是build菜单–&make module lib,目的是为了获得生成的jar文件,jar文件所在位置是lib\build\intermediates\bundles\debug\class.jar,复制jar文件重命名为lib.jar
(3) 导入lib.jar到plugin项目的libs目录下,开发plugin项目,注意Activity要继承DLBasePluginActivity ,R.layout.activity_test上就一个TextView,显示”这个界面来自Plugin”
插件项目Plugin的MainActivity:
public class MainActivity extends DLBasePluginActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
注意:plugin模块的buidle.gradle需要修改为如下:
dependencies {
provided fileTree(dir: 'libs', include: ['*.jar'])
provided 意思是编译时候使用,但不打包到APK中,这样做是因为我们的宿主项目host中已经包含了lib.jar,如果插件中也包含的话就会报找不到plugin中的Activity的错,原因是两个包重复,必须要用host中的DL框架来加载plugin,而不是plugin自带的DL框架
(4) 导入lib.jar到host项目的libs目录下,开发host项目
host项目的MainActivity:
public class MainActivity extends Activity {
private Button btnT
private TextView tvT
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.btnTest = (Button) findViewById(R.id.btn_test);
this.tvTip = (TextView) findViewById(R.id.tv_tip);
this.init();
private void init() {
String pluginFolder = "/mnt/sdcard/DynamicLoadHost";
File file = new File(pluginFolder);
File[] plugins = file.listFiles();
if (plugins == null || plugins.length == 0) {
this.tvTip.setVisibility(View.VISIBLE);
File plugin = plugins[0];
final PluginItem item = new PluginItem();
item.pluginPath = plugin.getAbsolutePath();
item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath);
if (item.packageInfo.activities != null && item.packageInfo.activities.length & 0) {
item.launcherActivityName = item.packageInfo.activities[0].
if (item.packageInfo.services != null && item.packageInfo.services.length & 0) {
item.launcherServiceName = item.packageInfo.services[0].
tvTip.setText("检测到一个插件:" + item.pluginPath);
DLPluginManager.getInstance(this).loadApk(item.pluginPath);
this.btnTest.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "开始调用插件", Toast.LENGTH_SHORT).show();
usePlugin(item);
private void usePlugin(PluginItem pluginItem) {
DLPluginManager pluginManager = DLPluginManager.getInstance(this);
pluginManager.startPluginActivity(this, new DLIntent(pluginItem.packageInfo.packageName, pluginItem.launcherActivityName));
public static class PluginItem {
public PackageInfo packageI
public String pluginP
public String launcherActivityN
public String launcherServiceN
public PluginItem() {
host的activity_main.xml:
xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="没有检测到插件"
android:id="@+id/btn_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_tip"
android:layout_marginTop="10dp"
android:text="测试调用插件"
注意:Host的AndroidManifest.xml中需要额外的声明几个DL框架中的类,否则运行时候找不到Activity.
Host的AndroidManifest.xml:
package="com.host"
xmlns:android="/apk/res/android"&
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&
android:name="android.permission.READ_EXTERNAL_STORAGE"/&
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"&
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"&
android:name="android.intent.action.MAIN"/&
android:name="android.intent.category.LAUNCHER"/&
android:name="com.ryg.dynamicload.DLProxyActivity"
android:label="@string/app_name"&
android:name="com.ryg.dynamicload.proxy.activity.VIEW"/&
android:name="android.intent.category.DEFAULT"/&
android:name="com.ryg.dynamicload.DLProxyFragmentActivity"
android:label="@string/app_name"&
android:name="com.ryg.dynamicload.proxy.fragmentactivity.VIEW"/&
android:name="android.intent.category.DEFAULT"/&
android:name="com.ryg.dynamicload.DLProxyService"/&
(5) 编译plugin项目,将生成的plugin-debug.apk文件放入/mnt/sdcard/DynaminLoadHost目录下,然后运行Host,运行结果如下:
1 宿主和插件没有任何联系,但是插件需要继承DLBasePluginActivity,这个不太友好
2 侵入式的,对插件apk的开发限制太多,例如:必须继承DLBasePluginActivity,启动时候必须调用startPluginActivity(new
DLIntent(getPackageName(),TestActivity.class))
3 这个框架学习成本高,限制多,联调不方便,不建议使用
4 目前比较好的插件有360公司的DroidPlugin, 以及类似友盟的第三方解决方案ApkPlug
1 Android Studio中可以不用jar文件吗?
Host可以,但plugin不可以。宿主项目可以依赖于lib项目,但是plugin必须使用jar文件,原因参见下面第3条
2 Host项目如何直接依赖lib项目,而不用jar文件?
修改host文件的build.gradle文件
dependencies {
compile project(':lib')
修改lib的build.gradle文件,不修改的话就会和host项目中的support-v4包冲突
dependencies {
provided fileTree(dir: 'libs', include: ['*.jar'])
3 为什么plugin必须使用jar文件?
Plugin不能将lib模块打包到apk中,所以不能使用compile,只能使用provided,所以如果不用jar则plugin模块的build.gradle只能如下:
dependencies {
provided project(':lib')
呵呵,可惜这样是不行的,project只能使用compile ,不能使用provided ,百度了半天没有解决这个问题,如果你有办法欢迎留言
6 参考博客:
<h2 id="7-转载请注明来自梧桐那时雨的博客httpblogcsdnnetfuchaoszarticledetails 转载请注明来自“梧桐那时雨”的博客:
如果觉得这篇博客对你有帮助,就给博主留个言或者顶一下呗,鼓励一下博主创作出更多优质博客,Thank you
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:9240次
排名:千里之外
原创:27篇
评论:23条
(3)(6)(8)(8)(1)(1)

我要回帖

更多关于 wfdservice.apk是什么 的文章

 

随机推荐