Android学习笔记(二)

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学习笔记(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,577评论 25 707
  • Activity是什么 Activity是四大组件之一,它提供一个界面让用户点击和各种滑动操作 Activity栈...
    叫我吹神阅读 2,516评论 0 4
  • 第十章:Android的消息机制 Handler是Android消息机制的上层接口,开发人员只需要与它交互即可,底...
    loneyzhou阅读 592评论 0 1
  • 认同感:用故事包装事实的艺术 作者:【美】吉姆·西诺雷利 和很多书评说一样,书名是认同感,但重点确实故事。怎么让对...
    满财财阅读 569评论 0 1
  • 物理上有这么一条定理,是热力学的,就是说一个系统的每一个自由度都具有相同的能量(\frac{kT, 2})。 这个...
    LostAbaddon阅读 2,819评论 3 8