辉夜的博客

繁花似锦,辉夜如昼

0%

Context

Context是一个控制全局资源的抽象类,由Android系统负责具体实现。各个组件都需要利用Context才能访问res中的字符串,图片等资源。同时一些应用级功能也要通过Context实现

正常设置UI时的Context

正常设置UI是使用setContentView(R.layout.activity_main),这个函数会调用getDelegate().setContentView(layoutResID);递归地查找并且生成定义在xml中的各个View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//in AppCompatActivity.java
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
//调用了Activity的Delegate的同名方法,设置了mDelegate.mContext
getDelegate().setContentView(layoutResID);
}

//in AppCompatDelegate.java
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
/*这一句会触发后续的递归查找*/
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}

在这个过程中AppCompactDelegat类带有一个字段mContext,这个字段用在了解析xml度过程中。可以看到,调用getDelegate时会设置这个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Return The AppCompatDelegate being used by this Activity.
*/
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}

//in AppCompatDelegate.java(抽象类)
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);
}

//in AppCompatDelegateImpl.java(实现)
AppCompatDelegateImpl(Activity activity, AppCompatCallback callback) {
//调用另一个构造器
this(activity, null, callback, activity);
}

//in AppCompatDelegateImpl.java
private AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback,
Object host) {
mContext = context;
mAppCompatCallback = callback;
mHost = host;
...
}

我们可以发现Activity本身就是mDelegate的context和callback。事实上Activity隔着漫长的继承链继承了Context类。
正是因为有了Context,这个解析过程在能够把id和xml中的内容联系起来。

直接使用View

这里以imageview为例,通常我们都是在xml中定义的View组件,这样它们就会在上述的一系列过程中被加载出来,最后作为一整个activity呈现。
如果单独定义iv,就要显式指定Context,这里我们仍然用activity作为context,并通过他访问应用的启动图标,然后呈现在内容页

1
2
3
ImageView iv = new ImageView(this);
iv.setImageResource(R.mipmap.ic_launcher);
setContentView(iv);

在这里,同样是通过Context才能用R文件中的int常量直接找到图片。

横跨两行的单元格:

类别 属性 功能描述
根据父容器定位 android:layout centerInParent 组件位于父容器中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerVertical 组件位于父容器垂直中央位置
android:layout_alignParentTop 组件与父容器顶部对齐
android:layout_alignParentLeft 组件与父容器左部对齐
android:layout_alignParentRight 组件与父容器右部对齐
android:layout_alignParentBottom 组件与父容器底部对齐
根据兄弟组件定位 android:layout_above 组件位于某组件上部
android:layout_below 组件位于某组件下部
android:layout_toLeftOf/toRightOf 组件位于某组件左/右侧
android:layout_alignTop/Left/Right/Below 组件与某组件对齐

横跨两行的单元格:

类别 属性 功能描述
根据父容器定位 android:layout centerInParent 组件位于父容器中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerVertical 组件位于父容器垂直中央位置
android:layout_alignParentTop 组件与父容器顶部对齐
android:layout_alignParentLeft 组件与父容器左部对齐
android:layout_alignParentRight 组件与父容器右部对齐
android:layout_alignParentBottom 组件与父容器底部对齐
根据兄弟组件定位 android:layout_above 组件位于某组件上部
android:layout_below 组件位于某组件下部
android:layout_toLeftOf/toRightOf 组件位于某组件左/右侧
android:layout_alignTop/Left/Right/Below 组件与某组件对齐

Android UI组件

User Interface,用户接口。包括输入框,文本,按钮,播放器,图片,列表等。常规的组件大多由android.widget包中提供,这些组件有通用的属性和方法,也有自己特定的属性和方法。比如所有组件都有id,height,width,margin等属性,而TextView有setText的方法。

常规UI组件

安卓组件以左上角为原点,向下向右为正方向。这也是组件的默认布置为止
带有layout的属性通常是组件相对于父容器的对齐方式,带有padding的则是子元素相对于容器的对齐方式

组件属性

高级UI组件

高级组件通常继承自ViewGroup,可以内含多个View组件,也可以继续嵌套ViewGroup

布局

布局将ui组合成页面,主要控制大小、位置、层级

线性布局

属性 功能描述
android:orientation 布局内组件的排列方向
android:layout_weight 布局内组件大小权重
android:divider 布局内组件间分割线
android:showDividers 布局内组件间分割线位置
android:dividerPadding 布局内分割线padding

linearlayout的分割线功能可以很容易的画出有质感的ui

weight功能是指示分配剩余空间时的权重(剩余空间可正可负),各个组件按照权重加权平均分配剩余的空间。

如果想要在linearlayout里做出垂直于orientation方向的效果,就要再嵌套一个另一方向的linearlayout

相对布局

相对布局提供了根据父容器和兄弟容器进行定位的方法

类别 属性 功能描述
根据父容器定位 android:layout centerInParent 组件位于父容器中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerHorizontal 组件位于父容器水平中央位置
android:layout_centerVertical 组件位于父容器垂直中央位置
android:layout_alignParentTop 组件与父容器顶部对齐
android:layout_alignParentLeft 组件与父容器左部对齐
android:layout_alignParentRight 组件与父容器右部对齐
android:layout_alignParentBottom 组件与父容器底部对齐
根据兄弟组件定位 android:layout_above 组件位于某组件上部
android:layout_below 组件位于某组件下部
android:layout_toLeftOf/toRightOf 组件位于某组件左/右侧
android:layout_alignTop/Left/Right/Below 组件与某组件对齐

层级布局FrameLayout

属性 描述
android:foreground 设置前景图像(永远位于最前)
android:foregroundGravity 设置前景图像Gravity

各个组件从前到后依次在后方作为背景

限制布局ConstraintLayout

