欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 線程優(yōu)化知識(shí)點(diǎn)學(xué)習(xí)

 更新時(shí)間:2022年08月11日 16:02:15   作者:小塵  
這篇文章主要為大家介紹了Android線程優(yōu)化知識(shí)點(diǎn)學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在實(shí)際項(xiàng)目開發(fā)中會(huì)頻繁的用到線程,線程使用起來(lái)是很簡(jiǎn)單,但是濫用線程會(huì)帶來(lái)性能問(wèn)題, 比如啟動(dòng)一個(gè)線程至少 占用16kb的內(nèi)存、線程過(guò)多會(huì)導(dǎo)致cpu的頻繁切換而cpu切換成本是很高的、消耗大量用戶電量等問(wèn)題, 所以應(yīng)該讓app的線程數(shù)保持在合理水平,這是性能優(yōu)化中很重要的一部分。本文對(duì)線程優(yōu)化方面的知識(shí)做了一個(gè)全面總結(jié),主要內(nèi)容如下:

一、線程調(diào)度原理解析

線程調(diào)度的原理

在任意時(shí)刻,CPU 只能執(zhí)行一條機(jī)器指令,每個(gè)線程只有獲得了 CPU 的使用權(quán)之后才能執(zhí)行指令,也就是說(shuō) 在任意時(shí)刻,只有一個(gè)線程占用 CPU,處于運(yùn)行狀態(tài)。而我們平常所說(shuō)的 多線程并發(fā)運(yùn)行,實(shí)際上說(shuō)的是多個(gè)線程輪流獲取 CPU 的使用權(quán),然后分別執(zhí)行各自的任務(wù)。其實(shí)在可運(yùn)行池當(dāng)中有多個(gè)處于就緒狀態(tài)的線程在等待 CPU,而 JVM 負(fù)責(zé)線程調(diào)度,按照特定機(jī)制為多個(gè)線程分配 CPU 使用權(quán)。

上面的描述提到了三個(gè)主要信息:

  • 在任意時(shí)刻,只有一個(gè)線程占用 CPU,處于運(yùn)行狀態(tài)
  • 多線程并發(fā)運(yùn)行,實(shí)際上說(shuō)的是多個(gè)線程輪流獲取 CPU 的使用權(quán)
  • JVM 負(fù)責(zé)線程調(diào)度,按照特定機(jī)制為多個(gè)線程分配 CPU 使用權(quán)

線程調(diào)度模型

線程調(diào)度模型可以分為兩類,分別是 分時(shí)調(diào)度模型 和 搶占式調(diào)度模型。

  • 分時(shí)調(diào)度模型:讓所有線程輪流獲取 CPU 的使用權(quán),而且均分每個(gè)線程占用 CPU 的時(shí)間片,這種方式非常公平
  • 搶占式調(diào)度模型:JVM 使用的是搶占式調(diào)度模型,讓優(yōu)先級(jí)高的線程優(yōu)先獲取到 CPU 的使用權(quán),如果在可運(yùn)行池當(dāng)中的線程優(yōu)先級(jí)都一樣,那就隨機(jī)選取一個(gè)

Android 的線程調(diào)度

Android 的線程調(diào)度從兩個(gè)因素決定,一個(gè)是 nice 值(即線程優(yōu)先級(jí)),一個(gè)是 cgroup(即線程調(diào)度策略)。

對(duì)于 nice 值來(lái)說(shuō),它首先是在 Process 中定義的,值越小,進(jìn)程優(yōu)先級(jí)越高,默認(rèn)值是 THREAD_PRIORITY_DEFAULT = 0,主線程的優(yōu)先級(jí)也是這個(gè)值。修改 nice 值只需要在對(duì)應(yīng)的線程下設(shè)置即可:

public class MyRunnable implements Runnable {<!-- -->
	@Override
	public void run() {<!-- -->
		Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT)
	}
}
// 附上 setThreadPriority() 文檔說(shuō)明
/**
 * Set the priority of the calling thread, based on Linux priorities.  See
 * {@link #setThreadPriority(int, int)} for more information.
 * 
 * @param priority A Linux priority level, from -20 for highest scheduling
 * priority to 19 for lowest scheduling priority.
 * 
 * @throws IllegalArgumentException Throws IllegalArgumentException if
 * &lt;var&gt;tid&lt;/var&gt; does not exist.
 * @throws SecurityException Throws SecurityException if your process does
 * not have permission to modify the given thread, or to use the given
 * priority.
 * 
 * @see #setThreadPriority(int, int)
 */
