Linux_vim_"*"命令

@./&重复Ex命令

@:可以用来重复任意Ex命令,或者也可以输入&来重复上次的:substitute命令(本身也是一条Ex命令)。

Vim提供了一个:substitute命令专门用于查找替换任务,不过用上面介绍的技术,也可以手动修改第一处地方,然后再一个个地查找替换其他匹配项。.命令可以把我们从繁重的工作中解放出来,而即将登场的另一个有用的单键命令,则能够让我们方使地在匹配项间跳转。

在下面这段文本中,每一行都出现了单词"content"。

1
2
3
...We're waiting for content before the site can go live...
...If you are content with this, let's go ahead with it...
...We'll launch as soon as we have the content..

假设想用单词"copy"(意义同"copywriting")来替代"content"。也许只要用替换命令就行了,像下面这样::%s/content/copy/g。但是如果我们运行上面这条命令,就会出现"If you are ‘copy’ with this,“这样的句子,之所以会有这种问题,是因为"content"一词有两种含义,一个是"copy"的同义词,另一个是"happy"的同义词。用专业的话说,我们是在处理拼写相同,但含义和发音都不同的词。所以在替换时,一定要小心每一步操作。不能想当然地用"copy"替换每一个"content”,而是要时刻留神,对每个地
方都要问"这里要修改吗?“,然后回答"修改"或者"不改”。:substitute命令能胜任这项工作。

*命令–查找当前光标下的单词

可以先用/content,把光标移动到指定的单词上。然后按*键,就可以找到下一个同词。

image-20220901180716483

刚开始,把光标移到单词"content"上,然后使用*命令对它进行查找。这会产生两个结果:一是光标跳到下一个匹配项的第一个字符上,二是所有出现这个词的地方都被高亮显示出来。如果没有看到高亮,运行一下 :set hls。执行过一次查找"content"的命令后,现在只需按n键就可以跳到下一个匹配项。在本例中,按一次*,两次n,会遍历完所有的匹配项,从而跳回到本次查找的起点。

总结

  1. *命令的作用是光标跳到下一个匹配项的第一个字符上。本质上是先/\<content\>搜索匹配项,在点按n
  2. 开启搜索匹配项同时高亮::set hls

使修改可重复

当光标位于"content"的开头时,就可以着手修改它。这包括两步操作:首先要删除单词"content",然后输入替代的单词。cw命令会删除从光标位置到单词结尾间的字符,并进入插入模式,接下来就可以输入单词"copy"了。Vim会把我们离开插入模式之前的全部按键操作都记录下来,因此整个cw copy<Esc>会被当成一个修改。也就是说,执行.命令会删除从光标到当前单词结尾间的字符,并把它修改为"copy"。

合而为一

每次按n键时,光标就会跳到下一个"content"单词所在之处,而按键时,它就会把光标下的单词改为"copy"。

如果想替换所有地方,就可以不加思考地一直按n.n.n.以完成所有的修改(但是,这种情况下也可以用:%s/content/copy/g命令)。然而,由于我们需要留意不符合要求的匹配项,所以在按了n之后,要审视一下当前的匹配项,然后决定是否把它改为"copy"。如果需要修改的话,就按.命令,反之则不用。无论决定是什么,都可以再次按n移到下一个地方,如此循环往复,直到完成全部的修改。

Android_进程

Activity启动后生成几个Thread

Activity启动后,除了main thread,还有两个Binder Thread。

主线程是怎么产生的

  1. 由ZygoteInit启动,经由一系列系统调用后最终才执行Activity本身的onCreate函数,Zygote为Activity创建的主线程是ActivityThread。

Service的主线程是什么

  1. Service也是寄存于ActivityThread之中的,并且启动流程和Activity基本一致;即Activity和Service应用程序的主线程都是ActivityThread。
  2. 启动Service时,也同样需要两个Binder线程的支持。

同一个程序包中有两个Acivity

两个Activity之间是什么关系?需要有多少线程的支持?

AndroidManifest.xml:

1
2
3
4
5
6
7
8
9
10
11
12
/* AndroidManifest.xml */
<activity android:name="./ActivityThreadTest"
android:label="@string/title_activity_activity_thread_test" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".ActivityThreadTest2"
android:label="@string/title_activity_activity_thread_test" >
</activity>

ActivityThreadTest.java

1
2
3
4
5
6
7
8
/* ActivityThreadTest.java */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("ActivityThreadTest1", "We are in ThreadTest1");
setContentView(R.layout.activity_activity_thread_test);
Intent intentThread2 = new Intent(this, ActivityThreadTest2.class);
startActivity(intentThread2);
}

当断点停在ActivityThreadTest2的onCreate函数中时,线程的分布情况如下:

image-20220906104610197

其中揭示了以下几个现象。

当ActivityThreadTest2被执行时,主线程始终只有一个。
此时ActivityThreadTest暂时退出了运行。
Binder线程数量有所变化。

同一个AndroidManifest.xml中定义的组件都运行于同一个进程中

对于同一个AndroidManifest.xml中定义的四大组件,除非有特别声明,否则它们都运行于同一个进程中(并且均由主线程来处理事件)。

根据操作系统的基础知识,如果两个对象处于同一个进程空间中,那么内存区域应该是可共享访问的。可以利用这个原理来论证先后启动的同一个包里的两个Activity是否共存于同一个进程中。先让ActivityThreadTest拥有一个static的变量,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* ActivityThreadTest.java */
public class ActivityThreadTest extends Activity {
static int TestForCoexist = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("ActivityThreadTest1", "We are in ThreadTest1");
setContentView(R.layout.activity_activity_thread_test);
Intent intentThread2 = new Intent(this, ActivityThreadTest2.class);
TestForCoexist = 2;
startActivity(intentThread2);
}
}

ActivityThreadTest中有一个静态的变量TestForCoexist。其初始值为-1,并在Activity启动后(onCreate 中)被修改为2。

接着看ActivityThreadTest2的源码实现:

1
2
3
4
5
6
7
8
9
10
/* ActivityThreadTest2.java */
public class ActivityThreadTest2 extends Activity {
@override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
Log.i("ActivityThreadTest2","We are in ThreadTest2");
setContentView(R.layout.activity_activity_thread_test);
Log.i("ActivityThreadTest2","TestForCoexist="
+ ActivityThreadTest.TestForCoexist);//实验结果将在这里呈现
}

image-20220906110131202

运行这个应用程序,来看看最终的输出,TestForCoexist为2。

这就足够证明两个 Activity 是在同一个进程空间中了。

仔细观察,还会发现二者所处的应用程序PID和TID值是相同的。这都证明了一个结论,那就是同一个程序包里的两个Activity默认确实都运行于同一个进程中。

让不同包的组件运行于相同的进程中

Android提供了特殊的方式让不是同一个包里的组件也可以运行于相同的进程中。优势就是,它们可以非常方便地进行资源共享,而不用经过费时费力的进程间通信。分为两种情况:

针对个别组件

可以在AndroidManifest.xml文件中的<activity><service><receiver><provider>(四大组件都支持,可以根据需要来添加)标签中加入android:process属性来表明这一组件想要运行在哪个进程空间中。

针对整个程序包

可以直接在<application>标签中加入android:process属性来指明想要依存的进程环境。

结论

  1. 四大组件并不是程序(进程)的全部,而只是它的"零件"。
  2. 应用程序启动后,将创建ActivityThread主线程。
  3. 同一个包中的组件将运行在相同的进程空间中。
  4. 不同包中的组件可以通过一定的方式运行在一个进程空间中。
  5. 一个Activity应用启动后至少会有3个线程:即一个主线程和两个Binder线程。