diff --git a/README.md b/README.md
index ce1e482..94e2127 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@

## Java
+### Java基础
+* [Java之位运算符](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E4%B9%8B%E4%BD%8D%E8%BF%90%E7%AE%97%E7%AC%A6.md)
+
+
### Java高新技术
* [泛型--GenericType]
* [虚拟机中的泛型信息](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/%E5%8F%8D%E5%B0%84(%E4%BA%8C)%E4%B9%8B%E8%99%9A%E6%8B%9F%E6%9C%BA%E4%B8%AD%E6%B3%9B%E5%9E%8B%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF.md)
@@ -11,8 +15,22 @@
* [Java8--时间API]
* [Java8--StreamAPI]
+### Java多线程
+* [Java多线程之内存可见性synchronized,volatile](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java多线程之内存可见性.md)
+* [Java多线程之线程中断interruption协作机制](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E4%B8%AD%E6%96%AD%E7%BA%BF%E7%A8%8B.md)
+* [Java多线程之线程的状态及方法](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%8A%E6%96%B9%E6%B3%95.md)
+
+### Java并发
+* [同步工具类之CountDownLatch,Semaphore,Barrier](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E5%B9%B6%E5%8F%91%E4%B9%8B%E5%90%8C%E6%AD%A5%E5%B7%A5%E5%85%B7%E7%B1%BB.md)
+* [Java并发之线程池(一)之ThreadPoolExecutor](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E5%B9%B6%E5%8F%91%E5%A4%9A%E7%BA%BF%E7%A8%8B(%E4%B8%80)%E4%B9%8BThreadPoolExecutor.md)
+* [Java并发之线程池(二)之Executors]
+* [Java并发之阻塞队列](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E5%B9%B6%E5%8F%91%E4%B9%8B%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97.md)
+
+
+
### Java设计模式
* [观察者模式](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F.md)
+* [单例模式](https://github.com/showdy/Android_Note/blob/master/showdy_note/java/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8BSingleton.md)
@@ -20,10 +38,16 @@
## Android
### UI控件篇
-* [Material Design风格控件]:
- * [Toolbar详解](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/material_design/toolbar%E8%AF%A6%E8%A7%A3.md)
-
+ * [Toolbar](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/material_design/toolbar%E8%AF%A6%E8%A7%A3.md)
+ * [TabLayout](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/material_design/Tablayout%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%B7%E5%BC%8F.md)
+### Android基础:
+* [Activity](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8BActivity.md)
+* [Service](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6__Service.md)
+* [BroadcastReceiver](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/Android_BroadcastReceiver.md)
+* [BitmapFactory.Options](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/BitmapFactory_Options.md)
+
+
### Android自定义View篇
* [自定义View--自定义基础](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/view/%E8%87%AA%E5%AE%9A%E4%B9%89View%E5%9F%BA%E7%A1%80%E7%AF%87.md)
* [自定义View--View绘制的绘制流程](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/view/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E4%B9%8BView%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B.md)
@@ -38,30 +62,23 @@
### Android异步消息篇
-* [Android异步处理技术之Hanlder](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/android%E5%BC%82%E6%AD%A5%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6%E4%B9%8BHandler.md)
-* [Android异步处理技术之HandlerThread及IntentService源码分析](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/Android%E5%BC%82%E6%AD%A5%E6%9C%BA%E5%88%B6%E4%B9%8BHandlerThread%E5%92%8CIntentService%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md)
-* [Android异步处理技术之AsyncQueryHandler]
-* [Android异步处理技术之AsyncTask]
-* [Android异步处理技术之Executor Framwork]
-* [Android异步处理技术之Loader]
+* [Android异步消息之Hanlder](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/android%E5%BC%82%E6%AD%A5%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6%E4%B9%8BHandler.md)
+* [撸一个自己的Handler异步消息机制](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/%E6%92%B8%E4%B8%80%E4%B8%AA%E8%87%AA%E5%B7%B1%E7%9A%84Handler%E5%BC%82%E6%AD%A5%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.md)
+* [Android异步消息之HandlerThread及IntentService源码分析](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/Android%E5%BC%82%E6%AD%A5%E6%9C%BA%E5%88%B6%E4%B9%8BHandlerThread%E5%92%8CIntentService%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md)
+* [Android异步消息之LocalBroadcastManager](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/LocalBroadcastManager%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md)
+* [Android异步消息之AsyncQueryHandler]
+* [Android异步消息之AsyncTask](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/AsyncTask%E4%BD%BF%E7%94%A8%E5%8F%8A%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md)
+* [Android异步消息之Executor Framwork]
+* [Android异步消息之Loader]
+
+### Android 网络篇
+* [Volley官方文档翻译](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/open_source/volley%E6%96%87%E6%A1%A3%E7%BF%BB%E8%AF%91.md)
+* [Volley源码解析]()
+* [使用Volley进行Https请求]()
+* [使用Volley进行图片上传]()
### Android开发中常用的功能
* [Android_APK全量更新策略](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/apk%E6%9B%B4%E6%96%B0%E7%AD%96%E7%95%A5.md)
* [拍照,相册及裁剪Android N适配](https://github.com/showdy/Android_Note/blob/master/showdy_note/android/strategy/%E4%BD%BF%E7%94%A8%E7%B3%BB%E7%BB%9F%E7%9B%B8%E5%86%8C%E5%9B%BE%E7%89%87%E6%88%96%E6%8B%8D%E7%85%A7%E5%B9%B6%E8%A3%81%E5%89%AA%E4%B9%8BAndroid_N%E9%80%82%E9%85%8D.md)
-
-### Android开发之网络篇
-
-
-
-### Android Drawable篇
-* [ShapeDrawable]
-* [LayerDrawable]
-* [StateListDrawable]
-* [LevelListDrawable]
-* [TransitionDrawable]
-* [InsetDrawable]
-* [ScaleDrawable]
-* [ClipDrawable]
-* [自定义Drawable]
diff --git a/showdy_note/android/Android_BroadcastReceiver.md b/showdy_note/android/Android_BroadcastReceiver.md
new file mode 100644
index 0000000..0193676
--- /dev/null
+++ b/showdy_note/android/Android_BroadcastReceiver.md
@@ -0,0 +1,185 @@
+### Android Broadcast
+
+#### Broadcast使用场景
+
+Android广播分为两个方面:广播发送者和广播接受者.通常情况下,BroadcastRecevier指广播接受者,广播作为Android组件之间的通讯方式,使用场景有:
+
+* 同一个APP内部的同一个组件类的消息通讯(单线程或者多个线程)
+* 同一个APP内部不同的组件之间的消息通讯(单个进程)
+* 同一个APP具有多个进程不同组件之间的消息通讯
+* 不同APP之间的组件之间的通讯
+* Android系统在特定情况下与APP之间的通讯.
+
+#### Broadcast实现的基本流程为:
+* 广播接受者BroadcastRecevier通过Binder机制向AMS(Activity manager Service)进行注册
+* 广播发送者通过Binder机制向AMS发送广播
+* AMS查找符合条件(intentFilter/permission)的BroadcastRecevier,将广播发送给ReceiverDispatcher,Dispatcher将广播发送到BroadcastReceiver(一般情况是Activity)的消息循环队列中;
+* 消息循环执行此广播,回调到BoradcastReceiver中的onReceiver()方法中.
+
+
+#### 广播注册方式
+
+* 静态注册:
+
+```xml
+
+
+
+
+```
+
+其中属性:
+
+* android:exported: 此广播能否接受其他APP发出的广播,这个属性默认值由intent-filter决定,如果有intent-filter,默认值为true,否则为false(Activity/Service中同样适用).
+* android:name: 广播接受者名
+* android:permission: 如果设置,具有相同权限的广播发送的广播才能被此接受者接受.
+* android:process: 广播接受者所处在的进程,默认为app.
+
+```java
+
+
+
+
+
+
+
+
+
+```
+
+* 动态注册:
+
+> 动态注册广播对使用的Context要注意,因为广播接受者的存在取决于注册的context,如果是Activity,广播在当前Activity中有效,如果是Application context则与App应用生命周期相同.
+
+ ```java
+
+ registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
+
+ registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
+
+ ```
+
+#### 广播发送及其广播类型
+* 广播的类型:
+ * Normal Broadcast 普通广播
+ * Ordered Broadcast 有序广播
+ * Sticky Broadcast 粘性广播(api21中废弃)
+ * System Broadcat 系统广播
+ * Local Broadcast APP内部广播
+
+* 广播发送的方式:
+ * `sendOrderedBroadcast(Intent, String)` 发送有序广播
+ * `sendBroadcast(Intent) ` 发送普通广播
+ * `LocalBroadcastManager.sendBroadcast ` 发送应用内广播
+
+#### 不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型
+
+* 对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;
+
+* 对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;
+
+* 对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。
+
+> 注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。
+
+#### 如何在广播接收者onReceiver中进行耗时操作
+
+广播接收者有生命周期,但是很短,当onReceiver()执行完毕,他生命周期就结束了.这次BroadcastRece已经不处于active状态,被系统杀掉的几率很高.如果此时去开线程进行异步超过或者打开Dialog都还没达到相应的效果就被系统杀掉,因为这个Receiver组件在运行,但是只是一个执行完毕的空进程.这情况下可以使用下面方法,来保持Receiver处于active状态,即便系统想要快速结束receive,也可以把操作移动其他线程防止主线程卡顿.
+
+* goAsync()
+* JobService()
+
+```java
+
+ public class MyBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = "MyBroadcastReceiver";
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ final PendingResult pendingResult = goAsync();
+ AsyncTask asyncTask = new AsyncTask() {
+ @Override
+ protected String doInBackground(String... params) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Action: " + intent.getAction() + "\n");
+ sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
+ Log.d(TAG, log);
+ // Must call finish() so the BroadcastReceiver can be recycled.
+ pendingResult.finish();
+ return data;
+ }
+ };
+ asyncTask.execute();
+ }
+ }
+
+
+```
+
+
+#### 广播的安全隐患以及相应的措施
+
+Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:
+
+* 1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
+
+* 2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
+
+无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
+
+* 1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
+
+* 2.在广播发送和接收时,都增加上相应的permission,用于权限验证;
+
+* 3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
+
+App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
+
+相比于全局广播,App应用内广播优势体现在:1.安全性更高;2.更加高效。
+
+为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。
+
+```java
+
+ //registerReceiver(mBroadcastReceiver, intentFilter);
+ //注册应用内广播接收器
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ //unregisterReceiver(mBroadcastReceiver);
+ //取消注册应用内广播接收器
+ localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
+
+ Intent intent = new Intent();
+ intent.setAction(BROADCAST_ACTION);
+ intent.putExtra("name", "qqyumidi");
+ //sendBroadcast(intent);
+ //发送应用内广播
+ localBroadcastManager.sendBroadcast(intent);
+
+```
+
+### 面试题:
+
+1. 广播中如何进行耗时操作?(Service/Notification)
+ * goAsync()
+ * JobService
+
+2. 广播是否可以开启Activity?
+
+ 广播启动activity很可能影响用户体验,何况有时接受者还不止一个,可以考虑使用Notification.
+
+3. 广播来更新界面是否合适?
+
+ 如果不是频繁更新刷新,可以是广播来达到效果.对于频繁地刷新动作,不要使用广播,广播发送和接收使用具有一定的代价,他的传输是通过Binder机制实现,那么系统会为广播做进程之间通讯做准备很好性能,另外,广播的接收具有一定的延时性,可能导致卡顿(Binder传输).
+
+4. 有时候基于数据安全考虑,我们想发送广播只有自己(本进程)能接收到,那么该如何去做呢?如果不使用LocalBroadcastManger,该怎么实现?
+
+ 可能使用Handler,往主线程的消息池(Message Queue)发送消息,只有主线程的Handler可以分发处理它,广播发送的内容是一个Intent对象,我们可以直接用Message封装一下,留一个和sendBroadcast一样的接口。在handleMessage时把Intent对象传递给已注册的Receiver。
diff --git "a/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円__Service.md" "b/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円__Service.md"
index 450842b..deb69c1 100644
--- "a/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円__Service.md"
+++ "b/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円__Service.md"
@@ -11,6 +11,8 @@
* `startService(Intent service)`,通过intent值来指定启动哪个Service,可以直接指定目标Service的名,也可以通过Intent的action属性来启动设置了相应action属性的Service,使用这种方式启动的Service,当启动它的Activity被销毁时,是不会影响到它的运行的,这时它仍然继续在后台运行它的工作。直至调用`StopService(Intent service)`方法时或者是当系统资源非常紧缺时,这个服务才会调用onDestory()方法停止运行。所以这种Service一般可以用做,处理一些耗时的工作。
* 四大组件默认都是和activity运行在同一个主线程中的,那就是说activity通过startservice方法启动一个服务后,被启动的服务和activity都是在同一个线程中的。所以当我主动销毁了这个activity,但是他所在的线程还是存在的,只不过是这个activity他所占用的资源被释放掉了,这个activity所在的主线程只有当android内存不足才会被杀死掉,否则一般的情况下这个activity所在的应用程序的线程始终存在,也就是这个activity所启动的服务也会一直运行下去。
#####Service
+```java
+
public class LifeService extends Service {
private static final String TAG = "LifeService";
@@ -31,7 +33,11 @@
super.onDestroy();
}
}
+```
+
##### Activity
+```java
+
public class MainActivity extends AppCompatActivity {
@Override
@@ -51,6 +57,7 @@
stopService(intent);
}
}
+```
##### 运行结果:

@@ -60,6 +67,8 @@
* bindService开启的服务,可以调用到服务中的方法.
* 启动的LifeService是和MainActivity在同一个进程里的,因为在注册服务时,没有配置它的android:process = "xxxx" 属性。
##### Service
+```java
+
public class LifeService extends Service {
private static final String TAG = "LifeService";
@@ -107,7 +116,11 @@
Log.d(TAG, "服务里的方法被调用了");
}
}
+```
+
##### Activity
+```java
+
public class MainActivity extends AppCompatActivity {
private ServiceConnection conn;
@@ -149,6 +162,7 @@
mBinder.callMethodInService();
}
}
+```
##### 运行结果

@@ -163,6 +177,8 @@
* 混合方式开启服务: 保证服务后台长期运行, 还能调用服务中的方法.

##### Service
+```java
+
public class LifeService extends Service {
private static final String TAG = "LifeService";
@@ -214,8 +230,11 @@
}
}
+```
##### Activity
+```java
+
public class MainActivity extends AppCompatActivity {
private ServiceConnection conn;
private LifeService.Mybind mBinder;
@@ -264,7 +283,8 @@
mBinder.callMethodInService();
}
}
-
+```
+
##### 运行结果

@@ -277,18 +297,28 @@
#### 接口
> 利用接口屏蔽方法内部实现的细节, 只暴露需要暴露的方法.
##### IService
+```java
+
public interface IService {
void callMethodInService();
}
+```
+
##### Service
+```java
+
private class Mybind extends Binder implements IService {
public void callMethodInService() {
methodInService();
}
}
+```
+
##### Activity
- private class MyServiceConnection implements ServiceConnection {
+```java
+
+ private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -300,14 +330,222 @@
}
}
+```
-### Service组件的三种通讯方式:
-* `startService`
-* `bindService`
-* `AIDL(android interface definition language)`
+### IntentService 类
+Service 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务也许是最好的选择。
+
+IntentService 执行以下操作:
+
+* 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
+* 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
+* 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
+* 提供 onBind() 的默认实现(返回 null)。
+* 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
+
+综上所述,您只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)
+
+```java
+
+ public class HelloIntentService extends IntentService {
+
+ /**
+ * A constructor is required, and must call the super IntentService(String)
+ * constructor with a name for the worker thread.
+ */
+ public HelloIntentService() {
+ super("HelloIntentService");
+ }
+
+ /**
+ * The IntentService calls this method from the default worker thread with
+ * the intent that started the service. When this method returns, IntentService
+ * stops the service, as appropriate.
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ // Normally we would do some work here, like download a file.
+ // For our sample, we just sleep for 5 seconds.
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // Restore interrupt status.
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+```
+您只需要一个构造函数和一个 onHandleIntent() 实现即可。如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。
+
+### 执行多线程耗时操作Service
+
+正如上一部分中所述,使用 IntentService 显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展 Service 类来处理每个 Intent。
+
+为了便于比较,以下提供了 Service 类实现的代码示例,该类执行的工作与上述使用 IntentService 的示例完全相同。也就是说,对于每个启动请求,它均使用工作线程执行作业,且每次仅处理一个请求。
+
+```java
+
+ public class HelloService extends Service {
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+
+ // Handler that receives messages from the thread
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ // Normally we would do some work here, like download a file.
+ // For our sample, we just sleep for 5 seconds.
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // Restore interrupt status.
+ Thread.currentThread().interrupt();
+ }
+ // Stop the service using the startId, so that we don't stop
+ // the service in the middle of handling another job
+ stopSelf(msg.arg1);
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ // Start up the thread running the service. Note that we create a
+ // separate thread because the service normally runs in the process's
+ // main thread, which we don't want to block. We also make it
+ // background priority so CPU-intensive work will not disrupt our UI.
+ HandlerThread thread = new HandlerThread("ServiceStartArguments",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+
+ // Get the HandlerThread's Looper and use it for our Handler
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
+
+ // For each start request, send a message to start a job and deliver the
+ // start ID so we know which request we're stopping when we finish the job
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ mServiceHandler.sendMessage(msg);
+
+ // If we get killed, after returning from here, restart
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // We don't provide binding, so return null
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
+ }
+ }
+```
+
+正如您所见,与使用 IntentService 相比,这需要执行更多工作。
-### Service执行耗时操作
-#### IntentService
+但是,因为是由您自己处理对 onStartCommand() 的每个调用,因此可以同时执行多个请求。此示例并未这样做,但如果您希望如此,则可为每个请求创建一个新线程,然后立即运行这些线程(而不是等待上一个请求完成)。
+
+请注意,onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:
+
+* **`START_NOT_STICKY`**
+
+ 如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
+* **`START_STICKY`**
+
+ 如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
+* **`START_REDELIVER_INTENT`**
+
+ 如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
+
+### 前台服务
+
+```java
+
+ public class ForegroundService extends Service {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ showNotification();
+ }
+
+ private void showNotification() {
+
+ //创建点击跳转Intent
+ Intent inten = new Intent(this, MainActivity.class);
+ //创建任务栈Builder
+ TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
+ taskStackBuilder.addParentStack(MainActivity.class);
+ taskStackBuilder.addNextIntent(inten);
+ PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ //创建通知详细信息
+ Notification notification = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle("foreground service")
+ .setContentText("show details news")
+ .setWhen(System.currentTimeMillis())
+ .setContentIntent(pendingIntent)
+ .build();
+
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(0, notification);
+ startForeground(0,notification);
+
+ }
+ }
+
+```
+
+### 系统服务
+
+
+系统服务提供了很多便捷服务,可以查询Wifi、网络状态、查询电量、查询音量、查询包名、查询Application信息等等等相关多的服务,具体大家可以自信查询文档,这里举例几个常见的服务
+
+1. 判断Wifi是否开启
+
+ ```java
+
+ WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
+ boolean enabled = wm.isWifiEnabled();
+ ```
+
+2. 获取系统最大音量
+
+
+ ```java
+
+ AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
+ int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
+ ```
+3. 获取当前音量
+
+ ```java
+
+ AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
+ int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);
+ ```
+4. 判断网络是否有连接
+
+ ```java
+
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ boolean isAvailable = info.isAvailable();
+ ```
+### AIDL跨进程服务
-### service服务运行在ForeGground
\ No newline at end of file
+### AccessibilityService无障碍服务
diff --git "a/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円344円271円213円Activity.md" "b/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円344円271円213円Activity.md"
index fcbdfdc..41e5460 100644
--- "a/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円344円271円213円Activity.md"
+++ "b/showdy_note/android/Android345円233円233円345円244円247円347円273円204円344円273円266円344円271円213円Activity.md"
@@ -1,60 +1,157 @@
-### Activity
-#### 和Activity生命周期有关的几个问题:
-1. onSaveInstanceState方法在Activity的哪两个生命周期方法之间调用?
-
-2. 弹出一个Dialog时,onPause会调用吗?什么情况下会,什么情况下不会?
-
-3. 横竖屏切换的时候,生命周期方法是如何调用的?如何进行配置呢?
+###Activity的生命周期
-4. Activity调用了onDestory方法,就会在Activity的任务栈消失吗?
-
-5. 永久性质的数据,应该在哪个生命周期方法中保存?
-
-6. 在onCreate或者onRestoreInstance方法中恢复数据时,有什么区别?
-#### Activity的生命周期

-
* Activity的创建和销毁
- * onCreate();
- > 执行Activity某些基本设置的一些代码,比如声明用户界面(xml文件),定义成员变量,配置某些UI等等,oncreate一般是必须要实现的.
- * onDestroy();
- > 一般不需要实现,因为本地类引用与Activity一同销毁,并且您的Activity应在 onPause() 和 onStop() 期间执行大多数清理操作。 但是,如果您的Activity包含您在 onCreate() 期间创建的后台线程或其他如若未正确关闭可能导致内存泄露的长期运行资源,应在 onDestroy() 期间终止它们.
+ * **`onCreate()`**: 执行Activity某些基本设置的一些代码,比如声明用户界面(xml文件),定义成员变量,配置某些UI等等,oncreate一般是必须要实现的.
+
+ * **`onDestroy()`**:一般不需要实现,因为本地类引用与Activity一同销毁,并且您的Activity应在 onPause() 和 onStop() 期间执行大多数清理操作。 但是,如果您的Activity包含您在 onCreate() 期间创建的后台线程或其他如若未正确关闭可能导致内存泄露的长期运行资源,应在 onDestroy() 期间终止它们.
* Activity开始和停止
- * onStart();
- > onStop() 方法应基本清理所有Activity的资源,将需要在Activity重新开始时重新实例化它们。但是,还需要在Activity初次创建时重新实例化它们(没有Activity的现有实例)。出于此原因,应经常使用 onStart() 回调方法作为 onStop() 方法的对应部分,因为系统会在它创建您的Activity以及从停止状态重新开始Activity时调用 onStart() 。
- * onStop();
- > Activity收到 onStop() 方法的调用时,它不再可见,并且应释放几乎所有用户不使用时不需要的资源。 一旦您的Activity停止,如果需要恢复系统内存,系统可能会销毁该实例。 在极端情况下,系统可能会仅终止应用进程,而不会调用Activity的最终 onDestroy() 回调,因此您使用 onStop() 释放可能泄露内存的资源非常重要。尽管 onPause() 方法在 onStop()之前调用,您应使用 onStop() 执行更大、占用更多 CPU 的关闭操作,比如向数据库写入信息。
+ * **`onStart()`**: onStop() 方法应基本清理所有Activity的资源,将需要在Activity重新开始时重新实例化它们。但是,还需要在Activity初次创建时重新实例化它们(没有Activity的现有实例)。出于此原因,应经常使用 onStart() 回调方法作为 onStop() 方法的对应部分,因为系统会在它创建您的Activity以及从停止状态重新开始Activity时调用 onStart() 。
+
+ * **`onStop()`**: Activity收到 onStop() 方法的调用时,它不再可见,并且应释放几乎所有用户不使用时不需要的资源。 一旦您的Activity停止,如果需要恢复系统内存,系统可能会销毁该实例。 在极端情况下,系统可能会仅终止应用进程,而不会调用Activity的最终 onDestroy() 回调,因此您使用 onStop() 释放可能泄露内存的资源非常重要。尽管 onPause() 方法在 onStop()之前调用,您应使用 onStop() 执行更大、占用更多 CPU 的关闭操作,比如向数据库写入信息。
+
* Activity运行和暂停
- * onResume();
- > Activity获得焦点,实现onResume()初始化在 onPause() 期间释放的组件并且执行每当Activity进入"继续"状态时必须进行的任何其他初始化操作(比如开始动画和初始化只在Activity具有用户焦点时使用的组件)。
- * onPause();
- > 当系统为您的Activity调用 onPause() 时,它从技术角度看意味着您的Activity仍然处于部分可见状态,但往往说明用户即将离开Activity并且它很快就要进入"停止"状态。 您通常应使用 onPause() 回调:
+ * **`onResume():`** Activity获得焦点,实现onResume()初始化在 onPause() 期间释放的组件并且执行每当Activity进入"继续"状态时必须进行的任何其他初始化操作(比如开始动画和初始化只在Activity具有用户焦点时使用的组件)。
+
+ * **`onPause()`**: 当系统为您的Activity调用 onPause() 时,它从技术角度看意味着您的Activity仍然处于部分可见状态,但往往说明用户即将离开Activity并且它很快就要进入"停止"状态。 您通常应使用 onPause() 回调:
* 停止动画或其他可能消耗 CPU 的进行之中的操作。
* 提交未保存的更改,但仅当用户离开时希望永久性保存此类更改(比如电子邮件草稿)。
* 释放系统资源,比如广播接收器、传感器手柄(比如 GPS) 或当您的Activity暂停且用户不需要它们时仍然可能影响电池寿命的任何其他资源。
* 但是要注意: **不得使用** onPause() 永久性存储用户更改(比如输入表格的个人信息)。 只有在您确定用户希望自动保存这些更改的情况(比如,电子邮件草稿)下,才能在 onPause()中永久性存储用户更改。但您应避免在 onPause() 期间执行 CPU 密集型工作,比如**向数据库写入信息**,因为这会拖慢向下一Activity过渡的过程(您应改为在 onStop()期间执行高负载关机操作。
-
+
+
* 数据存储与恢复:
-
+
+ 
当系统开始停止您的Activity时,它会 调用 onSaveInstanceState() (1),因此,您可以指定您希望在 Activity 实例必须重新创建时保存的额外状态数据。如果Activity被销毁且必须重新创建相同的实例,系统将在 (1) 中定义的状态数据同时传递给 onCreate() 方法(2) 和 onRestoreInstanceState() 方法(3)。
- * onSaveInstanceState()
- * 处于onstop()方法前, 但是与onpause()没有多少必然的联系
- * onRestoreInstanceState()
- * 处于onresume前,
- * 您可以选择实现系统在 onStart() 方法之后调用的 onRestoreInstanceState(),而不是在onCreate() 期间恢复状态。 系统只在存在要恢复的已保存状态时调用 onRestoreInstanceState() ,因此您无需检查 Bundle 是否为 null:
+ * **`onSaveInstanceState()`**:处于onstop()方法前, 但是与onpause()没有多少必然的联系,可能在onPause()前调用,也可能在onStop()前调用
+
+ * **`onRestoreInstanceState()`**: 处于onresume前,您可以选择实现系统在 onStart() 方法之后调用的 onRestoreInstanceState(),而不是在onCreate() 期间恢复状态。 系统只在存在要恢复的已保存状态时调用 onRestoreInstanceState() ,因此您无需检查 Bundle 是否为 null:
* 还有一些的其他的生命周期方法:
- * onPostCreate()
- > 当activity建立后调用,即在onstart()和onRestoreInstanceState()完成后调用
- * onPostResume()
- > onCreate->onStart->onPostCreate->onResume->onPostResume
+ * **`onPostCreate()`**: 当activity建立后调用,即在onstart()和onRestoreInstanceState()完成后调用
+ * **`onPostResume()`**: onCreate->onStart->onPostCreate->onResume->onPostResume
+
+#### 和Activity生命周期有关的几个问题:
+
+1. onSaveInstanceState方法在Activity的哪两个生命周期方法之间调用?
+
+ **onSaveInstanceState()的调用与onPause()的调用没有先后之分,可能在onStop()前,也可能在onPause()前,但是可以保证一定在onStop()之前.**
+
+2. 弹出一个Dialog时,onPause会调用吗?什么情况下会,什么情况下不会?
+
+ **首先,弹出的是本Activity的Dialog,并不会有任何生命周期方法调用。Dialog是一个View,它本身就依附在Acitivty上,可以理解为是属于本Activity的,所以它的焦点也自然是本Activity的焦点,自然不会有什么生命周期方法调用了。如果其他Activity的Dialog弹出了,onPause才会调用。**
+
+3. 横竖屏切换的时候,生命周期方法是如何调用的?如何进行配置呢?
+
+ **横竖屏切换时,如果不做任何配置,生命周期方法的回调顺序为:
+ `onPause–onSaveInstanceState–onStop–onDestory–onCreate–onStart–onResume`
+ 也就是说Activity被销毁并重建了。如果不想这样可以在清单文件中的Activity添加一行配置:
+ `android:configChanges="keyboardHidden|orientation|screenSize"`**
+
+4. Activity调用了onDestory方法,就会在Activity的任务栈消失吗?
+
+ **如果是点击back键销毁Activity,相当于调用了Activity的finish(),将Activity从任务栈中退出,再调用onDestroy(),如果Activity是意外被销毁,直接调用onDestroy(),Activity是不会从任务栈中清除的.**
+
+5. 永久性质的数据,应该在哪个生命周期方法中保存?
+
+ **由于系统在紧急情况必须内存,onPause(),onStop(),onDestroy()三个方法,唯一能保证调用的只有onPause()方法,其他两个方法可能不会调用,所以在此方法中做重要数据的持久化存储,但是要注意的是,onPause()是非常轻量级的,不能做耗时操作,而由于无法保证系统会调用 onSaveInstanceState(),只应利用它来记录 Activity 的瞬态(UI 的状态)而切勿使用它来存储持久性数据**
+
+6. 在onCreate或者onRestoreInstance方法中恢复数据时,有什么区别?
+
+ 区别: onRestoreInstanceState()一旦被调用,其参数Bundle saveInstance一定是有值的,我们不需要额外判断是否为null,但是onCreate()却不行,onCreate()正常启动,其参数Bundle saveInstance()为null,这个需要额外去判断.官方建议采用onResotreInstanceState()去恢复数据.
+
+ **Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。onRestoreInstanceState()在onStart() 和 onPostCreate(Bundle)之间调用。**
+
+7. 如果一个Activity在用户可见时才处理某个广播,不可见时注销掉,那么应该在哪两个生命周期的回调方法去注册和注销BroadcastReceiver呢?
+ **应在onStart()中注册,而在onStop()中注销;一般情况下会选择在onStop()和onDestroy()中进行资源释放的操作, onPause() 调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验。.**
+
+8. 如果有一些数据在Activity跳转时(或者离开时)要保存到数据库,那么你认为是在onPause好还是在onStop执行这个操作好呢?
+
+ **Activity A启动 Activity B必须要经历的生命周期为: Activity A先调用onPause(),然后Activity B调用onCreate(),onStart(),onResume(),接着Activity A再调用onStop().故而,Activity A在跳转前需要先在onPause()中将数据持久化存储,以便Activity B可以调用.**
+
+
+### Activity任务栈
+
+* 任务栈: 指在执行特定作业时与用户交互的一系列Activity,这些Activity按照各自的打开顺序排列在堆栈(返回栈),任务栈是一种"先进后出"的栈结构.
+
+
+
+#### Activity的启动模式:
+
+* **`standard`**:标准模式.系统默认的启动模式,每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在.谁启动了这Activity,那么这个Activity就运行在那个Activity所在的栈中.如果启动Activity时传入ApplicationContext会报错,因为非Activity类型Context并没有所谓的栈,解决这个问题需要给待启动Activity设置`FLAG_ACTIVITY_NEW_TASK`标记位,相当于以singleTask模式启动.
+
+* **`singleTop`**: 栈顶复用模式.如果新Activity已经为栈顶,那么Activity不会被重新创建,同时onNewIntent()会被调用,但是Activity的onCreate()和onStart()不会被调用;如果新的Activity存在但是不在栈顶,那么Activity仍然会重新创建.
+
+* **`singleTask`**: 栈内复用模式.只要Activity在一个栈中存在,多次启动Activity都不会重新创建实例,和singleTop一样,系统会调用onNewIntent().但是singleTask模式具有clearTop的效果,会导致栈内待启动Activity上面的Activity被出栈.
+
+* **`singleInstance`**: 单例模式.singleInstance是一种加强版的singleTask模式,具有此启动模式Activity单独存在一个栈内复用.
+
+
+#### 启动模式应用场景:
+
+* 假如目前有2个任务栈,前台任务栈有12,而后台任务栈有XY,假设XY的启动模式均为singleTask,那么启动Y时,整个后台任务栈都会被切换到前台,这时后退列表就变为12XY,当点击back键时,列表中Activity会一一出栈.
+
+
+
+* 但是启动的不是Y而是X,情况就不一样了.
+
+
+
+
+
+#### 任务相关性
+
+* **`TaskAffinity`**: 任务相关性.
+
+ 这个参数标识一个Activity所需要的任务栈的名称,默认情况为应用包名.当然,可以为每个Activity指单独的TaskAffinity属性,属性名需和包名不同,否则没有意义.TaskAffinity属性主要和singleTask启动模式以及allowTaskReparenting属性配对使用,其他情况没有意义.另外任务栈分为前台任务栈和后台任务栈,后台任务栈中所有Activity处于暂停状态.
+
+ 当TaskAffinity与singleTask启动模式使用时, 他具有该模式Activity目前任务栈的名称,待启动的Activity会运行在名字和TaskAffinity相同的任务栈.
+
+ 当TaskAffinity与allowTaskReparenting结合使用时,情况比较复杂,会产生特殊的效果.
+
+
+#### 清理任务栈
+
+如果用户长时间离开任务,则系统会清除所有 Activity 的任务,根 Activity 除外。 当用户再次返回到任务时,仅恢复根 Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。可以使用下列几个 Activity 属性修改此行为:
+
+* **`alwaysRetainTaskState`**
+
+ 如果在任务的根 Activity 中将此属性设置为 "true",则不会发生刚才所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中。
+* **`clearTaskOnLaunch`**
+
+ 如果在任务的根 Activity 中将此属性设置为 "true",则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 换而言之,它与 alwaysRetainTaskState 正好相反。 即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。
+* **`finishOnTaskLaunch`**
+
+ 此属性类似于 clearTaskOnLaunch,但它对单个 Activity 起作用,而非整个任务。 此外,它还有可能会导致任何 Activity 停止,包括根 Activity。 设置为 "true" 时,Activity 仍是任务的一部分,但是仅限于当前会话。如果用户离开然后返回任务,则任务将不复存在。
+
+
+#### Activity的Flags
+
+* **`FLAG_ACTIVITY_NEW_TASK`**:
+
+ 该标记的作用是为Activity指定"singleTask"启动模式,效果和XML中指定一样. service中启动activity需要用到此标记.
+
+* **`FLAG_ACTIVITY_SINGLE_TOP`**:
+
+ 该标记的作用是为Activity指定"singleTop"启动模式,效果和XML中指定一样.
+
+* **`FLAG_ACTIVITY_CLEAR_TOP`**:
+
+ 具有此标记的Activity,当启动时,在同一个任务栈中所有位于他上面的Activity都要被清除出栈,此标记一般与singleTask启动模式一起使用.在这种情况下,若被启动Activity的实例已经存在,那么系统会调用onNewIntent.如果被启动Activity采用的standard模式,那么连同他之上的activity都要出栈,系统会创建新的activity实例放入栈顶.
+
+
+
+### 参考
+* [**ANDROID INSTANCESTATE**](http://stormzhang.com/android/2014/02/21/android-instancestate/)
+* [**面试题: 怎么理解Activity的生命周期?**](http://www.jianshu.com/p/ae6e1d93cc8e)
+* [**Activity的生命周期,你足够了解吗?**](http://blog.csdn.net/melodev/article/details/52075141)
-### Activity任务栈
\ No newline at end of file
diff --git "a/showdy_note/android/AsyncTask344円275円277円347円224円250円345円217円212円346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/showdy_note/android/AsyncTask344円275円277円347円224円250円345円217円212円346円272円220円347円240円201円345円210円206円346円236円220円.md"
new file mode 100644
index 0000000..b18f509
--- /dev/null
+++ "b/showdy_note/android/AsyncTask344円275円277円347円224円250円345円217円212円346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -0,0 +1,465 @@
+## AsyncTask
+
+### AsyncTask使用注意事项:
+* `AsyncTask`只能被执行(`execute`方法)一次,多次执行将会引发异常.
+* 任务的取消只能打了一个标记,并不是真正取消,需要手动去掉用;
+
+### 构建AsyncTask抽象类的三个泛型参数;
+* `AsyncTask`是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:
+ * `Params`:启动任务时输入的参数类型.
+ * `Progress`:后台任务执行中返回进度值的类型.
+ * `Result`:后台任务执行完成后返回结果的类型.
+### AsyncTask四个方法:
+* `onPreExecute()`;
+ > 执行任务之前,一般做变量的初始化,或者ui的隐藏或者显示;该方法无参数
+* `doInBackground(T...params)`;//对应泛型参数params
+> 后台执行任务,属于子线程,当调用publishProgress()方法时,会触发系统自动调用onProgressUpdate();
+* `onProgressUpdate(T... values)`;//对应泛型参数progress
+> 用于更新进度
+* `onPostExecute(T...result)`;//对应泛型参数result
+> 任务结束后调用,一般处理返回的结果,或者改变ui显示.
+### AsyncTask历史版本问题:
+
+在Android1.6之前,AsyncTask是串行执行任务,Android1.6时候AsyncTask开始采用线程池处理并行任务,但是从Android3.0开始,为了避免AsyncTask带来的并发错误,又采用线程池串行执行任务,尽管此处,Android3.0后,AsyncTask还是支持并发执行任务,不过需要调用`executeOnExecutor()`方法.
+
+### 加载网络图片的实例:
+
+```java
+ public class ImageActivity extends Activity {
+ private ImageView imageView ;
+ private ProgressBar progressBar ;
+ private static String URL = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.image);
+ imageView = (ImageView) findViewById(R.id.image);
+ progressBar = (ProgressBar) findViewById(R.id.progressBar);
+ //通过调用execute方法开始处理异步任务.相当于线程中的start方法.
+ new MyAsyncTask().execute(URL);
+ }
+
+ class MyAsyncTask extends AsyncTask {
+
+ //onPreExecute用于异步处理前的操作
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ //此处将progressBar设置为可见.
+ progressBar.setVisibility(View.VISIBLE);
+ }
+
+ //在doInBackground方法中进行异步任务的处理.
+ @Override
+ protected Bitmap doInBackground(String... params) {
+ //获取传进来的参数
+ String url = params[0];
+ Bitmap bitmap = null;
+ URLConnection connection ;
+ InputStream is ;
+ try {
+ connection = new URL(url).openConnection();
+ is = connection.getInputStream();
+ //为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
+ Thread.sleep(3000);
+ BufferedInputStream bis = new BufferedInputStream(is);
+ //通过decodeStream方法解析输入流
+ bitmap = BitmapFactory.decodeStream(bis);
+ is.close();
+ bis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ //onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ super.onPostExecute(bitmap);
+ //隐藏progressBar
+ progressBar.setVisibility(View.GONE);
+ //更新imageView
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+ }
+```
+
+
+
+### 模拟加载进度条:
+
+```java
+
+ public class ProgressActivity extends Activity{
+ private ProgressBar progressBar;
+ private MyAsyncTask myAsyncTask;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.progress);
+ progressBar = (ProgressBar) findViewById(R.id.progress);
+ myAsyncTask = new MyAsyncTask();
+ //启动异步任务的处理
+ myAsyncTask.execute();
+ }
+
+ //AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (myAsyncTask != null && myAsyncTask.getStatus() == Status.RUNNING) {
+ //cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
+ myAsyncTask.cancel(true);
+ }
+ }
+
+ class MyAsyncTask extends AsyncTask{
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ super.onProgressUpdate(values);
+ //通过publishProgress方法传过来的值进行进度条的更新.
+ progressBar.setProgress(values[0]);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ //使用for循环来模拟进度条的进度.
+ for (int i = 0;i < 100; i ++){ + //如果task是cancel状态,则终止for循环,以进行下个task的执行. + if (isCancelled()){ + break; + } + //调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新. + publishProgress(i); + try { + //通过线程休眠模拟耗时操作 + Thread.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + } + } + } +``` + +------------------------------------------------------------------------ + +### AsyncTask的源码解析: + +AsyncTask的使用分两步: + +```java + + myAsyncTask = new MyAsyncTask(); + //启动异步任务的处理 + myAsyncTask.execute(); + +``` + +从执行起步任务的起始点开始, 进入execute()方法: + +```java + + public final AsyncTask execute(Params... params) {
+ return executeOnExecutor(sDefaultExecutor, params);
+ }
+
+ @MainThread
+ public final AsyncTask 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(); //执行前准备工作
+ mWorker.mParams = params;//赋值
+ exec.execute(mFuture); //执行异步任务
+ return this;
+ }
+
+```
+一个异步进入,先判断该任务是否在执行,或者执行完毕,如果是,则抛出异常,说明一个任务只能被执行一次.否则,将任务状态改变为RUNNING,并将传进来的params传给mWorker, 那么mWorker是什么?,继续看源码:
+
+
+```java
+
+ public AsyncTask() {
+ mWorker = new WorkerRunnable() {
+ public Result call() throws Exception {
+ mTaskInvoked.set(true);
+ Result result = null;
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ //noinspection unchecked
+ result = doInBackground(mParams);
+ Binder.flushPendingCommands();
+ } catch (Throwable tr) {
+ mCancelled.set(true);
+ throw tr;
+ } finally {
+ postResult(result);
+ }
+ return result;
+ }
+ };
+
+ mFuture = new FutureTask(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);
+ }
+ }
+ };
+ }
+
+ private static abstract class WorkerRunnable implements Callable {
+ Params[] mParams;
+ }
+```
+可以看到mWorker在构造方法中完成了初始化工作,因为是个抽象类,就new了一个具体子类,实现call方法,并将原子类变量mTaskInvoked=true,最后调用doInbackGround(mParams),并将返回的result作为参数给postResult(),在postResult()方法中:
+
+```java
+
+ private Result postResult(Result result) {
+ @SuppressWarnings("unchecked")
+ Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
+ new AsyncTaskResult(this, result));
+ message.sendToTarget();
+ return result;
+ }
+
+```
+在postReuslt()中可以看到,采用异步消息机制,发送一个message.what为MESSAGE_POST_RESULT的消息,将异步执行的结果切换到主线,完成线程的切换工作.
+
+```java
+
+ private static class InternalHandler extends Handler {
+ public InternalHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @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;
+ }
+ }
+ }
+
+```
+
+可以看到Handler使用的是Looper.getMainLooper,说明Handler接收消息发生主线程,收到MESSAGE_POST_RESULT执行finish():
+
+```java
+
+ private void finish(Result result) {
+ if (isCancelled()) {
+ onCancelled(result);
+ } else {
+ onPostExecute(result);
+ }
+ mStatus = Status.FINISHED;
+ }
+
+```
+
+可以看到在finish()对异步任务是否取消做了判断,如果异步任务已经取消,则调用onCancel()方法,否则调用onPostResult(),这里也可以看到当异步任务被取消后, onPostExecute()是不会执行.最后将状态置为FINISHED.构造函数仅仅只是完成了mWorker的初始化工作,并采用FutureTask将mWorker包装了下,并未真正执行,当然在任务执行结束后,会调用postResultNotInvoked(get()),来查看任务是否已经执行:
+
+```java
+
+ private void postResultIfNotInvoked(Result result) {
+ final boolean wasTaskInvoked = mTaskInvoked.get();
+ if (!wasTaskInvoked) {
+ postResult(result);
+ }
+ }
+
+```
+如果mTaskInvoked不为true,则postResult(),但是mWorker初始化的时,就已经将mTaskResult置为true,所以这个方法不会调用.
+
+下面看看 `exec.execute(mFuture); `到底做了什么:
+
+```java
+
+ public final AsyncTask execute(Params... params) {
+ return executeOnExecutor(sDefaultExecutor, params);
+ }
+
+```
+
+知道exec实际上是`sDefaultExecutor`:
+
+```java
+
+ public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
+
+ private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
+
+ private static class SerialExecutor implements Executor {
+ final ArrayDeque mTasks = new ArrayDeque();
+ Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ THREAD_POOL_EXECUTOR.execute(mActive);
+ }
+ }
+ }
+
+```
+
+sDefaultExecutor实际上SerialExecutor的一个实例,其内部维护一个双端队列(数组实现),执行execute(),会调用offer()将任务放入队列尾部, 然后判断mActive否为空,如果为null,则调用scheduleNext()从队列尾部取出一个任务,调用THREAD_POOL_EXECUTOR真正执行任务:
+
+```java
+
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE_SECONDS = 30;
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final BlockingQueue sPoolWorkQueue =
+ new LinkedBlockingQueue(128);
+
+ public static final Executor THREAD_POOL_EXECUTOR; //可以并行执行的线程池
+
+ static {
+ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+ CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+ sPoolWorkQueue, sThreadFactory);
+ threadPoolExecutor.allowCoreThreadTimeOut(true);
+ THREAD_POOL_EXECUTOR = threadPoolExecutor;
+ }
+
+```
+
+线程池最大支持`CUP_COUNT*2+1`的线程并发,加上长度为128的阻塞队列,如果任务的数量为`CUP_COUNT*2+1+128`是否会因为任务数过多而抛出异常,实际上不可能,再来先看下SerialExecutor这个类:
+
+```java
+ private static class SerialExecutor implements Executor {
+ final ArrayDeque mTasks = new ArrayDeque();
+ Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ THREAD_POOL_EXECUTOR.execute(mActive);
+ }
+ }
+ }
+
+
+```
+如果此时有10个任务同时调用execute()方法,第个任务加入队列,mActive=null,从队列尾部取出一个任务,然后交给线程池去执行,然后第二任务入队,但是此时mActive不为null,不会执行scheduleNext()方法,只能等待第一个任务执行完毕,再调用scheduleNext().内部虽然是个线程池,但是确实个串行的线程池.
+
+那么AysncTask是否就不能并发执行任务呢? 其实AsyncTask是可以并发执行任务的,不过要调用
+
+```java
+
+ public final AsyncTask executeOnExecutor(Executor exec, Params... params) {
+ ....
+ }
+
+```
+然后自己传入一个可以并发执行任务的线程池,比如AsyncTask提供的THREAD_POOL_EXECUTOR.在AsyncTaskCompat中:
+
+```java
+
+ public static AsyncTask executeParallel(
+ AsyncTask task,
+ Params... params) {
+ if (task == null) {
+ throw new IllegalArgumentException("task can not be null");
+ }
+
+ if (Build.VERSION.SDK_INT>= 11) {
+ // From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR
+ AsyncTaskCompatHoneycomb.executeParallel(task, params);
+ } else {
+ // Before API 11, all tasks were run in parallel
+ task.execute(params);
+ }
+
+ return task;
+ }
+
+ class AsyncTaskCompatHoneycomb {
+
+ static void executeParallel(
+ AsyncTask task,
+ Params... params) {
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
+ }
+
+ }
+
+```
+
+可以看出在API 11之前调用`execute()`就表示并发执行任务,在API需要调用`executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)`来执行并发任务.
diff --git "a/showdy_note/android/AsyncTask347円232円204円344円275円277円347円224円250円.md" "b/showdy_note/android/AsyncTask347円232円204円344円275円277円347円224円250円.md"
deleted file mode 100644
index 4a20fb5..0000000
--- "a/showdy_note/android/AsyncTask347円232円204円344円275円277円347円224円250円.md"
+++ /dev/null
@@ -1,150 +0,0 @@
-## AsyncTask的基本使用
-
-### AsyncTask使用注意事项:
-* `AsyncTask`只能被执行(`execute`方法)一次,多次执行将会引发异常.
-* 任务的取消只能打了一个标记,并不是真正取消,需要手动去掉用;
-
-### 构建AsyncTask抽象类的三个泛型参数;
-* `AsyncTask`是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:
- * `Params`:启动任务时输入的参数类型.
- * `Progress`:后台任务执行中返回进度值的类型.
- * `Result`:后台任务执行完成后返回结果的类型.
-### AsyncTask四个方法:
-* `onPreExecute()`;
- > 执行任务之前,一般做变量的初始化,或者ui的隐藏或者显示;该方法无参数
-* `doInBackground(T...params)`;//对应泛型参数params
-> 后台执行任务,属于子线程,当调用publishProgress()方法时,会触发系统自动调用onProgressUpdate();
-* `onProgressUpdate(T... values)`;//对应泛型参数progress
-> 用于更新进度
-* `onPostExecute(T...result)`;//对应泛型参数result
-> 任务结束后调用,一般处理返回的结果,或者改变ui显示.
-
-### 加载网络图片的实例:
-
-```java
- public class ImageActivity extends Activity {
- private ImageView imageView ;
- private ProgressBar progressBar ;
- private static String URL = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.image);
- imageView = (ImageView) findViewById(R.id.image);
- progressBar = (ProgressBar) findViewById(R.id.progressBar);
- //通过调用execute方法开始处理异步任务.相当于线程中的start方法.
- new MyAsyncTask().execute(URL);
- }
-
- class MyAsyncTask extends AsyncTask {
-
- //onPreExecute用于异步处理前的操作
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- //此处将progressBar设置为可见.
- progressBar.setVisibility(View.VISIBLE);
- }
-
- //在doInBackground方法中进行异步任务的处理.
- @Override
- protected Bitmap doInBackground(String... params) {
- //获取传进来的参数
- String url = params[0];
- Bitmap bitmap = null;
- URLConnection connection ;
- InputStream is ;
- try {
- connection = new URL(url).openConnection();
- is = connection.getInputStream();
- //为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
- Thread.sleep(3000);
- BufferedInputStream bis = new BufferedInputStream(is);
- //通过decodeStream方法解析输入流
- bitmap = BitmapFactory.decodeStream(bis);
- is.close();
- bis.close();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return bitmap;
- }
-
- //onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- super.onPostExecute(bitmap);
- //隐藏progressBar
- progressBar.setVisibility(View.GONE);
- //更新imageView
- imageView.setImageBitmap(bitmap);
- }
- }
- }
-```
-
-
-
-### 模拟加载进度条:
-
-```java
-
- public class ProgressActivity extends Activity{
- private ProgressBar progressBar;
- private MyAsyncTask myAsyncTask;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.progress);
- progressBar = (ProgressBar) findViewById(R.id.progress);
- myAsyncTask = new MyAsyncTask();
- //启动异步任务的处理
- myAsyncTask.execute();
- }
-
- //AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
- @Override
- protected void onPause() {
- super.onPause();
- if (myAsyncTask != null && myAsyncTask.getStatus() == Status.RUNNING) {
- //cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
- myAsyncTask.cancel(true);
- }
- }
-
- class MyAsyncTask extends AsyncTask{
- @Override
- protected void onProgressUpdate(Integer... values) {
- super.onProgressUpdate(values);
- //通过publishProgress方法传过来的值进行进度条的更新.
- progressBar.setProgress(values[0]);
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- //使用for循环来模拟进度条的进度.
- for (int i = 0;i < 100; i ++){ - //如果task是cancel状态,则终止for循环,以进行下个task的执行. - if (isCancelled()){ - break; - } - //调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新. - publishProgress(i); - try { - //通过线程休眠模拟耗时操作 - Thread.sleep(300); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - return null; - } - } - } -``` - ------------------------------------------------------------------------- - -### AsyncTask的进阶学习: diff --git a/showdy_note/android/BitmapFactory_Options.md b/showdy_note/android/BitmapFactory_Options.md index 1fa0ee7..2761f3e 100644 --- a/showdy_note/android/BitmapFactory_Options.md +++ b/showdy_note/android/BitmapFactory_Options.md @@ -1,9 +1,13 @@ -### inPreferredConfig(建议的配置参数) -**[位图解码](http://blog.csdn.net/ccpat/article/details/46834089)** +## BitmapFactory.options +BitmapFactory.Options类是BitmapFactory对图片进行解码时使用的一个配置参数类,其中定义了一系列的public成员变量,每个成员变量代表一个配置参数。 + +### 图片解码建议配置(inPreferredConfig) +* 参数inpreferredconfig表示图片解码时使用的颜色模式,也就是图片中每个像素颜色的表示方式 * 图片颜色: * 计算机表示一个颜色都需要将颜色对应到一个颜色空间中的某个颜色值,常见的颜色空间为RGB,CMYK等. * JPEG格式支持RGB,CMYK,而PNG支持RGB,此外绝大数显示器只支持RGB颜色的输入,计算机显示一张图片时,如果图片本身不是RGB颜色空间编码,需将其转化为RGB颜色空间的颜色后在显示,所以非RGB显示会有失真. + * 颜色透明度: * 图片包含颜色信息和透明信息,计算机中用一个单独的透明通道表示(Alpha通道),JPEG格式图片不支持透明度,PNG/GIT格式支持透明度. @@ -13,10 +17,10 @@ * inperferredConfig参数 * BitmapFactory.Options类是BitmapFractory对图片进行解码时使用的配置参数类, 其中定义一系列public的成员变量(配置参数),inperferredConfig表示图片解码时使用的颜色模式: * inpreferredConfig参数有四个值: - * ALPHA_8: 每个像素用8个byte存储,存储的是图片的透明值 - * RGB_565:每个像素用16个byte存储,分别为5-R,6-G,5-B通道. - * ARGB-4444:每个像素用16个byte存储,即每个通道用4位表示 - * ARGB_8888:每个像素用32个byte存储,每个通道用8位表示. + * ALPHA_8: 每个像素用占8位,存储的是图片的透明值,占1个字节 + * RGB_565:每个像素用占16位,分别为5-R,6-G,5-B通道,占2个字节 + * ARGB-4444:每个像素占16位,即每个通道用4位表示,占2个字节 + * ARGB_8888:每个像素占32位,每个通道用8位表示,占4个字节 * 图片解码时,默认使用ARGB_8888模式: @@ -25,9 +29,11 @@ * 使用inperferredConfig注意: - * 如果inPreferredConfig不为null,解码器会尝试使用此参数指定的颜色模式来对图片进行解码,如果inPreferredConfig为null或者在解码时无法满足此参数指定的颜色模式,解码器会自动根据原始图片的特征以及当前设备的屏幕位深,选取合适的颜色模式来解码,例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用ARGB_8888来解码。 + * 如果inPreferredConfig不为null,解码器会尝试使用此参数指定的颜色模式来对图片进行解码,如果inPreferredConfig为null或者在解码时无法满足此参数指定的颜色模式,解码器会自动根据**原始图片的特征**以及**当前设备的屏幕位深**,选取合适的颜色模式来解码,例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用ARGB_8888来解码。 * 根据inperferredConfig的解析,就会发现如下bm1,bm2,bm3结果会一样: + ```java + InputStream stream = getAssets().open(file); Options op1 = new Options(); op1.inPreferredConfig = Config.ALPHA_8; @@ -38,6 +44,8 @@ Options op3 = new Options(); op3.inPreferredConfig = Config.ARGB_8888; Bitmap bm3 = BitmapFactory.decodeStream(stream, null, op3); + ``` + * 疑点: 1. 当出现不满足情况时,使用的合适配置是如何选取的? * 1. 如果inPreferredConfig为null,解码时使用的颜色模式会根据图片源文件的类型进行选取,如果图片文件的颜色模式为CMYK,或RGB565,则选取RGB_565。如果是其他类型,则选取ARGB_8888。 * 2. 如果inPreferredConfig指定的选项在解码时无法满足,并不会再根据图片文件的类型来选取合适的选项,而是直接使用ARGB_8888选项来解码。例如,图片源文件为RGB566编码的BMP图片,使用ALPHA_8选项来解码时属于不满足的情况,这时会选取ARGB_8888选项来解码,而不是选取RGB565。和inPreferredConfig为null时选取的"合适的"选项并不相同。 @@ -47,10 +55,7 @@ * 所有情况下ALPHA_8配置都不满足 * 绝大多数情况下RGB565选项都不满足 -### inPremultiplied -### inBitmap -[**Bitmap的内存管理**](http://hukai.me/android-training-course-in-chinese/graphics/displaying-bitmaps/manage-memory.html) - +### 优化Bitmap的内存使用(inBitmap) * 在Android 2.2 (API level 8)以及之前,当垃圾回收发生时,应用的线程是会被暂停的,这会导致一个延迟滞后,并降低系统效率。 从Android 2.3开始,添加了并发垃圾回收的机制, 这意味着在**一个Bitmap不再被引用之后,它所占用的内存会被立即回收**。 * 在Android 2.3.3 (API level 10)以及之前, **一个Bitmap的像素级数据(pixel data)是存放在Native内存空间中的**。 这些数据与Bitmap本身所占内存是隔离的,**Bitmap本身被存放在Dalvik堆中**。我们无法预测在Native内存中的像素级数据何时会被释放,这意味着程序容易超过它的内存限制并且崩溃。 **自Android 3.0 (API Level 11)开始, 像素级数据则是与Bitmap本身一起存放在Dalvik堆中**。 @@ -58,22 +63,53 @@ * 在Android 2.3.3 (API level 10) 以及更低版本上,**推荐使用recycle()方法**。 如果在应用中显示了大量的Bitmap数据,我们很可能会遇到OutOfMemoryError的错误。 recycle()方法可以使得程序更快的释放内存。>Caution:只有当我们确定这个Bitmap不再需要用到的时候才应该使用recycle()。在执行recycle()方法之后,如果尝试绘制这个Bitmap, 我们将得到"Canvas: trying to use a recycled bitmap"的错误提示。
-* 从Android 3.0 (API Level 11)开始,引进了**BitmapFactory.Options.inBitmap**字段。 如果使用了这个设置字段,decode方法会在加载Bitmap数据的时候去重用已经存在的Bitmap。这意味着Bitmap的内存是被重新利用的,这样可以提升性能,并且减少了内存的分配与回收。
- * You should still always use the returned Bitmap of the decode method and not assume that reusing the bitmap worked, due to the constraints outlined above and failure situations that can occur.
-
- * 总是使用解码方法,因为不能保证重用的bitmap会起作用,(例如,位图大小不匹配就无法重用)
-
- * As of {@link android.os.Build.VERSION_CODES#KITKAT}, any mutable bitmap can be reused by {@link BitmapFactory} to decode any other bitmaps as long as the resulting {@link Bitmap#getByteCount() byte count} of the decoded bitmap is less than or equal to the {@link Bitmap#getAllocationByteCount() allocated byte count} of the reused bitmap.
- * 版本4.4后,任何mutable的图片都可以被BitmapFactory重用成任何其他的位图,只要源位图的大小(Bitmap.getByteCount())比重用位图(Bitmap.getAllocateByteCount)小或者相等
- * 版本KITKAT前,重用的位图大小必须和源位图大小相同,而且位图格式必须是JPEG或者PNG(无论是流形式还是资源形式图片)
- * 如果重用位图设置了Bitmap.Config.configuration将会覆盖inperferredConfig的设置,如果有的话.
+* 从Android 3.0 (API Level 11)开始,引进了**BitmapFactory.Options.inBitmap**字段。如果这个值被设置了,decode方法会在加载内容的时候去reuse已经存在的bitmap. 这意味着bitmap的内存是被reused的,这样可以提升性能, 并且减少了内存的allocation与de-allocation.
+ * reused的bitmap必须和原数据内容大小一致, 并且是JPEG 或者 PNG 的格式 (或者是某个resource 与 stream).
+ * reused的bitmap的configuration值如果有设置,则会覆盖掉inPreferredConfig值.
+ * 你应该总是使用decode方法返回的bitmap, 因为你不可以假设reusing的bitmap是可用的(例如,大小不对).
+
+ ```java
-### inJustDecodeBounds / inSmapleSize
-[**高效加载大图片**](http://hukai.me/android-training-course-in-chinese/graphics/displaying-bitmaps/load-bitmap.html )
+ private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
+ // inBitmap only works with mutable bitmaps, so force the decoder to
+ // return mutable bitmaps.
+ options.inMutable = true;
+ if (cache != null) {
+ // Try to find a bitmap to use for inBitmap.
+ Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
+ if (inBitmap != null) {
+ // If a suitable bitmap has been found,
+ // set it as the value of inBitmap.
+ options.inBitmap = inBitmap;
+ }
+ }
+ }
+
+ static boolean canUseForInBitmap( Bitmap candidate, BitmapFactory.Options targetOptions) {
+ if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.KITKAT) {
+ // From Android 4.4 (KitKat) onward we can re-use
+ // if the byte size of the new bitmap is smaller than
+ // the reusable bitmap candidate
+ // allocation byte count.
+ int width = targetOptions.outWidth / targetOptions.inSampleSize;
+ int height = targetOptions.outHeight / targetOptions.inSampleSize;
+ int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
+ return byteCount <= candidate.getAllocationByteCount(); + } + // On earlier versions, + // the dimensions must match exactly and the inSampleSize must be 1 + return candidate.getWidth() == targetOptions.outWidth + && candidate.getHeight() == targetOptions.outHeight + && targetOptions.inSampleSize == 1; + } + ``` +### 高效加载大图片(inJustDecodeBounds / inSmapleSize) * 如果设置为true,将不返回bitmap, 但是Bitmap的outWidth,outHeight等属性将会赋值,允许调用查询Bitmap,而不需要为Bitmap分配内存. * 例如加载一张很大的位图, 如果直接解码会造成OOM,做法是: * 1.先拿到位图的尺寸后,进行放缩后再加载位图 + + ```java BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; @@ -81,8 +117,11 @@ int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType; + ``` * 2.计算inSampleSize + ```java + public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; @@ -104,9 +143,12 @@ return inSampleSize; } + ```>**设置inSampleSize为2的幂是因为解码器最终还是会对非2的幂的数进行向下处理,获取到最靠近2的幂的数。详情参考inSampleSize的文档**
* 3.放缩后再加载小位图:
-
+
+ ```java
+
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
@@ -122,13 +164,110 @@
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
-
+ ```
+### inPremultiplied
+* 如果设置了true(默认是true),那么返回的图片RGB都会预乘透明通道A后的颜色
+* 系统View或者Canvas绘制图片,不建议设置为fase,否则会抛出异常,这是因为系统会假定所有图像都预乘A通道的已简化绘制时间.
+* 设置inPremultiplied的同时,设置inScale会导致绘制的颜色不正确.
+
+### inDither
+设置是否抖动处理图片.
+
### inMutable
-> 如果设置为true,将返回一个mutable的bitmap,可用于修改BitmapFactory加载而来的bitmap's effects.
+ 如果设置为true,将返回一个mutable的bitmap,可用于修改BitmapFactory加载而来的bitmap.
+
+* `BitmapFactory.decodeResource(Resources res, int id)`获取到的bitmap是mutable的,而`BitmapFactory.decodeFile(String path)`获取到的是immutable的
+* 可以使用`Bitmap copy(Config config, boolean isMutable)`获取mutable位图用于修改位图pixels.
+
+
+### inDesity
+
+* 设置位图的像素密度,即每英寸有多少个像素
+* 如果inScale设置了,同时inDensity的值和inTargetDensity不同时,这个时候图片将缩放位inTartgetDensity指定的值.
+* 如果设置为0,则`BitmapFactory.decodeResource(Resources,int)`和`BitmapFactory.decodeResource(Resources, int,BitmapFactory.Options)`,`BitmapFactory.decodeResourceStream()` 将`inTargetDensity`用`DisplayMetrics.densityDpi`来设置,其它函数则不会对bitmap进行任何缩放。
+
+###inTargetDensity:
+* 设置绘制位图的屏幕密度,与inScale和inDesity一起使用,来对位图进行放缩.
+* 如果设置为0, `BitmapFactory.decodeResource(Resources,int)`, `BitmapFactory.decodeResource(Resources, int, BitmapFactory.Options)`,`BitmapFactory.decodeResourceStream()`将按照DisplayMetrics的density处理.
+
+### inScreenDensity
+* 表示正在使用的实际屏幕的像素密度.纯粹用于运行在兼容性代码中的应用程序,其中inTargetDensity实际上是看到的应用程序的密度,而非真正的屏幕密度.
+* inDesity, inTargetDensity,inScreenDensity这三个参数主是确定是否需要对bitmap进行缩放处理,如果缩放,缩放后的W和H应该是多少,缩放比例主要是通过:InTargetDenisity/inDensity作为缩放比例。
+
+### inScale
+* 当inScale设置为true时,且inDenstiy和inTargetDensity也不为0时,位图将在加载时(解码)时放缩去匹配inTargetDensity,在绘制到canvas时不会依赖图像系统放缩.
+* BitmapRegionDecoder会忽略这个标记.
+* 此标记默认为true,如果需要非放缩的位图,可以设置为false,9-patch图片会忽略这标记而自动放缩适配.
+* 如果inPremultipled设置为false,并且图片有A通道,设置这个标记为true,会导致位图出现不正确的颜色.
+
+
+
+### inTargetDensity,inScale,inDesity之间的关系:
+说三者之间的关系前,先谈下系统位图放缩规则,做个试验(使用小米3作为测试机):将一张144*144的ic_lanucher.png(系统默认在xxhdpi包下)分别放置在hdpi,xhdpi,xxhdpi三个文件夹,打印出位图的大小.
+
+ ```java
+
+ Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.ic_launcher);
+ Log.d(TAG, "size:" + bitmap.getByteCount());
+ ```
+
+ ```java
+
+ hdpi包size:331776
+ xhdpi包size:186624
+ xxhdpi包size:82944
+
+ ```
+
+我们知道一张`144*144`的 ic_lanucher.png所占的实际内存为 `144*144*4=82944`字节,那么为什么同一张图片放在不同包下表现不一样的大小?
+
+屏幕密度与Drawable目录有着如下的关系:
+
+|目录| 屏幕密度|
+| :------------ |:---------------|
+|drawable-ldpi| 120dpi|
+|drawable-mdpi |160dpi|
+|drawable-hdpi |240dpi|
+|drawable-xhdpi|320dpi|
+|drawable-xxhdpi|480dpi|
+
+当使用decodeResuore()解码drawable目录下的图片时, 会根据手机的屏幕密度,到对应的文件夹中查找图片,如果图片存在于其他目录,则会对该图片进行放缩处理在显示,放缩处理的规则:
+
+**`scale= 设备屏幕密度/drawable目录设定的屏幕密度`**
+
+**`图片内存=int(图片长度*scale+0.5f)* int(图片宽度*scale)*单位像素占字节数`**
+
+由于实验使用的小米3,屏幕密度为480,则当图片放入在hdpi时:`scale= 480/240;`
+图片放入xhdpi:`scale=480/320`;
+图片放入xxhdpi时:`scale= 480/480`;
+
+说完系统加载位图使用的放缩规则后,再来说说这三个标记之间的关系:
+
+inDesity: 位图使用的像素密度
+inTargetDesity: 设备的屏幕密度
+inScale: 是否需要放缩位图
+
+清楚这三者的含义,就可以在加载图片时,根据图片在不同设备上的使用,可以放缩来加载位图:
+
+**`放缩规则 scale= inTargetDensity/inDesity;`**
+
+```java
+
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inScaled = true;
+ options.inDensity = getBitmapDensity();
+ options.inTargetDensity =Resources.getSystem().getDisplayMetrics().densityDpi ;
+ Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options);
+
+```
+
+### 开发中遇到的问题:
-* BitmapFactory.decodeResource(Resources res, int id)获取到的bitmap是mutable的,而BitmapFactory.decodeFile(String path)获取到的是immutable的
-* 可以使用Bitmap copy(Config config, boolean isMutable)获取mutable位图用于修改位图pixels.
+在手机上加载大图时根据屏幕的密度对图片进行缩放,因此我们使用最大的图片资源,这样的话对于任何的手机屏幕,都会对图像进行压缩,不会造成视觉上的问题.但Android系统升级到4.4之后,发现之前开发的App运行起来非常的卡,严重影响了用户体验。后来发现跟Bitmap.decodeByteArray的底层实现有关。而android4.4以前的BitmapFactory.cpp中nativeDecodeByteArray调用doDecode函数时不会根据density进行缩放处理.4.4后由于一张图片缩放加载后,在内存放大很多,导致内存占有量过大,造成卡顿.
-### inDesity, inTargetDensity,inScreenDensity,inScale
-
\ No newline at end of file
+###参考资料
+* [**Android inpreferredconfig参数分析**](http://blog.csdn.net/ccpat/article/details/46834089)
+* [**Bitmap的内存管理**](http://hukai.me/android-training-course-in-chinese/graphics/displaying-bitmaps/manage-memory.html)
+* [**高效加载大图片**](http://hukai.me/android-training-course-in-chinese/graphics/displaying-bitmaps/load-bitmap.html )
+* [**Android坑档案:你的Bitmap究竟占多大内存?**](https://zhuanlan.zhihu.com/p/20732309?refer=bennyhuo)
diff --git "a/showdy_note/android/LocalBroadcastManager346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/showdy_note/android/LocalBroadcastManager346円272円220円347円240円201円345円210円206円346円236円220円.md"
new file mode 100644
index 0000000..6694254
--- /dev/null
+++ "b/showdy_note/android/LocalBroadcastManager346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -0,0 +1,518 @@
+### LocalBroadcastManager优势:
+
+ Helper to register for and send broadcasts of Intents to local objects within your process. This has a number of advantages over sending global broadcasts with {@link android.content.Context#sendBroadcast}:
+
+ * You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
+ * It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
+ * It is more efficient than sending a global broadcast through the system.
+
+相对于BroadcastReceiver来说,LocalBroadcastManager有如下优势:
+
+ * 发送的广播只会在当前APP中传播,不会泄露给其他APP,确保数据传输的安全性.
+ * 其他APP的广播无法发送到本地APP中,不用担心安全漏洞被其他APP利用.
+ * 比系统全局广播更加高效.
+
+
+### LocalBraodcastManager源码如下:
+
+```java
+
+ /**
+ * Helper to register for and send broadcasts of Intents to local objects
+ * within your process. This has a number of advantages over sending
+ * global broadcasts with {@link android.content.Context#sendBroadcast}:
+ *
+ *
You know that the data you are broadcasting won't leave your app, so
+ * don't need to worry about leaking private data.
+ *
It is not possible for other applications to send these broadcasts to
+ * your app, so you don't need to worry about having security holes they can
+ * exploit.
+ *
It is more efficient than sending a global broadcast through the
+ * system.
+ *