Android学习(一)
1.回调函数
- 定义
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。 - 详细解释
客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B姓甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。Web Service以及Java的RMI都用到回调机制,可以访问远程服务器程序。 - 举例(帮助理解)
某天,我打电话向你请教问题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。 - 回调函数的实现
它是通过接口或者内部类来实现的。
Button bt_add = (Button) findViewById(R.id.bt_add);
bt_add.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
OnClickListener()是一个接口,new OnClickListener()并非是实例化接口,而是表示一个匿名内部类实现该接口,onClick()方法就是回调方法,在Button被点击这个事件发生时,我们使用onClick()回调方法对事件进行处理。
2.Manifest
Package
指定该Android应用的报名,该包名可用于唯一地标识应用
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chen.activitytest">
Application
<!-- allowBackup:关闭应用程序数据的备份和恢复功能,注意该属性值默认为true,
如果你不需要你的应用被恢复导致隐私数据暴露(如果值为true,甚至可以直接通过adb命令获取该应用中的数据),
必须手动设置此属性。-->
<!-- icon:图标 -->
<!-- label:应用名称 -->
<!-- roundIcon:圆形图标 -->
<!-- supportsRtl:android4.2有一个新特性 layoutRtl,当然是对于开发者而言的,主要是方便开发者去支持阿拉伯语/波斯语等阅读习惯是从右往左的。
可以在manifest的application标签添加:android:supportsRtl 取值:true/false
这样就可以打开layoutRtl这个功能。如果当前系统语言是阿拉伯语/波斯语,打开了这个功能的应用的布局就会自动变成从右往左的,当然前提是布局没有写死控件间的位置。
-->
<!-- taskAffinity:设置Activity任务栈的名称,可忽略 -->
<!-- hardwareAccelerated:开启硬件加速,一般应用不推介使用。就算非要使用也最好在某个Activity单独开启,避免过大的内存开销。 -->
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
Activity
<!-- screenOrientation:该属性是用来设置当前的activity在屏幕上显示的方向 -->
<!-- launchMode:该属性是用来设置Activity的启动模式(Activity的启动模式有四种:standard、singleTop、singleTask和singleInstance) -->
<!-- configChanges:该属性是用于捕获手机状态的改变 -->
<!-- taskAffinity:指出了它希望进入的Task -->
<!-- windowSoftInputMode:共有9个属性,可以分别为软键盘设置禁止、显示、大小调整等情况。 -->
<!-- excludeFromRecents:设置为true后,当用户按了“最近任务列表”时候,该activity不会出现在最近任务列表中,可达到隐藏应用的目的。 -->
<!-- exported:是否支持其它应用调用当前组件。
默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。 -->
<activity
android:name=".activity.WelcomeActivity"
android:launchMode="singleTop"
android:taskAffinity="taskName"
android:exported="true"
android:excludeFromRecents="true"
android:windowSoftInputMode="stateVisible"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize|fontScale">
>
<intent-filter>
<!-- 指定该Activity是该程序的入口 -->
<action android:name="android.intent.action.MAIN"/>
<!-- 指定加载该应用时运行该Activity -->
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
3.setContentView()——初窥源码
public class ActivityTest extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
}
↓
↓ setContentView(R.layout.activity_test);
↓
@Override
public class AppCompatActivity extends ... {
public void setContentView(@LayoutRes int layoutResID) {
this.getDelegate().setContentView(layoutResID);
}
↓
↓ getDelegate().setContentView(layoutResID);先找getDelegate()
↓ getDelegate()也在AppCompatActivity 中
↓
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
}
↓
↓ mDelegate = AppCompatDelegate.create(this, this);
↓ 点create,进AppCompatDelegate类里面去看
public abstract class AppCompatDelegate {
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
}
所以最后调用的是 AppCompatDelegateImpl 中的 setContentView
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
}
接着去看 ensureSubDecor 方法:
private void ensureSubDecor() {
if (!this.mSubDecorInstalled) {
mSubDecor = createSubDecor();//核心代码
//省略其它代码。。。
}
}
点createSubDecor方法进去
private ViewGroup createSubDecor() {
//这里是获取Activity的theme,并根据主题风格为subDecor确定加载哪个布局
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
......
mWindow.getDecorView();//创建DecirView
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;//开始根据主题、style选择合适布局文件并加载到subDecor中
......
mWindow.setContentView(subDecor);把subDecor加载到DecorView布局文件中的容器中
......
return subDecor;
}
想看一下mWindow是什么,所以点进去
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
final Window mWindow;
}
发现是AppCompatDelegateImpl类里面定义的一个常量,这个时候要通过看AppCompatDelegateImpl的构造器来找mWindow到底是啥
AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback) {
mContext = context;
mWindow = window;
mAppCompatCallback = callback;
........
}
发现是通过给构造器传入window参数,现在就要往上找,还记得最初是AppCompatDelegate里初始化
public abstract class AppCompatDelegate {
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
在这里初始化的,activity就是AppCompatActivity ,window就是activity.getWindow()
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
}
window就是AppCompatActivity.getWindow(),但是AppCompatActivity中没有getWindow()方法,getWindow()是在其父类Activity中实现
public class Activity extends ... ... {
private Window mWindow;
final void attach(Context context, ......) {
attachBaseContext(context);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
}
public @Nullable Window getWindow() {
return mWindow;
}
}
最后找到window就是PhoneWindow对象。这时候想进PhoneWindow类里看一下setContentView方法,结果出现问题进不去PhoneWindow这个类
于是我上网搜索了这个类里面的setContentView()方法
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent); //画重点
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
以及getDecorView()方法
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor(); ---> 调用installDecor()
}
return mDecor;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1); ---> generateDecor就是创建DecorView对象
....省略其它代码....
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); ---> 给DecorView对象加载布局文件
并准备好R.id.content容器
....省略其它代码.....
}
}
到此,对于setContentView()的源码分析暂时告一段落。
我的第一次总结完毕。