Android基础知识,查漏补缺(一)

参考: ①Java基础知识点整理
            ②《Android从入门到精通》

写在前面:Android应该是大二下学期学的,当时也学的不是很透彻,干了八个月爬虫的我,现在因为生活所迫不得不重新捡起来,但是靠当时的技术肯定是不行的,所以我把我不会的手敲了一边给整理出来,既加深了印象又当整理了笔记给记下来。如果你也跟我一样或者对Android基础知识想巩固一下,就跟着我一起查漏补缺吧。

查漏补缺第一课

1、什么是JDK?

全称:Java Development Kit ,是整个Java的核心,包括了Java运行环境、Java工具和Java基础的类库。

2、Android系统架构概览

Linux内层核:显示驱动、相机驱动、蓝牙驱动、Flash内存驱动、Binder IPC驱动、USB驱动屏幕、键盘驱动、WiFi驱动、音频驱动、能源管理
Android运行环境(持久层库、Dalvik虚拟机器):接口管理器、3D引擎、数据存储、包管理器、位图及矢量、浏览器引擎、2D图像引擎、中间协议、libc函数库、库
应用程序框架:活动管理器、窗口管理器、内容提供器、视图系统、通知管理器、包管理器、电话管理器、资源管理器、本地管理器、XMPP服务
应用程序:主屏幕、联系人、电话、浏览器

3、Intent-filter是干啥的

Intent-filter描述了Activity启动的位置和时间。每当一个Activity(或者操作系统)要执行一个操作时,它将创建一个Intent的对象,这个Intent对象可以描述你想做什么,你想处理什么数据,数据的类型,以及一些其他信息。

4、AndroidManifest.xml分析

参数说明
Manifest根节点,描述了package中所有的内容
Xmln:android包含命名空间的声明。Xmlns:android-http://schemas.android.com/apk/res/android,使得Android中各种标准属性能在文件中使用,提供了大部分元素中的数据。
Package声明应用程序包
application包含package中application级别组件声明的根节点。此元素可包含application的一些全局和默认的属性,如标签、icon(图标)、主题、必要的权限,等等。一个manifest能包含零个或一个此元素(不能大于一个)
android:icon应用程序图标
android:label应用程序名字
activityActivity是与用户交互的主要工具,是用户打开一个应用程序的初始页面,大部分被使用到的其他页面也由不同的activity所实现,并声明在另外的activity标记中,注意,每一个activity必须有一个标记对应,无论它给外部使用或是只用于自己的package中。如果一个activity没有对应的标记,将不能运行它。另外,为了支持运行时查找activity,可包含一个或多个元素来描述activity所支持的操作。
Android:name应用程序默认启动的activity
Intent-filter声明了指定的一组组件支持的Intent值,从而形成了IntentFilter。除了能在此元素下指定不同类型的值,属性也能放在这里来描述一个操作所需的唯一标签、icon和其他信息
action组件支持的Intent Action
Category组件支持的Intent Category。这里指定了应用程序默认启动的activity
User-sdk该应用程序所使用的SDK版本相关

5、”value”子目录

       “Value”子目录专门用于存放Android应用程序中用到的各种类型的数据,不同类型的数据存放在不同的文件中,例如文件string.xml会定义字符串和数值,文件arrays.xml会定义数组。例如”first”项目的字符串文件string.xml的实现源码如下图所示:

<?xml version=1.0” encoding=”utf-8?>
<resources>
	<string name=”hello”>Hello World,HelloAndroid</string>
	<string name = “app_name”>HelloAndroid</string>
<resources>

        在类string中使用的每个静态常量名与元素中name属性值相同。上述常量定义文件的代码非常简单,只定义了两个字符串资源,请不要小看上面的几行代码。它们的内容很”露脸”,里面的字符直接显示在手机屏幕中,就像动态网站中的HTML一样。

6、Fragment

6.1、谈一谈Fragment的生命周期

· Fragment从创建到销毁整个生命周期中涉及到的方法依次为:

onAttach()
onCreate()
onCreateView ()
onActivityCreated()
onStart()
onResume()
onPause()
onStop()
onDestroyView()
onDestory()
onDetach()