属性 功能描述
layout_constraintLeft_toLeftOf 组件左部与某组件左部对齐
layout_constraintLeft_toRightOf 组件左部与某组件右部对齐
layout_constraintRight_toLefOf 组件右部与某组件左部对齐
layout_constraintRight_toRightOf 组件右部与某组件右部对齐
layout_constraintTop_toTopof 组件顶部与某组件顶部对齐
layout_constraintTop_toBottomOf 组件右顶与某组件底部对齐
layout_constraintBotom_toTopof 组件底部与某组件顶部对齐
layout_constraintBottom_toBottomOf 组件底部与某组件底部对齐
layout_constraintBascline_toBasclincO 组件基线与某组件基线对齐
laycut_constraintStart_toEndOf 组件首部与某组件尾部对齐
layout_constraintStart_toStartOf 组件首部与某组件首部对齐
layout_constraintEnd_toStartOf 组件尾部与某组件首部对齐
layout_constraintEnd_toEndOf 组件尾部与某组件尾部对齐

如果限制信息确定了图片某几条边的位置,会覆盖height,width属性

总结:

总结

渲染

布局是用xml中声明,java中实现的。因此布局的渲染需要经过加载、解析和渲染的三个过程,

在activity的onCreate方法中调用setContentView时,它会创建DecorView,最终通过LayoutInflater加载了XML文件

1
LayoutInflater.from(mContext).inflate(resId, contentParent)

在这个加载器中,它会调用一个xml解析器,解析器根据tag生成View,组织ViewGroup并把他们添加到DecorView中…

此时屏幕还没有被绘制,在onResume()中会“借用”ViewRootImpl对象的requestLayout方法来触发配置。

在这之后,由VSync一帧为单位控制界面的刷新。00

交互

查找View

交互最基本的流程是先获取View实例,再添加相应的监听器(比如按钮的onClickListener())

查找的过程中,activity调用DecorView的findViewById方法,每个ViewGroup都会迭代查找自己的孩子,方法是在ViewGroup和View中都实现findViewById,Group会判断自己的id并且调用所有孩子的findViewById。对于View而言,这个方法要么返回自己,要么返回NULL,对于Group而言,他将进行下一轮迭代。这样所有人都只需要知道自己的id,Group再额外记录自己有哪些孩子,就可以从DecorView开始查找到任何被解析过的View了。

监听事件

Android系统中常见的事件监听器有如下几种:

  1. 单击事件(View.OnClickListener):当用户触碰到某个组件或者方向键被按下时产生该事件,该事件的处理方法是onClick().

  2. 焦点事件(View.OnFocusChangeListener):组件得到或者失去焦点时产生该事件,事件处理方法是onFocusChange()。

  3. 按键事件(View.OnKey Listener):用户按下或者释放设备上的某个按键时产生,事件处理方法是 onKey()。

  4. 触碰事件(View.OnTouchListener):设备具有触摸屏功能时,触碰屏幕产生该事件。事件处理方法是onTouch()。

  5. 创建上下文菜单事件(View.OnCreateContextMenu Listener):创建上下文菜单时产生该事件,事件处理方法是 onCreateContextMenu()。

屏幕触摸

所有事件都来自于屏幕触摸,交互事件是对屏幕点击的二次封装!触摸事件MotionEvent包含了触摸的时间、位置等,Activity和View都有onTouchEvent()用于处理触摸事件

接收到一个屏幕事件之后,具体要由哪个UI控件来响应,涉及到时事件的处理流程。触摸一个点很可能同时触摸了多个View,几个Layout还有至少一个Activity。在实践处理中,ViewGroup具有分发、拦截、响应的功能,Activity和View则不具有拦截的功能。
事件处理
出处(https://juejin.cn/post/6844904041487532045)

如何理解onLongClickListener,onClickLisener,onTouchEvent的关系呢?在onTouchEvent在ACTION_DOWN时判断是否达到长按延时,而在ACTION_UP的时候才执行短按延时,此时如果已经出发了了长按执行的监听器,就不再执行短按的监听器,所以说onClickListener是在抬起手指时才触发的,并且不能与onLongClickListener一同触发。

交互总结

从谁发出,经过了谁,到达哪个对象,有何种行为。

动画

帧动画

核心原理就是一帧一帧的播放图片,优点是简单、方便,缺点是功能单一,并且消耗资源。相比于gif,帧动画更可控。

帧动画是通过animation-list声明的,有子标签item,item包含drawable和duration两个属性。

补间动画

补间动画可以控制透明度,旋转,缩放,平移,由系统自动由插值器(Interpolator)

这两种动画都是属于View动画,只能处理View对象,并且无法修改View的属性。(会出现动画无法点击的情况)

属性动画

属性动画可以作用于任何一个对象,并且真实的改变了对象的属性。

总结

真正的动画需要结合各个动画才能做出更好的效果。

自定义UI

自定义View

写四个构造器,对应四种场景。按需复写各方法,并且用各个方法绘制出View,然后再onTouchEvent中处理UI的行为和动画

总结

Intent的概念与使用

关于Intent,目前只用在启动activity上,通常启动一个activity时需要传递一个intent参数给指定的activity,同时也可以在这个Intent中夹带一些参数进行传递。

用显示Intent启动Activity

显示intent就是指定了要启动的activity的类的intent,这种intent通常是用在app内activity之间的交互。接下来就来在一个项目里注册两个activity来看一看如何用intent启动。

如果不直接新建activity,就要新建一个java类,并令它继承Activity类,还要在res文件夹中新建layout文件定义其ui。之后在java文件中实现Activity的各个方法,通常在onCreate方法中进行layout与activity的绑定:

1
2
3
4
5
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myaty);
}

之后为了使用这个activity中,我们还需要在AndroidManifest文件中进行注册,注意activity的name会与manifest的package组合起来作为Activity类的名称,所以在这里注册时可以使用简写,也可以在name中写全包名.类名

1
2
3
4
5
6
7
8
9
10
11
<manifest ...
package="com.example.study4intent">

<application ...>
<activity android:name=".MyAty">
<!-- name added to package as the name of an Activity object-->
<!-- use com.example.study4intent.MyAty instead-->
...
</activity>
</application>
</manifest>

到这里,我们就已经新建了一个activity,并且可以绑定它对应的java和res文件了。如果直接在AS中新建activity的话,ide会自动帮我们完成这些配置工作。

