android 初探

Posted diyigechengxu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 初探相关的知识,希望对你有一定的参考价值。

安卓系统分为四层:

  linux内核层:为安卓设备硬件提供驱动;

  系统运行库层:一些c/c++库,支持安卓系统。如sqlite支持数据库;open-gl支持3d绘图;webkit支持浏览器内核;安卓运行时库的一些核心库支持java语言编写安卓应用

  应用框架层:提供安卓编码的一些api;

  应用层:手机上的应用程序;

安卓的四大组件:

  活动(Activity,应用中看到的东西),服务(Service,运行在后台),广播接收器(Brocast Receiver,电话,短信等),内容提供器(Content Provider,应用间共享数据)

 

Activity:

Activity状态:

  活动状态,位于栈顶的页面;

  暂停状态,非栈顶,但可见;

  停止状态,非栈顶,不可见,其成员变量和状态可能会被系统回收;

  销毁状态,从栈中移除,系统会回收掉。

Activity生命周期:

  onCreate():页面第一次创建的时候调用,完成页面布局,初始化工作,事件绑定等;

  onStart(): 页面由不可见到可见状态调用;

  onResume(): 页面已经可以和用户交互;----requestLayout()布局的绘制流程,并会checkThread()。在检查线程方法调用之后,就不可以在子线程中更新UI了。

  onPause(): 即将进入或者加载另一个页面,可释放一些资源,如:文件句柄,定时器,sqlite的操作等;

  onStop(): 页面完全不可见的时候调用;

  onDestroy(): 页面被销毁的时候调用;

  onRestart(): 页面重新变为可见的时候调用;

onSaveInstanceState(Bundle bundle): 通过bundle保存当前activity的变量或者数据信息,在onCreate中取出来。

 

getTaskId() 任务id。一个task表示的是启动一个app,并在app的activity做跳转,以及跳转到另一个app页面的跟踪记录。

activity的lauchmode:1、standard,标准加载模式,正常的任务栈

          2、singleTop,如果activity在栈顶,则调用onNewIntent(),如果不在栈顶,则类同standard,放入栈顶中

          3、singleTask, 整个activity任务栈中,会只有一个activity,重复的activity只会调用onNewIntent(),如果当前activity不是栈顶,则栈顶的activity会调用onDestroy()

          4、singleInstance, 类似于singleTask,但会生成一个task任务栈。

 

一个app启动的时候,会自动启动一个主线程,然后调用ActivityThread的main函数,调用Looper.prepareMainLooper()创建一个Looper对象,并调用Looper.loop()处理消息队列和用户实践。ActivityThread就是一个普通的class,并非一个thread类。ActivityThread启动后,会调用ActivityManagerService的远程代理ActivityManagerNative.getDefault(),并attach(ApplicationThread对象)和AMS进行交互。

 

Instrumentation主要用来创建application对象,启动activity和管理activity生命周期回调。

 

H extents handle 主要处理ActivityManagerService发送过来的消息:

  handleLauchActivity: 通过classLoader加载activity;

            调用activity.attach()将activity和windowManager关联,管理activity的页面展示;

 

AMS提供了一个ArrayList mHistory来管理所有的activity,activity在AMS中的形式是ActivityRecord,task在AMS中的形式为TaskRecord,进程在AMS中的管理形式为ProcessRecord。如下图所示

 

  • 所有的ActivityRecord会被存储在mHistory管理;

  • 每个ActivityRecord会对应到一个TaskRecord,并且有着相同TaskRecord的ActivityRecord在mHistory中会处在连续的位置;

  • 同一个TaskRecord的Activity可能分别处于不同的进程中,每个Activity所处的进程跟task没有关系;



Activity启动时ActivityManagerService会为其生成对应的ActivityRecord记录,并将其加入到回退栈(back stack)中,另外也会将ActivityRecord记录加入到某个Task中。请记住,ActivityRecord,backstack,Task都是ActivityManagerService的对象,由ActivityManagerService进程负责维护,而不是由应用进程维护。
 
在回退栈里属于同一个task的ActivityRecord会放在一起,也会形成栈的结构,也就是说后启动的Activity对应的ActivityRecord会放在task的栈顶
 
