Android学习笔记(二)

作者: 南山伐木 | 来源:发表于2017-02-20 17:05 被阅读53次

    Android通信原理。
    是通过一个轮回机制Looper来管理线程之间的通信。Looper是一个死循环体,内部包含一个消息队列(MessageQueue),looper的作用就是负责不断循环地从这个消息队列取出消息然后执行处理。在Android启动的主线程中默认存在一个Looper。
    ★具体步骤为:
    ●由一个Handler向关联的Looper发送消息(Message)。当Looper处理消息时,再发回handler处理。在实例化一个handler时,可指定到一个Looper,若没有指定,则默认为主线程。
    ●创建自己的Looper:
    创建一个线程类WorkThread继承Thread,
    ▲在其run方法中,添加:

    Looper.prepare();//会自动执行以下操作: 1.创建一个消息队列 Queue 2.创建Looper 3.将Looper绑定到当前线程
    looper = Looper.myLooper();// 获得当前线程上绑定的Looper对象
    synchronized (this) {notifyAll();}// 有消息了就唤醒其他线程
    looper.loop();// 进入死循环,等待处理Queue中的消息
    

    ▲添加一个得到looper的方法:

    public Looper getLooper() {
    // 若没有消息在队列中无法返回一个消息,可先等待,以保证主线程能取出消息
    synchronized (this) {
    while (looper == null) {    
      try { 
       wait();
      } catch (InterruptedException e) {}
      }
     }
      return looper; 
    }
    

    ★相当于Android内部的 new HanderThread(name);可直接创建一个自己的Looper;
    ▲在主方法中创建此类的一个实例,将new WorkThread().getLooper()放在Handler的参数中,则用handler发送的消息都在自此looper中,主要用于主界面向工作线程发送数据。
    ●发送信息时,共有6种发送方法;
    ●接回消息进行处理时会调用 Handler 的dispatchMessage方法dispatchMessage(Message msg),有三种方法进行处理:
    ▲在 Message 上外接 Runnable 回调
    ▲在 Handler 上外接 Callback 回调
    ▲重写 handleMessage()其逻辑为:◇若存在 msg 的回调对象,执行此回调对象;◇在Handler 上存在回调对象,执行此回调对象:该方法返回 true,结束;该方法返回 false,执行重写的 handleMessage() 方法;◇以上两个回调对象都不存在, 那么执行重写的 handleMessage() 方法
    ●Looper 处理消息:按优先级执行;msg上的Callback() > handler上的Callback() > handler(){..}中的handlMessage(..)方法。

    需用Handler发送一个消息到Looper消息队列中,并在Hnadler匿名内部类中处理消息数据。程序根据handler发送信息的先后顺序添加到Looper队列中,然后在hanleMessage(msg){}方法中从队列中按先后顺序取出处理。根据msg.what标记值的不同进行相应的处理。
    handler关联哪个对象就在哪个对象中处理消息。

    ★异步任务AsyncTask
    是一个抽象类,用于被继承;主要用于耗时的操作,如下载,加载图片、计算大量任务等;
    AsyncTask<params,progress,result>:
    参数分别为:
    params:启动任务执行输入参数的类型;
    progress:后台任务完成的进度值的类型;
    Result:后台执行完成后返回结果的类型;
    方法:
    ▲doInBackground(params…):在后台线程中执行;重写该方法就是后将要完成的任务,该方法可调用publishProgress(progress…values);向更新任务发送参数以更新进度;
    ▲onProgressUpdata(progress…values):主线程中执行;在doInBackgroud()方法中调用了publishProgress()方法后会触发此方法,可将传来的数据加载到一个进度条上;
    ▲onPreExcute():主线程中执行;该方法在后台操作前调用,用于完成一些初始化操作,如加载进度条;
    ▲onPostExecute(result):主线程中执行,当后台操作完成后将结果在本方法中处理;
    ▲直接执行:.execute(params);//直接执行单个异步任务线程;
    ▲线程池中执行:.executeOnExecutor(Excutor pool, params);// 加入到线程池处理;

    ★单线程轮询机制:在手机中多个线程并发时cpu大多时间用于切分时间,执行程序的时间反而减少,故一般不超过10个,在对ListView的每个item加载内容时,不能对每个item创建一个线程,否则会产生大量的线程,直接造成内存溢出。故用单线程轮询机制。
    使用:
    ▲先创建一个成员线程变量workThread和一个包含多个任务的集合List<Task>;
    ——>在run()方法中创建一个可控循环while (isLoop){...},并遍历list中是否有任务while (!tasks.isEmpty()) {...},若有,就取出一个任务执行task = tasks.remove(0);若没有,就进行等待synchronized (this) {wait();};
    ——>集合中没有该任务 if (!tasks.contains(task)){再放入},将任务放入集合中时list.add(task)并唤醒线程,synchronized (workThread) { workThread.notify();},故在创建任务类时应重写equals(Object o)方法。

    ◆瞬态对象:
    回调,即在主线程中创建一个回调对象Callback,交给工作线程,当工作线程在这个回调对象中处理完自己的操作后再回传给主线程,主线程再进行进一步的操作;有异步耗时操作就需要回调,可用于内部通信,传递数据;如按钮上的事件监听;
    使用:
    ●先定义一个接口,提供一个要实现的方法:

    public interface Callback {
      void ImageLoaded(String path, Bitmap bmp);
    }
    

    ●在工作线程的构造方法中以此接口为对象,并将参数传入到此接口的方法中:

    public AsynchoizedTask(Context context, final Callback callback) {  
       this.handler = new Handler() {   
       public void handleMessage(Message msg) {
         ImageLoadTask task = (ImageLoadTask) msg.obj;
         callback.ImageLoaded(task.path, task.bmp);     
      }
    };
    

    ●在主线程创建此工作线程实例时,就必须实现接口中的方法,并可直接使用参数中传递的数据:

    this.task = new AsynchoizedTask(context, new Callback() {
      public void ImageLoaded(String path, Bitmap bmp) {
      ImageView imv = (ImageView) lv.findViewWithTag(path);
      if (imv != null && bmp != null) {
       imv.setImageBitmap(bmp); 
      }
     }
    });
    

    ▲强引用,即直接引用类对象,如Student s= new Student();java垃圾回收器不会随意销毁此对象,当内存不足时java垃圾回收器宁可抛出OutOfMemoryException,也不会销毁对象;
    ▲软引用SoftReference<T>(T t),若内存足够则垃圾回收器不会回收它,当内存不足时会自动回收,故适合做缓存,会自动判断内存是否足够,内存足够就创建一个对象,否则返回一个null;
    HashMap<String, SoftReference<Bitmap>> caches = new HashMap<String, SoftReference<Bitmap>>()
    ▲弱引用(WeakReference),只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存。
    ▲虚引用(PhantomReference),虚引用不会决定对象的生命周期,若一个对象持有虚引用,那么它和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

    ◆内存泄露:若对象一直被引用,则垃圾回收器不能回收它,一直常驻内存中,无法被释放。把Activity对象传给其他生命周期比当前Activity生命周期更长或生命周期不确定的对象,则会造成内存泄露。
    如何防止内存泄露:把自身引用传给另一个对象时,应先判断对方生命周期是否比自身生命周期长,若是在自身销毁前要先销毁引用我的对象。在Adroid中重写onDestroy()方法中销毁。
    ★[匿名内部类会持有外部类的引用;故若外部类销毁时若内部类还没有被销毁就会造成内存泄露]
    ▲典型内存泄露;

    new Thread(){
       run(){//在run方法中若引用了Activity对象,则在Activity销毁后,由于工作线程留了对其的引用,无法让Activity被回收。}}.start()
    

    ▲经典内存泄露:

    public class AA{
      public static ArrayList<Activity> arr= new ArrayList<Activity>;
    }//然后每次将启动的Activity添加到集合中,造成在Activity结束后集合中仍保留对其的引用,不能完全销毁
    

    ◆Android中的Context对象
    Application :全局组件,当程序启动时创建,销毁时回收。
    ▲Service:是四大组件之一;是Context;控制器;是全局单实例;且不包含界面;其实例所在的继承拥有较高的优先级;适合用于长时间后台运行的场合,但不能直接在Service生命周期方法执行耗时代码,以免造成ANR;只能启动一个工作线程处理。
    ★主线程操作:UI操作、事件处理方法、组件的生命周期方法。[Service也在主线程中进行]
    ▲创建 Service,
    创建一个类entends Service ——>注册——>重写生命周期方法(onStart())
    ▲启动Service
    创建一个启动意图Intent——>调用Context的startService(intent)方法;
    ▲停止Service的两种方式
    1、调用context.stopService(intent)方法
    2、在Service内部调用
    stopSelf()
    stopSelf(int startId)

    ▲启动模式下Service的生命周期方法:
    onCreate : 每个Service实例创建时执行
    onStartCommand:每次启动Service实例都会执行
    onDestroy: 每个Service实例销毁时执行
    ★[onStart() 与 onStartCommand()区别:onStart()方法在2.0之前使用,onStartCommand在2.0之后使用。在onStartCommand方法中调用了一个onStart()方法并返回一个整型值,分别对象服务的状态值,以差别系统在出现异常时是否应重启该服务。]
    ★当一个Android程序启动时,若没有新建线程,默认都有三条线程被执行。一个Main线程,两个Binder线程(都用于发消息,进行通信;一个用于与Main线程通信,一个与组件事件线程通信,线程通信都是基于类似C/S请求响应模式);
    ▲Android相当于一个容器,所有Activity都运行这个框架中,所有的Activity和通信都由AMS进行管理。Main线程作用是不断取出消息,应主要用于调度其他工作线程启动。
    ▲耗时操作:若一段代码超过0.2秒则是耗时操作。所有的耗时操作都应启动一个工作线程去执行。
    ▲进程优先级:
    前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程;
    ◎前台进程:※包含运行状态的Activity的进程;※包含正在执行生命周期方法的组件实例的进程;※包含与运行状态的Activity实例绑定的Service实例;※包含执行了setForeground(true)方法的Service实例。
    ◎可见进程:※包含暂停状态的Activity实例的进程;※包含与暂停状态的activity实例绑定的service实例服务进程;
    ◎服务进程:一个进程里至少包含一个Service实例;
    ◎后台进程:包含停止的Activity实例的进程
    ◎空进程:不包含任何的组件实例。

    ★多耗时任务,使用消息队列替代单线程轮询机制
    在Service中使用消息队列处理原理:主线程intent将任务参数发送到service中,在Service中创建一个包含Looper的工作线程HandlerThread,在

    onStartCommand(Intent intent, int flags, int startId){  
       Message.obtain(handler, 0, startId, 0, intent).sendToTarget();
    };
    

    将Intent发送到handler中进行处理。
    ★经典写法:

    public void onCreate() {super.onCreate();
      this.handlerThread = new HandlerThread("workThread");// 初始化时的操作
      this.handlerThread.start();// 必须启动工作线程
      Looper looper = this.handlerThread.getLooper();// 将当前handler关联到工作线程的looper
      this.handler = new Handler(looper) {
        public void handleMessage(Message msg) {            
        Intent intent = (Intent) msg.obj; // 取出消息发来的参数
        int startId = msg.arg1;
        onHandlerIntent(intent); // 执行下载任务具体业务方法
        stopSelf(startId); // 执行完业务后结束本次启动  }   
      };
    }
    

    可将业务处理方法封装为一个抽象方法,那么其他类继承该类只需重写此方法即可:protected abstract void onHandlerIntent(Intent intent);
    ★★[以上代码即为IntentService中的源代码,只需继承IntentService,重写onHandlerIntent(...)方法即可;注意:继承此方法时必须构造其无参构造方法,否则报错]

    原文地址:Android学习笔记(二)

    相关文章

      网友评论

        本文标题:Android学习笔记(二)

        本文链接:https://www.haomeiwen.com/subject/kveewttx.html