Android小知识点整理(持续更新)

目录

序号 知识点 网址
1 asset文件夹与raw文件夹的区别 http://www.jb51.net/article/38531.htm
2 synchronized、对象锁与类锁 http://zhh9106.iteye.com/blog/2151791
3 ActionBarDrawerToggle.onOptionsItemSelected(item)
4 DrawerLayout的openDrawer与closeDrawer参数解析
5 Scanner实现优雅的InputString转String http://unmi.cc/java-convert-inputstream-to-string/?utm_source=tuicool&utm_medium=referral
6 setTheme()不起作用
7 解析OKHttp怎样实现默认支持https的
8 FragmentPagerAdapter

内容

1、asset文件夹与raw文件夹的区别

res/raw和assets的相同点:

两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。

res/raw和assets的不同点:

  1. res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
  2. res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

读取文件资源:

###1. 读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作

InputStream is =getResources().openRawResource(R.id.filename);  

###2. 读取assets下的文件资源,通过以下方式获取输入流来进行写操作

AssetManager am = null;  
am = getAssets();  
InputStream is = am.open("filename");  

注意

  1. Google的Android系统处理Assert有个bug,在AssertManager中不能处理单个超过1MB的文件,不然会报异常,raw没这个限制可以放个4MB的Mp3文件没问题。

  2. assets 文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像 xml, java 文件被预编译,可以存放一些图片,html,js, css 等文件。

2、synchronized、对象锁与类锁

java的内置锁

java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

对象的内置锁和对象的状态之间是没有内在的关联的,虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域并不一定通过内置锁来保护。当获取到与对象关联的内置锁时,并不能阻止其他线程访问该对象,当某个线程获得对象的锁之后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,是为了免去显式地创建锁对象。

所以synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。

java的对象锁和类锁

java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的  

修饰非静态方法时是获得对象锁,等同于synchronized(this),修饰静态方法时获得的是类锁,等同于synchroized(类.class)   对象锁和类锁是不同的锁

其他

String类型和Java中其他的复杂类型不同。在使用String型变量时,只要给这个变量赋一次值,Java就会创建个新的String类型的实例。   i++,i–,++i,–i都不是原子操作,都是线程非安全的  

result = i++;先赋值再自增的实现过程是创建一个临时对象,在执行完自增操作后,将临时对象赋值给result

int i = 0;
i = i++;
System.out.println(i+"");  

输出结果为 0

3、ActionBarDrawerToggle常用方法解析

ActionBarDrawerToggle是DrawerLayout.DrawerListener的实现类,可以通过addDrawerListener()方法对DrawerLayout进行设置(setDrawerListener()已经废弃)  

ActionBarDrawerToggle的构造方法中的openDrawerContentDescRes与closeDrawerContentDescRes是描述开关状态的字符串,用作辅助功能(盲人提示)中  

ActionBarDrawerToggle.syncState()方法是指同步drawerlayout的开过状态,google示例在onPostCreate()方法中调用,onPostCreate()方法在生命周期onCreate()方法后执行,说明Activity彻底启动起来。源码注释中没有对推荐在onPostCreate()方法中调用做过多解释,并表示应该在任何可能drawerlayout状态改变但ActionBarDrawerToggle没有得到通知的情况下调用。

ActionBarDrawerToggle.onOptionsItemSelected(item)与ActionBarDrawerToggle.toggle()
toggle()方法是切换drawerlayout开关状态的方法,但是是私有方法
所以官方推荐的drawerlayout开关状态切换的实现方式是:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(mDrawerToggle.onOptionsItemSelected(item)){//查看源码可知,menuId为android.R.id.home,执行私有方法toggle();
         return true;
        }
        return super.onOptionsItemSelected(item);
    }  

原理通过查看源码可知,当item的id为android.R.id.home时(还有其他条件,这里不作考虑),ActionBarDrawerToggle会执行toggle()

4、DrawerLayout的openDrawer与closeDrawer参数解析