到这里,我们就可以试着用显示intent启动activity了,这里选择用一个按钮来触发新activity的启动:

1
2
3
4
5
6
7
8
9
10
findViewById(R.id.button).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent =
new Intent(MainActivity.this, MyAty.class);
startActivity(intent);
}
}
);

java允许传new出来的参数,而new抽象类需要实现抽象方法,这种嵌套方式写出来的代码非常紧凑,不过通常我们会把findViewById的结果声明为private变量保存下来方便以后调用。

查看Intent构造器可知Intent可以用一个Context类型对象和一个class泛型来构造,这就是我们的显示Intent要传递的包名和类名。

1
public Intent(Context packageContext, Class<?> cls)

隐式Intent

Intent除了用显式指定的包名和类名构造之外,也可以先通过一个标记构造,之后再寻找标记对应的类。最常见的方法是使用action,也就是一个字符串。如果用action构造了一个intent,那么就可以直接用这个字符串搜索到注册了这个action的类。(小疑惑,如果有两个一样的action会怎样呢?)

注册action

在androidmanifest文件中,activity下加入intent-fliter标签:

1
2
3
4
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="qaq"/>
</intent-filter>

注意,新版Android可能会报错,要求指定activity是否exported,直接用快速修复功能设置为true即可。下文会讲到。

category的name暂时选择default就可以,而action的name则要注册为对应的字符串,标准的action格式是<package_name>.action.start.<class_name>,不过这个格式实在过于复杂,我们也可以把action的名字放在要启动的类中,作为一个常量

1
2
3
4
public class MyAty extends Activity {
public static final String ACTION = "qaq";
...
}

这样在使用Intent时,就不需要直接输入长长的字符串,使用MyAty.ACTION就可以了

用隐式Intent启动同一个app内的另一个Activity

基本的方法和之前都是相同的,区别就是构造intent时调用了另一种构造器:

1
2
3
4
5
6
7
8
9
10
11
findViewById(R.id.button).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent =
- new Intent(MainActivity.this, MyAty.class);
+ new Intent("qaq");
startActivity(intent);
}
}
);
1
public Intent(String action)

当然,隐式Intent最主要的作用并不是用来启动同一个应用内的activity,因为此时我们是可以直接获得这个activity的类的定义的。而在跨应用通讯时,隐式Intent就有用处了。

用Intent跨应用启动activity

还记得之前AS可能会要求我们添加的exported属性吗:

1
2
3
4
5
6
7
        <activity android:name=".MyAty"
+ android:exported="false">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="qaq"/>
</intent-filter>
</activity>

这是安卓权限管理引入的机制:只有设置为exported的activity对其他应用才是可见的,现在我们可以把它改为true,然后在project中新建一个package(app),并且在另一个app中启动这个app的activity

1
2
3
4
5
6
7
8
9
10
11
12
findViewById(R.id.btn_start_myaty).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
startActivity(new Intent("qaq"));
} catch (Exception e) {
Toast.makeText(MainActivity.this, "无法启动MyAty",Toast.LENGTH_SHORT).show();
}
}
}
);

这里使用了try结构来捕获可能的异常,如果前面没有正确的设置exported的话,就可能导致异常的出现。捕获到异常之后,用Toast组件打印一条提示信息。

Activity

activity的使用

  1. 在Android Menifest文件中注册
  2. 在xml文件中设定layout
  3. 在java文件中绑定activity

GridView + Adaptor, GridView + Adaptor, ViewPager + Adaptor

activity生命周期

  1. onCreate
    程序创建,进行文件的绑定
  2. onStart
    程序不可见,用户不可交互,进行一些
  3. onResume
    程序可见,用户可以交互
  4. onPause
    程序部分不可见,(比如被弹窗遮挡),在这里是最后一个比较靠谱的数据保存点(Android 5.0-)。
    之后也可以在onStop中进行数据保存
    如果之后恢复可见,会调用onResume
  5. onStop
    程序被完全遮挡,如果恢复,经由onRestart回到onStart
  6. onDestroy
    程序被销毁,注意,很多场景下可能不会走到这一步,而是在停止之后直接被系统回收

配置变化(字体改变,屏幕旋转,键盘显示隐藏…)时的生命周期:先杀死再重建,杀死时在onPause之前调用onSaveInstanceState,
恢复时在onStart之后调用onRestoreInstanceState

如果不希望重建activity,可以在android_menifest文件中配置,在配置改变时调用onConfigurationChange,而不销毁。

案例:接完电话之后,程序crush!

  • 解释:页面被回收导致本地变量被清空

  • 解决:添加判空逻辑避免空指针

    • 利用两个InstanceState保存和恢复现场!

activity启动模式

对于每个activity,都可以在android:launchMode中配置启动模式来达到消除重复activity的效果

  1. standard(默认)
    每次启动页面都会在页面栈中添加一个新实例
  2. singleTop
    不允许栈顶有多个相同实例,试图创建新示例时会回调onNewInstance,然后无事发生
  3. singleTask
    不允许栈内有多个实例,如果试图创建新示例,将弹出页面之上的所有页面,然后回调onNewInstance
  4. singleInstance
    全局只能有一个实例,应用较少(银行登陆界面,通讯录黄页)

应用:返回首页的小按钮

  • 实现:将主页activity设置为singleTask

Fragment

安卓设计fragment的初衷是为了解决页面碎片化的问题,不过平时开发中fragment经常用于提高速度(activity之间交互需要进行IPC),
以及进行组件的分离(在屏幕的各个区域用不同的fragment承载功能)

基本用法

  1. 创建布局文件
  2. 在java中加载布局文件
  3. 在activity中加载fragment
    3.1 静态绑定-在布局文件中绑定
    3.2 通过FragmentManager加载

一般使用androidx.fragment,而不是android.app.fragment,以确保版本兼容

生命周期

  1. onAttach
    使用activity资源时可以在此进行获取
  2. onCreateView
    常用于初始化
  3. onResume
    运行状态
  4. onDestroyView
    资源回收

此外也可以通过FragmentTranscation.maxLifeCycle设置可到达的最远生命周期

与activity交互

