主頁 > 移動端開發 > Java反射全決議(使用、原理、問題、在Android中的應用)

Java反射全決議(使用、原理、問題、在Android中的應用)

2021-01-24 19:24:06 移動端開發

前言

今天說Java模塊內容:反射,

反射介紹

正常情況下,我們知曉我們要操作的類和物件是什么,可以直接操作這些物件中的變數和方法,比如一個User類:

User user=new User();
user.setName("Bob");

但是有的場景,我們無法正常去操作:

  • 只知道類路徑,無法直接實體化的物件,
  • 無法直接操作某個物件的變數和方法,比如私有方法,私有變數,
  • 需要hook系統邏輯,比如修改某個實體的引數,

等等情況,

所以我們就需要一種機制能讓我們去操作任意的類和物件,

這種機制,就是反射,簡單的說,反射就是:

對于任意一個,都能夠知道這個類的所有屬性和方法;
對于任意一個物件,都能夠呼叫它的任意方法和屬性,

常用API舉例

先設定一個User類:

package com.example.testapplication.reflection;
public class User {
    private int age;
    public String name;

    public User() {
        System.out.println("呼叫了User()");
    }

    private User(int age, String name) {
        this.name = name;
        this.age = age;
        System.out.println("呼叫了User(age,name)"+"__age:"+age+"__name:"+name);
    }

    public User(String name) {
        this.name = name;
        System.out.println("呼叫了User(name)"+"__name:"+name);
    }

    private String getName() {
        System.out.println("呼叫了getName()");
        return this.name;
    }

    private String setName(String name) {
    	this.name = name;
        System.out.println("呼叫了setName(name)__"+name);
        return this.name;
    }

    public int getAge() {
        System.out.println("呼叫了getAge()");
        return this.age;
    }    
}

獲取Class物件

主要有三種方法獲取Class物件

  • 根據類路徑獲取類物件
  • 直接獲取
  • 實體物件的getclass方法