public static final native void setThreadPriority(int priority)
        throws IllegalArgumentException, SecurityException;

nice 值它還有其他的優(yōu)先級(jí)可選:

public class Process {
    /**
     * Standard priority of application threads.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
	public static final int THREAD_PRIORITY_DEFAULT = 0;
    /**
     * Lowest available thread priority.  Only for those who really, really
     * don't want to run if anything else is happening.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_LOWEST = 19;	
    /**
     * Standard priority background threads.  This gives your thread a slightly
     * lower than normal priority, so that it will have less chance of impacting
     * the responsiveness of the user interface.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_BACKGROUND = 10;    
    /**
     * Standard priority of threads that are currently running a user interface
     * that the user is interacting with.  Applications can not normally
     * change to this priority; the system will automatically adjust your
     * application threads as the user moves through the UI.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_FOREGROUND = -2;
    /**
     * Standard priority of system display threads, involved in updating
     * the user interface.  Applications can not
     * normally change to this priority.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_DISPLAY = -4;    
    /**
     * Standard priority of the most important display threads, for compositing
     * the screen and retrieving input events.  Applications can not normally
     * change to this priority.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
    /**
     * Standard priority of video threads.  Applications can not normally
     * change to this priority.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_VIDEO = -10;
    /**
     * Standard priority of audio threads.  Applications can not normally
     * change to this priority.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_AUDIO = -16;
    /**
     * Standard priority of the most important audio threads.
     * Applications can not normally change to this priority.
     * Use with {@link #setThreadPriority(int)} and
     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
     * {@link java.lang.Thread} class.
     */
    public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;
    /**
     * Minimum increment to make a priority more favorable.
     */
    public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;
    /**
     * Minimum increment to make a priority less favorable.
     */
    public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;    
}

在實(shí)踐過(guò)程當(dāng)中,如果只有 nice 值是不足夠的。比如有一個(gè) app 它有1個(gè)前臺(tái)線程,而且它還有10個(gè)后臺(tái)線程,雖然后臺(tái)線程的優(yōu)先級(jí)比較低,但是數(shù)量比較多,這10個(gè)后臺(tái)線程對(duì) CPU 的消耗量是可以影響到前臺(tái)線程的性能的。所以 Android 需要一種機(jī)制來(lái)處理這種情況,也就是 cgroup。

Android 借鑒了 Linux 的 cgroup 來(lái)執(zhí)行 更嚴(yán)格的前臺(tái)和后臺(tái)調(diào)度策略,后臺(tái)優(yōu)先級(jí)的線程會(huì)被隱式的移動(dòng)到后臺(tái) group,而其他 group 的線程如果處于工作狀態(tài),那么后臺(tái)這些線程它們將會(huì)被限制,只有很小的幾率能夠利用 CPU。

這種分離的調(diào)度策略既允許了后臺(tái)線程來(lái)執(zhí)行一些任務(wù),同時(shí)又不會(huì)對(duì)用戶可見(jiàn)的前臺(tái)線程造成很大的影響,讓前臺(tái)線程有更多的 CPU。

或許你會(huì)有疑問(wèn):哪些線程會(huì)被移到后臺(tái) group?

  • 第一種就是那些 手動(dòng)設(shè)置了優(yōu)先級(jí)比較低的線程
  • 第二種就是 不在前臺(tái)運(yùn)行的那些應(yīng)用程序的線程

線程調(diào)度小結(jié)

  • 線程過(guò)多會(huì)導(dǎo)致 CPU 頻繁切換,降低線程運(yùn)行效率。 在前面講解啟動(dòng)優(yōu)化的時(shí)候有強(qiáng)調(diào)要充足的利用線程比如異步啟動(dòng)任務(wù),但是線程也不能無(wú)限制的使用
  • 正確認(rèn)識(shí)任務(wù)重要性決定哪種優(yōu)先級(jí)。 一般情況下線程工作量和優(yōu)先級(jí)是成反比,比如線程的工作量越大,所做的工作沒(méi)那么重要,那這個(gè)線程的優(yōu)先級(jí)應(yīng)該越低
  • 線程的優(yōu)先級(jí)具有繼承性。 比如在 A 線程創(chuàng)建了 B 線程,在我們沒(méi)有指定線程優(yōu)先級(jí)的情況下,B 線程的優(yōu)先級(jí)是和 A 一樣的。所以我們?cè)?UI 線程中創(chuàng)建線程,線程的優(yōu)先級(jí)是和 UI 線程一樣的,這就會(huì)導(dǎo)致 UI 線程搶占 CPU 時(shí)間片的概率會(huì)變少

