本文是Android面試題整理中的一篇,內容包括html
- 在Application中註冊一個ActivityLifecycleCallbacks來監聽Activity的銷燬
- 經過IdleHandler在主線程空閒時進行檢測
- 檢測是經過WeakReference實現的,若是沒有被回收會再次調用gc再確認一遍
- 確認有泄漏後,dump hprof文件,並開啓一個進程IntentService經過HAHA進行分析
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.okio:okio:1.13.0'
複製代碼
File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
OkHttpClient mOkHttpClient=builder.build();
複製代碼
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
複製代碼
RequestBody formBody = new FormEncodingBuilder()
.add("size", "10")
.build();
Request request = new Request.Builder()
.url("http://api.1-blog.com/biz/bizserver/article/list.do")
.post(formBody)
.build();
複製代碼
Call call = mOkHttpClient.newCall(request);
複製代碼
Response mResponse=call.execute();
if (mResponse.isSuccessful()) {
return mResponse.body().string();
} else {
throw new IOException("Unexpected code " + mResponse);
}
複製代碼
call.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(Response response) throws IOException {
String str = response.body().string();
Log.i("wangshu", str);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "請求成功", Toast.LENGTH_SHORT).show();
}
});
}
});
複製代碼
由於如下緣由,因此咱們須要封裝:android
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
複製代碼
/** 將要運行的異步請求隊列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/**正在運行的異步請求隊列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 正在運行的同步請求隊列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
複製代碼
//AsyncCall 的excute方法
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);
if (canceled) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
複製代碼
getResponseWithInterceptorChain是用責任鏈的方式,執行攔截器,對請求和請求結果進行處理git
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
複製代碼
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
//從攔截器列表取出攔截器
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
複製代碼
在ConnectInterceptor中,咱們獲取到了connection的實例,該實例是從ConnectionPool中取得github
StreamAllocation是Connection維護的鏈接,如下是類內註解web
<ul>
* <li><strong>Connections:</strong> physical socket connections to remote servers. These are
* potentially slow to establish so it is necessary to be able to cancel a connection
* currently being connected.
* <li><strong>Streams:</strong> logical HTTP request/response pairs that are layered on
* connections. Each connection has its own allocation limit, which defines how many
* concurrent streams that connection can carry. HTTP/1.x connections can carry 1 stream
* at a time, HTTP/2 typically carry multiple.
* <li><strong>Calls:</strong> a logical sequence of streams, typically an initial request and
* its follow up requests. We prefer to keep all streams of a single call on the same
* connection for better behavior and locality.
* </ul>
複製代碼
ConnectionPool經過Address等來查找有沒有能夠複用的Connection,同時維護一個線程池,對Connection作回收工做面試
Retrofit幫助咱們對OkHttp進行了封裝,使網絡請求更加方便segmentfault
dependencies {
compile 'com.squareup.retrofit2:retrofit:2.0.2'
}
複製代碼
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fanyi.youdao.com/") // 設置網絡請求的Url地址
.addConverterFactory(GsonConverterFactory.create()) // 設置數據解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava平臺 .build();
複製代碼
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
複製代碼
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//對 發送請求 進行封裝
Call<Reception> call = request.getCall();
複製代碼
//發送網絡請求(異步) call.enqueue(new Callback<Translation>() {
//請求成功時回調
@Override
public void onResponse(Call<Translation> call, Response<Translation> response) {
//請求處理,輸出結果
response.body().show();
}
//請求失敗時候的回調
@Override
public void onFailure(Call<Translation> call, Throwable throwable) {
System.out.println("鏈接失敗");
}
});
// 發送網絡請求(同步) Response<Reception> response = call.execute();
複製代碼
Retrofit 經過builder模式建立,咱們能夠對其進行各類設置:設計模式
咱們的每一個method對應一個Call, Call的建立分爲兩步:api
關鍵在第一步,第一步是經過動態代理實現的緩存
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);//1
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
複製代碼
loadServiceMethod方法會首先在緩存裏查找是否有該method對應的ServiceMethod,沒有的話調用build方法建立一個
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
// 設置線程同步鎖
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
// ServiceMethod類對象採用了單例模式進行建立
// 即建立ServiceMethod對象前,先看serviceMethodCache有沒有緩存以前建立過的網絡請求實例
// 若沒緩存,則經過建造者模式建立
serviceMethod 對象 if (result == null) {
// 下面會詳細介紹ServiceMethod生成實例的過程
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
複製代碼
ServiceMethod的建立過程便是對method的解析過程,解析過程包括:對註解的解析,尋找合適的CallAdapter和Convert等
OkHttpCall實現了Call接口,當執行excute或enqueue請求命令時,內部經過傳入的CallFactory(OkHttpClient)執行網絡請求
若是咱們沒有對CallAdapter進行設置,它的值將是Android平臺的默認設置,其adapt方法以下
public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.delegate = delegate;
// 把上面建立並配置好參數的OkhttpCall對象交給靜態代理delegate
// 靜態代理和動態代理都屬於代理模式
// 靜態代理做用:代理執行被代理者的方法,且可在要執行的方法先後加入本身的動做,進行對系統功能的拓展
this.callbackExecutor = callbackExecutor;
// 傳入上面定義的回調方法執行器
// 用於進行線程切換 }
複製代碼
ExecutorCallbackCall對OkHttpCall進行了裝飾,會調用CallBackExcutor對OkHttpCall執行的返回結果進行處理,使其位於主線程
Fresco是一個圖片加載庫,能夠幫助咱們加載圖片顯示,控制多線程,以及管理緩存和內存等
dependencies {
// 其餘依賴
compile 'com.facebook.fresco:fresco:0.12.0'
// 在 API < 14 上的機器支持 WebP 時,須要添加
compile 'com.facebook.fresco:animated-base-support:0.12.0'
// 支持 GIF 動圖,須要添加
compile 'com.facebook.fresco:animated-gif:0.12.0'
// 支持 WebP (靜態圖+動圖),須要添加
compile 'com.facebook.fresco:animated-webp:0.12.0'
compile 'com.facebook.fresco:webpsupport:0.12.0'
// 僅支持 WebP 靜態圖,須要添加
compile 'com.facebook.fresco:webpsupport:0.12.0'
}
複製代碼
Fresco.initialize(Context context);
複製代碼
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="130dp"
android:layout_height="130dp"
fresco:placeholderImage="@drawable/my_drawable"
/>
複製代碼
Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/logo.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);
複製代碼
Fresco由兩部分組成,Drawees負責圖片的呈現,ImagePipeline負責圖片的下載解碼和內存管理
Drawees 負責圖片的呈現。它由三個元素組成,有點像MVC模式。
繼承於 View, 負責圖片的顯示。
通常狀況下,使用 SimpleDraweeView 便可。 你能夠在 XML 或者在 Java 代碼中使用它,經過 setImageUri 給它設置一個 URI 來使用,這裏有簡單的入門教學:開始使用
你可使用 XML屬性來達到各式各樣的效果。
DraweeHierarchy 用於組織和維護最終繪製和呈現的 Drawable 對象,至關於MVC中的M。
你能夠經過它來在Java代碼中自定義圖片的展現
DraweeController 負責和 image loader 交互( Fresco 中默認爲 image pipeline, 固然你也能夠指定別的),能夠建立一個這個類的實例,來實現對所要顯示的圖片作更多的控制。
若是你還須要對Uri加載到的圖片作一些額外的處理,那麼你會須要這個類的。
Fresco 的 Image Pipeline 負責圖片的獲取和管理。圖片能夠來自遠程服務器,本地文件,或者Content Provider,本地資源。壓縮後的文件緩存在本地存儲中,Bitmap數據緩存在內存中。
在5.0系統如下,Image Pipeline 使用 pinned purgeables 將Bitmap數據避開Java堆內存,存在ashmem中。這要求圖片不使用時,要顯式地釋放內存
SimpleDraweeView自動處理了這個釋放過程,因此沒有特殊狀況,儘可能使用SimpleDraweeView,在特殊的場合,若是有須要,也能夠直接控制Image Pipeline。
ImagePipeline加載圖片流程
- 檢查內存緩存,若有,返回
Image pipeline 默認有3個線程池:
- 3個線程用於網絡下載
ImagePipeLine有三級緩存
- 解碼後的Bitmap緩存
- 未解碼圖片的內存緩存
- 磁盤緩存
Fresco 相對於Glide/Picaso等擁有更多的功能,如圖片的漸進式加載/動圖/圓角等,
Fresco採用三級緩存:
- 解碼後的Bitmap緩存
- 未解碼圖片的內存緩存
- 磁盤緩存
Glide兩級緩存:
- 根據ImageView控件尺寸得到對應的大小的bitmap來展現,能夠緩存原始數據或者resize後數據
- 磁盤緩存
Fresco經過CloseableReference管理圖片,經過圖片控件DraweeView來顯示圖片和控制圖片釋放,雖然擴展性高,可是擴展起來麻煩;對項目有必定侵入性
EventBus使用了觀察者模式,方便咱們項目中進行數據傳遞和通訊
compile 'org.greenrobot:eventbus:3.0.0'
複製代碼
EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);
複製代碼
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
/* Do something */
}
複製代碼
EventBus.getDefault().post(new MessageEvent("Hello !....."));
複製代碼
該註解內部有三個成員,分別是threadMode、sticky、priority。
- threadMode表明訂閱方法所運行的線程
- sticky表明是不是粘性事件
- priority表明優先級
- POSTING:表示訂閱方法運行在發送事件的線程。
- MAIN:表示訂閱方法運行在UI線程,因爲UI線程不能阻塞,所以當使用MAIN的時候,訂閱方法不該該耗時過長。
- BACKGROUND:表示訂閱方法運行在後臺線程,若是發送的事件線程不是UI線程,那麼就使用該線程;若是發送事件的線程是UI線程,那麼新建一個後臺線程來調用訂閱方法。
- ASYNC:訂閱方法與發送事件始終不在同一個線程,即訂閱方法始終會使用新的線程來運行。
在註冊以前便把事件發生出去,等到註冊以後便會收到最近發送的粘性事件(必須匹配)。注意:只會接收到最近發送的一次粘性事件,以前的會接受不到,demo
參見連接