组件获取

  • getActivity.findViewById(R.id.xx)
  • getFragmentManager.findFragmentById()

数据传递

  1. setArguments(Bundle bundle)
  2. 方法调用
  3. viewmodel,handler,broadcast

service

基本用法

  1. android_menifest注冊
  2. java中实现类
  3. 加载:需要交互用bindService,不需要交互用startService

生命周期

start, bind, destroy
//TODO

通信方式

  1. 定义Binder子类,实现getService方法,返回Service对象
  2. 实现Service类onBind方法,返回Binder对象
  3. 实例化ServiceConnection对象
    //TODO

broadcast

静态广播:使用receiver,在java中实现BroadcastReceiver,用onRecieve接收,用Context.sendBroadcast发送
动态广播:

常用广播:网络/电量变化,屏幕点亮/熄灭,安装卸载升级软件

接下来的几讲介绍修通,修通是精神分析疗法中的一个重要概念。同之前的课程一样,我们还是从大背景讲起。

精神分析治疗的理论路线

精神分析疗法的最终目的应该是让病人产生持久性的改变。弗洛伊德的精神分析讨论的是:压抑导致心理冲突。心理冲突是神经症的心理机制,那么就来解决冲突。心理冲突最尖锐的时候是三到五岁,神经症对应心理发展阶段3~5岁,此时的压抑最为强烈,主要是对性的压抑,这就是弗洛伊德的理论。

客体关系理论指出,三岁以前两岁以内的时期,孩子不具有自己反抗自己、自己管理自己的能力,压抑比较弱。两岁以内,不完全受本能的推动,而很可能是受人际关系这个动力的推动。
弗洛伊德注意到本能似乎是很正确的,但那时很少有人注意到人际关系的重要。客体关系理论强调孩子除了吃的本能之外,还对养育者有着建立关系的需要,这种需要直接推动心理的发展。

  • 实验:布猴与铁猴
  • 小猴依偎在布猴妈妈身边蹭, 并不理会铁猴的奶水。

人和妈妈建立感情的需求,推动人心理的形成,这就是重要的依恋理论。4~6个月开始,孩子就能区分妈妈和陌生人。而四个月之前的孩子有没有人际交往作为生活动力的可能呢?孩子被抱起来之后会转头做吸吮动作,这是否是纯粹的条件反射呢?

  • 实验:妈妈在喂奶之后给孩子做鬼脸,从出生之后开始每天重复
  • 到9~12天,孩子一看看到妈妈看他,没等妈妈做鬼脸,孩子先做鬼脸

可见,这个实验一下子拜托了本能决定论,人为了和妈妈交流,天然的有建立亲情的需要和动力。到四个月之后,安全、情感交流、亲密感的需要更明显的体现了出来。
在两岁之前,孩子都有对人际关系的需要,如果此时没有得到母爱,那么孩子的发展就会受到影响。四个月时,孩子面对陌生人的表现有以下几种:

  • 眼睛追踪陌生人,露出微笑
  • 眼睛追踪陌生人,面无表情
  • 眼睛并不转动,毫无反应

这三种反应出依次降低的心理功能,因为孩子的反应就体现出妈妈的养育方式。这又会进一步影响孩子两岁以后各种心理功能的发展。

这就是客体关系理论的基本理念:两岁以内的母爱缺乏导致心理功能的缺损。可以说,弗洛伊德的学说是压抑的学说,客体关系理论是功能缺损的学说。
心理功能缺损有许多表现,最终都成为人格障碍。这都是因为两岁关系母婴关系出了问题。

弗洛伊德的学说就像是种子的概念,客体关系理论则像是土壤的学说,一个是本能决定论的角度,一个是环境(母婴关系)的学说。这两个学说同时正确,相互补充。我们现在做精神分析必须把两套理论结合起来才是完整的,全面的。

精神分析治疗的工作

把潜意识意识化,这就是经典精神分析(弗洛伊德)的任务。而现代精神分析需要做的事还有修补人格的功能缺损

  • 比喻:精神分析的治疗与修自行车
    先补气,再休整,再换零件。支持疗法、人格重整

精神分析有这样那样的方法,甚至于不同流派也有各种各样的方法,不过最终最重要的是心理咨询师如何组装内心的知识结构,并且灵活的将其运用于治疗中

精神分析的技术路线

潜意识的催眠术:提出歇斯底里的机制,找到了把潜意识意识化
自由联想:扩大适应症,疗效更持久。
梦的分析:通过梦理解与解释潜意识
会谈-对峙、澄清、解释、修通 :稳定而持久的效果

从经典精神分析挖创伤解决症结到修补心理功能的现代精神分析,心理治疗从治疗导向逐渐转变为发展导向。发展是心理治疗的最终目标(这句话并不容易真正做到)

  • Tip:修通的地位
    在会谈成为主要治疗技术时的一个重要手段。

治疗关系的发展

治疗师作为:屏幕、解释者、容器、内化客体

精神分析治疗手段

建立工作联盟,分析处理移情和阻抗(处理治疗关系),自由联想变形使用(倾听、沉默、打断),偶尔使用梦的分析,更多的则是理解、解释、??、修通

我们对病人施加影响,根本上决定于病人。但治疗师也要对病人的心理施加影响,在施加影响的时候,精神分析又有何独到之处呢?
催眠、暗示、情感宣泄,这都属于非特异性的方法。而自由联想、梦的分析、语言干预中的对峙澄清解释修通则带有特异性。
人本主义等也用对峙澄清,而精神分析式的解释由于涉及到阻抗的处理,是精神分析干预中最独特的,也是精神分析干预中最重要的手段。

理解与解释

什么是理解与解释

理解就是理解症状的含义,即症状和心理需要之间的关系

  • 案例:失女的父亲
    地震时,教学楼倒塌,女儿遇难。父亲拿着手机,不吃不睡,不开口。

解释:

  • 应激反应,急性期的情感休克,麻木 —— 这是理解与解释吗?

说明因果关系不是理解:这是站在旁观者的角度进行的理论上的因果阐释,而没有共情

  • 你很愤怒,联系别的家长追查,想上诉,这是因为你心疼女儿。