openDrawer(View drawerView)
openDrawer(View drawerView, boolean animate)
openDrawer(int gravity)
openDrawer(int gravity, boolean animate)
closeDrawer(View drawerView)
closeDrawer(View drawerView, boolean animate)
closeDrawer(int gravity)
closeDrawer(int gravity, boolean animate)

drawerView:drawerlayout布局分为主布局(内容)和侧滑菜单两部分,drawerView是指侧滑菜单的根布局或者根View

animate:是否使用动画显示侧滑菜单的开关,默认true

gravity:侧滑菜单布局的gravity设置为left和right将会决定侧滑菜单在左侧还是右侧,gravity参数是Gravity.LEFT或者Gravity.RIGHT,表明操作的是左侧菜单还是右侧菜单,如果没有右侧菜单,参数却是Gravity.RIGHT,运行将报错崩溃

5、Scanner实现优雅的InputString转String  

public static String inputStream2String(InputStream in){
        Scanner scanner = new Scanner(in, "UTF-8");
        String result = scanner.useDelimiter("\\A").next();
        scanner.close();
        return result;
    }  

这种写法比传统的BufferedReader一行一行读和自定义buffer大小,循环readBytes这两种写法优雅很多,使用Apache commons IOUtils的IOUtils.toString(InputStream)和谷歌的guava的CharStreams .toString(InputStream)虽然写法同样简洁,但是要导入第三方库,这里不做过多介绍。

java.util.Scanner是Java5的新特征,是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。   特点是:

  • 多种输入,File,input,System.in,String等
  • 与正则结合使用
  • 实现了Iterator接口

“\A”是输入开始的正则表达式

6、setTheme()不起作用

setTheme()不起作用可能是因为没有写在onCreate()前,正确的写法是:  

public void onCreate(Bundle savedInstanceState) {    
    this.setTheme(R.style.Default);        
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  
}  

在ContextThemeWrapper源码的initializeTheme()方法中。有如下代码:

final boolean first = mTheme == null;
if (first) {...  

不知道和这个代码有没有关系

7、解析OKHttp怎样实现默认支持https的

OKHttp默认支持Https,实际是默认支持证书是CA认证的https,这和大多数网上教程中信任所有证书的Https配置是有区别的。


    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }   
    

以上是OKHttpClient的构造方法中的代码,没有设置SSLSocketFactory时,使用的systemDefaultTrustManager()返回的TrustManager。


private X509TrustManager systemDefaultTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
          TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
            + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }  

核心代码就是trustManagerFactory.init((KeyStore) null),此时初始化的就是系统内置的根证书,根证书中有CA认证的公钥,可以验证服务端证书的合法性,因为CA认证就是对服务端证书签名。这部分可以参考这里理解。

所以,如果只是实现单向认证的Https,而且证书是CA认证的,那么OKHttp是默认支持的,不需要做任何设置。

8、FragmentPagerAdapter

FragmentPagerAdapter是PagerAdapter的子类,适用fragment数量较小的场景,因为使用此Adater会缓存fragment实例,源码如下:


 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }
    
    

   可以看出,初始化的时候会通过mFragmentManager.findFragmentByTag(name)先去取fragment实例,而不是getItem()方法。

注意和ViewPager.setOffscreenPageLimit方法的区别

   setOffscreenPageLimit(n)是保存当前item前后n页不去destroyItem,所以只要在当前fragment前后n页的fragment是不会再次执行instantiateItem方法的。FragmentPagerAdapter不设置setOffscreenPageLimit的话是会移除不在展示页前后1页的fragmen的(默认OffscreenPageLimit = 1),只不过下次加载时会从缓存中取出fragment实例而已,但是两者并没有联系和冲突,可以用时使用,只不过在逻辑上容易混淆。
   

   使用FragmentPagerAdapter每个fragment只会执行一次onCreate方法,每次instantiateItem时,会执行一次onCreateView方法,也就是多次onCreateView方法,如果4个Item(Fragment)的ViewPager设置setOffscreenPageLimit(3),则所有fragment只要初始化则不会移除,只会执行一次onCreate和一次onCreateView方法.