android 進階(有待整理)

參考 http://blog.sina.com.cn/s/blog_4c451e0e0102wofv.html

二、使用 NDK 的注意事項:

  1. 用 minSdkVersion 對應的 NDK 來編譯;
  2. 讓所有的 .so 文件都動態鏈接相同的 C++ 運行時;
  3. 為每個 CPU 架構提供對應的 .so 文件
    或在 build.gradle 中用 ndk。adiFilters 顯式指定支持的ABI;
  4. 所有的設備都支持 armeabi 架構的 .so 文件,但影響性能和兼容性;

三、Builder 模式:

  1. AS 中安裝 InnerBuilder 插件來簡化 Builder 模式的創建過程;

  2. 例子:
    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);
     }
    

    }
    }

四、版本新特性:

  1. Android 3.0 引入屬性動畫;
  2. Android 3.0 開始引入 Loader 異步數據加載框架;
  3. Android 4.4 引入過渡動畫;
  4. Android 4.0 在 UI 線程中進行網絡操作拋出 NetworkOnMainThreadException 異常

*. Activity/View 超 5 秒,BroadcastReceiver 超 10 秒,Service 超 20 秒 ANR 產生

五、ANR的避免和檢測:

  1. 在 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());

  1. BlockCanary 非侵入式的性能監控函數庫,監控主線程的卡頓,原理:
    利用主線程的消息隊列處理機制,
    通過對比消息分發開始和結束的時間點來判斷是否超過設定的時間。

    1. 添加依賴:
      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'
      }

    2. 在 Application 類的派生類的 onCreate 中進行配置和初始化:
      // 在主進程初始化調用
      BlockCanary.install(this, new AppBlockCanaryContext()).start();

    3. 創建 BlockCanaryContext 派生類,實現各種上下文:
      public class AppBlockCanaryContext extends BlockCanaryContext {
      // 包括:應用標識符、用戶uid、網絡類型、判斷閾值、Log保存位置等
      }

六、異步處理技術:

  • Android 系統如果檢測到非主線程更新 UI 組件,
    那么就會拋出 CalledFromWrongThreadException 異常!
  1. Thread 是 Android 中異步處理技術的基礎,創建線程有兩種方法:
  1. 繼承 Thread 類并重寫 run 方法:
    public class MyThread extends Thread {
    @Override
    public void run() {
    // 具體實現邏輯
    }
    }

MyThread mt = new MyThread();
mt.start();

  1. 實現 Runnable 接口并實現 run 方法:
    public class MyRunnable implements Runnable {
    @Override
    public void run() {
    // 具體實現邏輯
    }
    }

MyRunnable mr = new MyRunnable();
Thread thread = new Thread(mr);
thread.start();

  1. 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);
    // 處理接收到的消息
    }
    }

  2. AsyncQueryHandler 是一個抽象類,繼承自 Handler,
    通過封裝 ContentResolver、HandlerThread、Handler等
    實現 ContentProvider 上面執行異步操作,
    這些操作會被放到一個單獨的子線程中執行,當操作結束獲取到結果后,
    將通過消息的方式傳遞給調用 AsyncQueryHandler 的線程,通常就是主線程。
    方法 回調函數
    startQuery onQueryComplete
    startInsert onInsertComplete
    startUpdate onUpdateComplete
    startDelete onDeleteComplete

  3. 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 文件中注冊:

  1. 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();

  2. 預定義的線程池都是基于 ThreadPoolExecutor 類之上構建的,
    而通過 ThreadPoolExecutor 開發者可以自定義線程池的一些行為。

    ThreadPoolExecutor tpe = new ThreadPoolExecutor(
    int corePoolSize, // 核心線程數,始終保有這么多線程
    int maximumPoolSize, // 最大線程數,不能超過這么多線程
    long keepAliveTime, // 空閑時長,超出核心線程數即注銷
    TimeUnit unit, // 空時單位(可選)
    // TimeUnit 類中的
    // NANOSECONDS、MICROSECONDS、
    // MILLISECONDS、SECONDS
    BlockingQueue workQueue); // 任務緩沖隊列

  3. 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() 是一樣的!

  4. Loader 是 Android 3.0 開始引入一個異步數據加載框架,
    使得在 Activity 或 Fragment 中異步加載數據變得很簡單,
    同時它在數據源發生變化時,能夠及時發出消息通知。

    1. AsyncTaskLoader 是 Loader 子類,基于 AsyncTask 實現異步數據加載的抽象類,
      其派生實例類必須實現 loadInBackground 方法,在其中進行具體的數據加載操作。

    2. CursorLoader 是 AsyncTaskLoader 的子類,
      封裝了對 ContentResolver 的 query 操作,
      實現從 ContentProvider 中查詢數據的功能。

    3. 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);
      }
      

      }

七、數據序列化:

序列化是將數據結構或者對象轉換成可用于存儲或者傳輸的數據格式的過程。
  反序列化是將序列化過程中生成的數據還原數據結構或者對象的過程。

  1. 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;
    

    }

  2. 將 OutputStream 封裝到 ObjectOutputStream 對象中,
    調用 writeObjet 方法將對象進行序列化。
    調用 readObject 方法將數據轉換成某種類型的 InputStream 。

    對象序列化是基于字節的!

    • Reader 和 Writer 方法是基于字符方式。
  3. 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];
          }
      }
      

      }

  4. SQLiteDatabase 默認目錄是 /data/data/包名/database
    sqlcipher 函數庫實現操作數據庫敏感信息加解密(256位AES加密)。

  5. 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 的加解密開源庫。
  6. JSON(JavaScript Object Notation)是一種輕量級的數據交換格式。
    幾乎 80% 的 APP 與服務端的通信都是使用 JSON 格式。

  7. ProtocolBuffer 是 Google 設計的與語言、平臺無關的一種輕便高效的序列化結構。
    移動端應該使用 Nano-ProtocolBuffer 版本。

  8. FlatBuffers 是 Google 為游戲開發或其他對性能敏感的應用程序創建的,
    開源的跨平臺的高效的序列化函數庫(對 C++/Java 等語言接口的支持)。

    • 這個更適合移動
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容