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