二、Android 異步方式匯總

Thread

使用 Thread 創(chuàng)建線程是最簡(jiǎn)單、常見(jiàn)的異步方式,但在實(shí)際項(xiàng)目中,它也就只有這個(gè)優(yōu)點(diǎn)了,并不推薦直接使用 Thread 創(chuàng)建線程,主要有以下幾點(diǎn)原因:

  • 不易復(fù)用,頻繁創(chuàng)建及銷毀開銷大
  • 復(fù)雜場(chǎng)景不易使用

HandlerThread

是 Android 提供的一個(gè)自帶消息循環(huán)的線程,它內(nèi)部使用 串行的方式執(zhí)行任務(wù),比較 適合長(zhǎng)時(shí)間運(yùn)行,不斷從隊(duì)列中獲取任務(wù)的場(chǎng)景。

IntentService

繼承了 Android Service 組件,內(nèi)部創(chuàng)建了 HandlerThread,相比 Service 是在主線程執(zhí)行,IntentService 是 在子線程異步執(zhí)行不占用主線程,而且 優(yōu)先級(jí)比較高,不易被系統(tǒng) kill。

AsyncTask

AsyncTask 是 Android 提供的工具類,內(nèi)部的實(shí)現(xiàn)是使用了線程池,它比較大的好處是無(wú)需自己處理線程切換,但需要注意 AsyncTask 不同版本執(zhí)行方式不一致的問(wèn)題。

線程池

java 提供了線程池,在實(shí)際項(xiàng)目中比較推薦使用線程池的方式實(shí)現(xiàn)異步任務(wù),它主要有以下優(yōu)點(diǎn):

  • 易復(fù)用,減少線程頻繁創(chuàng)建、銷毀的時(shí)間
  • 功能強(qiáng)大:定時(shí)、任務(wù)隊(duì)列、并發(fā)數(shù)控制等,java 提供了 Executors 工具類可以很方便的創(chuàng)建一個(gè)線程池,也可以自己定制線程池

RxJava

RxJava 由強(qiáng)大的 Scheduler 集合提供,內(nèi)部實(shí)際也是使用的線程池,它封裝的非常完善,可以根據(jù)任務(wù)類型的不同指定使用不同的線程池,比如 IO 密集型的任務(wù)可以指定 Schedulers.IO,CPU 密集型任務(wù)可以指定 Schedulers.Computation。

Single.just(xxx)
	.subscribeOn(Schedulers.IO) // 指定工作線程類型為 IO 密集型
	.observeOn(AndroidSchedulers.mainThread()) // 指定下游接收所在線程
	.subscribe();

三、Android線程優(yōu)化實(shí)戰(zhàn)

線程使用準(zhǔn)則

  • 嚴(yán)禁使用直接new Thread()的方式
  • 提供基礎(chǔ)線程池供各個(gè)業(yè)務(wù)線使用: 避免各個(gè)業(yè)務(wù)線各自維護(hù)一套線程池,導(dǎo)致線程數(shù)過(guò)多
  • 根據(jù)任務(wù)類型選擇合適的異步方式: 比如優(yōu)先級(jí)低且長(zhǎng)時(shí)間執(zhí)行可以使用Handler Thread,再比如:有一個(gè)任務(wù)需要定時(shí)執(zhí)行,使用線程池更適合
  • 創(chuàng)建線程必須命名: 方便定位線程歸屬于哪一個(gè)業(yè)務(wù)方,在線程運(yùn)行期可以使用Thread.currentThread().setName修改名字
  • 關(guān)鍵異步任務(wù)監(jiān)控: 異步不等于不耗時(shí),如果一個(gè)任務(wù)在主線程需要耗費(fèi)500ms,那么它在異步任務(wù)中至少需要500ms,因?yàn)楫惒饺蝿?wù)中優(yōu)先級(jí)較低,耗費(fèi)時(shí)間很可能會(huì)高于500ms,所以這里可以使用AOP的方式來(lái)做監(jiān)控,并且結(jié)合所在的業(yè)務(wù)場(chǎng)景,根據(jù)監(jiān)控結(jié)果來(lái)適時(shí)的做一些相對(duì)應(yīng)的調(diào)整
  • 重視優(yōu)先級(jí)設(shè)置: 使用Process.setThreadPriority();設(shè)置,并且可以設(shè)置多次

