Android开发细节--查漏补缺(二):易忘难懂

这篇文章先汇总一下部分在平常容易遇到或可能遇到的一些难以理解或者容易忘记是什么的问题。


  1. android:supportsRtl="true"是什么?
    android:supportsRtl="true" 故名思义,“Rtl”就是“right to left” ,一般用于适配language文本方向,如切换系统语言为阿拉伯文时,actionbar布局要变为从右向左排列,就需要这个属性。

注意:
此属性最低SDK版本为17(Android 4.2.x)
在使用这个属性时,尽量不适用layout_marginRight这类,而是使用layout_marginEnd这种。


  1. Android res/menu/my_nemu.xml中不易记的属性:
  • android:alphabeticShortcut="n" :表示快捷键为:Ctrl+n 、Alt+n
  • android:orderInCategory = "3":表示在菜单列表中的排序,值可以为大于等于0的任何整数,数值越小,排序越前。
  • android:checkableBehavior = "single|all|none":分别表示radio button、checkbox、checkable=false
  • ……: 用一个组,包含几个menu_item,这样,组内部的item的顺序就可以重新定义。

  1. Android中用Java代码设置菜单项快捷键的方法:
menu.setQwertyMode(true);
menu.findItem(TWO_ID).setNumericShortcut('2');

  1. 中子view的属性:
  • android:below="@+id/xx" :表示本view在xx的下面
  • android:above="@+id/xx":表示本view在xx的上面

  1. implements Parcelable的对象的一般写法:(详见我的另一篇博文:Android AIDL基础 -- Parcelable接口)
public class Student implements Parcelable{
  ///-------------------------------
  /// (1):基本类型、String/CharSequence、Bundle、IBinder、Serializable、Parcelable 的成员
  private T t;
  ……
  ///---------------------------------
  ///(2):构造方法(可选,怎么方便赋值成员变量值并构建实例,就怎么写)
  public Student() {
      ……
  }
 //=========================================================
 //  下面是Parcelable需要实现的方法
 //=========================================================
  protected Student(Parcel in) {
      ///建议:按成员变量顺序写【因为:需要与writeToParcel()方法中的写入顺序一致】
      id = in.readInt();//如:基本数据类型成员
      name = in.readString();//如:String类型成员
      home = in.readParcelable(Address.class.getClassLoader());//如:Parcelable类型成员
      ……
  }
  public static final Creator CREATOR = new Creator() {
      ///CREATOR成员,格式必须如下,‘CREATOR’变量名也固定。
      @Override
      public Student createFromParcel(Parcel in) {
          return new Student(in);
      }
      @Override
      public Student[] newArray(int size) {
          return new Student[size];
      }
  };
  @Override
  public int describeContents() {
      /// 描述标志,默认为0即可
      return 0;
  }
  @Override
  public void writeToParcel(Parcel dest, int flags) {
      ////写入Parcel的方法【需要与writeToParcel()方法中的写入顺序一致】
      dest.writeInt(id);
      dest.writeString(name);
      dest.writeParcelable(home, flags);
      ……
  }
}
  1. Android Studio获取Android签名证书SHA1的简易步骤:
  • Android Studio中打开terminal或者配置了全局变量之后直接打开终端输入:
    keytool -list -v -keystore 你的签名文件所在的路径
  • 输入密钥库密码
  • 成功显示:MD5、SHA1、SHA256等信息

    想知道Eclipse的获取方式,更详细的地址可以看:SHA1查看


  1. JSON封装库的效率对比:
    如果对JSON的解析速度敏感:大文件用:Jackson,小文件用:Gson,两种文件都有用:JSON.simple

  1. Java代码执行new一个类实例对象的过程中,static块、static方法、main方法的执行顺序:
  • static块【最先执行,因为静态代码块属于类层次,在类编译时就执行】
  • -> main方法
  • -> 成员块
  • -> 构造方法
  • -> static方法【在被调用时才执行】