Fragment:
 
 
技术图片
生命周期示意图如上。
  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

1、Fragment的onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()都是在Activity的onStart()中调用的。

2、Fragment的onResume()在Activity的onResume()之后调用。

3、当点击F1的按钮,调用replace()替换为F2,且不加addToBackStack()时,F1最后调用了onDestroy()onDetach()

4、当点击F1的按钮,调用replace()替换为F2,且加addToBackStack()时,F1被替换时,最后只调到了onDestroyView(),并没有调用onDestroy()onDetach()。当用户点返回按钮回退事务时,F1会调onCreateView()->onStart()->onResume()。

 

FragmentTransaction有一些基本方法,下面给出调用这些方法时,Fragment生命周期的变化:

  • add(): onAttach()->…->onResume()。
  • remove(): onPause()->…->onDetach()。
  • replace(): 相当于旧Fragment调用remove(),新Fragment调用add()。
  • show(): 不调用任何生命周期方法,调用该方法的前提是要显示的 Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为true。
  • hide(): 不调用任何生命周期方法,调用该方法的前提是要显示的Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为false。
  • detach(): onPause()->onStop()->onDestroyView()。UI从布局中移除,但是仍然被FragmentManager管理。
  • attach(): onCreateView()->onStart()->onResume()。
调用commit() 不会立即执行事务,而是在 Activity 的 UI 线程(“主”线程)可以执行该操作时再安排其在线程上运行。不过,如有必要,您也可以从 UI 线程调用  executePendingTransactions()以立即执行 commit() 提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务。
 
DialogFragment显示浮动对话框。使用此类创建对话框可有效地替代使用Activity 类中的对话框帮助程序方法,因为您可以将片段对话框纳入由 Activity 管理的片段返回栈,从而使用户能够返回清除的片段。
 
 
ListView:
view的显示分为三步骤:1、onMeasure();2、onLayout();3、onDraw()
 
  技术图片
 
  • mCurrentScrap:ArrayList类型,用于存储离屏的 View
  • mScrapViews
    • ArrayList[] 类型
    • 数组中每个元素都是 ArrayList类型,效果同 mCurrentScrap
    • mScrapViews[0] 就是 mCurrentScrap
    • 其数组长度应为 ViewTypeCount。因为针对不同的 ViewType,ListView 都要有一个专门的 ArrayList链表来缓存它对应的 View
  • mActiveView:ArrayList类型,被 layoutChildren() 用于缓存屏幕上的 View。
    • 假设在某一时刻ListView中显示了10个子View,position依次为从0到9。然后我们手指向上滑动,且向上滑动了一个子View的高度,ListView需要绘制下一帧。这时候ListView在layoutChildren方法中把这10个子View都放入到了RecycleBin的mActiveViews数组中了,然后清空了children数组,然后调用fillDown方法,向ListView中依次添加position1到10的子View,在添加position为1的子View的时候,由于在上一帧中position为1的子View已经被放到mActiveViews数组中了,这次直接可以将其从mActiveViews数组中取出来,这样就是直接复用子View,所以说RecycleBin的mActiveViews数组主要是用于直接复用的。
 
Adapter是适配器的意思,它在ListView和数据源之间起到了一个桥梁的作用,借助Adapter这个桥梁来去访问真正的数据源,Adapter的接口都是统一的,因此ListView不用再去担心任何适配方面的问题。而Adapter又是一个接口(interface),它可以去实现各种各样的子类,每个子类都能通过自己的逻辑来去完成特定的功能,以及与特定数据源的适配操作,比如说ArrayAdapter可以用于数组和List类型的数据源适配,SimpleCursorAdapter可以用于游标类型的数据源适配。
 
在展示listview的第一屏时,所有的子View都是调用LayoutInflater的inflate()方法加载出来的,这样就会相对比较耗时。
 
当ListView向下滑动的时候,就会进入一个for循环当中,从上往下依次获取子View,子View都按照传入的参数值进行相应的偏移,这样就实现了随着手指的拖动,ListView的内容也会随着滚动的效果。如果这个子View已经移出屏幕了,将这个View加入到废弃缓存当中,并将子View给detach掉。并将count计数器加1,计数器用于记录有多少个子View被移出了屏幕。
 