線程池優(yōu)化實(shí)戰(zhàn)

接下來(lái)針對(duì)線程池的使用來(lái)做一個(gè)簡(jiǎn)單的實(shí)踐,還是打開我們之前的項(xiàng)目,這里說(shuō)一下每次實(shí)踐的代碼都是基于第一篇啟動(dòng)優(yōu)化的那個(gè)案例上寫的。

首先新建一個(gè)包async,然后在包中創(chuàng)建一個(gè)類ThreadPoolUtils,這里我們創(chuàng)建可重用且固定線程數(shù)的線程池,核心數(shù)為5,并且對(duì)外暴露一個(gè)get方法,然后我們可以在任何地方都能獲取到這個(gè)全局的線程池:

public class ThreadPoolUtils {
    //創(chuàng)建定長(zhǎng)線程池,核心數(shù)為5
    private static ExecutorService mService = Executors.newFixedThreadPool(5, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable runnable) {
            Thread thread = new Thread(runnable,"ThreadPoolUtils");//設(shè)置線程名
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設(shè)置線程優(yōu)先級(jí)
            return thread;
        }
    });
    //獲取全局的線程池
    public static ExecutorService getService(){
        return mService;
    }
}

然后使用的時(shí)候就可以在你需要的地方直接調(diào)用了,并且你在使用的時(shí)候還可以修改線程的優(yōu)先級(jí)以及線程名稱:

        //使用全局統(tǒng)一的線程池
        ThreadPoolUtils.getService().execute(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); //修改線程優(yōu)先級(jí)
                String oldName = Thread.currentThread().getName();
                Thread.currentThread().setName("Jarchie"); //修改線程名稱
                Log.i("MainActivity","");
                Thread.currentThread().setName(oldName); //將原有名稱改回去
            }
        });

四、定位線程創(chuàng)建者

如何確定線程創(chuàng)建者

當(dāng)你的項(xiàng)目做的越來(lái)越大的時(shí)候一般情況下線程都會(huì)變的非常多,最好是能夠?qū)φw的線程數(shù)進(jìn)行收斂,那么問(wèn)題來(lái)了,如何知道某個(gè)線程是在哪里創(chuàng)建的呢?不僅僅是你自己的項(xiàng)目源碼,你依賴的第三方庫(kù)、aar中都有線程的創(chuàng)建,如果單靠人眼review代碼的方式,工作量很大而且你還不一定能找的全。

并且你這次優(yōu)化完了線程數(shù),你還要考慮其他人新加的線程是否合理,所以就需要能夠建立一套很好的監(jiān)控預(yù)防手段。然后針對(duì)這些情況來(lái)做一個(gè)解決方案的總結(jié)分析,主要思想就是以下兩點(diǎn):

  • 創(chuàng)建線程的位置獲取堆棧
  • 所有的異步方式,都會(huì)走到new Thread

解決方案:

  • 特別適合Hook手段
  • 找Hook點(diǎn):構(gòu)造函數(shù)或者特定方法
  • Thread的構(gòu)造函數(shù)

可以在構(gòu)造函數(shù)中加上自己的邏輯,獲取當(dāng)前的調(diào)用棧信息,拿到調(diào)用棧信息之后,就可以分析看出某個(gè)線程是否使用的是統(tǒng)一的線程池,也可以知道某個(gè)線程具體屬于哪個(gè)業(yè)務(wù)方。

Epic實(shí)戰(zhàn)

Epic簡(jiǎn)介

  • Epic是一個(gè)虛擬機(jī)層面、以Java Method為粒度的運(yùn)行時(shí)Hook框架
  • 支持Android4.0-10.0(我的手機(jī)上程序出現(xiàn)了閃退,后來(lái)查找原因發(fā)現(xiàn)這個(gè)庫(kù)開源版本一些高版本手機(jī)好像不支持)

Epic使用

  • implementation 'me.weishu:epic:0.6.0'
  • 繼承XC_MethodHook,實(shí)現(xiàn)相應(yīng)邏輯
  • 注入Hook:DexposedBridge.findAndHookMethod

