关于DamnSmallLinux4.11RC1发布的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于1121DamnSingle(25)、1121DamnSingle(25分)、Androi
关于Damn Small Linux 4.11 RC1 发布的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于1121 Damn Single (25)、1121 Damn Single (25 分)、Android Small 使用教程、Android Small 源码分析 (一) 启动流程等相关知识的信息别忘了在本站进行查找喔。
本文目录一览:- Damn Small Linux 4.11 RC1 发布
- 1121 Damn Single (25)
- 1121 Damn Single (25 分)
- Android Small 使用教程
- Android Small 源码分析 (一) 启动流程
Damn Small Linux 4.11 RC1 发布
设计运行在古董PC上的Damn Small Linux还活着,开发者发布了4.11 RC1, 上个版本4.10还是在2008年11月发布的。新版的主要变化为:修正bug,替代和更新部分应用程序,JWM升级到2.1.0;Dillo更新到 3.0.2,改进CSS支持;XChat 1.8.9;sic 1.1 IRC客户端;XCalc-color;修改桌面功能,在JWM和Fluxbox之间切换可以不再需要关闭X,等等。
下载地址:dsl-4.11.rc1.iso (50.6MB, MD5).
1121 Damn Single (25)
"damn Single (单身狗)" is the Chinese nickname for someone who is being single. You are supposed to find those who are alone in a big party,so they can be taken care of.
Input Specification:
Each input file contains one test case. For each case,the first line gives a positive integer N (<=50000),the total number of couples. Then N lines of the couples follow,each gives a couple of ID‘s which are 5-digit numbers (i.e. from 00000 to 99999). After the list of couples,there is a positive integer M (<=10000) followed by M ID‘s of the party guests. The numbers are separated by spaces. It is guaranteed that nobody is having bigamous marriage (重婚) or dangling with more than one companion.
Output Specification:
First print in a line the total number of lonely guests. Then in the next line,print their ID‘s in increasing order. The numbers must be separated by exactly 1 space,and there must be no extra space at the end of the line.
Sample Input:
3 11111 22222 33333 44444 55555 66666 7 55555 44444 10000 88888 22222 11111 23333
Sample Output:
题目大意:给出n对数,每一对数表示一对情侣的id;再给出m个id进行查询,找出没有情侣,或者情侣没有出现的人,按顺序输出这些人的id。
思路:1.用数组来记录情侣关系cp[i]=j,cp[j]=i 表示i,j是情侣关系,不存在情侣的记为cp[i]=-1。
2.记录每一个人的出现与否,exist[i]=1,表示i出现,exis[i]=0 表示i没有出现,将出现的人依次记录在present[i]中
3.满足条件的为没有情侣的--cp[present[i]]=-1,以及情侣没有出现的--exist[cp[present[i]]]==0;
要求按id顺序输出,用set来保存满足条件的id,插入过程中,自动排序,也可以保存在数组中,最后再排序
注意点:当 输入项比较多的时候,应该避免使用cin,cout这两个的效率较低5 10000 23333 44444 55555 88888
1 #include<iostream> 2 #include<vector> 3 #include<set> 4 using namespace std; 5 int main(){ 6 vector<int> cp(100001,-1),present(100001),exist(100001,0); 7 set<int> ans; 8 int n,m,i,a,b,cnt=0; 9 cin>>n; 10 for(i=0; i<n; i++){ 11 scanf("%d%d",&a,&b); 12 cp[a] = b; 13 cp[b] = a; 14 } 15 cin>>m; 16 for(i=0; i<m; i++){ 17 scanf("%d",&present[i]); 18 exist[present[i]] = 1; 19 } 20 for(i=0; i<m; i++){ 21 if(!(cp[present[i]]!=-1 && exist[cp[present[i]]])){ 22 cnt++; 23 ans.insert(present[i]); 24 } 25 } 26 cout<<cnt<<endl; 27 for(auto it=ans.begin(); it!=ans.end(); it++){ 28 if(it==ans.begin()) printf("%05d",*it); 29 else printf(" %05d",*it); 30 } 31 }
1121 Damn Single (25 分)
"damn Single (单身狗)" is the Chinese nickname for someone who is being single. You are supposed to find those who are alone in a big party,so they can be taken care of.
Input Specification:
Each input file contains one test case. For each case,the first line gives a positive integer N (≤50,000),the total number of couples. Then N lines of the couples follow,each gives a couple of ID‘s which are 5-digit numbers (i.e. from 00000 to 99999). After the list of couples,there is a positive integer M (≤ 10,000) followed by M ID‘s of the party guests. The numbers are separated by spaces. It is guaranteed that nobody is having bigamous marriage (重婚) or dangling with more than one companion.
Output Specification:
First print in a line the total number of lonely guests. Then in the next line,print their ID‘s in increasing order. The numbers must be separated by exactly 1 space,and there must be no extra space at the end of the line.
Sample Input:
3 11111 22222 33333 44444 55555 66666 7 55555 44444 10000 88888 22222 11111 23333
Sample Output:
5 10000 23333 44444 55555 88888
//初始化用用-1 //因为有人的编号可能是00000 //所以第二个点不过 #include<cstdio> const int maxn = 100010; int spouse[maxn] = {0}; int main(){ int n,id1,id2; for(int i = 0; i < maxn; i++) spouse[i] = -1; scanf("%d",&n); while(n--){ scanf("%d%d",&id1,&id2); spouse[id1] = id2; spouse[id2] = id1; } scanf("%d",&n); while(n--){ scanf("%d",&id1); if(spouse[id1] >= 0 && spouse[spouse[id1]] >= 0){ spouse[id1] = -2; }else if(spouse[id1] >= 0 &&spouse[spouse[id1]] == -2){ spouse[spouse[id1]] = 2; }else{ spouse[id1] = -3; } } int cnt = 0; for(int i = 0; i < maxn; i++){ if(spouse[i] < -1) cnt++; } printf("%d\n",cnt); for(int i = 0; i < maxn; i++){ if(spouse[i] < -1){ printf("%05d",i); cnt--; if(cnt > 0) printf(" "); } } return 0; }
Android Small 使用教程
1. 首先在github上下载Small的源码下来
Small
2.添加AS的插件
解压文件依次打开(Android→ templates→ activities)你会看到一个叫做 SmallLauncher 的文件夹,将这个文件夹拷贝到 AndroidStudio 的 plugins\android\lib\templates\activities. 重新启动AS, 就可以看到新建的模板了。
3. 新建宿主工程
File->New->New Project 创建一个工程,在选择 Activity 时选择@Small模板
由于 IDE 的一个 bug,无法合并build.gradle脚本, 需要在根目录下的build.gradle脚本里,打开注释的语句:
buildscript {
dependencies {
classpath ''net.wequick.tools.build:gradle-small:1.2.0-alpha6''
}
}
apply plugin: ''net.wequick.small''
small {
aarVersion = ''1.2.0-alpha6''
}
4.创建module App.main
i. 模块名形如:app.*, lib.或者web. ii. 包名包含:.app., .lib.或者.web.
5 gradle 中 buildLib 和 buildBundle
生成的so文件在armeabi 文件夹中, 需要更改文件夹名字x86,以后每次更改了lib都需要替换掉. 需要注意在 app module中的Bundle.json 中包名是否和so对应。
Android Small 源码分析 (一) 启动流程
17 年春节前这段时间一直抱着 Small (aarVersion = ''1.1.0-alpha1''),打算从零开始一个新的项目。心想 Small 源代码也该好好看看,究竟从哪里开始才好呢? 还是从官方例子出发,从启动开始深入。
首先从 Small 在 Github 上的 Sample 项目开始了解 Small 的基本状况,如果要深入了解 Small 的具体实现,就必须深入到其中的 DevSample 项目,因为这才是 Small 的源码所在。
通过 UML 工具(CodeIris 插件)可以一窥其内部,得到相关的类图,这里是以 Smalll 类为源头,省略一部分类,大致了解到核心所在。
从实际应用 Small 过程中,想必会关注到 bundle.json 这个文件,可以看得出 BundleLaucher 是个关键类。因此将其抽取出来,分析关联的类结构 (这里省略覆盖方法)。
BundleLauncher 以及 SoBundleLauncher 都是抽象类,具体的应用得细看 ApkBundleLauncher、ActivityLauncher 这两个类,我会在后面具体分析到。
到此,我们已经大致了解 Small 的源码目录结构,我们要退一步从 Small Sample 项目开始,也就是用实际应用的项目开始溯源。
一. Small 启动流程
##(一)Small 预处理 首先我们看一下宿主 app 的 Application 类:
public class Application extends android.app.Application {
public Application() {
// This should be the very first of the application lifecycle.
// It''s also ahead of the installing of content providers by what we can avoid
// the ClassNotFound exception on if the provider is unimplemented in the host.
Small.preSetUp(this);
}
@Override
public void onCreate() {
super.onCreate();
// Optional
Small.setBaseUri("http://m.wequick.net/demo/");//设定基本的跳转地址
Small.setWebViewClient(new MyWebViewClient());//设置网页的基本回调
Small.setLoadFromAssets(BuildConfig.LOAD_FROM_ASSETS);
}
private static final class MyWebViewClient extends WebViewClient {
...
}
}
Small.java 内部基本都是静态方法,是 Small 框架的程序入口。在 Application 的构造方法中调用 Small.preSetUp (this),意思就是这应该在 application 的生命周期中最早调用的,也是安装 Content Providers 之前,可以避免在宿主中发生 ClassNotFound 异常. 具体来看看 preSetUp 里面吧。
public static void preSetUp(Application context) {
if (sContext != null) {
return;
}
sContext = context;
// Register default bundle launchers
registerLauncher(new ActivityLauncher());
registerLauncher(new ApkBundleLauncher());
registerLauncher(new WebBundleLauncher());
Bundle.onCreateLaunchers(context);
}
通过 Bundle.registerLauncher 方法添加三个 BundleLauncher 到 Bundle.sBundleLaunchers 列表,在通过 Bundle.onCreateLaunchers 启动这三者的 onCreate 方法,但其实只有 ApkBundleLauncher 覆盖了 OnCreate 方法。 ApkBundleLauncher 是 bundle 加载的管理类,我们看看它的 OnCreate 是怎么定义的。
@Override
public void onCreate(Application app) {
super.onCreate(app);
Object/*ActivityThread*/ thread;
List<ProviderInfo> providers;
Instrumentation base;
ApkBundleLauncher.InstrumentationWrapper wrapper;
Field f;
// 通过getActivityThread反射获取ActivityThread的对象
thread = ReflectAccelerator.getActivityThread(app);
// 将自定义的InstrumentationWrapper替换掉原来的mInstumentation
// Small通过占坑的方式管理Activity的.重点就在这里
try {
f = thread.getClass().getDeclaredField("mInstrumentation");//取得mInstrumentation属性
f.setAccessible(true);
base = (Instrumentation) f.get(thread);
wrapper = new ApkBundleLauncher.InstrumentationWrapper(base);
f.set(thread, wrapper);
} catch (Exception e) {
throw new RuntimeException("Failed to replace instrumentation for thread: " + thread);
}
// 继续替换Message Handler,用于恢复Activity Info 到真实的Activity
try {
f = thread.getClass().getDeclaredField("mH");
f.setAccessible(true);
Handler ah = (Handler) f.get(thread);
f = Handler.class.getDeclaredField("mCallback");
f.setAccessible(true);
f.set(ah, new ApkBundleLauncher.ActivityThreadHandlerCallback());
} catch (Exception e) {
throw new RuntimeException("Failed to replace message handler for thread: " + thread);
}
// 获取App的provider列表
try {
f = thread.getClass().getDeclaredField("mBoundApplication");
f.setAccessible(true);
Object/*AppBindData*/ data = f.get(thread);
f = data.getClass().getDeclaredField("providers");
f.setAccessible(true);
providers = (List<ProviderInfo>) f.get(data);
} catch (Exception e) {
throw new RuntimeException("Failed to get providers from thread: " + thread);
}
// 保存到全局变量,便于以后替换管理
sActivityThread = thread;
sProviders = providers;
sHostInstrumentation = base;
sBundleInstrumentation = wrapper;
}
ApkBundleLauncher.InstrumentationWrapper 这个是关键类,Android activities 受 Instrumentation 监控。每一个 Activity 由 Activity 的 startActivityForResult 方法启动,通过 instrumentation 的 execStartActivity 方法激活生命周期;
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity( // Override entry 1
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
...
}
}
在 ActivityThread 的 performLaunchActivity 方法中通过 instrumentation 的 newActivity 方法实例化。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity( // Override entry 2
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
}
Small 想要做到动态注册 Activity,首先在宿主 manifest 中注册一个命名特殊的占坑 activity 来欺骗 startActivityForResult 以获得生命周期,再欺骗 performLaunchActivity 来获得插件 activity 实例。又为了处理之间的信息传递,因此有了后面的 ActivityThreadHandlerCallback。
##(二)LaunchActivity 启动初始化 Small.setUp 这个方法需要放在 OnStart () 中执行,避免一些问题的产生,初始化完成之后,回调 onComplete 方法。完成加载之后,之后就交给插件的业务逻辑了。
@Override
protected void onStart() {
super.onStart();
SharedPreferences sp = LaunchActivity.this.getSharedPreferences("profile", 0);
final SharedPreferences.Editor se = sp.edit();
final long tStart = System.nanoTime();
se.putLong("setUpStart", tStart);
Small.setUp(LaunchActivity.this, new net.wequick.small.Small.OnCompleteListener() {
@Override
public void onComplete() {
long tEnd = System.nanoTime();
se.putLong("setUpFinish", tEnd).apply();
long offset = tEnd - tStart;
if (offset < MIN_INTRO_DISPLAY_TIME) {
// 这个延迟仅为了让 "Small Logo" 显示足够的时间, 实际应用中不需要
getWindow().getDecorView().postDelayed(new Runnable() {
@Override
public void run() {
Small.openUri("main", LaunchActivity.this);
finish();
}
}, (MIN_INTRO_DISPLAY_TIME - offset) / 1000000);
} else {
Small.openUri("main", LaunchActivity.this);
finish();
}
}
});
}
现在我们跟踪进 setUp 方法,看看里面究竟做了什么。
public static void setUp(Context context, OnCompleteListener listener) {
if (sContext == null) {
// Tips for CODE-BREAKING
throw new UnsupportedOperationException(
"Please call `Small.preSetUp'' in your application first");
}
if (sHasSetUp) {
if (listener != null) {
listener.onComplete();
}
return;
}
Bundle.loadLaunchableBundles(listener);
sHasSetUp = true;
}
可以看到 Small 确认还没初始化之后,就递给了 Bundle 类,在其内部执行静态方法 loadLaunchableBundles,我们继续往下看。
protected static void loadLaunchableBundles(Small.OnCompleteListener listener) {
Context context = Small.getContext();
boolean synchronous = (listener == null);
if (synchronous) {
loadBundles(context);
return;
}
// Asynchronous
if (sThread == null) {
sThread = new LoadBundleThread(context);
sHandler = new LoadBundleHandler(listener);
sThread.start();
}
}
这个方法很简单,启动一个 LoadBundleThread 线程,一个 Handler 处理完成后的事项。在 LoadBundleThread 里面我们肯定会看到 bundle.json 的处理。
private static class LoadBundleThread extends Thread {
Context mContext;
public LoadBundleThread(Context context) {
mContext = context;
}
@Override
public void run() {
loadBundles(mContext);
sHandler.obtainMessage(MSG_COMPLETE).sendToTarget();
}
}
这里和之前的版本比较,会发现少了 Bundle.setupLaunchers (Context) 方法,仔细往下就会发现其实它被延后到 loadBundles 内部了。
private static void loadBundles(Context context) {
JSONObject manifestData;
try {
File patchManifestFile = getPatchManifestFile();// app路径/file/bundle.json
String manifestJson = getCacheManifest();//SharedPreferences的bundle.json字段
if (manifestJson != null) {
// Load from cache and save as patch
if (!patchManifestFile.exists()) patchManifestFile.createNewFile();
PrintWriter pw = new PrintWriter(new FileOutputStream(patchManifestFile));
pw.print(manifestJson);
pw.flush();
pw.close();
// Clear cache
setCacheManifest(null);
} else if (patchManifestFile.exists()) {
// Load from patch
BufferedReader br = new BufferedReader(new FileReader(patchManifestFile));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
manifestJson = sb.toString();
} else {
// Load from built-in `assets/bundle.json''
InputStream builtinManifestStream = context.getAssets().open(BUNDLE_MANIFEST_NAME);
int builtinSize = builtinManifestStream.available();
byte[] buffer = new byte[builtinSize];
builtinManifestStream.read(buffer);
builtinManifestStream.close();
manifestJson = new String(buffer, 0, builtinSize);
}
// Parse manifest file
manifestData = new JSONObject(manifestJson);
} catch (Exception e) {
e.printStackTrace();
return;
}
Manifest manifest = parseManifest(manifestData);
if (manifest == null) return;
setupLaunchers(context);
loadBundles(manifest.bundles);
}
由上可知,处理 bundle.json 有位置优先级关系:manifestJson > patchManifestFile > assets/bundle.json ,也就是 SharedPreferences > File > Assets 。因此,我们最初的配置在 Assets 里面的 bundle.json 会在最后处理。 接着回到先前提及的 setupLaunchers,回忆一下,在 preSetUp 中我们看到添加了 3 个 BundleLauncher,这里就是对三者的 setUp 进行调用。
protected static void setupLaunchers(Context context) {
if (sBundleLaunchers == null) return;
for (BundleLauncher launcher : sBundleLaunchers) {
launcher.setUp(context);
}
}
- ActivityLauncher 的 setUp 方法 我们继续跟踪进去,首先看看 ActivityLauncher 的 setUp 方法,它将注册在宿主的 activities 添加到 sActivityClasses 里面。
public class ActivityLauncher extends BundleLauncher {
...
@Override
public void setUp(Context context) {
super.setUp(context);
// Read the registered classes in host''s manifest file
PackageInfo pi;
try {
pi = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException ignored) {
// Never reach
return;
}
ActivityInfo[] as = pi.activities;
if (as != null) {
sActivityClasses = new HashSet<String>();
for (ActivityInfo ai : as) {
sActivityClasses.add(ai.name);
}
}
}
...
}
- ApkBundleLauncher 的 setUp 方法 这里与之前的 ApkBundleLauncher 的 onCreate 方法相呼应,通过动态代理的方式,将 Intent 参数重新包装来完成上面所说的欺骗方式。
public class ApkBundleLauncher extends SoBundleLauncher {
...
@Override
public void setUp(Context context) {
super.setUp(context);
Field f;
// AOP for pending intent
try {
f = TaskStackBuilder.class.getDeclaredField("IMPL");
f.setAccessible(true);
final Object impl = f.get(TaskStackBuilder.class);
InvocationHandler aop = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Intent[] intents = (Intent[]) args[1];
for (Intent intent : intents) {
sBundleInstrumentation.wrapIntent(intent);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
}
return method.invoke(impl, args);
}
};
Object newImpl = Proxy.newProxyInstance(context.getClassLoader(), impl.getClass().getInterfaces(), aop);
f.set(TaskStackBuilder.class, newImpl);
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
...
}
我们进去 wrapIntent 看看,如果不是精确命中,intent.getComponent () 为空,因此首先处理交给宿主的冲突问题,如果是系统或者宿主的 Action,直接退出;
private void wrapIntent(Intent intent) {
ComponentName component = intent.getComponent();
String realClazz;
if (component == null) {
// Try to resolve the implicit action which has registered in host.
component = intent.resolveActivity(Small.getContext().getPackageManager());
if (component != null) {
// A system or host action, nothing to be done.
return;
}
// Try to resolve the implicit action which has registered in bundles.
realClazz = resolveActivity(intent);
if (realClazz == null) {
// Cannot resolved, nothing to be done.
return;
}
} else {
realClazz = component.getClassName();
if (realClazz.startsWith(STUB_ACTIVITY_PREFIX)) {
// Re-wrap to ensure the launch mode works.
realClazz = unwrapIntent(intent);
}
}
if (sLoadedActivities == null) return;
ActivityInfo ai = sLoadedActivities.get(realClazz);
if (ai == null) return;
// Carry the real(plugin) class for incoming `newActivity'' method.
intent.addCategory(REDIRECT_FLAG + realClazz);
String stubClazz = dequeueStubActivity(ai, realClazz);
intent.setComponent(new ComponentName(Small.getContext(), stubClazz));
}
否则尝试处理插件内的冲突 (resolveActivity(intent)), 初始失败则退出。
private String resolveActivity(Intent intent) {
if (sLoadedIntentFilters == null) return null;
Iterator<Map.Entry<String, List<IntentFilter>>> it =
sLoadedIntentFilters.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, List<IntentFilter>> entry = it.next();
List<IntentFilter> filters = entry.getValue();
for (IntentFilter filter : filters) {
if (filter.hasAction(Intent.ACTION_VIEW)) {
// TODO: match uri
}
if (filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
// custom action
if (filter.hasAction(intent.getAction())) {
// hit
return entry.getKey();
}
}
}
}
return null;
}
如果精确命中,先解开 (unwrapIntent(intent);), 后面重新选取一个未使用的 activity 坑与之使用。
private String[] mStubQueue;
/** Get an usable stub activity clazz from real activity */
private String dequeueStubActivity(ActivityInfo ai, String realActivityClazz) {
if (ai.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
// In standard mode, the stub activity is reusable.
// Cause the `windowIsTranslucent'' attribute cannot be dynamically set,
// We should choose the STUB activity with translucent or not here.
Resources.Theme theme = Small.getContext().getResources().newTheme();
theme.applyStyle(ai.getThemeResource(), true);
TypedArray sa = theme.obtainStyledAttributes(
new int[] { android.R.attr.windowIsTranslucent });
boolean translucent = sa.getBoolean(0, false);
sa.recycle();
return translucent ? STUB_ACTIVITY_TRANSLUCENT : STUB_ACTIVITY_PREFIX;
}
int availableId = -1;
int stubId = -1;
int countForMode = STUB_ACTIVITIES_COUNT;
int countForAll = countForMode * 3; // 3=[singleTop, singleTask, singleInstance]
if (mStubQueue == null) {
// Lazy init
mStubQueue = new String[countForAll];
}
int offset = (ai.launchMode - 1) * countForMode;
for (int i = 0; i < countForMode; i++) {
String usedActivityClazz = mStubQueue[i + offset];
if (usedActivityClazz == null) {
if (availableId == -1) availableId = i;
} else if (usedActivityClazz.equals(realActivityClazz)) {
stubId = i;
}
}
if (stubId != -1) {
availableId = stubId;
} else if (availableId != -1) {
mStubQueue[availableId + offset] = realActivityClazz;
} else {
// TODO:
Log.e(TAG, "Launch mode " + ai.launchMode + " is full");
}
return STUB_ACTIVITY_PREFIX + ai.launchMode + availableId;
}
private static String unwrapIntent(Intent intent) {
Set<String> categories = intent.getCategories();
if (categories == null) return null;
// Get plugin activity class name from categories
Iterator<String> it = categories.iterator();
while (it.hasNext()) {
String category = it.next();
if (category.charAt(0) == REDIRECT_FLAG) {
return category.substring(1);
}
}
return null;
}
- ApkBundleLauncher 的 setUp 方法的 setUp 方法 启动一个新的 android 本身的 WebView,这里作者特别注释到:在 Android7.0 上,在第一次创建 WebView 的时候,它会用 WebView 的 Assets 路径替换掉原 Application Assets 路径,一旦发生我们先前所做的努力化为泡影,因此我们尽可能将它推到前面设置。
@Override
public void setUp(Context context) {
super.setUp(context);
if (Build.VERSION.SDK_INT < 24) return;
Bundle.postUI(new Runnable() {
@Override
public void run() {
// In android 7.0+, on firstly create WebView, it will replace the application
// assets with the one who has join the WebView asset path.
// If this happens after our assets replacement,
// what we have done would be come to naught!
// So, we need to push it enOOOgh ahead! (#347)
new android.webkit.WebView(Small.getContext());
}
});
}
准备工作都完成了,接着到调用 loadBundles 的方法,加载所有模块,这里在 prepareForLaunch() 包含了很多处理,这里暂且不详细讲了,不然又要扯很远再回来,下一节会详细讲。 等待这一切完成之后,就通过 Handler 通知完成,回到 LaunchActivity 的 OnStart 内继续执行。
private static void loadBundles(List<Bundle> bundles) {
sPreloadBundles = bundles;
// Prepare bundle
for (Bundle bundle : bundles) {
bundle.prepareForLaunch();//会产生IO操作
}
// Handle I/O
if (sIOActions != null) {
ExecutorService executor = Executors.newFixedThreadPool(sIOActions.size());
for (Runnable action : sIOActions) {
executor.execute(action);
}
executor.shutdown();
try {
if (!executor.awaitTermination(LOADING_TIMEOUT_MINUTES, TimeUnit.MINUTES)) {
throw new RuntimeException("Failed to load bundles! (TIMEOUT > "
+ LOADING_TIMEOUT_MINUTES + "minutes)");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
sIOActions = null;
}
// Wait for the things to be done on UI thread before `postSetUp`,
// as on 7.0+ we should wait a WebView been initialized. (#347)
while (sRunningUIActionCount != 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Notify `postSetUp'' to all launchers
for (BundleLauncher launcher : sBundleLaunchers) {
launcher.postSetUp();
}
// Wait for the things to be done on UI thread after `postSetUp`,
// like creating a bundle application.
while (sRunningUIActionCount != 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Free all unused temporary variables
for (Bundle bundle : bundles) {
if (bundle.parser != null) {
bundle.parser.close();
bundle.parser = null;
}
bundle.mBuiltinFile = null;
bundle.mExtractPath = null;
}
}
至此,启动流程基本讲完了,插件的加载过程,也就是 prepareForLaunch () 部分,我将在下一节再详细介绍。 敬请期待!!!
原链接在: 第一节:Android Small 源码分析 (一) 启动流程 第二节:Android Small 源码分析 (二) 插件加载过程
今天关于Damn Small Linux 4.11 RC1 发布的介绍到此结束,谢谢您的阅读,有关1121 Damn Single (25)、1121 Damn Single (25 分)、Android Small 使用教程、Android Small 源码分析 (一) 启动流程等更多相关知识的信息可以在本站进行查询。
本文标签: