開源Kotlin小項目,很是適合練手

一個很是小型的Kotlin項目,可是該有的都有。若是您以爲有意思就幫我star下,人多了我就會放更多的福利哦。githubhtml

先上福利。Release1.0java

fuli.gif

特色

  1. 列表顯示圖片,點擊查看更多。git

  2. 快速跳轉至頂部,底部,指定位置。github

  3. 收藏,查看歷史記錄。sql

  4. 設置壁紙。數據庫

  5. 離線緩存。數組

組成

  1. 語言:Kotlin,Java緩存

  2. 網絡請求:HttpUrlConnection網絡

  3. 數據庫:Sqlite框架

  4. 數據源:Jsoup

  5. 第三方庫:Glide

概述

1) 網絡請求

網絡框架並無使用RxRetrofit等,爲了保證精簡高效直接使用的HttpUrlConnection

  • get

val request = AsyncNetwork()
request.request(Constants.HOST_MOBILE_URL, null)
request.setRequestCallback(object : IRequestCallback {
    override fun onSuccess(httpURLConnection: HttpURLConnection?, response: String) {
        //todo
    }
})
  • post

val request = AsyncNetwork()
request.request(Constants.HOST_MOBILE_URL, mutableMapOf())
request.setRequestCallback(object : IRequestCallback {
    override fun onSuccess(httpURLConnection: HttpURLConnection?, response: String) {
        //todo
    }
})

2) 數據庫

數據庫沒有使用第三方框架,直接使用的sql語句。

CREATE TABLE tb_class_page_list ( 
                    _id           INTEGER PRIMARY KEY ASC AUTOINCREMENT,
                    href          STRING  UNIQUE,
                    description   STRING,
                    image_url     STRING,
                    id_class_page INTEGER REFERENCES tb_class_page (_id) ON DELETE CASCADE ON UPDATE CASCADE,
                    [index]       INTEGER);

3) 讀寫緩存

Serializable的效率遠低於Parcelable,因此採用Parcelable實現的緩存機制,速度快了大概7,8倍。

  • 讀取緩存

val helper = PageListCacheHelper(container?.context?.filesDir?.absolutePath)
val pageModel: Any? = helper.get(key)
  • 寫入緩存

val helper = PageListCacheHelper(context.filesDir.absolutePath)
helper.put(key, object)
  • 刪除緩存

val helper = PageListCacheHelper(context.filesDir.absolutePath)
helper.remove(key)

4) jsoup獲取數據

因爲數據是用從html頁面中提取的,因此速度偏慢,爲了避免影響體驗作了一套緩存機制,來作到流暢體驗。

Document doc = Jsoup.parse(html);
Elements elements = body.getElementsByTag("a");
String text = elements.get(0).text();
String imageUrl = elements.get(0).attr("src");
...

5) 組件

  • Activity fragment替代activity來顯示新界面

    由於activity須要在Manifiest中註冊,因此當有多個activity的時候,就須要編寫很長的Manifiest文件,嚴重影響了Manifiest的可讀性,界面的風格也比較笨重。因此一個新頁面就註冊一個activity不太合適,咱們經過用activity作爲容器添加不一樣的Fragment來達到註冊一個activity啓動多個不一樣頁面的效果。生命週期由activity管理,更方便簡潔。

    open class BaseFragmentActivity : BaseActionActivity() {
    
        companion object {
    
            private const val EXTRA_FRAGMENT_NAME = "extra_fragment_name"
            private const val EXTRA_FRAGMENT_ARG = "extra_fragment_arguments"
    
            @JvmOverloads
            @JvmStatic
            fun newInstance(context: Context, fragment: Class<*>, bundle: Bundle? = null,
                                          clazz: Class<out Activity> = getActivityClazz()): Intent {
                val intent = Intent(context, clazz)
                intent.putExtra(EXTRA_FRAGMENT_NAME, fragment.name)
                intent.putExtra(EXTRA_FRAGMENT_ARG, bundle)
                return intent
            }
    
            protected open fun getActivityClazz(): Class<out Activity> {
                return BaseFragmentActivity::class.java
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.cc_activity_base_fragment)
            val fragmentName = intent.getStringExtra(EXTRA_FRAGMENT_NAME)
            var fragment: Fragment? = null
            if (TextUtils.isEmpty(fragmentName)) {
                //set default fragment
                //fragment = makeFragment(MainFragment::class.java!!.getName())
            } else {
                val args = intent.getBundleExtra(EXTRA_FRAGMENT_ARG)
                try {
                    fragment = makeFragment(fragmentName)
                    if (args != null)
                        fragment?.arguments = args
                } catch (e: Exception) {
                    e.printStackTrace()
                }
    
            }
    
            if (fragment == null) return
    
            supportFragmentManager
                    .beginTransaction()
                    .replace(R.id.fragment_container, fragment)
                    .commit()
        }
    
        fun makeFragment(name: String): Fragment? {
            try {
                val fragmentClazz = Class.forName(name)
                return fragmentClazz.newInstance() as Fragment
            } catch (e: Exception) {
                e.printStackTrace()
            }
    
            return null
        }
    
    }

