主頁 > 移動端開發 > Java:一篇讀懂注解的實作原理總結及使用示例(二)

Java:一篇讀懂注解的實作原理總結及使用示例(二)

2021-09-18 14:43:07 移動端開發

舉例:創建自定義navigation注解處理器,分為兩大部分:

一、創建自定義的注解,定義注解類;

二、創建自定義的注解決議器,定義決議規則;

下面,我們來創建自己的注解,在專案根目錄上右鍵新建New->Moudule->Java Library,填寫自定義注解庫的名字libnavannotation,如下:

同樣需要繼續創建注解決議器:在專案根目錄上右鍵新建New->Moudule->Java Library,填寫自定義注解決議器庫的名字libnavcompiler:

通過上述的創建,我們專案的目錄中會多出如下的目錄:

將libnavannotation庫和libnavcompiler庫目錄中的build.gradle中的源兼容性和目標兼容性由版本7改為版本8:

java {
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
}

改為:

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

我們來添加依賴,只在自定義注解決議器libnavcompiler庫 中添加注解相關的依賴,完整build.gradle檔案內容如下:

plugins {
    id 'java-library'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':libnavannotation')
    implementation 'com.alibaba:fastjson:1.2.75'
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}

下面來正式的創建兩個注解類:ActivityDestination注解類和FragmentDestination注解類:

FragmentDestination注解類:

package com.mooc.libnavannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
public @interface FragmentDestination {

    /**當前頁面url**/
    String pageUrl();

    /**當前頁面是否需要登錄**/
    boolean needLogin() default false;

    /**當前頁面是否作為默認起始頁面**/
    boolean asStarter() default false;
}

ActivityDestination注解類:

package com.mooc.libnavannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
public @interface ActivityDestination {
    String pageUrl();

    boolean needLogin() default false;

    boolean asStarter() default false;
}

注解處理器Processor基礎概念

在開始之前,我們首先申明一個非常重要的問題:你應該明確注解的區別:

①在運行時(Runtime)通過反射機制運行處理的注解;

②在編譯時(Compile time)處理的注解;

注解處理器(Annotation Processor)是javac的一個工具,它用來在編譯時掃描和處理注解(Annotation),你可以對自定義注解,并注冊相應的注解處理器,到這里,我假設你已經知道什么是注解,并且知道怎么宣告的一個注解(我的第一篇文章已經說明了),如果你不熟悉注解,你可以在這官方檔案中得到更多資訊,注解處理器在Java 5開始就有了,但是從Java 6(2006年12月發布)開始才有可用的API,

一個注解的注解處理器,以Java代碼或者編譯過的位元組碼作為輸入,生成檔案通常是.java檔案作為輸出,這具體的含義什么呢?你可以生成Java代碼!這些生成的Java代碼是在生成的.java檔案中,所以你不能修改已經存在的Java類,例如向已有的類中添加方法,這些生成的Java檔案,會同其他普通的手動撰寫的Java源代碼一樣被javac編譯,

虛處理器AbstractProcessor

我們首先看一下處理器的API,每一個處理器都是繼承于AbstractProcessor,它的原始碼如下所示:

package javax.annotation.processing;

import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.Objects;
import javax.lang.model.element.*;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic;

/**
* 一種抽象注釋處理器,設計成大多數具體注釋處理器的方便超類,該類檢查注釋值以計算其子型別支持的選項、注釋型別和源版本,
* 在處理器初始化后,getter方法可能會使用可用的工具發出關于值得注意的條件的警告,
* 只要遵守該方法的通用Processor契約,子類可以自由重寫該類中任何方法的實作和規范,
*
**/
public abstract class AbstractProcessor implements Processor {
    /**
     * Processing environment providing by the tool framework.
     * 由工具框架提供的處理環境
     */
    protected ProcessingEnvironment processingEnv;
    private boolean initialized = false;

    /**
     * Constructor for subclasses to call.
     */
    protected AbstractProcessor() {}