//1、根據類路徑獲取類物件
try {
    Class clz = Class.forName("com.example.testapplication.reflection.User");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

//2、直接獲取
Class clz = User.class;

//3、物件的getclass方法
Class clz = new User().getClass();

獲取類的構造方法

1、獲取類所有構造方法

Class clz = User.class;
//獲取所有建構式(不包括私有構造方法)
Constructor[] constructors1 = clz.getConstructors();
//獲取所有建構式(包括私有構造方法)
Constructor[] constructors2 = clz.getDeclaredConstructors();

2、獲取類的單個構造方法

    try {
        //獲取無參建構式
        Constructor constructor1 = clz.getConstructor();

        //獲取引數為String的建構式
        Constructor constructor2 =clz.getConstructor(String.class);

        //獲取引數為int,String的建構式
        Class[] params = {int.class,String.class};
        Constructor constructor3 =clz.getDeclaredConstructor(params);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }

需要注意的是,User(int age, String name)為私有構造方法,所以需要使用getDeclaredConstructor獲取,

呼叫類的構造方法生成實體物件

1、呼叫Class物件的newInstance方法

這個方法只能呼叫無參建構式,也就是Class物件的newInstance方法不能傳入引數,

Object user = clz.newInstance();

2、呼叫Constructor物件的newInstance方法

Class[] params = {int.class,String.class};
Constructor constructor3 =clz.getDeclaredConstructor(params);
constructor3.setAccessible(true);
constructor3.newInstance(22,"Bob");

這里要注意下,雖然getDeclaredConstructor能獲取私有構造方法,但是如果要呼叫這個私有方法,需要設定setAccessible(true)方法,否則會報錯:

can not access a member of class com.example.testapplication.reflection.User with modifiers "private"

獲取類的屬性(包括私有屬性)

Class clz = User.class;
Field field1 = clz.getField("name");
Field field2 = clz.getDeclaredField("age");

同樣的,getField獲取public類變數,getDeclaredField可以獲取所有變數(包括私有變數屬性),

所以一般直接用getDeclaredField即可,

修改實體的屬性

接上例,獲取類的屬性后,可以去修改類實體的對應屬性,比如我們有個user的實體物件,我們來修改它的name和age,

//修改name,name為public屬性
Class clz = User.class;
Field field1 = clz.getField("name");
field1.set(user,"xixi");

//修改age,age為private屬性
Class clz = User.class;
Field field2 = clz.getDeclaredField("age");
field2.setAccessible(true);
field2.set(user,123);

獲取類的方法(包括私有方法)

    //獲取getName方法
    Method method1 = clz.getDeclaredMethod("getName");
	//獲取setName方法,帶引數
    Method method2 = clz.getDeclaredMethod("setName", String.class);
    //獲取getage方法
    Method method3 = clz.getMethod("getAge");

呼叫實體的方法

method1.setAccessible(true);
Object name = method1.invoke(user);


method2.setAccessible(true);
method2.invoke(user, "xixi");

Object age = method3.invoke(user);

反射優缺點

雖然反射很好用,增加了程式的靈活性,但是也有他的缺點:

  • 性能問題,由于用到動態型別(運行時才檢查型別),所以反射的效率比較低,但是對程式的影響比較小,除非對性能要求比較高,所以需要在兩者之間平衡,
  • 不夠安全,由于可以執行一些私有的屬性和方法,所以可能會帶來安全問題,
  • 不易讀寫,當然這一點也有解決方案,比如jOOR庫,但是不適用于Android定義為final的欄位,

Android中的應用

插件化(Hook)

Hook 技術又叫做鉤子函式,在系統沒有呼叫該函式之前,鉤子程式就先捕獲該訊息,鉤子函式先得到控制權,這時鉤子函式既可以加工處理(改變)該函式的執行行為,還可以強制結束訊息的傳遞,

在插件化中,我們需要找到可以hook的點,然后進行一些插件的作業,比如替換Activity,替換mH等等,這其中就用到大量反射的知識,這里以替換mH為例:

// 獲取到當前的ActivityThread物件
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);

//獲取這個物件的mH
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);


//替換mh為我們自己的HandlerCallback
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH));

動態代理

動態代理的特點是不需要提前創建代理物件,而是利用反射機制在運行時創建代理類,從而動態實作代理功能,

public class InvocationTest implements InvocationHandler {
    // 代理物件(代理介面)
    private Object subject;

    public InvocationTest(Object subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable {
        //代理真實物件之前
        Object obj = method.invoke(subject, args);
        //代理真實物件之后
        return obj;
    }
}

三方庫(注解)

我們可以發現很多庫都會用到注解,而獲取注解的程序也會有反射的程序,比如獲取Activity中所有變數的注解:

public void getAnnotation(Activity activity){
    Class clazz = activity.getClass();
    //獲得activity中的所有變數
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
        //獲取變數上加的注解
        MyAnnotation test = field.getAnnotation(MyAnnotation.class);
        //...
    }
}

這種通過反射處理注解的方式稱作運行時注解,也就是程式運行狀態的時候才會去處理注解,
但是上文說過了,反射會在一定程度上影響到程式的性能,所以還有一種處理注解的方式:編譯時注解,

所用到的注解處理工具是APT

APT是一種注解處理器,可以在編譯時進行掃描和處理注解,然后生成java代碼檔案,這種方法對比反射就能比較小的影響到程式的運行性能,

這里就不說APT的使用了,下次會專門有章節提到~

反射可以修改final型別成員變數嗎?

final我們應該都知道,修飾變數的時候代表是一個常量,不可修改,那利用反射能不能達到修改的效果呢?

我們先試著修改一個用final修飾的String變數,

public class User {
    private final String name = "Bob";
    private final Student student = new Student();
    
    public String getName() {
        return name;
    }