其中和Activity有不少名称相同作用相似的方法,而不同的方法有:

  • onAttach(): 当Fragment和Activity建立关联时调用;
  • onCreateView(): 当fragment创建视图调用,在onCreate之后;
  • onActivityCreated(): 当与Fragment相关联的Activity完成onCreate()之后调用;
  • onDestoryView(): 在Fragment中的布局被移除时调用;
  • onDetach(): 当Fragment和Activity解除关联时调用;

6.2、谈一谈Fragment和Activity的区别?

相似点: 都可包含布局、可有自己的生命周期
不同点:

  • Fragment相比较于Activity多出4个回调周期,在控制操作上更灵活;
  • Fragment可以在XML文件中直接写入。也可以在Activity中动态添加;
  • Fragment可以使用show()/hide()或者replace()随时对Fragment进行切换,并且切换的时候不会出现明显的效果,用户体验会好;Activity属于让也可以进行切换,但是Activity之间切换会有明显的翻页或者其他的效果,在小部分内容的切换上给用户的感觉不是很好;

6.3、Fragment中add与replace的区别?(Fragment重叠)

  • add不会重新初始化Fragment,replace每次都会。所以在Fragment生命周期内获取数据,使用replace会重复获取。
  • 添加相同的Fragment时,replace不会有任何变化, add会报IllegalStateException异常;
  • replace先remove掉相同id的所有Fragment,然后在add当前的这个Fragment。所以如果使用add一般会伴随hide(),避免布局重叠;
  • 使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的Fragment会销毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;
// Fragment布局重叠解决方案,方法不唯一,仅供参考
private int mCurrentPos = -1;
private List<Fragment> mFragments = new ArrayList<>();
private  void switchFragmentIndex(int position){
	FragmentTransaction transaction = getSupportFragmentManager().begainTransaction();
	if(mCurrentPos!=-1){
		transcation.hide(mFragments.get(mCurrentPos));
	}
	if(!mFragments.get(position).isAdded()){//避免Fragment重复添加
		transaction.add(R.id.fl_content,mFragments.get(position));
	}
	transaction.show(mFragments.get(position)).commit();
	mCurrentPos = position;//记录当前Fragment下移
}

6.4、getFragmentManager、getSupportFragmentManager、getChildFragmentManager之间的区别?

  • getFragmentManager()所得到的是所在Fragment的父容器的管理器,getChildFragmentManager()所得到的是在Fragment里面的子容器的管理器,如果是Fragment嵌套Fragment,那么就需要利用getChildFragmentManager();
  • 因为Fragment是3.0Android系统API版本才出现的组件,所以3.0以上系统可以直接调用getFragmentManager()来获取FragmentManager()对象,而3.0以下则需要调用getSupportFragmentManager()来间接获取:

6.5、FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景

  • 相同点:二者都继承PagerAdapter
  • 不同点:FragmentPagerAdapter 的,饿哦和Fagment会持久保存在FragmentManager中,只要用户可以返回到页面中,它都不会被销毁。因此使用于那些数据相对静态 的页,Fragment数量也比较少 的那种;
    FragmentStatePagerAdapter只保留当前页面,当页面不可见时,该Fragment就会被 消除,释放其资源。因此适用于那些数据动态性 较大、占用内存 较多,多Fragment的情况;

7、Handler

7.1、谈谈消息机制Handler的作用?有哪些要素?流程是怎样的?

  • 负责跨线程通信,这是因为在主线程不能做耗时操作,而子线程不能更新UI,所以当子线程中进行耗时操作后更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
  • 具体分为四大要素:
    1、Message(消息): 需要被传递的消息,消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息。
    2、 MessageQueue(消息队列): 负责消息的存储与管理,负责管理由Handler发送过来的Message。读取会自动删除消息,单链表维护,插入和删除上有优势。在其next()方法中会无线循环,不断判断是否有消息,有就返回这条信息并移除。
    3、 Handler(消息处理): 负责Message的发送及处理。主要向消息池发送各种消息事件(Handler.sendMessage())和处理相应消息事件,(Handler.handleMessage()),按照先进先出执行,内部使用的是单链表的结构
    4、 Looper(消息池): 负责关联线程以及消息的分发,在该线程下从MessageQueue获取Message,分发给Handler,Looper创建的时候会创建一个MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会返回messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。