    /**
     * If the processor class is annotated with {@link
     * SupportedOptions}, return an unmodifiable set with the same set
     * of strings as the annotation.  If the class is not so
     * annotated, an empty set is returned.
     *
     * @return the options recognized by this processor, or an empty
     * set if none
     * 如果處理器類使用SupportedOptions注釋,則回傳一個不可修改的集合,該集合具有與注釋相同的字串集,如果類沒有這樣注釋,則回傳一個空集,
     * 回傳:此處理器識別的選項,如果沒有,則為空集
     */
    public Set<String> getSupportedOptions() {
        SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class);
        if  (so == null)
            return Collections.emptySet();
        else
            return arrayToSet(so.value());
    }

    /**
     * If the processor class is annotated with {@link
     * SupportedAnnotationTypes}, return an unmodifiable set with the
     * same set of strings as the annotation.  If the class is not so
     * annotated, an empty set is returned.
     *
     * @return the names of the annotation types supported by this
     * processor, or an empty set if none
     * 
     * 如果處理器類使用SupportedAnnotationTypes注釋,則回傳一個不可修改的集合,該集合具有與注釋相同的字串集,如果類沒有這樣注釋,則回傳一個空集,
     * 回傳:此處理器支持的注釋型別的名稱,如果沒有,則回傳一個空集
     */
    public Set<String> getSupportedAnnotationTypes() {
            SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
            if  (sat == null) {
                if (isInitialized())
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                             "No SupportedAnnotationTypes annotation " +
                                                             "found on " + this.getClass().getName() +
                                                             ", returning an empty set.");
                return Collections.emptySet();
            }
            else
                return arrayToSet(sat.value());
        }

    /**
     * If the processor class is annotated with {@link
     * SupportedSourceVersion}, return the source version in the
     * annotation.  If the class is not so annotated, {@link
     * SourceVersion#RELEASE_6} is returned.
     *
     * @return the latest source version supported by this processor
     *
     * 如果處理器類使用SupportedSourceVersion注釋,則在注釋中回傳源版本,如果類沒有這樣注釋,SourceVersion,RELEASE_6回傳,
     * 回傳:此處理器支持的最新源版本
     */
    public SourceVersion getSupportedSourceVersion() {
        SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class);
        SourceVersion sv = null;
        if (ssv == null) {
            sv = SourceVersion.RELEASE_6;
            if (isInitialized())
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                         "No SupportedSourceVersion annotation " +
                                                         "found on " + this.getClass().getName() +
                                                         ", returning " + sv + ".");
        } else
            sv = ssv.value();
        return sv;
    }


    /**
     * Initializes the processor with the processing environment by
     * setting the {@code processingEnv} field to the value of the
     * {@code processingEnv} argument.  An {@code
     * IllegalStateException} will be thrown if this method is called
     * more than once on the same object.
     *
     * @param processingEnv environment to access facilities the tool framework
     * provides to the processor
     * @throws IllegalStateException if this method is called more than once.
     *
     * 通過將processingEnv欄位設定為processingEnv引數的值,使用處理環境初始化處理器,如果在同一物件上多次呼叫此方法,將引發IllegalStateException,
     * processingEnv—用于訪問工具框架提供給處理器的設施的環境
     * Throws:IllegalStateException——如果該方法被多次呼叫,
     */
    public synchronized void init(ProcessingEnvironment processingEnv) {
        if (initialized)
            throw new IllegalStateException("Cannot call init more than once.");
        Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment");

        this.processingEnv = processingEnv;
        initialized = true;
    }

    /**
     * {@inheritDoc}
     *
     * 處理來自上一輪的型別元素上的一組注釋型別,并回傳該處理程式是否宣告這些注釋型別,如果回傳true,則宣告注釋型別,后續處理器將不會被要求處理它們;
     * 如果回傳false,則不宣告注釋型別,隨后的處理器可能會被要求處理它們,處理器可以總是回傳相同的布林值,也可以根據選擇的條件改變結果,
     * 如果處理器支持“*”且根元素沒有注釋,則輸入集將為空,處理器必須優雅地處理一組空的注釋,
     */
    public abstract boolean process(Set<? extends TypeElement> annotations,
                                    RoundEnvironment roundEnv);

    /**
     * Returns an empty iterable of completions.
     *
     * @param element {@inheritDoc}
     * @param annotation {@inheritDoc}
     * @param member {@inheritDoc}
     * @param userText {@inheritDoc}
     */
    public Iterable<? extends Completion> getCompletions(Element element,
                                                         AnnotationMirror annotation,
                                                         ExecutableElement member,
                                                         String userText) {
        return Collections.emptyList();
    }

    /**
     * Returns {@code true} if this object has been {@linkplain #init
     * initialized}, {@code false} otherwise.
     *
     * @return {@code true} if this object has been initialized,
     * {@code false} otherwise.
     */
    protected synchronized boolean isInitialized() {
        return initialized;
    }

    private static Set<String> arrayToSet(String[] array) {
        assert array != null;
        Set<String> set = new HashSet<String>(array.length);
        for (String s : array)
            set.add(s);
        return Collections.unmodifiableSet(set);
    }
}

我們繼承上面的父類并實作父類的方法:

 
package com.example;  
  
public class MyProcessor extends AbstractProcessor {  
  
    @Override  
    public synchronized void init(ProcessingEnvironment env){ }  
  