如果ListView中有新的子View移入屏幕就会尝试从废弃缓存中获取View。所以它们之间就形成了一个生产者和消费者的模式,ListView中的子View其实来来回回就那么几个,移出屏幕的子View会很快被移入屏幕的数据重新利用起来,因而不管我们加载多少数据都不会出现OOM的情况,甚至内存都不会有所增加。
 
getView()方法来去获取一个View。就是我们平时使用ListView时最最经常重写的一个方法了,这里getView()方法中传入了三个参数,分别是position,convertView和this。convertView就是我们之间利用过的View,只不过被移出屏幕后进入到了废弃缓存中,现在又重新拿出来使用而已。然后我们只需要把convertView中的数据更新成当前位置上应该显示的数据,那么看起来就好像是全新加载出来的一个布局一样
 
 
RecyclerView:
技术图片

RecyclerView 滑动场景下的回收复用涉及到的结构体两个:mCachedViews 和 RecyclerViewPool,mCachedViews 优先级高于 RecyclerViewPool。

回收时,最新的 ViewHolder 都是往 mCachedViews 里放,如果它满了,那就移出一个扔到 ViewPool 里好空出位置来缓存最新的 ViewHolder。

复用时,也是先到 mCachedViews 里找 ViewHolder,但需要各种匹配条件,概括一下就是只有原来位置的卡位可以复用存在 mCachedViews 里的 ViewHolder,如果 mCachedViews 里没有,那么才去 ViewPool 里找。

在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一样,只要 type 一样,有找到,就可以拿出来复用,重新绑定下数据即可。 

 

一定要全部替代ListView?

  NO!!!列表页展示界面,需要支持动画,或者频繁更新,局部刷新,建议使用RecyclerView,更加强大完善,易扩展;其它情况(如微信卡包列表页)两者都OK,但ListView在使用上会更加方便,快捷。

 

ListView的缓存机制对比:

RecyclerView比ListView多两级缓存,支持多个离ItemView缓存,支持开发者自定义缓存处理逻辑,支持所有RecyclerView共用同一个RecyclerViewPool(缓存池)。

RecyclerView 中的一缓 mAttachedScrap 与 ListView 中的一缓 mActiveViews 功能是基本相似的,为了屏幕内 item 快速复用而存在

RecyclerView 中的二缓 mCachedViews 加上四缓 RecyclerViewPool 合在一起与 ListView 的二缓 mScrapedViews意义相同,为了即将给即将入屏的 item 复用而存在。
RecyclerView的优势在于
  a.mCacheViews的使用,可以做到屏幕外的列表项ItemView进入屏幕内时也无须bindView快速重用;
  b.mRecyclerPool可以供多个RecyclerView共同使用,在特定场景下,如viewpaper+多个列表页下有优势.客观来说,RecyclerView在特定场景下对ListView的缓存机制做了补强和完善。



广播

  android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。

  1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;

 

  2.广播发送者通过binder机制向AMS发送广播;

 

  3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;

 

  4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

 

  

  同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题  

  同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。

  

EventBus

EventBus.getDefault().register(this);意思是让EventBus扫描当前类,把所有onEvent开头的方法使用Map,Key为方法的参数类型,Value中包含我们的方法记录下来。这样在register执行完成以后,我们的onEventMainThread就已经以键值对的方式被存储到EventBus中了。 EventBus会根据post中实参的类型,去Map中查找对应的方法,于是找到了我们的onEventMainThread,最终调用反射去执行我们的方法。

技术图片技术图片技术图片

 

单例的写法:

 

private EventBus instance;

public static EventBus shareInstance()

  if (instance == null)

    synchronized (EventBus.class)
      if (instance == null)
        instance = new EventBus();
      
    
  
  return instance;

 

反射

Java 开发中定义一个类,往往是要通过许多修饰符来配合使用的。它们大致分为 4 类。

  • 用来限制作用域,如 public、protected、priviate。
  • 用来提示子类复写,abstract。
  • 用来标记为静态类 static。
  • 注解。

