就在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
* 可以当作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