Android学习日记

Android学习(一)

1.回调函数

  1. 定义
    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
  2. 详细解释
    客户程序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都用到回调机制,可以访问远程服务器程序。
  3. 举例(帮助理解)
    某天,我打电话向你请教问题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。
  4. 回调函数的实现
    它是通过接口或者内部类来实现的。
    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()的源码分析暂时告一段落。

我的第一次总结完毕。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值