代碼中使用

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //Hook Thread類的構(gòu)造函數(shù),兩個(gè)參數(shù):需要Hook的類,MethodHook的回調(diào)
        DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
            //afterHookedMethod是Hook此方法之后給我們的回調(diào)
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param); //Hook完成之后會(huì)回調(diào)到這里
                //實(shí)現(xiàn)自己的邏輯,param.thisObject可以拿到線程對(duì)象
                Thread thread = (Thread) param.thisObject;
                //Log.getStackTraceString打印當(dāng)前的調(diào)用棧信息
                Log.i(thread.getName() + "stack", Log.getStackTraceString(new Throwable()));
            }
        });
    }

如果你的手機(jī)支持的話,這個(gè)時(shí)候運(yùn)行程序應(yīng)該就可以看到線程打印出來(lái)的堆棧信息了

五、優(yōu)雅實(shí)現(xiàn)線程收斂

線程收斂常規(guī)方案

  • 根據(jù)線程創(chuàng)建堆??剂亢侠硇?,使用統(tǒng)一線程庫(kù)
  • 各業(yè)務(wù)線需要移除自己的線程庫(kù)使用統(tǒng)一的線程庫(kù)

基礎(chǔ)庫(kù)如何使用線程

  • 直接依賴線程庫(kù)
  • 缺點(diǎn):線程庫(kù)更新可能會(huì)導(dǎo)致基礎(chǔ)庫(kù)也跟著更新

基礎(chǔ)庫(kù)優(yōu)雅使用線程

  • 基礎(chǔ)庫(kù)內(nèi)部暴露API:setExecutor
  • 初始化的時(shí)候注入統(tǒng)一的線程庫(kù)

舉個(gè)栗子:

比如這里有一個(gè)日志工具類,我們將它作為應(yīng)用的日志基礎(chǔ)庫(kù),假設(shè)它內(nèi)部有一些異步操作,原始的情況下是它自己內(nèi)部實(shí)現(xiàn)的,然后現(xiàn)在在它內(nèi)部對(duì)外暴露一個(gè)API,如果外部注入了一個(gè)ExecutorService,那么我們就使用外部注入的這個(gè),如果外部沒(méi)有注入,那就使用它默認(rèn)的,代碼如下所示:

public class LogUtils {
    private static ExecutorService mExecutorService;
    public static void setExecutor(ExecutorService executorService){
        mExecutorService = executorService;
    }
    public static final String TAG = "Jarchie";
    public static void i(String msg){
        if(Utils.isMainProcess(BaseApp.getApplication())){
            Log.i(TAG,msg);
        }
        // 異步操作
        if(mExecutorService != null){
            mExecutorService.execute(() -> {
                ...
            });
        }else {
            //使用原有的
            ...
        }
    }
}

統(tǒng)一線程庫(kù)

  • 區(qū)分任務(wù)類型:IO密集型、CPU密集型
  • IO密集型任務(wù)不消耗CPU,核心池可以很大(網(wǎng)絡(luò)請(qǐng)求、IO讀寫等)
  • CPU密集型任務(wù):核心池大小和CPU核心數(shù)相關(guān)(如果并發(fā)數(shù)超過(guò)核心數(shù)會(huì)導(dǎo)致CPU頻繁切換,降低執(zhí)行效率)

舉個(gè)栗子:根據(jù)上面的說(shuō)明,可以做如下的設(shè)置:

    //獲取CPU的核心數(shù)
    private int CPUCOUNT = Runtime.getRuntime().availableProcessors();
    //cpu線程池,核心數(shù)大小需要和cpu核心數(shù)相關(guān)聯(lián),這里簡(jiǎn)單的將它們保持一致了
    private ThreadPoolExecutor cpuExecutor = new ThreadPoolExecutor(CPUCOUNT, CPUCOUNT,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), sThreadFactory);
    //IO線程池,核心數(shù)64,這個(gè)數(shù)量可以針對(duì)自身項(xiàng)目再確定
    private ThreadPoolExecutor iOExecutor = new ThreadPoolExecutor(64, 64,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), sThreadFactory);
    //這里面使用了一個(gè)count作為標(biāo)記
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "ThreadPoolUtils #" + mCount.getAndIncrement());
        }
    };

