就在2月7号,公司年后第二天上班,我提了离职,到2月18号拿到了离职证明。 至此,我开启了失业之旅…….
早就在知乎上看到各种关于移动端工作难找的帖子,所以心里也是做好了准备。 从提辞职那天起,我就开始投简历,两个星期下来,加上同学推荐的,一共面了 3家,当然是各种被吊打,但是现在回头来看,又觉得其实那些面试题或者笔试题 并没有那么难,只能怪自己没有好好准备就跑去面了,浪费了3家都不错的互联网公司。
其中有一家也是奇葩,笔试完面试完,CTO让我回来仿照他们家App做个Demo, 我以为Demo做出来会有戏,于是乎用周末两天把Demo做出来了,发给他们, 过了一天,我看他们还没回我邮件,我就打电话给他们HR,让她帮我提醒CTO 查看邮件,然后过了一个星期,邮件还是没有得到回复,这时候我才发觉被pass掉了……. 后来想想,还是得怪自己当时的笔试和面试做得too菜….怪我咯
好了,我的苦逼面试经历就说到这里了,下面说回正题了 (我在想要不要把我有限的笔试题和面试题也写篇博客…….)
DynamicLoadApk是任玉刚大神的热插件作品,项目地址 如果还不是很清楚热插件是啥玩意的话,可以看这里 其实已经有篇很好的关于DynamicLoadApk源码解析,在这里
只是我觉得既然看了源码,就应该记录一下自己思路,免得看过就忘了
DynamicLoadApk的实现总结为两个字:代理模式 总体设计图:
DLPluginManger简单来说是通过DexClassLoader来加载插件里的组件,组件是指 Activity,Service,Fragment等(目前好像就支持这两个,BTW:此框架已经停止维护了)
BasePlugin是所有插件组件的基类,拿DLBasePluginActivity来说,所有的插件 Activity都必须继承它,通过attach方法跟代理Activity绑定,后面会说到。
PRoxy代理,拿DLProxyActivity来说,在宿主App里面会有一个同名的Activity, 用来管理插件里的组件Activity的生命周期,下面是宿主App的Manifest.xml:
<manifest package="com.host" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/APPTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name="com.ryg.dynamicload.DLProxyActivity" android:label="@string/app_name"> <intent-filter> <action android:name="com.ryg.dynamicload.proxy.activity.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name="com.ryg.dynamicload.DLProxyFragmentActivity" android:label="@string/app_name"> <intent-filter> <action android:name="com.ryg.dynamicload.proxy.fragmentactivity.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <service android:name="com.ryg.dynamicload.DLProxyService"/> </application></manifest>可以看到宿主App里面已经注册好了DLPluginActivity,然后具体的实现是在插件里
来看看DLBasePluginActivity里的代码
public class DLBasePluginActivity extends Activity implements DLPlugin { private static final String TAG = "DLBasePluginActivity"; /** * 代理activity,可以当作Context来使用,会根据需要来决定是否指向this */ protected Activity mProxyActivity; /** * 等同于mProxyActivity,可以当作Context来使用,会根据需要来决定是否指向this<br/> * 可以当作this来使用 */ protected Activity that; protected DLPluginManager mPluginManager; protected DLPluginPackage mPluginPackage; protected int mFrom = DLConstants.FROM_INTERNAL; @Override public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) { Log.d(TAG, "attach: proxyActivity= " + proxyActivity); mProxyActivity = (Activity) proxyActivity; that = mProxyActivity; mPluginPackage = pluginPackage; } @Override public void onCreate(Bundle savedInstanceState) { if (savedInstanceState != null) { mFrom = savedInstanceState.getInt(DLConstants.FROM, DLConstants.FROM_INTERNAL); } if (mFrom == DLConstants.FROM_INTERNAL) { super.onCreate(savedInstanceState); mProxyActivity = this; that = mProxyActivity; } mPluginManager = DLPluginManager.getInstance(that); Log.d(TAG, "onCreate: from= " + (mFrom == DLConstants.FROM_INTERNAL ? "DLConstants.FROM_INTERNAL" : "FROM_EXTERNAL")); }DLPlugin是一个接口,抽象出Activity所有生命周期方法,如下:
public interface DLPlugin { public void onCreate(Bundle savedInstanceState); public void onStart(); public void onRestart(); public void onActivityResult(int requestCode, int resultCode, Intent data); public void onResume(); public void onPause(); public void onStop(); public void onDestroy(); public void attach(Activity proxyActivity, DLPluginPackage pluginPackage); public void onSaveInstanceState(Bundle outState); public void onNewIntent(Intent intent); public void onRestoreInstanceState(Bundle savedInstanceState); public boolean onTouchEvent(MotionEvent event); public boolean onKeyUp(int keyCode, KeyEvent event); public void onWindowAttributesChanged(LayoutParams params); public void onWindowFocusChanged(boolean hasFocus); public void onBackPressed(); public boolean onCreateOptionsMenu(Menu menu); public boolean onOptionsItemSelected(MenuItem item);}DLBasePluginActivity 的attach()方法,是把插件里的Activity跟代理Activity绑定,这里还涉及到一个DLAttachable,DLAttachable也是一个接口,如下:
public interface DLAttachable { /** * when the proxy impl ( {@see DLProxyImpl#launchTargetActivity()} ) launch * the plugin activity , dl will call this method to attach the proxy activity * and pluginManager to the plugin activity. the proxy activity will load * the plugin's resource, so the proxy activity is a resource delegate for * plugin activity. * * @param proxyActivity a instance of DLPlugin, {@see DLBasePluginActivity} * and {@see DLBasePluginFragmentActivity} * @param pluginManager DLPluginManager instance, manager the plugins */ public void attach(DLPlugin proxyActivity, DLPluginManager pluginManager);}在DLProxyActivity里就是通过实现这个接口来绑定插件Activity的,如下:
public class DLProxyActivity extends Activity implements DLAttachable { protected DLPlugin mRemoteActivity; private DLProxyImpl impl = new DLProxyImpl(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); impl.onCreate(getIntent()); } @Override public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) { mRemoteActivity = remoteActivity; }就是通过DLBasePluginActivity的attach和DLProxyActivity的attch来实现插件Activity和代理Activity的双向绑定。
这两个attach方法是在DLProxyImpl的launchTargetActivity()中实现,如下:
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) protected void launchTargetActivity() { try { Class<?> localClass = getClassLoader().loadClass(mClass); Constructor<?> localConstructor = localClass.getConstructor(new Class[] {}); Object instance = localConstructor.newInstance(new Object[] {}); mPluginActivity = (DLPlugin) instance; ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager); Log.d(TAG, "instance = " + instance); // attach the proxy activity and plugin package to the mPluginActivity mPluginActivity.attach(mProxyActivity, mPluginPackage); Bundle bundle = new Bundle(); bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL); mPluginActivity.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } }至此,就实现了把插件里的组件(Activity为例)和代理类(Activity为例)的双向绑定, 在宿主的MainActivity里便可以调起插件里的Activity,实现如下:
public class MainActivity extends Activity { private Button btnTest; private TextView tvTip; @Override 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); return; } //调用第一个插件 File plugin = plugins[0]; final PluginItem item = new PluginItem(); item.pluginPath = plugin.getAbsolutePath(); item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath); //获取插件的启动Activity的名称 if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) { item.launcherActivityName = item.packageInfo.activities[0].name; } //获取插件启动Service的名称 if (item.packageInfo.services != null && item.packageInfo.services.length > 0) { item.launcherServiceName = item.packageInfo.services[0].name; } //显示插件 tvTip.setText("检测到一个插件:" + item.pluginPath); //加载插件 DLPluginManager.getInstance(this).loadApk(item.pluginPath); //添加监听器 this.btnTest.setOnClickListener(new View.OnClickListener() { @Override 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)); } //插件Bean public static class PluginItem { public PackageInfo packageInfo; public String pluginPath; public String launcherActivityName; public String launcherServiceName; public PluginItem() { } }}这就是设计模式的伟大之处啊! 其实不用这种代理模式也是可以实现动态加载的,就是全部用反射去实现,这样的话效率肯定会差太多….. 不得不说大神写的东西就是难看懂,我看了两天才明白这其中的关系..GG
新闻热点
疑难解答