    public Student getStudent() {
        return student;
    }
}


    User user = new User();
    Class clz = User.class;
    Field field1 = null;
    try{
        field1=clz.getDeclaredField("name");
        field1.setAccessible(true);
        field1.set(user,"xixi");
        System.out.println(user.getName());
    }catch(NoSuchFieldException e){
        e.printStackTrace();
    }catch(IllegalAccessException e){
        e.printStackTrace();
    }

列印出來的結果,還是Bob,也就是沒有修改到,

我們再修改下student變數試試:

field1 = clz.getDeclaredField("student");
field1.setAccessible(true);
field1.set(user, new Student());

列印:
修改前com.example.studynote.reflection.Student@77459877
修改后com.example.studynote.reflection.Student@72ea2f77

可以看到,對于正常的物件變數即使被final修飾也是可以通過反射進行修改的,

這是為什么呢?為什么String不能被修改,而普通的物件變數可以被修改呢?

先說結論,其實String值也被修改了,只是我們無法通過這個物件獲取到修改后的值,

這就涉及到JVM的行內優化了:

行內函式,編譯器將指定的函式體插入并取代每一處呼叫該函式的地方(背景關系),從而節省了每次呼叫函式帶來的額外時間開支,

簡單的說,就是JVM在處理代碼的時候會幫我們優化代碼邏輯,比如上述的final變數,已知final修飾后不會被修改,所以獲取這個變數的時候就直接幫你在編譯階段就給賦值了,

所以上述的getName方法經過JVM編譯行內優化后會變成:

    public String getName() {
        return "Bob";
    }

所以無論怎么修改,都獲取不到修改后的值,

有的朋友可能提出直接獲取name呢?比如這樣:

//修改為public
public final String name = "Bob";

//反射修改后,列印user.name
field1=clz.getDeclaredField("name");
field1.setAccessible(true);
field1.set(user,"xixi");
System.out.println(user.name);

不好意思,還是列印出來Bob,這是因為System.out.println(user.name)這一句在經過編譯后,會被寫成:

System.out.println(user.name)

//經過行內優化

System.out.println("Bob")

所以:

反射是可以修改final變數的,但是如果是基本資料型別或者String型別的時候,無法通過物件獲取修改后的值,因為JVM對其進行了行內優化,

那有沒有辦法獲取修改后的值呢?

有,可以通過反射中的Field.get(Object obj)方法獲取:

//獲取field對應的變數在user物件中的值
System.out.println("修改后"+field.get(user));

反射獲取static靜態變數

說完了final,再說說static,怎么修改static修飾的變數呢?

我們知道,靜態變數是在類的實體化之前就進行了初始化(類的初始化階段),所以靜態變數是跟著類本身走的,跟具體的物件無關,所以我們獲取變數就不需要傳入物件,直接傳入null即可:

public class User {
	public static String name;
}


field2 = clz.getDeclaredField("name");
field2.setAccessible(true);
//獲取靜態變數
Object getname=field2.get(null);
System.out.println("修改前"+getname);

//修改靜態變數
field2.set(null, "xixi");
System.out.println("修改后"+User.name);

如上述代碼:

  • Field.get(null) 可以獲取靜態變數,
  • Field.set(null,object) 可以修改靜態變數,

怎么提升反射效率

  • 1、快取重復用到的物件

利用快取,其實我不說大家也都知道,在平時專案中用到多次的物件也會進行快取,誰也不會多次去創建,

但是,這一點在反射中尤為重要,比如Class.forName方法,我們做個測驗:

    long startTime = System.currentTimeMillis();
    Class clz = Class.forName("com.example.studynote.reflection.User");
    User user;
    int i = 0;
    while (i < 1000000) {
        i++;
        //方法1,直接實體化
        user = new User();
        //方法2,每次都通過反射獲取class,然后實體化
        user = (User) Class.forName("com.example.studynote.reflection.User").newInstance();
        //方法3,通過之前反射得到的class進行實體化
        user = (User) clz.newInstance();
    }

    System.out.println("耗時:" + (System.currentTimeMillis() - startTime));

列印結果:

1、直接實體化
耗時:15