详细可以看看本人的相关文章:Java面试相关(一)-- Java类加载全过程


  1. 在写Fragment的时候,Fragment.onCreateView()方法中,一般是这样的一个结构:
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,  ViewGroup container,  Bundle savedInstanceState) {
if (rootLayout == null) {
  rootLayout = inflater.inflate(R.layout.fragment_reclist, null);///①
  rootLayout = inflater.inflate(R.layout.fragment_reclist, container, false);///②
  ///rootLayout = inflater.inflate(R.layout.fragment_reclist, container);///③这样不行!!
  ……  
}
……
return rootLayout;
}

这里,要注意代码中注释掉的地方,这里有一个对于Fragment的知识点:
inflater.inflate(id, ViewGroup);
说明:
 这个方法传入:【目的布局的R.layout 的id】 + 【目的布局的父容器】。那么,对于Fragment的来说,Fragment的onCreateView()方式最终返回的View会自动判断本Fragment所在的父容器位置。
为什么上面代码③不行呢? 答:因为inflater.inflate(id, ViewGroup);最终会调用inflater.inflate(id, ViewGroup,boolean); 方法,当ViewGroup不为null时,这方法的第三个参数值会为true,表示的是‘inflate出来的布局自动加到它的父viewgroup中’,那么问题就来了,如果这个父布局是一个ViewPager或者其他不易确定子View放置位置的容器,那么,方法就会报错,因为找不到插入点!所以,对于Fragment的布局,一般情况不需要设置自动添加到父布局,所以,上述代码①和②二选一都可以。


  1. Fragment 与 它所在的Activity的生命周期方法执行次序:
    下面用一个常用布局作为示例【FragmentActivity中用ViewPager装载两个Fragment 部分代码示例】来演示 ,看运行的Log就可以说明整个执行顺序:
public class CommonTabActivity extends FragmentActivity {
……
/// TabLayout + ViewPager的主界面容器
PagerAdapter mPagerAdapter;
ViewPager mViewPager;
TabLayout mTabLayout;
////两个 Fragment
RecListFragment homeFragment;
RecListFragment collectionFragment;
……
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_commontab);
   initView();
}
private void initView() {
  ///初始化ViewPager等控件
  ……
   
      mPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
          @Override
          public Fragment getItem(int position) {
              switch (position) {
/////////////////////////使用第一个Fragment
                  case 0:
                  default:  
                      if(homeFragment==null){
                          homeFragment = new RecListFragment();
                      }
                      return homeFragment;
///////////////////////////使用第二个Fragment
                  case 1: 
                      if (collectionFragment==null){
                          collectionFragment =  new RecListFragment();
                      }
                      return collectionFragment;
              }
          }
          …………
      };
      mViewPager.setAdapter(mPagerAdapter);
      mTabLayout.setupWithViewPager(mViewPager);
}
}

