奇虎360Replugin的相关介绍已经很多了。这是一个简单的demo。更新下载平台用的公司内网。大家集成Replugin以后,也需要服务端配合,做一个插件管理系统,这样才能好好玩。
关于host部分
贴图好呢?还是代码好呢?
1.在project中的build.gradle中配置:
buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }
2.在app的build.gradle
apply plugin: 'replugin-host-gradle'
这句要添加到android{
。。。。。。。
}后面
repluginHostConfig { /** * 是否使用 AppCompat 库 * 不需要个性化配置时,无需添加 */ useAppCompat = true // /** // * 背景不透明的坑的数量 // * 不需要个性化配置时,无需添加 // */ // countNotTranslucentStandard = 6 // countNotTranslucentSingleTop = 2 // countNotTranslucentSingleTask = 3 // countNotTranslucentSingleInstance = 2 }
3.dependencies{ 。。。。}中添加
compile 'com.qihoo360.replugin:replugin-host-lib:2.2.4'
4.application
package zyc.com.replugin.application; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; import com.qihoo360.replugin.RePlugin; import com.qihoo360.replugin.RePluginApplication; import com.qihoo360.replugin.RePluginCallbacks; import com.qihoo360.replugin.RePluginConfig; import com.qihoo360.replugin.RePluginEventCallbacks; import zyc.com.replugin.BuildConfig; import zyc.com.replugin.util.PluginUpdateUtil; /** * Created by zhangyc on 2018/5/7. */ public class MainApp extends RePluginApplication { // @Override // protected void attachBaseContext(Context base) { // super.attachBaseContext(base); // // // FIXME 允许接收rpRunPlugin等Gradle Task,发布时请务必关掉,以免出现问题 // RePlugin.enableDebugger(base, BuildConfig.DEBUG); // } // @Override // public void onCreate() { // super.onCreate(); // OkGo.getInstance().init(this); // } @Override protected RePluginConfig createConfig() { RePluginConfig c = new RePluginConfig(); // 允许“插件使用宿主类”。默认为“关闭” c.setUseHostClassIfNotFound(true); // FIXME RePlugin默认会对安装的外置插件进行签名校验,这里先关掉,避免调试时出现签名错误 c.setVerifySign(!BuildConfig.DEBUG); // 针对“安装失败”等情况来做进一步的事件处理 c.setEventCallbacks(new HostEventCallbacks(this)); c.setMoveFileWhenInstalling(false); // FIXME 若宿主为Release,则此处应加上您认为"合法"的插件的签名,例如,可以写上"宿主"自己的。 // RePlugin.addCertSignature("AAAAAAAAA"); // 在Art上,优化第一次loadDex的速度 // c.setOptimizeArtLoadDex(true); return c; } private class HostEventCallbacks extends RePluginEventCallbacks { private static final String TAG = "HostEventCallbacks"; public HostEventCallbacks(Context context) { super(context); } @Override public void onInstallPluginFailed(String path, InstallResult code) { // FIXME 当插件安装失败时触发此逻辑。您可以在此处做“打点统计”,也可以针对安装失败情况做“特殊处理” // 大部分可以通过RePlugin.install的返回值来判断是否成功 if (BuildConfig.DEBUG) { Log.d(TAG, "onInstallPluginFailed: Failed! path=" + path + "; r=" + code); } super.onInstallPluginFailed(path, code); } @Override public void onStartActivityCompleted(String plugin, String activity, boolean result) { // FIXME 当打开Activity成功时触发此逻辑,可在这里做一些APM、打点统计等相关工作 super.onStartActivityCompleted(plugin, activity, result); } } @Override protected RePluginCallbacks createCallbacks() { return new UpdateRePluginCallbacks(this); } /***
//这个回调在跳转的时候找不到目标Activity时,进行下载对应模块 public class UpdateRePluginCallbacks extends RePluginCallbacks { public UpdateRePluginCallbacks(Context context) { super(context); } public boolean onPluginNotExistsForActivity(Context context, String plugin, Intent intent, int process) { if("host".equals(plugin)|| RePlugin.getPluginInfo(plugin)!=null){ //已按照 Toast.makeText(MainApp.this,"不能这个样子的",Toast.LENGTH_SHORT).show(); }else { PluginUpdateUtil updateUtil=new PluginUpdateUtil(context); updateUtil.installPlugin(context,intent,plugin); } return true; } } }
5.本地插件版本 检查->下载->更新->安装->打开的逻辑实现
/** * 检查插件 */ public void checkPlugin() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); // builder.sslSocketFactory(sslSocketFactory, trustAllCert); builder.connectTimeout(30, TimeUnit.SECONDS) .retryOnConnectionFailure(true); // .addInterceptor(loggingInterceptor); OkHttpClient okHttpClient = builder.build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(PublicContact.SERVER_IP) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build(); VersionService tmp = retrofit.create(VersionService.class); tmp.getHotFix(PublicContact.APP_KEY_PLUGINUPDATE,PublicContact.FRAEWORK_VERSION). subscribeOn(Schedulers.io()). observeOn(AndroidSchedulers.mainThread()). subscribe(new Consumer<PublicResponseEntity<List<HotFixInfo>>>() { @Override public void accept(PublicResponseEntity<List<HotFixInfo>> hotfixs) throws Exception { Log.i(">>>",hotfixs.getMsg()); List<HotFixInfo> needUpdatePlugins=new ArrayList<>(); //本地插件信息列表 List<PluginInfo> localPlugins = RePlugin.getPluginInfoList(); if (0!=hotfixs.getCode()||null==hotfixs.getData()){ return; } for (HotFixInfo hotfit: hotfixs.getData()) { //远程插件信息列表 int code=Integer.valueOf(hotfit.getNo()); Log.i(TAG,hotfit.getDownloadUrl()); //选出需要下载的插件, //可以在这里进行相关的逻辑处理,可以根据plugin的versioncode进行判断 for (PluginInfo pluginInfo:localPlugins){ //把本地版本低于服务端版本的放在一个集合中 if (pluginInfo.getVersion()<code){ needUpdatePlugins.add(hotfit); } } } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.i("rx------",throwable.getMessage()); } }); }
下载:
/** * 下载 */ public void downloadPlugin(final Context mcontext, String downloadUrl, final Intent intent, final String plugin){ //监听下载进度 final ProgressDialog dialog = new ProgressDialog(mcontext); dialog.setProgressNumberFormat("%1d KB/%2d KB"); dialog.setTitle("下载"); dialog.setMessage("正在下载,请稍后..."); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setCancelable(false); dialog.show(); Retrofit.Builder retrofitBuilder = new Retrofit.Builder() .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .baseUrl(downloadUrl+"/"); OkHttpClient.Builder builder = ProgressHelper.addProgress(null); try { // 自定义一个信任所有证书的TrustManager,添加SSLSocketFactory的时候要用到 final X509TrustManager trustAllCert = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } }; final SSLSocketFactory sslSocketFactory = new SSLSocketFactoryCompat(trustAllCert); builder.sslSocketFactory(sslSocketFactory, trustAllCert); } catch (Exception e) { throw new RuntimeException(e); } DownloadApi retrofit = retrofitBuilder .client(builder.build()) .build().create(DownloadApi.class); ProgressHelper.setProgressHandler(new DownloadProgressHandler() { @Override protected void onProgress(long bytesRead, long contentLength, boolean done) { Log.e("是否在主线程中运行", String.valueOf(Looper.getMainLooper() == Looper.myLooper())); Log.e("onProgress",String.format("%d%% done\n",(100 * bytesRead) / contentLength)); Log.e("done",TAG + String.valueOf(done)); dialog.setMax((int) (contentLength/1024)); dialog.setProgress((int) (bytesRead/1024)); if(done){ dialog.dismiss(); } } }); retrofit2.Call<ResponseBody> call = retrofit.retrofitDownload(downloadUrl); call.enqueue(new Callback<ResponseBody>() { public PluginInfo info; @Override public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) { try { InputStream is = response.body().byteStream(); String appName=null; try { appName=mcontext.getResources().getString(R.string.app_name); }catch (Exception e){ appName="tk"; } File file = new File(Environment.getExternalStorageDirectory(), plugin+".apk"); FileOutputStream fos = new FileOutputStream(file); BufferedInputStream bis = new BufferedInputStream(is); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); fos.flush(); } fos.close(); bis.close(); is.close(); //下载完成,自动安装插件 String path=file.getAbsolutePath(); installPlugins(path,intent); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) { } }); }
ok,目前就这样吧。
附上github的host地址,以及plugin的地址
git@github.com:zhangyc/RePluginHost.gitgit@github.com:zhangyc/RePluginHost.git