说到这里,闭上的双眼留出泪水。

  • 追查上诉,可能得到赔款,但你真正想要的是给女儿一个交代

说到这里,激动的来握手。这时候就可以建立治疗关系了。

可见,对“心疼”的共情是不够的,没有真正表达出他的愿望,理解他的心理需要

  • 后续,引着他的动力解决当前的问题:先吃饭看病,做父亲的自己活得好才能给女儿一个交代等等。。。

解释就意味着让潜意识的内容进入意识,或者说让精神创伤的原因进入到意识。透过谈话帮助病人自己也没有意识到的内容进入病人和医生的意识,这样的一个工作就是揭示。解释属于语言干预的范畴。

理解与解释,经常是阐述一些因果关系,弗洛伊德的潜意识决定论会解释精神症与潜意识的关系,揭示内在的需要和动机。许多精神症都是通过象征化的手段来表达心中的欲望,如果通过直接的方式满足了欲望,这个象征化的手段就不需要了。

  • 例:让男生帮自己拧水瓶盖,间接的表达欲望。

  • tip:神经症用变通、拐弯的方式表达了自己心理的欲望,而不能拐弯也无法表达的欲望的就成为人格障碍。

理解的功能与形式

  1. 与以往的经验保持一致,获得一致性的感受,是一种理解。

把当前的认知与过往的经验联系起来,建立逻辑联系,这就是理解的形式之一(普通心理学)。一旦理解了,就获得前后一致的内在感受,一旦不理解,就出现经验和认知的矛盾,无法获得一致性的内在感受。
比如遇到违背常识的事情,很希望获得背后的原理解释,要不然就感觉很疑惑。

  1. 完成未完成的事情完成,这也是一种理解
  • 例:没看完的鬼故事
    只有看完了才能获得心理上的一致感!
  1. 把两个事情联系起来获得整体感,也是一种理解

比如常见的谐音的“寓意”,常见的“兆头”,把自然现象和事物与平安、美好等期盼联系起来。首先给自己一个解释:吃了苹果可以得到好兆头,然后再去削苹果。两个无关的事物通过心理上的理解连接在一起,从而获得了一个整体感,这个整体的两个部分具有心里意义上的联系:吃苹果表达平安。

理解的心理机制

借着这种连接,心里的愿望也得以实现。这种连接事实上是任意的。

  • 例:“预言梦”
    做了一个梦,梦见一个人推铅球。二十年后看到一个人推铅球,说“我的梦有预言作用”

不管“预言”在多久之后实现,都可以在心理上建立联系。梦的记忆和推铅球的认知在当事人的感受中联系起来,这就说出一句话“我的梦具有预言作用”。两个无关的事情一链接,就得到了新的东西。而这东西背后表达的情感是什么呢——获得自主感、自信心、特殊感。

链接的是认知的是记忆,推动链接的是背后的心理需要,让人宁愿把这两个事情做链接。

  • 例:孩子看世界
    太阳升起,因为我起床。太阳落下,因为我睡觉
    妈妈高兴,因为我乖。妈妈生气,因为我不乖
    妈妈去世了? 因为我把妈妈气死了
    妈妈离开了? 因为我把妈妈气走了
  • 例:养猫不能留小猫
    人们说:小猫把老猫气走了。人们大多真的这么认为。
    其实是老猫宁愿离开家,把家留给小猫,老猫感情上还是想念女儿的,只是宁可离开这个家

这也是一种心理意义上的理解,所以有亲人去世的时候,每个活着的人都会感到内疚,检阅自己的过错,恨意消失,想着那个人的好,变成自责。这其实都是心理内部加工过的东西,通过把无关的事情建立联系,借以表达内在的心理情感和心理需要。
理解的思维活动,和潜意识的愿望与需要的关系就是这样。我们必须说清楚这个阶段,才能理解病人为何存在着非理性的认知观念。

理解的意义

非理性的认知观念重点在于非理性,重点并非他到底想的是怎样的怀念头,而是他为什么宁愿想坏的念头。认知疗法无法解决,只有精神分析才能回答这个问题:出于内在的需要。

  • 例:走不出的失恋
    充满理想化的回忆,温习爱的痛苦,这才能保持自我的同一性,因此宁愿那么想。

“理解”的心理机制可以帮助我们理解一些临床现象。

其次,理解还可以帮助我们在病人心理内部建立新的连接,表达原来无法表达的情感与愿望。
我们经常能够在悲剧作品中看到这样的表达,比如传统中国悲剧中总是有一个浪漫式的结尾,通过一种纯粹心理上的理解来获得故事的完整性,并且表达我们内心对于美好故事的愿望。

每个人的心中都会有未完成的故事情节,如同鲁迅写的《风筝》故事,(这里老师记错了结局),鲁迅并没有释然,而是因为弟弟不冷不热的回应更深刻的感受到了未完成事件的痛苦。

对于未完成事件,如果能找到从前的那个情景,可以完成。如果不能找到,只能通过自己的想象去完成。

  • 例:交通事故之后的小伙
    汽车时刹车不及下坡时撞车,女友惨死。
    治疗过程中,要帮助他完成这个恋爱故事

未完成的事件会导致心理发展的停滞。

  • 例:地震中的灾民
    地震中一个尸体头滚落,卫生局长用砖头把头和尸体接起来,久久难以忘怀。

要不要继续讲地震的故事呢?要的,如果不讲,那种惨状就一直停留在头脑里,停滞不前。这个时候就需要我们在内心帮助他完成这个故事。想象家人看到尸体的悲伤,想象家人对好心人的举动,想象你的回应。
慢慢的,回忆的内容从血肉模糊的变成家里人,最后淡化,没有了。
通过整体性的连接、理解之后,能够表达更多的情感,在心理治疗上具有积极的作用。人看到灾后的一系列事情,时间上进行了动态的发展。
在讲地震的这个完整故事的过程中,我们可能从“人的生命真脆弱”,来到看到救灾时的“人类的勇气与善良值得歌颂”,这种情况下,人的心理因为新的解释得到了发展。

  • 例:忘不掉的初恋
    解释:人们总是以为第一次的体验就是正确的,就好比第一次爱人敲了一个窗户,其他人敲了另外的窗户,你就以为那些都不是爱人
    问题:我只想等着那一类人来敲我的窗
    解释2:爱人敲了一扇窗你以为是爱情,爱人敲了另一扇窗,你却不去听了
    问题:没人来敲窗(守株待兔式的爱)
    解释3:我们不再被动的等待一个爱人,而是主动的创造一个爱人。
    问题:没人来敲窗总是感觉很寂寞?
    解释4:主动走出去,去敲别人家的窗

