二、使用 NDK 的注意事項:
- 用 minSdkVersion 對應的 NDK 來編譯;
- 讓所有的 .so 文件都動態鏈接相同的 C++ 運行時;
- 為每個 CPU 架構提供對應的 .so 文件
或在 build.gradle 中用 ndk。adiFilters 顯式指定支持的ABI; - 所有的設備都支持 armeabi 架構的 .so 文件,但影響性能和兼容性;
三、Builder 模式:
AS 中安裝 InnerBuilder 插件來簡化 Builder 模式的創建過程;
-
例子:
public class User {
private final String mName; // 必選
private final String mGender; // 必選
private final int mAge; // 必選private final String mPhoto; // 可選
private User(Builder builder) {
mName = build.mName;
mGender = build.mGender;
mAge = build.mAge;mPhoto = build.mPhoto;
}
public static final class Builder {
private String mName;
private String mGender;
private int mAge;private String mPhoto; public Builder() {} public Builder setName(String val) { mName = val; return this; } public Builder setGender(String val) { mGender = val; return this; } public Builder setAge(int val) { mAge = val; return this; } public Builder setPhoto(String val) { mPhoto; = val; return this; } public User build() { return new User(this); }
}
}
四、版本新特性:
- Android 3.0 引入屬性動畫;
- Android 3.0 開始引入 Loader 異步數據加載框架;
- Android 4.4 引入過渡動畫;
- Android 4.0 在 UI 線程中進行網絡操作拋出 NetworkOnMainThreadException 異常
*. Activity/View 超 5 秒,BroadcastReceiver 超 10 秒,Service 超 20 秒 ANR 產生
五、ANR的避免和檢測:
- 在 Application 或 MainActivity 類的 onCreate 方法中執行 StrictMode 嚴格模式:
// 線程策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectCustomSlowCalls() // 檢測自定義耗時操作
.detectDiskReads() // 檢測是否存在磁盤讀取操作
.detectDiskWrites() // 檢測是否存在磁盤寫入操作
.detectNetwork() // 檢測是否存在網絡操作
.penaltyLog() // 檢測將警告輸出到 LogCat
.build());
// 虛擬機策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks() // 檢測是否存在Activity泄漏
.detectLeakedClosableObjects() // 檢測是否存在未關閉的 Closable 對象泄漏
.detectLeakedSqlLiteObjects() // 檢測是否存在 Sqlite 對象泄漏
.setClassInstanceLimit() // 檢測實例個數是否超過限制
.penaltyLog() // 檢測將警告輸出到 LogCat
.build());
-
BlockCanary 非侵入式的性能監控函數庫,監控主線程的卡頓,原理:
利用主線程的消息隊列處理機制,
通過對比消息分發開始和結束的時間點來判斷是否超過設定的時間。-
添加依賴:
dependencies {
compile 'com.github.moduth:blockcanary-android:1.2.1'// 僅在 debug 包啟用 BlockCanary 進行卡頓監控和提示
debugCompile 'com.github.moduth:blockcanary-android:1.2.1'
releaseCompile 'com.github.moduth:blockcanary-no-op:1.2.1'
} 在 Application 類的派生類的 onCreate 中進行配置和初始化:
// 在主進程初始化調用
BlockCanary.install(this, new AppBlockCanaryContext()).start();創建 BlockCanaryContext 派生類,實現各種上下文:
public class AppBlockCanaryContext extends BlockCanaryContext {
// 包括:應用標識符、用戶uid、網絡類型、判斷閾值、Log保存位置等
}
-
六、異步處理技術:
- Android 系統如果檢測到非主線程更新 UI 組件,
那么就會拋出 CalledFromWrongThreadException 異常!
- Thread 是 Android 中異步處理技術的基礎,創建線程有兩種方法:
- 繼承 Thread 類并重寫 run 方法:
public class MyThread extends Thread {
@Override
public void run() {
// 具體實現邏輯
}
}
MyThread mt = new MyThread();
mt.start();
- 實現 Runnable 接口并實現 run 方法:
public class MyRunnable implements Runnable {
@Override
public void run() {
// 具體實現邏輯
}
}
MyRunnable mr = new MyRunnable();
Thread thread = new Thread(mr);
thread.start();
-
HandlerThread 是一個集成了 Looper 和 MessageQueue 的線程,
當啟動 HandlerThread 時,
會同時生成 Looper 和 MessageQueue,然后等待消息進行處理。
開發者不需要自己去創建和維護 Looper,
只有一個消息隊列,順序執行,線程安全,它的用法和普通線程一樣:HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();// 只有當 HandlerThread 準備好接收消息之后才會返回
mHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 處理接收到的消息
}
} AsyncQueryHandler 是一個抽象類,繼承自 Handler,
通過封裝 ContentResolver、HandlerThread、Handler等
實現 ContentProvider 上面執行異步操作,
這些操作會被放到一個單獨的子線程中執行,當操作結束獲取到結果后,
將通過消息的方式傳遞給調用 AsyncQueryHandler 的線程,通常就是主線程。
方法 回調函數
startQuery onQueryComplete
startInsert onInsertComplete
startUpdate onUpdateComplete
startDelete onDeleteComplete-
IntentService 是一個抽象類,使用前需要繼承并實現 onHandleIntent 方法,
具有 Service 一樣的生命周期,也提供了后臺線程處理異步任務。
順序執行所有的任務,
Context.startService 傳遞一個 Intent 類型的參數可以啟動 IntentService,
當所有任務處理完成后,IntentService 將會自動結束生命周期。public class SimpleIntentService extends IntentService {
public SimpleIntentService() {
// 子類構造方法中需要調用 super 傳入子類名字
super(SimpleIntentService.class.getName());setIntentRedelivery(true); } @Override protected void onHandleIntent(Intent intent) { // 在后臺線程中調用 }
}
在 AndroidManifest.xml 文件中注冊:
-
Executor 的主要目的是分離任務的創建和執行,基于 Executor 的接口定義:
public interface Executor {
void execute(Runnable command);
}public class SimpleExecutor implements executor {
@Override
public void execute(Runnable runnable) {
// 最簡單的方法就是直接在這個方法中創建一個線程來執行 Runnable
// 實際應用中 execute 方法很少是這么簡單的,
// 通常需要增加類似隊列、任務優先級等功能,最終實現一個線程池。
new Thread(runnable).start();
}
}固定大小的線程池:Executors.newFixedThreadPool(線程池中線程的個數);
可變大小的線程池:Executors.newCachedThreadPool();
空閑線程等待 60 秒來執行新任務,超時自動銷毀。
單個線程的線程池:Executors.newSingleThreadExecutor(); -
預定義的線程池都是基于 ThreadPoolExecutor 類之上構建的,
而通過 ThreadPoolExecutor 開發者可以自定義線程池的一些行為。ThreadPoolExecutor tpe = new ThreadPoolExecutor(
int corePoolSize, // 核心線程數,始終保有這么多線程
int maximumPoolSize, // 最大線程數,不能超過這么多線程
long keepAliveTime, // 空閑時長,超出核心線程數即注銷
TimeUnit unit, // 空時單位(可選)
// TimeUnit 類中的
// NANOSECONDS、MICROSECONDS、
// MILLISECONDS、SECONDS
BlockingQueue workQueue); // 任務緩沖隊列 -
AsyncTask 是在 Executor 框架基礎上進行的封裝,
它實現將耗時任務移動到工作線程中執行,
同時提供方便的接口實現工作線程和主線程的通信,一般會用到如下方法:public class MyAsyncTask extends AsyncTask {
@Override
protected void onPreExecute() {
// 在執行實際的后臺操作前被 UI thread 調用。
// 可以在該方法中做一些準備工作,如在界面上顯示一個進度條
}// 除了 doInBackground 方法是在工作線程中執行, // 其他的都是在主線程中執行 @Override protected Result doInBackground(Params... params) { // 該方法運行在后臺線程中,主要負責執行那些很耗時的后臺計算工作 } @Override protected void onProgressUpdate(Progress... progress) { // 更新實時的任務進度 } @Override protected void onPostExecute(Result result) { // 任務執行的結果作為此方法的參數傳入 } @Override protected void onCancelled() { // 用戶調用取消時,要做的操作 }
}
new MyAsyncTask().execute(param);
或
new MyAsyncTask().executeOnExecutor(Executors.newCachedThreadPool());注:
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
THREAD_POOL_EXECUTOR 是一個 corePoolSize 為 CPU 核數 + 1 的線程池,
超過指定個數的就要等待。executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)
與 API Level > 13 的系統上執行 execute() 是一樣的! -
Loader 是 Android 3.0 開始引入一個異步數據加載框架,
使得在 Activity 或 Fragment 中異步加載數據變得很簡單,
同時它在數據源發生變化時,能夠及時發出消息通知。AsyncTaskLoader 是 Loader 子類,基于 AsyncTask 實現異步數據加載的抽象類,
其派生實例類必須實現 loadInBackground 方法,在其中進行具體的數據加載操作。CursorLoader 是 AsyncTaskLoader 的子類,
封裝了對 ContentResolver 的 query 操作,
實現從 ContentProvider 中查詢數據的功能。-
LoaderManager 是一個抽象類,用來管理一個或多個加載器對象的。
Activity 和 Fragment 與之默認關聯,
只需要通過 getLoaderManager 方法即可獲取其實例對象。主要有三個回調接口方法:
onCreateLoader 初始化并返回一個新的 Loader 實例;
onLoadFininshed 當一個加載器完成加載過程之后會回調這個方法;
onLoaderReset 當一個加載器被重置并且數據無效時會回調這個方法;public class MyActivity extends ListActivity
implements LoaderManager.LoaderCallbacks {// 只獲取 ID 和用戶名兩個字段 static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; SimpleCursorAdapter mAdapter; private void initAdapter() { mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, new String[] {ContactsContract.Contacts.DISPLAY_NAME}, new int[]{android.R.id.text1}, 0); setListAdapter(mAdapter); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 通過 LoderManager 初始化 Loader,這會回調到 onCreateLoader getLoaderManager().initLoader(0x0, // 加載器的標識 null, // 初始化加載器的參數,可以空 // LoaderCallbacks 實現類實例 this); } @Override public Loader onCreateLoader(int id, Bundle args) { // 初始化并返回一個新的裝載器 // return new CursorLoader(this, ContactsContract.Contacts.CONTENT_URI, CONTACTS_SUMMARY_PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + "ASC"); } @Override public void onLoadFininshed(Loader loader, Cursor c) { // 后臺線程中加載完數據后,回調這個方法將數據傳遞給主線程 mAdapter.swapCursor(c); } @Override public void onLoaderReset(Loader loader) { // Loader 被重置后的回調,在這里可以重新刷新頁面數據 mAdapter.swapCursor(null); }
}
七、數據序列化:
序列化是將數據結構或者對象轉換成可用于存儲或者傳輸的數據格式的過程。
反序列化是將序列化過程中生成的數據還原數據結構或者對象的過程。
-
Serializable 是 Java 語言自帶的序列化方案,只需實現 Serializable 接口即可!
序列化將 Java 對象轉換成字節序列的過程,反序列化是將字節序列恢復成 Java 對象。
Serializable 接口是一種標識接口,無需實現任何方法!
Java 使用反射機制,在序列化的過程中會創建很多臨時對象,容易觸發垃圾回收,
序列化的過程比較慢,對于性能要求很嚴格的場景不建議使用這種方案。public class MySerializable implements Serializable {
// serialVersionUID 在反序列化過程中用于驗證序列化對象的發送者和接收者
// 是否為該對象加載了與序列化兼容的類對象,
// 如果接收者加載的對象的 serialVersionUID
// 和發送者加的對象的 serialVersionUID 取值不同,
// 則反序列化過程會出現 InvalidClassException 異常!// Add default serial version ID private static final long serialVersionUID = 1L; 或 // Add generated serial version ID private static final long serialVersionUID = 4603642343377807741L;
}
-
將 OutputStream 封裝到 ObjectOutputStream 對象中,
調用 writeObjet 方法將對象進行序列化。
調用 readObject 方法將數據轉換成某種類型的 InputStream 。對象序列化是基于字節的!
- Reader 和 Writer 方法是基于字符方式。
-
Parcelable 是 Android SDK 提供的,基于內存的,Android 中跨進程對象的傳遞一般使用 Parcelable 。
Serializable 是 JDK 提供的接口,基于磁盤或網絡的。-
Android Studio 中可以安裝一個名為 Android Parcelable code generator 的插件。
定義好數據類,右鍵選擇 Parcelable 菜單項,
該插件自動幫我們轉換成實現了 Parcelable 接口的形式:public class DataClassExam {
public String mName;
public Long mTime;
}public class DataClassExam implements Parcelable {
public String mName;
public Long mTime;public DataClassExam() {} @Override public int describeContents() { // 接口內容的描述,一般默認返回零即可! return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // 將類的數據寫到 Parcel 容器中 dest.writeString(this.mName); dest.writLong(this.mTime); } @Override protected DataClassExam(Parcel in) { // 順序要與 writeToParcel 方法中的一致 this.mName = in.readString(); this.mTime = in.readLong(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public DataClassExam createFromParcel(Parcel source) { // 反序列化,將 Parcel 還原成對象 return new DataClassExam(source); } public DataClassExam[] newArray(int size) { // 提供外部類反序列化這個數組使用 return new DataClassExam[size]; } }
}
-
SQLiteDatabase 默認目錄是 /data/data/包名/database
sqlcipher 函數庫實現操作數據庫敏感信息加解密(256位AES加密)。-
SharedPreferences 保存應用的一些常用配置信息,其本質是一個鍵值對存儲。
是以 XML 文件的形式保存在 /data/data/包名/shared_prefs 目錄中的。
讀取和存儲數據,主要可以分三步:SharedPreferences sp = context.getSharedPreferences("偏好名",
// 操作模式
Context.MODE_PRIVATE);
// SharedPreferences的四種操作模式:
// Context.MODE_PRIVATE
// 為默認操作模式,代表該文件是私有數據,只能被應用本身訪問,
// 在該模式下,寫入的內容會覆蓋原文件的內容// Context.MODE_APPEND
// 模式會檢查文件是否存在,存在就往文件追加內容,否則就創建新文件// 以下兩個用來控制其他應用是否有權限讀寫該文件
// Context.MODE_WORLD_READABLE 表示當前文件可以被其他應用讀取
// Context.MODE_WORLD_WRITEABLE 表示當前文件可以被其他應用寫入sp.get數據類型(鍵名,默認值);
SharedPreferences.Editor editor = sp.edit();
editor.put數據類型(鍵名,值);
editor.commit();
- secure-preferences 是 SharedPreferences 的安全封裝類。
conceal 是 Facebook 的加解密開源庫。
- secure-preferences 是 SharedPreferences 的安全封裝類。
JSON(JavaScript Object Notation)是一種輕量級的數據交換格式。
幾乎 80% 的 APP 與服務端的通信都是使用 JSON 格式。ProtocolBuffer 是 Google 設計的與語言、平臺無關的一種輕便高效的序列化結構。
移動端應該使用 Nano-ProtocolBuffer 版本。-
FlatBuffers 是 Google 為游戲開發或其他對性能敏感的應用程序創建的,
開源的跨平臺的高效的序列化函數庫(對 C++/Java 等語言接口的支持)。- 這個更適合移動