    @Override  
    public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }  
  
    @Override  
    public Set<String> getSupportedAnnotationTypes() { }  
  
    @Override  
    public SourceVersion getSupportedSourceVersion() { }  
  
}
  • init(ProcessingEnvironment env): 每一個注解處理器類都必須有一個空的建構式,然而,這里有一個特殊的init()方法,它會被注解處理工具呼叫,并輸入ProcessingEnviroment引數,ProcessingEnviroment提供很多有用的工具類Elements, Types和Filer,后面我們將看到詳細的內容,
  • process(Set<? extends TypeElement> annotations, RoundEnvironment env) : 這相當于每個處理器的主函式main(),你在這里寫你的掃描、評估和處理注解的代碼,以及生成Java檔案,輸入引數RoundEnviroment,可以讓你查詢出包含特定注解的被注解元素,后面我們將看到詳細的內容,
  • getSupportedAnnotationTypes(): 這里你必須指定,這個注解處理器是注冊給哪個注解的,注意,它的回傳值是一個字串的集合,包含本處理器想要處理的注解型別的合法全稱,換句話說,你在這里定義你的注解處理器注冊到哪些注解上,
  • getSupportedSourceVersion(): 用來指定你使用的Java版本,通常這里回傳SourceVersion.latestSupported(),然而,如果你有足夠的理由只支持Java 6的話,你也可以回傳SourceVersion.RELEASE_6,我推薦你使用前者,

在Java 7及以后的版本中,你也可以在自定義注解處理器類上使用注解來代替getSupportedAnnotationTypes()和getSupportedSourceVersion()這兩個方法,像這樣:

@SupportedSourceVersion(SourceVersion.latestSupported())
@SupportedAnnotationTypes({
   // 合法注解全名的集合
 })
public class MyProcessor extends AbstractProcessor {
 
    @Override
    public synchronized void init(ProcessingEnvironment env){ }
 
    @Override
    public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
}

因為兼容的原因,特別是針對Android平臺,建議使用多載getSupportedAnnotationTypes()和getSupportedSourceVersion()方法代替@SupportedAnnotationTypes和@SuppozrtedSourceVersion,

接下來的你必須知道的事情是,注解處理器是運行它自己的虛擬機JVM中,是的,你沒有看錯,javac啟動一個完整Java虛擬機來運行注解處理器,這對你意味著什么?你可以使用任何你在其他java應用中使用的的東西,使用guava,如果你愿意,你可以使用依賴注入工具,例如dagger或者其他你想要的類別庫,但是不要忘記,即使是一個很小的處理,你也要像其他Java應用一樣,注意演算法效率,以及設計模式,

創建注解處理器NavProcessor,在下面的目錄中:

package com.mooc.libnavcompiler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.auto.service.AutoService;
import com.mooc.libnavannotation.ActivityDestination;
import com.mooc.libnavannotation.FragmentDestination;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

/**
 * APP頁面導航資訊收集注解處理器
 * <p>
 * AutoService注解:就這么一標記,annotationProcessor  project()應用一下,編譯時就能自動執行該類了,
 * <p>
 * SupportedSourceVersion注解:宣告我們所支持的jdk版本
 * <p>
 * SupportedAnnotationTypes:宣告該注解處理器想要處理那些注解
 */
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.mooc.libnavannotation.FragmentDestination", "com.mooc.libnavannotation.ActivityDestination"})
public class NavProcessor extends AbstractProcessor {
    private Messager messager;
    private Filer filer;
    private static final String OUTPUT_FILE_NAME = "destination.json";

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        //日志列印,在java環境下不能使用android.util.log.e()
        messager = processingEnv.getMessager();
        //檔案處理工具
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //通過處理器環境背景關系roundEnv分別獲取 專案中標記的FragmentDestination.class 和ActivityDestination.class注解,
        //此目的就是為了收集專案中哪些類 被注解標記了
        Set<? extends Element> fragmentElements = roundEnv.getElementsAnnotatedWith(FragmentDestination.class);
        Set<? extends Element> activityElements = roundEnv.getElementsAnnotatedWith(ActivityDestination.class);