这是很好的例子!不同的解释要尽量的贴近内在的想法,建立和他内心的联系,才能一步步的把他带出来,用贴近内心的解释来推动行为。通过这种解释,内心多出了新的想法,以新的视角推动人去行动,改变了他行为的方式。

这就是对理解是什么的基本的认识。

解释

治疗师先对自己进行理解,然后将心比心的去理解病人,就叫做对病人的理解。看到别人流眼泪勾起了自己流眼泪时的感受,投射给病人,就是理解(认同性的投射)。
理解了而不表达,只是理解。把理解说给病人听,就是解释。将心比心地用自我理解理解病人,又把对病人的理解说给病人听,这样的临床操作就叫解释。解释的前提和基础就是理解,也就是理解对方的动机、需要、行动背后的心理含义
病人借助我们的解释,最终不是要理解我们的说法,而是要加深对自我的理解。通过自我理解,病人内心的不一致、未完成得以解决,内心的愿望得以表达,也丰富了他内心的想法。自我理解整理散乱的记忆,接续未完成的事件,可以修补心理功能的缺损,让心理更有整体性,更符合现实发展的逻辑。丰富心理功能是解释的客观效果,而达成病人的愿望是解释的主观动力:借着自我理解,病人表达了自己压抑的东西——把潜意识的东西意识化。
自由联想回忆起内在的感受,而借助医生的解释,潜意识的愿望能够在意识层面得以觉察和表达,这是解释最重要的功能。精神分析的解释是精神分析治疗的眼珠子。移情和阻抗的处理都需要理解。

人总是希望“解释世界”,要赋予事物意义,要完成未完成的事件(例:没想通的压轴题)。对外界的认知和观念要想成为我们相信的内容,即加入了情感、信念的坚定认同,是需要有一个过程的。这句话为修通埋下伏笔。

grpc Benchmarks

QPS Benchmark

The “Queries Per Second Benchmark” allows you to get a quick overview of the throughput and latency characteristics of grpc.

To build the benchmark type

1
$ ./gradlew :grpc-benchmarks:installDist

from the grpc-java directory.

You can now find the client and the server executables in benchmarks/build/install/grpc-benchmarks/bin.

The C++ counterpart can be found at https://github.com/grpc/grpc/tree/master/test/cpp/qps

The netty benchmark directory contains
the standard benchmarks used to assess the performance of GRPC. Since these
benchmarks run on localhost over loopback the performance of the underlying network is considerably
different to real networks and their behavior. To address this issue we recommend the use of
a network emulator to make loopback behave more like a real network. To this end the benchmark
code looks for a loopback interface with ‘benchmark’ in its name and attempts to use the address
bound to that interface when creating the client and server. If it cannot find such an interface it
will print a warning and continue with the default localhost address.

To attempt to standardize benchmark behavior across machines we attempt to emulate a 10gbit
ethernet interface with a packet delay of 0.1ms.

Linux

On Linux we can use netem to shape the traffic appropriately.

1
2
3
4
5
6
7
8
9
10
# Remove all traffic shaping from loopback
sudo tc qdisc del dev lo root
# Add a priority traffic class to the root of loopback
sudo tc qdisc add dev lo root handle 1: prio
# Add a qdisc under the new class with the appropriate shaping
sudo tc qdisc add dev lo parent 1:1 handle 2: netem delay 0.1ms rate 10gbit
# Add a filter which selects the new qdisc class for traffic to 127.127.127.127
sudo tc filter add dev lo parent 1:0 protocol ip prio 1 u32 match ip dst 127.127.127.127 flowid 2:1
# Create an interface alias call 'lo:benchmark' that maps 127.127.127.127 to loopback
sudo ip addr add dev lo 127.127.127.127/32 label lo:benchmark

to remove this configuration

1
2
sudo tc qdisc del dev lo root
sudo ip addr del dev lo 127.127.127.127/32 label lo:benchmark

Other Platforms

Contributions are welcome!

Visualizing the Latency Distribution

The QPS client comes with the option --save_histogram=FILE, if set it serializes the histogram to FILE which can then be used with a plotter to visualize the latency distribution. The histogram is stored in the file format of HdrHistogram. That way it can be plotted very easily using a browser based tool like https://hdrhistogram.github.io/HdrHistogram/plotFiles.html. Simply upload the generated file and it will generate a beautiful graph for you. It also allows you to plot two or more histograms on the same surface in order two easily compare latency distributions.

JVM Options