6) 序列化性能

性能測試,Serializable VS Externalizable,爲了不干擾,咱們使用AndroidTest進行測試。

模型

class Model1 implements Serializable {
    String text;
    int code;
    boolean bool;
    Model1 child;
}

class Model2 extends Model1 implements Externalizable {

    public Model2() {
    }

    @Override
    public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
        text = input.readUTF();
        code = input.readInt();
        bool = input.readBoolean();

        child = new Model2();
        child.text = input.readUTF();
        child.code = input.readInt();
        child.bool = input.readBoolean();
    }

    @Override
    public void writeExternal(ObjectOutput output) throws IOException {
        output.writeUTF(text);
        output.writeInt(code);
        output.writeBoolean(bool);
        if (child != null) {
            output.writeUTF(child.text);
            output.writeInt(child.code);
            output.writeBoolean(child.bool);
        }
    }
}

測試

@Test
public void serializableVSExternalizable() throws Exception {
    List<Model1> testModel1 = new ArrayList<>();
    for (int i = 0; i < 50000; i++) {
        Model1 model1 = new Model1();
        model1.text = "Hello World " + i;
        model1.code = i;
        model1.bool = false;

        Model1 child = new Model1();
        child.text = "Hello World Child" + i;
        child.code = i;
        child.bool = false;

        model1.child = child;
        testModel1.add(model1);
    }
    long startTime = System.currentTimeMillis();
    File file = new File("/sdcard/serializable");
    ObjectOutputStream oStream = new ObjectOutputStream(new FileOutputStream(file));
    oStream.writeObject(testModel1);
    oStream.close();
    Log.e("serializable", "write time " + (System.currentTimeMillis() - startTime));
    startTime = System.currentTimeMillis();
    ObjectInputStream iStream = new ObjectInputStream(new FileInputStream(file));
    testModel1 = (List<Model1>) iStream.readObject();
    iStream.close();
    Log.e("serializable", "read time " + (System.currentTimeMillis() - startTime));
    testModel1 = null;

    List<Model2> testModel2 = new ArrayList<>();
    for (int i = 0; i < 50000; i++) {
        Model2 model2 = new Model2();
        model2.text = "Hello World " + i;
        model2.code = i;
        model2.bool = false;

        Model2 child = new Model2();
        child.text = "Hello World Child" + i;
        child.code = i;
        child.bool = false;

        model2.child = child;
        testModel2.add(model2);
    }
    startTime = System.currentTimeMillis();
    file = new File("/sdcard/externalizable");
    oStream = new ObjectOutputStream(new FileOutputStream(file));
    oStream.writeObject(testModel2);
    oStream.close();
    Log.e("externalizable", "write time " + (System.currentTimeMillis() - startTime));
    startTime = System.currentTimeMillis();
    iStream = new ObjectInputStream(new FileInputStream(file));
    testModel2 = (List<Model2>) iStream.readObject();
    iStream.close();
    Log.e("externalizable", "read time " + (System.currentTimeMillis() - startTime));
}

結果

序列化5000個對象
Serializable:寫入耗時4026 ms,讀取耗時177 ms
Externalizable:寫入耗時2680 ms,讀取耗時79 ms

序列化50000個對象
Serializable:寫入耗時46872 ms,讀取耗時1807 ms
Externalizable:寫入耗時41334 ms,讀取耗時792 ms

從結果上能夠看到Externalizalbe相比於Serializable是稍微快一些點無論是寫入仍是讀取速度。對象存儲還可使用一些對象關係映射(ORM)型的數據庫。如GreenDao等等。

7) Java中的深拷貝

因爲System.arrayCopy()該方法拷貝數組的時候,若是是基本數據類型則是深拷貝,若是是對象類型則會是淺拷貝,沒法作到深拷貝,因此想深拷貝一個數組就得循環建立對象並賦值,這顯得很麻煩。因此項目中使用序列化的方法進行深拷貝。PS:Serializable序列化方式讀取的時候並不會調用對象構造方法,而Externalizable序列化方式讀取時會調用對象的無參構造方法。

@SuppressWarnings("unchecked")
public static <T> T deepCopyOrThrow(T src) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    out.writeObject(src);

    ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
    ObjectInputStream in = new ObjectInputStream(byteIn);
    return (T) in.readObject();
}

public static <T> T deepCopy(T src) {
    try {
        return deepCopyOrThrow(src);
    } catch (Exception ignore) {
        ignore.printStackTrace();
        return null;
    }
}
相關文章
相關標籤/搜索