        if (!fragmentElements.isEmpty() || !activityElements.isEmpty()) {
            HashMap<String, JSONObject> destMap = new HashMap<>();
            //分別 處理FragmentDestination  和 ActivityDestination 注解型別
            //并收集到destMap 這個map中,以此就能記錄下所有的頁面資訊了
            handleDestination(fragmentElements, FragmentDestination.class, destMap);
            handleDestination(activityElements, ActivityDestination.class, destMap);

            //app/src/main/assets
            FileOutputStream fos = null;
            OutputStreamWriter writer = null;
            try {
                //filer.createResource()意思是創建源檔案
                //我們可以指定為class檔案輸出的地方,
                //StandardLocation.CLASS_OUTPUT:java檔案生成class檔案的位置,/app/build/intermediates/javac/debug/classes/目錄下
                //StandardLocation.SOURCE_OUTPUT:java檔案的位置,一般在/ppjoke/app/build/generated/source/apt/目錄下
                //StandardLocation.CLASS_PATH 和 StandardLocation.SOURCE_PATH用的不多,指的了這個引數,就要指定生成檔案的pkg包名了
                FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", OUTPUT_FILE_NAME);
                String resourcePath = resource.toUri().getPath();
                messager.printMessage(Diagnostic.Kind.NOTE, "resourcePath:" + resourcePath);

                //由于我們想要把json檔案生成在app/src/main/assets/目錄下,所以這里可以對字串做一個截取,
                //以此便能準確獲取專案在每個電腦上的 /app/src/main/assets/的路徑
                String appPath = resourcePath.substring(0, resourcePath.indexOf("app") + 4);
                String assetsPath = appPath + "src/main/assets/";

                File file = new File(assetsPath);
                if (!file.exists()) {
                    file.mkdirs();
                }

                //此處就是穩健的寫入了
                File outPutFile = new File(file, OUTPUT_FILE_NAME);
                if (outPutFile.exists()) {
                    outPutFile.delete();
                }
                outPutFile.createNewFile();

                //利用fastjson把收集到的所有的頁面資訊 轉換成JSON格式的,并輸出到檔案中
                String content = JSON.toJSONString(destMap);
                fos = new FileOutputStream(outPutFile);
                writer = new OutputStreamWriter(fos, "UTF-8");
                writer.write(content);
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


        return true;
    }

    private void handleDestination(Set<? extends Element> elements, Class<? extends Annotation> annotationClaz, HashMap<String, JSONObject> destMap) {
        for (Element element : elements) {
            //TypeElement是Element的一種,
            //如果我們的注解標記在了類名上,所以可以直接強轉一下,使用它得到全類名
            TypeElement typeElement = (TypeElement) element;
            //全類名com.mooc.ppjoke.home
            String clazName = typeElement.getQualifiedName().toString();
            //頁面的id.此處不能重復,使用頁面的類名做hascode即可
            int id = Math.abs(clazName.hashCode());
            //頁面的pageUrl相當于隱士跳轉意圖中的host://schem/path格式
            String pageUrl = null;
            //是否需要登錄
            boolean needLogin = false;
            //是否作為首頁的第一個展示的頁面
            boolean asStarter = false;
            //標記該頁面是fragment 還是activity型別的
            boolean isFragment = false;

            Annotation annotation = element.getAnnotation(annotationClaz);
            if (annotation instanceof FragmentDestination) {
                FragmentDestination dest = (FragmentDestination) annotation;
                pageUrl = dest.pageUrl();
                asStarter = dest.asStarter();
                needLogin = dest.needLogin();
                isFragment = true;
            } else if (annotation instanceof ActivityDestination) {
                ActivityDestination dest = (ActivityDestination) annotation;
                pageUrl = dest.pageUrl();
                asStarter = dest.asStarter();
                needLogin = dest.needLogin();
                isFragment = false;
            }

            if (destMap.containsKey(pageUrl)) {
                messager.printMessage(Diagnostic.Kind.ERROR, "不同的頁面不允許使用相同的pageUrl:" + clazName);
            } else {
                JSONObject object = new JSONObject();
                object.put("id", id);
                object.put("needLogin", needLogin);
                object.put("asStarter", asStarter);
                object.put("pageUrl", pageUrl);
                object.put("className", clazName);
                object.put("isFragment", isFragment);
                destMap.put(pageUrl, object);
            }
        }
    }
}

下面兩篇文章其實是一篇文章的不同轉載,閱讀其一即可:

Java注解處理器https://www.race604.com/annotation-processing/ Java注解處理器使用詳解_Rukey7的博客-CSDN博客_注解處理器在這篇文章中,我將闡述怎樣寫一個注解處理器(Annotation Processor),在這篇教程中,首先,我將向您解釋什么是注解器,你可以利用這個強大的工具做什么以及不能做什么;然后,我將一步一步實作一個簡單的注解器,一些基本概念在開始之前,我們首先申明一個非常重要的問題:https://blog.csdn.net/github_35180164/article/details/52055994#

未完待續...

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301075.html

標籤:其他

上一篇:相比 XML , Compose 性能到底怎么樣?

下一篇:重復造輪子之LiveDataBus與PageEventBus

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more