When running a benchmark it’s often useful to adjust some JVM options to improve performance and to gain some insights into what’s happening. Passing JVM options to the QPS server and client is as easy as setting the JAVA_OPTS environment variables. Below are some options that I find very useful:

  • -Xms gives a lower bound on the heap to allocate and -Xmx gives an upper bound. If your program uses more than what’s specified in -Xmx the JVM will exit with an OutOfMemoryError. When setting those always set Xms and Xmx to the same value. The reason for this is that the young and old generation are sized according to the total available heap space. So if the total heap gets resized, they will also have to be resized and this will then trigger a full GC.
  • -verbose:gc prints some basic information about garbage collection. It will log to stdout whenever a GC happend and will tell you about the kind of GC, pause time and memory compaction.
  • -XX:+PrintGCDetails prints out very detailed GC and heap usage information before the program terminates.
  • -XX:-HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath=path when you are pushing the JVM hard it sometimes happens that it will crash due to the lack of available heap space. This option will allow you to dive into the details of why it happened. The heap dump can be viewed with e.g. the Eclipse Memory Analyzer.
  • -XX:+PrintCompilation will give you a detailed overview of what gets compiled, when it gets compiled, by which HotSpot compiler it gets compiled and such. It’s a lot of output. I usually just redirect it to file and look at it with less and grep.
  • -XX:+PrintInlining will give you a detailed overview of what gets inlined and why some methods didn’t get inlined. The output is very verbose and like -XX:+PrintCompilation and useful to look at after some major changes or when a drop in performance occurs.
  • It sometimes happens that a benchmark just doesn’t make any progress, that is no bytes are transferred over the network, there is hardly any CPU utilization and low memory usage but the benchmark is still running. In that case it’s useful to get a thread dump and see what’s going on. HotSpot ships with a tool called jps and jstack. jps tells you the process id of all running JVMs on the machine, which you can then pass to jstack and it will print a thread dump of this JVM.
  • Taking a heap dump of a running JVM is similarly straightforward. First get the process id with jps and then use jmap to take the heap dump. You will almost always want to run it with -dump:live in order to only dump live objects. If possible, try to size the heap of your JVM (-Xmx) as small as possible in order to also keep the heap dump small. Large heap dumps are very painful and slow to analyze.

Profiling

Newer JVMs come with a built-in profiler called Java Flight Recorder. It’s an excellent profiler and it can be used to start a recording directly on the command line, from within Java Mission Control or
with jcmd.

A good introduction on how it works and how to use it are http://hirt.se/blog/?p=364 and http://hirt.se/blog/?p=370.

Structure of RA

  • Title
  • Authors & Affiliation(单位)
  • Abstract
  • Introduction
  • Materials & Methods
  • Results & Discussion
  • Conclusion
  • Acknowledgements(致谢)
  • Appendix
  • Reference(引用)
  • Bibliography(参考)

Title

Type of Title

  • Declaration 声明性

    整句

  • Descriptive/neutral 描述性/中性标题

    短语+定语

  • Interrogative 疑问句式

    疑问

  • Compound 复合型标题

    由主副两部分构成(冒号或破折号分开)

How to read title

devide the title into sense group

1
Strategies | to re-establish stable granulation | after filamentous outgrowth | : Insignts | from lab-scale
  • 名词
  • 动宾短语
  • 介词短语

Abstract

  • at the beginning (or the end) of a research article, miniature of a research work.
  • independent, self-contained, well-structured 独立完整/自圆其说/结构严谨
  • Consist of Five Move(话步,语步)
    • background 是什么
    • problem 做了什么 (purpose/hypotheses)
    • method 怎么做的
    • result 有何结果 (major finding/important data)
    • conclusion 总结展望 (further study/future directions)

How to read abstract

  • focus on subject + verb

  • devide the abstract into five moves

  • focus on clue words:

    • “such”, “recently”,”today” that indicates present time can be found in move 1.
    • “here”, “this paper” with past tense usually come with move 2 and 3
    • method will be described with verbs.
    • “showed”“revealed”“indicated” are used to discribe result and “find” for finding
    • present and future tenses with subjective mood are used in making conclusion, with words like “could””would”.

keywords

Introduction

  • context : explains the rationale for undertaking the study and clearly describes the main purpose of conducing it.

  • purpose : to create a research space(CARS)

  • moves

    1. Establishing a territory
      现状描述词
    1
    Increasing interest has grown on ...
    1. Reviewing previous related research
      文献综述词
    1
    Several Studies have ...
    1. Establishing a niche(壁炉)
      转折否定词
    - However/while/but...
    - failed to 
    - ignored/neglected to
    
    1. Occupying the niche

在Activity间传递参数

写了个小脚本解决虚拟机频繁锁死的问题:

1
del C:\Users\huiyeruzhou\.android\*.lock