具体流程
 在主线程创建的时候会创建一个Looper,同时也会在looper内部创建一个消息队列。而在创建Handler的时候取出当前线程的Looper,并通过该Looper对象获得消息队列,然后Handler在子线程中通过MessageQueue.enqueueMessage 在消息队列中添加一条Message。
 通过Looper.loop() 开启消息循环轮询调用MessageQueue.next() ,取得对应的Message并且通过Handler.dispatchMessage 传递给Handler,最终调用Handler.handlerMessage 处理消息。

7.2、一个线程能否创建多个Handler,Handler和Looper之间的对应关系?

  • 一个Thread智能有一个Looper,一个MessageQueue,可以有多个Handler
  • 以一个线程为准,它们的数量级关系是:Thread(1):Looper(1):MessageQueue(1):Handler(N)

7.3、软引用和弱引用的区别

  • 软引用(SoftReference): 如果一个对象只具有软引用,如果内存空间充足,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以一直被程序使用。
  • 弱引用(WeakReference): 如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
  • 两者之间根本区别 在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。
引用对象GC回收时间用途生存时间
强引用never对象的一般状态JVM停止运行时
软引用内存不足时对象缓存内存不足时终止
弱引用GC时对象缓存GC后终止
虚引用unknowunkownunkown

7.4、Handler 引起的内存泄露原因以及最佳解决方案

  • 泄露原因:
    • Handler允许我们发送延时消息,如果在延时期间用户关闭了Activity,那么该Activity会泄露。这个泄露是因为Message会持有Handler,而又因为Java的特性,内部类会持有外部类,使得Activity会被Handler持有,这样最终就导致Activity泄露。
  • 解决方案
    • 将Handler定义成静态的内部类,在内部持有Activity的弱引用 ,并在Activity的onDestroy() 中调用handler.removeCallbacksAndMessages(null) 及时移除所有消息。

7.5、为什么系统不建议在子线程访问UI?

  • Android的UI控件不是线程安全 的,如果在多线程中并发访问可能会导致UI控件处不可预期的状态
  • 这时你可能会问为何系统不对UI控件的访问加上锁机制呢?因为
    1、 加锁机制会让UI访问逻辑变的复杂
    2、 加锁机制会降低UI的访问效率,因为加锁会阻塞某些线程的执行

7.6、Looper死循环为什么不会导致应用卡死?

  • 主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looper.loop()方法可能引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理时间就不会产生ANR异常。
  • 造成ANR 的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞 ,无法响应手势操作,不能及时刷新UI。
  • 阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的。

7.7、使用Handler的postDealy后消息队列会有什么变化?

  • 如果队列中只有这个消息,那么消息不会被发送,而是计算到时唤醒的时间,先将Looper阻塞,到时间就唤醒它。但如果此时要加入新消息,该消息队列的对头跟delay时间相比更长,则插入到头部,按照触发时间进行排序,队头的时间最小、队尾的时间最大。

7.8、可以在子线程直接new一个Handler吗?怎么做?

  • 不可以,因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程 而言,没有任务对象帮助我们维护Looper对象,所以需要我们自己动手维护。所以要在子线程开启Handler要先创建Looper,并开启Looper循环,
// 代码示例
new Thread(new Runnale()){
	@Override
	public void run(){
		Looper.prepare();
		new Handler(){
			@Override
			public void handleMessage(Message msg){
				super.handleMessage(msg);
				}
		};
		Looper.loop();
	}
}).start();

7.9、Message 可以如何创建?哪种效果更好,为什么?

  • 可以通过三种方法创建:
    • 直接生成实例Message m = new Message
    • 通过Message m = Message.obtain
    • 通过Message m = mHandler.obtainMessage()
    • 后两者效果更好,因为Android默认的消息池中消息数量10,而后两者是直接在消息池中取出一个Message实例,这样做就可以避免多生成Message实例。

写的不好,参供参考。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值