Class

获取Class:

  如果这个对象可以访问,那么调用 getClass()方法就可以获取到了它的相应的 Class 对象。这种方法不适合基本类型如 int、float 等等。不想创建这个类的实例的话,就需要通过 `.class` 这个标识。如果当前类不在开发环境中,那么可以通过传入一个类的全限定名称给Class.forName()获取Class

获取ClassName:

  getName()返回类的全限定名称

  getSimpleName()返回嵌套类内的类名

获取修饰符:

  Modifier.toString(xx.class.getModifiers())

Field

获取属性:

  Class.getDeclaredFields()//获取所有的属性,但不包括从父类继承下来的属性,访问了 private 修饰的成员,需要添加. field.setAccessible(true);

  Class.getFields()//获取自身的所有的 public 属性,包括从父类继承下来的属性。

获取属性类型:

  field.getType()//属性类型

  field.getGenericType()//包含泛型在内的属性类型

获取属性修饰符:类似于获取类的修饰符

获取属性的值:

  field.get(obj)

设置属性的值:

  field.set(obj,value)

Method

获取方法:

  Class.getDeclaredMethod()//获取所有的方法,但不包括从父类继承下来的方法

  Class.getMethod()//获取自身的所有的 public 方法,包括从父类继承下来的方法。

获取方法名:

  method.getName()

获取方法修饰符:类似于获取类的修饰符

获取参数:

  method.getParameters()//返回Parameter 数组

获取参数名:

  Parameter.getName()

获取参数类型:

  Parameter.getType()

  Parameter.getGenericType()//包含泛型在内的参数类型

获取参数修饰符:

  Parameter.getModifiers()

获取返回值类型:

  method.getReturnType()

  method.getGenericReturnType()//包含泛型在内的返回值类型

获取异常类型:

  method.getExceptionTypes()

  method.getGenericExceptionType()//包含泛型在内的异常类型

执行某个方法:

  method.invoke(obj,obj...args)

Constructor

获取构造方法:

  Class.getDeclaredConstructor()//获取当前类的所有构造方法

  Class.getConstructor()//获取当前类自身的所有的 public 构造方法

访问构造方法:

  Constructor.newInstance()

Array

获取数组元素类型:

  array.getComponentType()//Car[] 返回com.array.test.Car

动态创建数组:

  Array.newInstance(Class<?> componentType, int... dimensions)

数组的读取和赋值:

  arr.set(obj,value)

  arr.get(obj)

Enum
Class.isEnum()// 用来判定当前 Class 对象是不是枚举类型

Class.getEnumConstants()//获取当前类的所有的枚举常量

Field.isEnumConstant()//获取某属性是否为枚举常量

 

Looper


Looper中几个关键对象:Thread,
Looper,MessageQueue,Handler,Message

可以从一个线程创建关联到另一个线程 Looper 的 Handler,只要能拿到对应线程的 Looper 实例。

Handler---发送和处理与某线程的 MessageQueue 相关联的 Message/Runnable 对象。每个 Handler 实例只能与一个线程和它的消息队列相关联。它将 Message 和 Runnable 传递给绑定的消息队列,并在它们从队列里被取出时执行对应逻辑。

  Runnable 被封装成 Message 之后添加到 MessageQueue。Runnable 对象是被封装成 Message 对象后加入到消息队列的,Message.callback 被设置为 Runnable 本身

  创建 Handler 实例时要么提供一个 Looper 实例,要么当前线程有关联的 Looper。

  handler操作的MessageQueue是线程安全的,MessageQueue的message入队方法,使用了synchronized关键字

public class Handle

  final Looper mLooper

  final MessageQueue mQueue;

  final Callback mCallback;

  public Handle(Looper looper,Callback callback,bool aysnc)

    if (looper == null) throw 一个异常

    mLooper = looper;

    mQueue = looper.mQueue;

    mCallback = callback;

  

  public boolean enqueueMessage(MessageQueue mQueue,Message message, long updateTime)

    message.target = this;

  

  private static void handleCallback(Message message)

    message.callback.run();

  

  public void dispatchMessage(Message message)

    if (message.callback != null) handleCallback(message);

    else if (mCallback != null)

      if (mCallback.handleMessage()) return;

      handleMessage();

    

       