这下一键开锁了(
在Activity间传递参数的方法很简单,在Intnet里把参数压进去就可以了(此时还没有详细讲解什么是Intent):

1
2
3
4
public void onClick(View view) {
Intent i = new Intent(MainActivity.this, TheAty.class);
i.putExtra("data","qaq!QAQ__");
startActivity(i);}

intent传递参数的方式是键值对,值可以传很多类型,包括基本类型、数组、字符串等。
接收的方法也很简单:

1
2
3
Intent i =getIntent();
tv = findViewById(R.id.viewview);
tv.setText(i.getStringExtra("data"));

这里的”data”就是之前数据的名称。通过名称可以找到字符串,并且通过setText方法显示在文本框里。

使用Bundle传递多个参数

Bundle的使用方法和基本类型是类似的,先新建一个Bundle对象,然后调用对象的put<DataType>方法
再调用Intent对象的putExtras方法传递Bundle即可,接收的方法也是类似的:

1
2
3
4
5
6
Intent i = new Intent(MainActivity.this, TheAty.class);
Bundle b = new Bundle();
b.putString("data","qaq?QAQ>_<");
b.putInt("emm",611);
i.putExtras(b);
startActivity(i);

接收Bundle的数据时,可能会存在某个键无法在Bundle中找到对应值得情况,这种情况下Bundle类提供了
get<DataType>的重载方法以指定默认值:

1
2
3
tv.setText(String.format("%s,%d,%s",
b.getString("data"),b.getInt("emm"),
b.getString("ne!","abab")));//"ne!"对应的值不存在,显示为默认值"abab"

此外,Bundle也可以直接通过putExtra方法进行传递,这种情况可能适用于有多个Bundle的情况,此时
接受时也要使用getBundleExtra方法来进行查找。

传递值对象

Activity的启动模式

首先来看标准启动模式,也是默认的启动模式。我们来新建一个项目来演示不同的启动模式。首先为MainActivity添加一个TextView,两个Button
新建一个活动,叫做AnotherActivity,复制MainActivity的布局文件

java代码中设置TextView要显示的内容

1
tv.setText(format("Task ID:%d\nCurrent Instance id:%s",getTaskId(),this.toString()));

Task ID是任务栈id,而当前示例ID则是当前对象的地址,可以用来区分activity的示例。

接下来设置两个按钮,一个启动MainActivity一个启动AnotherActivity,复制上述代码到AnotherActivity.java,注意修改intent中package context为AnotherActivity.this,效果如图

接下来可以点按钮试试,可以观察到TaskId都是一样的,而实例的id是不同的,这就意味着点击按钮会创建新的示例,而且所有的示例都位于一个任务栈中。

在标准的启动模式中,我们启动一个Activity就会压栈一个新的实例,点击返回键就会弹栈。多次点击app的创建示例按钮,再按返回键,可以观察到他们依次被销毁的过程。

可以在AndroidManifest.xml文件中的Activity标签下添加Android:launchMode条目来指定启动模式,如果没有这个条目,则默认为Standard。

SingleTop启动模式

SingleTop,顾名思义,如果该活动的一个实例位于栈顶,就不能继续创建该活动的新实例。只有当栈顶实例不为该活动时,才能创建该活动的新实例。
下面在AndroidManifest.xml文件中配置MainActivity的启动模式为SingleTop.

1
2
3
4
 <activity
android:name=".MainActivity"
android:exported="true"
+ android:launchMode="singleTop">

启动程序,首先点击启动A活动按钮,应用没有反应。此时如果点击返回键会直接退出。
如果启动程序后先点击启动B活动按钮,再点击启动A活动按钮,可以发现这次成功的创建了A的新实例。这是因为创建了B活动之后,A活动的上一个实例已经不在栈顶,因此可以创建新的实例。

SingleTask模式

在这种情况下,一个任务栈中只能有一个该任务的实例,如果试图创建新实例,将会弹出所有位于该实例上的活动实例。
修改AndroidManifest文件,将启动模式修改为singleTask,启动应用程序。
点击启动A活动按钮,没有反应。点击启动B活动按钮,再点击启动A活动按钮,可以看到B活动退出,而显示的A活动正是第一个示例,如果此时再点击返回键,应用直接退出,说明所有B活动的示例都因为其位于A活动上方而被弹出了。

SingleInstance模式

在这种情况下,全局只有该活动的一个实例,第一次创建该活动时会创建一个新的任务栈,并且一个任务栈中只有这一个activity,而其他活动的实例会位于新的任务栈中。这种情况下,试图启动该活动的新实例时,会切换到保存该活动实例的任务栈。其他的任务栈不受影响。

SingleInstancePerTask

认识Activity

activity就是一个活动界面,软件通过界面与用户交互。
后退键回到上一个activity,home键回到桌面。这两个按键都会影响activity。
一个Android项目包含两个最基本的文件:activity_main.xml,MainActivity.java。

activity.xml是主界面的布局文件,修改其中的内容可以设计程序的ui。在设计模式下可以直接拖动组件放到布局中
在res/value/string.xml中可以修改字符串的内容。
也可以选中内容按CTRL-B跳转。

程序从onCreate开始执行,activity通过setContentView方法生成视图。由资源文件生成了R.txt中的layout activity_main
这会布局文件.xml的名称改变而随之改变。在res/layout上右键新建,可以创建新的布局文件。文件名首字母必须小写,推荐全小写并
用下划线进行单词分割。Root布局可以选择LinearLayout

1
2
3
4
5
6
7
8
my_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

</LinearLayout>

这是一个垂直走向的布局。
接下来可以把setContentView的参数改成R.layout.my_layout
这就是新建布局和修改activity绑定布局的方法。

工程目录结构

  • app/manifests
    AndroidManifests.xml配置文件目录
  • app/java
    源代码目录,可以在这里新建包和java文件
  • app/res
    资源文件目录
    • layout 布局资源
    • menu 菜单资源
    • values 尺寸、字符串、样式资源

启动自定义的Activity

在布局里创建一个按钮,自定义按钮的名字Text和idid

1
2
3
4
5
<Button
android:id="@+id/btn_change_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go to next activity" />

接下来可以使用
findViewById(R.id.<btn_id>)来找到这个按钮。再对这个函数的返回值调用setOnClickListener方法,并传入一个新建的事件监听器,即可完成对
“点击按钮”的响应:

1
2
3
4
5
6
7
8
findViewById(R.id.btn_change_activity).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {

}
}
);

创建新的Activity

在app/src/java右键新建,选择Activity-Empty Activity。也可以创建普通的JavaClass,再让其继承Activity即可。Android Studio会自动帮我们在manifest中注册activity,生成对应的java文件和布局xml文件。
接下来再来设计新的Activity的布局,可以把ViewText改成“这是另一个Activity”。

进行Activity切换

使用startActivity方法进行活动的切换,需要使用初始化器创建一个Intent实例作为startActivity的参数。

1
2
3
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, AnotherAty.class));
}

查看SDK帮助手册

新版的AS将手册集成到了源码中,可以在鼠标悬停时显示提示,无需额外下载,但也无法单独查看。需要安装带有源码的SDK,
https://blog.csdn.net/limitzchen/article/details/107747510,如果SDK没有源码,重新下载并且修改构建的SDK版本https://blog.csdn.net/hou09tian/article/details/120105837

Activity生命周期

一个Acticvity的生命周期如下图所示,从被创建开始,activity可能会经历许多次的暂停-恢复或停止-重启,还有可能因为再在后台被清除而重新创建。

我想有几点是需要注意的:1.activity只要不再活动就会Pause,但只有不可见才会Stop。2.按下返回键时,activity会被destroy,而按下home键则只会Stop。
通过导航键找到活动时,如果活动所在的内存已经被清除,就要重新创建,否则就只需Restart。

利用以上几个原则,我们可以得到用一个activity打开另一个activity时的生命周期图:

activity交互图

如果在AndroidManifest中把新打开的activity的主题设置为对话框,新的活动就不会完全遮住原有的活动。这样,原有的活动的onStop()就不会执行。

1
2
3
4
5
 <activity
android:name=".AnotherAty"
android:exported="false"
+ android:theme="@style/Theme.AppCompat.Dialog"
/>

要查看活动生命周期的状况,可以复写on<Action>系列方法,并再其中加入System.out.println();语句这样就可以在输出栏监控各个方法是否执行。