然后在你實(shí)際項(xiàng)目中需要區(qū)分具體的任務(wù)類型,針對(duì)性的選擇相應(yīng)的線程池進(jìn)行使用。 以上就是對(duì)于Android線程優(yōu)化方面的總結(jié)了,今天的內(nèi)容還好不算多,覺(jué)得有用的朋友可以看看。

以上就是Android 線程優(yōu)化知識(shí)點(diǎn)學(xué)習(xí)的詳細(xì)內(nèi)容,更多關(guān)于Android 線程優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Android中motion_toast的使用

    詳解Android中motion_toast的使用

    我們通常會(huì)用 toast(也叫吐司)來(lái)顯示提示信息,例如網(wǎng)絡(luò)請(qǐng)求錯(cuò)誤,校驗(yàn)錯(cuò)誤等等。本文為大家介紹一個(gè)非常有趣的toast組件 —— motion_toast,感興趣的可以了解一下
    2022-06-06
  • 詳解Android中點(diǎn)擊事件的幾種實(shí)現(xiàn)方式

    詳解Android中點(diǎn)擊事件的幾種實(shí)現(xiàn)方式

    本篇文章主要介紹了Android中點(diǎn)擊事件的實(shí)現(xiàn)方式,點(diǎn)擊事件的實(shí)現(xiàn)分為3中,詳細(xì)的介紹了三種的用法,有興趣的可以了解一下。
    2016-12-12
  • Android 中Manifest.xml文件詳解

    Android 中Manifest.xml文件詳解

    這篇文章主要介紹了Android 中Manifest.xml文件詳解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Android 通過(guò)觸摸動(dòng)態(tài)地在屏幕上畫矩形效果

    Android 通過(guò)觸摸動(dòng)態(tài)地在屏幕上畫矩形效果

    在屏幕上用手指畫出一個(gè)區(qū)域,返回所圈的區(qū)域坐標(biāo)。通過(guò)自定義view設(shè)置畫筆及對(duì)應(yīng)參數(shù),在onTouchEvent()回調(diào)函數(shù)里,對(duì)觸摸事件進(jìn)行判斷。畫出矩形圖形,具體實(shí)現(xiàn)代碼大家參考下本文
    2017-07-07
  • Android App中使用RatingBar實(shí)現(xiàn)星級(jí)打分功能的教程

    Android App中使用RatingBar實(shí)現(xiàn)星級(jí)打分功能的教程

    這篇文章主要介紹了Android App中使用RatingBar實(shí)現(xiàn)星級(jí)打分功能的教程,文中舉了一個(gè)使用SeekBar與RatingBar制作的應(yīng)用內(nèi)打分條的功能,非常簡(jiǎn)單,需要的朋友可以參考下
    2016-04-04
  • Flutter定時(shí)器、倒計(jì)時(shí)的快速上手及實(shí)戰(zhàn)講解

    Flutter定時(shí)器、倒計(jì)時(shí)的快速上手及實(shí)戰(zhàn)講解

    這篇文章主要給大家介紹了關(guān)于Flutter定時(shí)器、倒計(jì)時(shí)的快速上手及實(shí)戰(zhàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Android Studio報(bào):“Attribute application@theme or @ icon ”問(wèn)題的解決

    Android Studio報(bào):“Attribute application@theme or @ icon ”問(wèn)題的解

    這篇文章主要給大家介紹了關(guān)于Android Studio報(bào):“Attribute application@theme or @ icon ”問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Android 8.0系統(tǒng)中通知欄的適配微技巧

    Android 8.0系統(tǒng)中通知欄的適配微技巧

    這篇文章主要介紹了Android 8.0系統(tǒng)中通知欄的適配微技巧,Android 8.0系統(tǒng)最主要需要進(jìn)行適配的地方有兩處:應(yīng)用圖標(biāo)和通知欄。具體配置方法技巧大家參考下本文
    2018-04-04
  • Android 自定義TextView去除paddingTop和paddingBottom

    Android 自定義TextView去除paddingTop和paddingBottom

    這篇文章主要介紹了Android 自定義TextView去除paddingTop和paddingBottom的相關(guān)資料,這里提供實(shí)例來(lái)幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-09-09
  • android手機(jī)獲取唯一標(biāo)識(shí)的方法

    android手機(jī)獲取唯一標(biāo)識(shí)的方法

    這篇文章主要 為大家詳細(xì)介紹了android手機(jī)獲取唯一標(biāo)識(shí)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評(píng)論