MessageQueue---是作为Looper的一个成员变量而存在,当Looper实例化的时候,它也被初始化,并且当前的Thread对象也当做成员变量存起来。

Looper---Looper.prepare():创建一个Looper,并且将这个Looper存储到一个静态ThreadLocal变量中,而Looper中关联着Thread对象。注意如果当前线程已有Looper它会抛异常。

     Looper.loop():就是进入一个死循环,不断的从MessageQueue中取出Message,然后拿到Message.target,也就是Handler,执行它的dispatchMessage方法。

   另注: ThreadLocal的set和get方法可视为获取到了looper.mThread用来和looper一一对应

public final Class Looper

   final MessageQueue mQueue;

  Thread mThread;

  public static void prepare()

    if (sThreadLocal.get() != null) throw一个异常

    sThreadLocal.set(new Looper())

  

  private Looper()

    mQueue = new MessageQueue()

    mThread = Thread.currentThread()

  

  public static Looper myLooper()

    return sThreadLocal.get()

  

  public static void loop()
    while(true)

      Message message = mQueue.next()

      message.target.dispatchMessage(message)

    

  

 Service

一些关于其定义和使用的东西:https://blog.csdn.net/guolin_blog/article/details/11952435

 
全局获取context方法:
新建MyApplication extends Application
    private static Context context;
    public void onCreate()
      context = getApplicationContext();
      LitePalApplication.initialize(context);//如果已经配置了LitePal类似的application
    
    public static Context getContext() return context
  
修改AndroidManifest.xml 的 <application>标签 指定他的android:name="com.xxx.xxx.MyApplication"
就可以在全局通过MyApplication.getContext()获取了
 
Bitmap
内存管理:使用完bitmap,及时使用Bitmap.recycle()回收。
缓存:内存缓存硬盘缓存
图片大小:使用ImageView显示Bitmap时会占很多资源,使用BitMapFactory.Options对图片进行压缩。
     android默认颜色模式为ARGB_8888,显示质量最高,占用内存最大。若要求不高时可采用RGB_565等模式。
图片大小:图片长度*宽度*单位像素所占据字节数
      ARGB_4444:每个像素占用2byte内存
      ARGB_8888:每个像素占用4byte内存 (默认)
      RGB_565:每个像素占用2byte内存
捕获OOM异常:程序中设定如果发生OOM的应急处理方式。清理内存空间,强制Bitmap.recycle()回收等
 
 
引用类型

一:强引用

只要引用存在,垃圾回收器永远不会回收,当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
Object obj = new Object();

后面有若干个方法用到obj 如自定义方法:hanlde(obj );
只有当hanle方法执行完之后,obj这个引用被释放,对象才会被释放掉,这也是我们经常所用到的编码形式。

二:软引用

非必须引用,内存溢出之前进行回收,可以通过以下代码实现
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//当内存不足时可能null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;

如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就
会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的
高速缓存。

三:弱引用

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内
存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

四:虚引用

"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被
垃圾回收的活动。

 

HashMap

数组形式储存,链表辅助。put()的时候通过map的键对象的hashCode寻找下标,如果改下标有元素,则下标往后移。如果元素达到了hashmap容量的75%,则进行扩容为原来的两倍。get()的时候也是通过对比键对象的hashCode和equals()取出对应的map。

 

APK签名机制

1、程序遍历apk包中的所有文件(entry),对非文件夹非签名文件的文件,逐个生成SHA1的数字签名(类似摘要MD5)信息,再用Base64进行编码。生成MANIFEST.MF文件

2、对前一步生成的Manifest,使用SHA1-RSA算法,用私钥进行签名。生成CERT.SF文件

3、CERT.RSA文件中保存了公钥、所采用的加密算法等信息

以上是关于android 初探的主要内容,如果未能解决你的问题,请参考以下文章

android初探

从0到1实现一个Android路由——初探路由

Android中的MVP架构初探

Android 初探

Android 逆向初探

android核心服务初探