加载图片过大超出内存
解决办法:
缩放图片,在进行加载(将图片缩放至屏幕大小)
计算缩放比例
获取图片宽高
获取屏幕宽高
图片宽比宽获取宽的缩放比例
图片高比高获取高的缩放比例
宽高的缩放比例采用统一个值, 使用大的那个
缩放方法
//使用Bitmapfactory.Options
OPtions opts = new Options ();
opts.inJustDecodeBounds = true ;
BitmapFactory.decodeFile("图片",opts);
//设置为true时,BitmapFactory的decodeFile(String path,Options opt)并不会返回一个Bitmap对象,但是会返回图片的小 (宽、高)
//拿到图片的宽高
int width = opts.outWidth;
int height = opts.outHeight;
//设置缩放比例
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
Bitmap bm = BitmapFactory.decodeFile("sdcard/Download/hello.png", opts);
线程池
- 线程池的优点:
1、可复用线程,避免了线程的频繁创建与消亡,带来的性能上的开销
2、能够让线程得到统一的管理
3、能够避免创建过多的线程,线程间抢占资源,内存不足造成卡顿
4、可控制最大的并发线程数,提高系统的资源的利用率 - 常见的线程池
- ThreadPoolExecutor:最基础的线程池,下面几种都是通过ThreadPoolExecutor变种而来。
- 创建线程池
/** * 参数介绍 * * @param corePoolSize 核心线程数 (核心线程会一直在线程池中存活,一旦有任务时,会立即处理任务) * @param maximumPoolSize 线程池中最大的线程数(核心线程数 + 非核心线程数(非核心线程:当任务队列满载状态,才会创建一个非核心线程去处理任务,当再次满载再创建一个非核心线程...)) * @param keepAliveTime 非核心线程的存活时长 (是非核心线程执行完任务后等待下一个任务到来的空闲时长) * @param unit 非核心线程存活的时长单位 * @param workQueue 线程池中存放的任务队列 * @param threadFactory 对线程池中的线程进行一些设定 (线程名...可有可无) * @param handler 拒绝策略(当任务队列满载,且已达最大线程数时,再有任务加入会执行拒绝策略) */ ThreadPoolExecutor threadPool = new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); //执行任务,当调用完extcute之后,才会进入runnable执行任务 threadPool.extcute(Runnable runnable);
- 创建线程池
- FixedThreadPool:只有核心线程,并且任务队列无满载状态。适用于执行长期任务
- 创建线程池:
ExecutorService fixedThreadPool = Executor.newFixedThreadPool(核心线程数); //其实内部是通过ThreadPoolExecutor(corePoolSize,corePoolSize,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()), //执行任务 fixedThreadPool.execute(Runnable runnable);
- 创建线程池:
- CachedThreadPool:该线程池没有核心线程,只有非核心线程,并且非核心线程的存活时长为60s,以及无最大线程数(Integer.MAX_VALUE)。适用于执行为数较多的耗时短的任务
- 创建线程池:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool() //其实内部是通过ThreadPoolExecutor(0,Integer.MAX_VALUE,0,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>()), //执行任务 cachedThreadPool.execute(Runnable runnable)
- 创建线程池:
- SingleThreadPool:该线程池中只有1核心线程,无非核心线程,当多个任务同时添加时,无空闲线程则会将任务放到任务队列中排队等待执行。适用于一个一个的任务场景
- 创建线程池:
ExecutorService singleThreadPool = Executors.newSingleThreadPool() //其实内部是通过ThreadPoolExecutor(1,1,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()), //执行任务 singleThreadPool.execute(Runnable runnable)
- 创建线程池:
- ScheduledThreadPool:该线程池可设置核心线程数,无最大线程数(Integer.MAX_VALUE),线程存活时长为0,非核心线程空闲立马被回收。适用于周期性执行任务,延迟执行任务
- 创建线程池:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(核心线程数) //其实内部是通过ThreadPoolExecutor(corePoolSize,Integer.MAX_VALUE,0,TimeUnit.MILLISECONDS,new DelayedWorkQueue<Runnable>()), //延迟执行一次任务 scheduledThreadPool.schedule(runnable,延迟执行任务时长,时长单位) //首次执行任务延迟一定时长,后面每间隔一定时长触发任务 scheduledThreadPool.scheduleAtFixedRate(runnable,首次执行任务延迟时长,后面循环任务每间隔时长执行任务,时长单位) //首次执行任务延迟一定时长,后面每次会延迟一定时长后再执行任务 scheduledThreadPool.scheduleWithFixedDelay(runnable,首次执行任务延迟时长,后面循环任务会每次延迟时长执行任务,时长单位)
- 创建线程池:
- ThreadPoolExecutor:最基础的线程池,下面几种都是通过ThreadPoolExecutor变种而来。
- 执行任务的submit()和execture()的两个方法的区别:submit首先会将执行的任务Runnable打包成RunnableFuture,然后在调用execture并把RunnableFuture传到execture中,最后,submit会把打包好的RunnableFuture返回,而execture是无返回值的
- 关闭线程池:shutDown、shutDownNow两种方法
- shutDown:关闭线程池,可能线程池中还存有正在执行的任务,不会去中断,只是关闭后无法在往此线程池中添加任务了
- shutDownNow:关闭线程池,同时会中断线程池中正在执行的任务
- 四种拒绝策略:
- AbortPolicy:会拒绝任务加入,并且抛出RejectedExecutionException异常
- DiscardPolicy:会拒绝任务加入,不抛异常
- DiscardOldestPolicy:会抛弃队列最前的任务,然后将此任务放到队列中
- CallerRunsPolicy:该任务被线程池拒绝,会交由当前调用execute方法开启任务的线程去执行该任务
Handler消息机制
-
消息机制的流程:
1、创建一个handler对象,重写其中的handleMessage方法
2、每个线程在使用Handler时都需要绑定对应的且唯一
的Looper对象,主线程在启动的时候系统已经帮我们主动调用Looper.parper()去初始化一个Looper对象,子线程需要开发手动去调用Looper.parper()为当前线程绑定自己的Looper对象。
为什么在线程中使用handler需要绑定Looper对象呢?
①在new Handler()时,在构造方法中会去拿当前的Looper对象,如果没有会直接抛异常
②同时在构造方法中会将通过Looper构造方法中创建的消息队列取出,在发送消息时,需要将消息放到队列中,如果无法拿到对应的消息队列,直接抛异常public Handler(@Nullable Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } // 获取当前的Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } // 取出创建好的消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
唯一体现在,调用prepare()时,第一步会从ThreadLocal中获取当前线程的Looper对象,如果存在则直接抛异常,不存在才继续往下new Looper(),并保存到ThreadLocal中,实现线程与Looper的绑定
//Looper.prepare() public static void prepare() { prepare(true); } //初始化Looper对象 private static void prepare(boolean quitAllowed) { //如果当前线程中已经绑定了Looper,又重新创建一个新的Looper则抛异常,所以一个线程只有一个Looper对象 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } // sThreadLocal.get() 和sThreadLocal.set()都会以当前线程为唯一准则
3、通过Looper.loop()方法开启循环遍历消息队列获取消息,在其内部首先会去获取当前线程的Looper对象通过
myLooper()
,如果当前线程未绑定Looper,则会抛异常。然后会通过绑定的Looper对象获取其内部的消息队列,然后通过一个无线for循环,不断的通过queue.next()
获取队列中的消息,如果拿到的消息不为null,最终会通过msg.target.dispatchMessage(msg)
分发消息,msg.target <==> handler,通过到Message类中可以发现target就是Handler的引用,所以dispatchMessage其实是调用了Handler中的方法,在dispatchMessage中会调用handlerMessage(msg),即回到我们自定义Handler中的handlerMessage方法中处理消息。
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } me.mInLoop = true; final MessageQueue queue = me.mQueue; // ...省略一段代码 for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
4、如果消息队列中取到的Message对象不为null,调用msg.target.dispatchMessage(msg),msg.target就是handler对象,然后调用handler的dispatchMessage()内部会调handleMessage(),后面消息交由handler.handleMessage(msg)方法处理消息
/** * Handle system messages here. */ public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
5、发送消息,我们通过handler.sendMessage(msg)发送消息,其实内部调用的是sendDelayMessage(msg,0)方法,给延迟发送消息时间设置为0,之后在内部调用了sendMessageAtTime(),在该方法内部又调用了enqueueMessage(),在该方法内部会把当前handler对象传到Message.trager变量中
msg.traget = this;
然后将msg发送的消息对象,uptimeMillis更新时长,传入一个queue消息队列中通过queue.enqueueMessage();
存放到消息队列中//handler.sendMessage() public final boolean sendMessage(@NonNull Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { //msg.target 为当前handler对象 msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } //将消息存放到消息队列中 return queue.enqueueMessage(msg, uptimeMillis); }
6、消息队列的排序规则: 首先看
Message
对象里面是存放next:Message
下一个执行的消息;boolean enqueueMessage(Message msg, long when) { //..........省略不关心代码 // 1、设置添加的消息等待时常 msg.when = when; // 2、保存即将要处理的消息 Message p = mMessages; boolean needWake; // 3、当即将处理的消息不存在/准备添加的消息是立即执行/准备添加的消息等待时常<即将处理消息的等待时常,都会将消息添加到队列第一位 if (p == null || when == 0 || when < p.when) { // 4、将之前准备要执行的消息指向到要被添加的消息的next上 msg.next = p; // 5、将添加的消息,指向到全局消息变量,表示即将要被执行的消息,及队列第一位 mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; // 6、遍历所有消息,进入else中的消息指定不是在队列首位,取出每个Message.next的时常与添加的消息时常比较,时常小的,就会把遍历到的Message.next 指向于当前添加的Message,再将添加的Message.next指向于之前的Message for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
-
补充:
1、发送消息也可以通过handler.post(Runnable r),这种方式是传入一个Runnable对象,在post内部会调用getPostMessage®返回一个Message对象,在getPostMessage方法里面会将runnable对象存放到 Runnable callback变量中,返回一个message对象后,接着调用sendDelayMessage方法后面与上面流程基本一致。到了处理消息的地方dispatchMessage(),首先会去判断msg.callback不为null,直接msg.callback.run()。
2、保证一个线程只有一个Looper对象:因为在每次创建新的Looper对象时,都需要去ThreadLocal里面检查当前线程是否存在Looper对象,如果存在则抛出异常,于此同时,只有在创建Looper对象时,在Looper的构造方法中去创建MessageQueue消息队列
3、 任何线程都可以实例化Handler,只是任意线程只能有唯一的一个Looper对象进行绑定
4、关于Looper.loop()是一个阻塞的死循环,为什么主线程不会被ANR:在activity的生命周期、点击事件等都是通过主线程的Looper对象不断去消息队列中取消息,然后做不同的处理,如果当前正在处理一个消息,接着又来了另一个消息,但是当前的消息未处理完,导致Looper.loop()循环被卡住,无法正常的去获取下一个消息去处理,就会导致界面卡顿,卡顿时间长就造成了ANR,所以loop循环可能会阻塞主线程,但是一旦有消息写入,loop能够立即获取到,然后处理消息,就不会造成ARN,当消息队列中没有消息时,主线程会被阻塞进入休眠状态,会释放当前的cpu资源
几种异步方式的区别
-
AsyncTask:使用AsyncTask需要继承它,并且需要传入泛型<doInBackGround的参数类型,onProgressUpdate的参数类型,doInBackGround的返回值类型、onPostExecture的参数类型>
- onPreExecute():执行在主线程中,一般处理准备异步操作前的准备工作
- doInBackGround():执行在子线程中,处理异步操作的任务
- onPostExecute():执行在主线程中,异步操作执行完成后(doInBackGround)调用,可以进行界面刷新操作
- onProgressUpdate():当在doInBackGround中调用publishProgress时,才会执行在主线程中。可在doInBackGround()中调用publishProgress()来更新当前任务执行的进度
- 总结:AsyncTask是一个抽象的类,子类需要继承它,构造方法有三种重载方式,无参/传handle/传looper,最终都是调用传Looper的方式,并根据传入的Looper对象创建Handler;无参的构造方法默认调用传Looper的构造方法,并使用主线程的Looper对象;传handler的构造方法,最终也是调用传Looper对象的构造方法,并通过传入的handler获取其绑定的Looper对象;
当执行execute(Params... params)
,该方法会一次只能运行一个任务,当存在运行中的任务时,又去执行下一个任务会抛异常,接着会执行onPreExecute()
,此时还在主线程中执行,随即将任务FutureTask
也是Runnable对象放到线程池中,之后任务被执行后,会回调到WorkerRunnable.call
,在内部执行doInBackground
方法,在doInBackground中执行我们的任务逻辑,当任务执行完毕后,将结果作为postResult(result)
的参数,在postResult中通过之前创建的handler
将执行的结果发送到指定线程中执行AsyncTask.finish(result)
,在方法内执行onPostExecute(result)
;我们可以在doInBackground
中执行publishProgress
来更新任务执行的进度,在publishProgress
方法中会通过hanlder发送MESSAGE_POST_PROGRESS
到指定线程中更新进度消息,然后回调AsyncTask.onProgressUpdate
;
当执行AsyncTask.execute(runnable)
,会将runnable任务直接放到线程池中,不会回调onPreExecute、doInBackGround、onPostExecute; - 补充:
1、onPreExecute执行代码:
2、doInBackground的代码执行:/** 外部通过asyncTask对象执行任务 */ @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { // 坚持当前是否有正在执行的任务,存在就抛异常 if (mStatus != Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); } } mStatus = Status.RUNNING; // 在主线程中回调onPreExecute,可做异步前的准备工作 onPreExecute(); mWorker.mParams = params; // 封装一个FutureRunnable对象也是runnable对像,放到线程池中 exec.execute(mFuture); return this; }
3、onProgressUpdate代码执行逻辑:/** 当任务执行到 exec.execute(mFuture)的mFuture时,会回调FuturnRunnable.run(), 然后会回调mFuture.WorkerRunnable.call回调 */ public AsyncTask(@Nullable Looper callbackLooper) { mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 业务处理任务逻辑 result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { // 内部会通过构建的handler,将任务执行的结果发送到指定线程中回调onPostExecute postResult(result); } return result; } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } }; }
4、onPostExecute代码执行逻辑:/** 可在doInBackground回调中调用publishProgress来更新进度 */ @WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { // 通过handler发送更新进度到消息到指定的线程中,最终会回调AsyncTask.onProgressUpdate(result.mData) getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } private static class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; switch (msg.what) { // 处理执行任务结束消息 case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; // 处理更新任务执行进度消息 case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } } // 会在消息处理handleMessage执行 private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { // 回调onPostExecute onPostExecute(result); } mStatus = Status.FINISHED; }
-
HandlerThread:内部继承了Thread,本身就是一个线程,实现run方法,通过HandlerThread.startThread()开启线程,在run()内部维护了一个Looper对象,通过Looper.loop()不断循环消息队列,通过handler发送消息任务进行处理。
用法:
1、创建一个new HandlerThread("线程名称")
对象
2、HandlerThread.startThread()
开启线程
3、HandlerThread内部构建一个Handlernew Handler(HandlerThread.getLooper())
用于发送任务消息到子线程中处理,因为关联上HandlerThread当前线程的Looper对象,以至于Handler中的消息处理是在子线程中
4、通过HandlerThread.getThreadHandler()
获取到handler进行任务的发送
5、通过HandlerThread.quit()
关闭Looper.loop()退出任务,在退出前先移除消息队列中的消息 -
IntentService:是一个抽象的可以执行耗时操作的Service,且任务结束后自动关闭服务,在IntentService内部,维护了一个ServiceHandler(就是继承了Handler,且是绑定了子线程Looper)和一个HandlerThread。
实现原理就是通过开启一个HandlerThread的一个子线程,创建一个Handler绑定子线程的Looper,通过handler将消息发送到子线程中处理,在handleMessage
中回调抽象方法onHandleIntent
,交由业务层实现任务逻辑,当任务结束后,执行service的stopSelf()
关闭当前服务,并会停止loop轮训// 绑定了子线程的looper对象,将任务消息发送到handleMessage中执行onHandleIntent回调执行任务逻辑 private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); // 任务执行完毕后,关闭服务 stopSelf(msg.arg1); } } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); // 开启子线程,并为handler提供子线程Looper对象 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(@Nullable Intent intent, int startId) { // 构建任务消息,用于执行任务回调方法onHandleIntent Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { // HandleThread的run方法中,开启了Looper轮询,导致HandleThread子线程无法停止,所以,HandleThread需要 // 在合适的地方关闭Looper循环 mServiceLooper.quit(); } @WorkerThread protected abstract void onHandleIntent(@Nullable Intent intent);
1、需要继承IntentService类,实现onHandleIntent(),在这里面可执行耗时操作。同时需要在构造方法中调用super(name),作用是给当前的线程定义一个名称,必须调用,否则会报错
2、开启IntentService和正常的service开启方式一样,startService()开启
ANR的原因
- activity耗时操作超过5s
- broadcastReceiver耗时操作超过10s
- service耗时操作超过20s
内存优化
- 内存泄漏
- 单例造成的泄漏:(单例的静态特性,使其生命周期和应用的生命周期一样长,如果一个对象的已经没有用了,但是单例还持有引用,那么就会造成该对象无法被正常释放)
- 静态变量造成的泄漏:(静态变量的随着类加载就已经存在方法区,直到应用退出才被销毁,所以静态变量持有了短生命周期对象引用导致,短生命周期的引用本该销毁时,却被长生命周期引用所持有,无法释放)
解决方案可在适当时机置空
。 - 非静态内部类持有外部类的引用:(非静态内部类会默认持有外部类的引用this,当内部类生命周期长于外部类,导致外部类无法被正常释放)
典型场景Handler的使用、new Thread()、AsyncTask 、Timer和TimerTask页面销毁时及时cancel掉任务
- 集合中添加对象未及时清理
- bitmap不用时回收资源(bitmap.recycle)
- 加载大图时,尽量使用Options压缩一下
- 资源使用后未关闭(IO、Sqlite、File)
- 属性动画造成的泄漏:(当界面销毁,需要将动画及时cancel)
- WebView造成的泄漏:
解决方案WebView尽量在代码中创建,减少布局中使用,页面销毁时,先从容器中remove控件,然后 调用webView.destory
- 监听器的注册与反注册
动态注册广播,页面关闭时需要取消注册广播、注册观察者模式也需要及时取消注册
- 减少枚举使用
- 原因:枚举占用的内存比较大
- 尽可能使用优化过的数据容器(SparseArray、SparseBooleanArray、LongSparseArray代替HashMap)
- 原因: 相比HashMap占用的内存更低
- 布局优化
- 减少布局的嵌套,提高布局绘制性能
- 删除不必要的背景
- 布局中尽量使用include、merge、ViewStub(include:布局服用;merge:减少布局层级;ViewStub:当需要时才会去加载)
- 自定义view时,避免在onDraw中创建大量的临时变量,会频繁调用GC,造成内存抖动
- ListView使用注意点:
- 考虑条目复用convertView,使用ViewHolder
- 展示大量数据时,建议分页加载、分段加载
- 可以通过RecyclerView来替代
多进程
- 优点:
1、可以最大限度的获取系统资源,毕竟系统为每个进程分配的资源有限
2、进程间资源隔离,当前进程挂掉也不影响其他进程,很适合 Service 或一些辅助业务模块 - 缺点:
1、静态成员和单例模式失效(不同进程访问同一个类会产生多个副本)
2、线程同步机制失效 (不同进程锁的不是同个对象)
3、SharedPreferences 可靠性下降 (并发写操作,会造成丢数据)
4、Application 会多次创建 (每启动一个进程都会被分配一个新的虚拟机)