2、每次都通過反射獲取class,然后實體化
耗時:671

3、通過之前反射得到的class進行實體化
耗時:31

所以看出來,只要我們合理的運用這些反射方法,比如Class.forName,Constructor,Method,Field等,盡量在回圈外就快取好實體,就能提高反射的效率,減少耗時,

  • 2、setAccessible(true)

之前我們說過當遇到私有變數和方法的時候,會用到setAccessible(true)方法關閉安全檢查,這個安全檢查其實也是耗時的,

所以我們在反射的程序中可以盡量呼叫setAccessible(true)來關閉安全檢查,無論是否是私有的,這樣也能提高反射的效率,

  • 3、ReflectASM

ReflectASM 是一個非常小的 Java 類別庫,通過代碼生成來提供高性能的反射處理,自動為 get/set 欄位提供訪問類,訪問類使用位元組碼操作而不是 Java 的反射技術,因此非常快,

ASM是一個通用的Java位元組碼操作和分析框架, 它可以用于修改現有類或直接以二進制形式動態生成類,

簡單的說,這是一個類似反射,但是不同于反射的高性能庫,
他的原理是通過ASM庫,生成了一個新的類,然后相當于直接呼叫新的類方法,從而完成反射的功能,

感興趣的可以去看看原始碼,實作原理比較簡單——https://github.com/EsotericSoftware/reflectasm,

小總結:
經過上述三種方法,我想反射也不會那么可怕到大大影響性能的程度了,如果真的發現反射影響了性能以及實際使用的情況,也許可以研究下,是否是因為沒用對反射和沒有處理好反射相關的快取呢?

反射原理

如果我們試著查看這些反射方法的原始碼,會發現最終都會走到native方法中,比如

getDeclaredField方法會走到

 public native Field getDeclaredField(String name) throws NoSuchFieldException;

那么在底層,是怎么獲取到類的相關資訊的呢?

首先回顧下JVM加載Java檔案的程序:

  • 編譯階段,.java檔案會被編譯成.class檔案,.class檔案是一種二進制檔案,內容是JVM能夠識別的機器碼,
  • .class檔案里面依次存盤著類檔案的各種資訊,比如:版本號、類的名字、欄位的描述和描述符、方法名稱和描述、是不是public、類索引、欄位表集合,方法集合等等資料,
  • 然后,JVM中的類加載器會讀取位元組碼檔案,取出二進制資料,加載到記憶體中,并且決議.class檔案的資訊,
  • 類加載器會獲取類的二進制位元組流,在記憶體中生成代表這個類的 java.lang.Class物件,
  • 最后會開始類的生命周期,比如連接、初始化等等,

而反射,就是去操作這個 java.lang.Class物件,這個物件中有整個類的結構,包括屬性方法等等,

總結來說就是,.class是一種有順序的結構檔案,而Class物件就是對這種檔案的一種表示,所以我們能從Class物件中獲取關于類的所有資訊,這就是反射的原理,

歡迎關注

Android體系架構鏈接 (連載體系化文章、腦圖、面試專題):https://github.com/JiMuzz/Android-Architecture
歡迎關注和Star~??

參考

https://juejin.cn/post/6844903905483030536
https://www.zhihu.com/question/46883050
https://juejin.cn/post/6917984253360177159
https://blog.csdn.net/PiaoMiaoXiaodao/article/details/79871313
https://www.cnblogs.com/coding-night/p/10772631.html
https://www.jianshu.com/p/3382cc765b39
https://segmentfault.com/a/1190000015860183

拜拜

有一起學習的小伙伴可以關注下?? 我的公眾號——碼上積木,每天剖析一個知識點,我們一起積累知識,形成完整體系架構,公眾號回復111可獲得《面試題思考與解答》以往期刊,

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

標籤:其他

上一篇:Android事件分發機制二:viewGroup與view對事件的處理

下一篇:【Android】關于連續多次點擊控制元件的控制方案(新建監聽類)

標籤雲
其他(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