上面的结构大家应该都比较熟悉,那么,我测试后给大家列出几种情况下Activity和两个Fragment的生命周期方法执行情况:

  • 本Activity被startActivity()等方式启动:


    Android开发细节--查漏补缺(二):易忘难懂_第1张图片
  • 本Activity由于点击HOME、MENU等弹回桌面时:
     下图是Android N版本下点击MENU按键时的操作截图(类似平常我们长按HOME键弹出运行中程序):


    Android开发细节--查漏补缺(二):易忘难懂_第2张图片

    Android开发细节--查漏补缺(二):易忘难懂_第3张图片
  • 本Activity启动其他Activity,并被完全遮掩覆盖时:


    Android开发细节--查漏补缺(二):易忘难懂_第4张图片
  • Activity退出时:


    Android开发细节--查漏补缺(二):易忘难懂_第5张图片
  • 总结:

    • 启动Activity时,由于ViewPager默认采用预加载的方式,所以,虽然默认显示第一个Fragment,第二个Fragment依然会被加载和初始化。
    • 启动Activity时,Activity先执行了它本身的onCreate()->onStart()->onResume(),之后,ViewPager才真的开始运行起来并加载指定给他的Fragment,Fragment才开始它的构造方法和生命周期方法的执行。
    • 不论Activity是被另一个Activity覆盖、还是App被返回桌面、或者是弹出启动中应用的界面遮掩Activity,此时,Activity都是被完全遮掩(弹出Dialog等不全屏的窗体时,Activity属于部分被遮掩),此时,Activity都会调用到onStop()方法,并且这种方式中Fragment与Activity的生命周期方法执行顺序都一样(如上面截图)。
    • 当Activity退出时,①Fragment执行onStop()后,Activity再执行onStop() ②Fragment执行onDestroyView()和onDestroy()后,Activity才执行onDestroy()
  • 另外,如果到这里,忘了Fragment整个生命周期是怎么样的,这里,本人附上Fragment的生命周期图解

    Android开发细节--查漏补缺(二):易忘难懂_第6张图片
    Fragment的生命周期


  1. 关于v4、v7、v13兼容包:
  • Android Support v4:为了兼容Android1.6及更高版本而设计的。(eclipse新建工程时,都默认带有v4包)
  • Android Support v7:为了兼容Android2.1及以上版本而设计的,但不包含更低,故如果不考虑1.6,我们可以采用再加上这个包,另外注意,v7是要依赖v4这个包的,即,两个得同时被包含。
  • Android Support v13:为了Android 3.2及更高版本的,一般我们都不常用,平板开发中能用到。

  1. Activity中,onSaveInstanceState() 和 onRestoreInstanceState() 两个方法何时执行?
  • onSaveInstanceState():
    • 执行情况:当Activity处于容易被杀死的状态下时。
    1. 资源相关的系统配置发生改变时,如:手机突然横屏等系统配置变化;
    2. 点击HOME键退回桌面时(此时应用处于后台,Activity可能因内存不足而被回收);
    3. 长按HOME键启动任务管理器程序的时候(同样,Activity处于后台状态,容易被回收);
    4. 本Activity启动其他Activity时(原Activity进入后台后,容易被回收)。
    5. 锁屏时。
    • onSaveInstanceState()在onStop()方法前发生
  • onRestoreInstanceState():
    • 执行情况:在Activity以异常情况下终止后重新创建时
    • 需理解以下这种情况:当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。
    • onRestoreInstanceState()在onStart()方法之后执行
    • 注意:onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。
  • 示例截图(手机突然横屏):


    Android开发细节--查漏补缺(二):易忘难懂_第7张图片

  1. Java中,String、StringBuffer、StringBuilder三者的区别:
  • String 相对与后面两者来说,指向的内存空间是不可变的,也就是每次赋值,String对象指向的内存地址都将发生改变,相当于摒弃原地址、重建新地址。
  • StringBuffer和StringBuilder都是指向可变大小的内存空间的,即每次append(String)等字符串更改操作,都不会使其重新申请新一块内存地址然后指向它。
  • StringBuffer相对于StringBuilder来说,字符串的修改、添加等操作相对较慢(也就是字符串操作效率低一点),但是StringBuffer线程安全,对于多个线程访问时,StringBuffer通过内部锁机制进行多线程同步,所以,StringBuffer更加安全。
  • StringBuilder则没有了线程安全的功能,于是它提高了字符串操作的效率,但是线程不安全。

  1. Android自定义View的三个构造方法(一参,二参,三参)的区别:
  • public View(Context context):在java代码创建视图的时候被调用
  • public View (Context context, AttributeSet attrs):在xml创建,但是没有指定Style的时候被调用
  • public View (Context context, AttributeSet attrs, int defStyle):在xml中创建、并且指定了Style时调用

  1. onWindowFocusChanged() 和 onFocusChanged()的用法和区别?
  • 区别
    1. onWindowFocusChanged()在window$callback 和 View.java 两个类中,主要作为Window的焦点获取和失去时的一个事件监听,而window$callback当中所使用的该方法实际上也来自View.java,而onFocusChanged()只位于View.java类中,主要作为View的焦点获取与失去时的一个事件监听。
  • 两者的具体用法
    • onWindowFocusChanged()调用时机:由于window的焦点和view的焦点是分离的,所以这个方法被回调的时机是:窗口获取或者失去焦点时,如:
      1. 当启动Activity时,Activity当中的窗口获取到焦点时,即onResume()方法之后,会调用一次onWindowFocusChanged()。
      2. 从Activity跳转到另一个Activity,新的窗口会获取焦点,旧的Activity的窗口就会失去焦点。
      3. 打开软键盘进行输入,窗口失去焦点。
      4. 软键盘输入完毕消失,窗口重新获取焦点。
      5. 应用被移到后台,窗口失去焦点。
      6. 应用重新返回前台,窗口重新获取焦点。
    • onWindowFocusChanged()妙用(部分)
      1. 重写onWindowFocusChanged() 方法并在里面调用getWidth()和getHeight()方法,可以正确得到整个视图的宽高。【原理:onResume()完毕后Window才获取焦点,此时视图已经加载完毕,这样就避免了视图未加载完成时获取错误宽高】
      2. 当由于HOME键等其他原因导致App移到后台时,对数据的临时保存,此时,除了可以使用onPause()、onStop()、onSaveInstanceState()方式,还可以使用onWindowFocusChanged()方法来处理数据的自定义保存操作。

    • onFocusChanged()用法:在EditText、Button等本身可获取焦点的控件或者设置了可获取焦点、可通过触摸事件获取焦点的自定义View、TextView等控件下可以重写来处理焦点获取和失去焦点时的事件。【可以用于唤起系统软键盘:通过:重写View.onCreateInputConnection()等方法获取一个软键盘连接器并可以从中设定弹出的键盘形式,然后利用(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);获取InputMethodManager对象,就可以调用它来启动和关闭软键盘】

  1. 让Activity不随着屏幕旋转而销毁
  • 法一:(API13后有效)在manifest的Activity标签添加
    android:configChanges="orientation|keyboardHidden"

    注意:
    orientation表示屏幕方向,keyboardHidden键盘可见性(是否调出键盘)
    旋转屏幕时,此时Activity只调用:onConfigurationChanged(),不会调用onRestore…onSave…

  • 法二:设置App不可转屏
    android:screenOrientation=”landscape|portrait”表示:横向或者纵向,二选一

  1. Fragment与Activity的生命周期方法执行顺序:
    详细可以参考本人的另一篇文章:Android -- Fragment 基本用法、生命周期与细节注意
    Android开发细节--查漏补缺(二):易忘难懂_第8张图片

  1. 我们在Android中写xml中控件或者布局、主题等属性值的时候,会遇到“@android:drawable/xxx”,“@+id/yyyy”,“?attr/colorPrimary”等样子的属性值引用写法。那么,这几种写法的区别是什么?
  • @+id/ thing_id这个好理解,有个 + 号表示需要在R.xml中新添加一个这个id值,然后,供以后被调用。【Java中调用:findViewById("R.id.thing_id"); xml中调用:android:toLeftOf="@id/thing.id" 等方式】
  • @[+][package:]type/name:这种形式,表示引用一个已存在的属性值。
    1. @drawable/img1:表示引用我们自定义的drawable属性值(引用自己的内部资源)
    2. @+android:drawable/search_icon:带了一个 + 号,表示引用android.R.drawable中的资源,如果此资源不存在,则在自己项目的R.drawable中创建一个search_icon的标识。
    3. @android:drawable/search_icon:表示引用系统(android.*)的资源,没有则报Android.content.res.Resource$NotFoundException的异常。
  • ?[namespace:type/]name:表示引用属性。【不需要明确指出它的类型】
    如:①?android:attr/android:textDisabledColor 和②?android:textDisabledColor,两者是一样的,前者声明了索要属性值的类型而后者没有说明,两者都可以获取系统中的主题属性,只要我们提供想要的属性名,它将会在主题中被查找(因为资源工具知道需要的属性资源,所以我们不需要显式声明这个属性)。

感谢阅读~

本篇文章将持续更新~
喜欢的读者可以点个关注,本人将持续发布Android相关文章~

你可能感兴趣的:(Android开发细节--查漏补缺(二):易忘难懂)