主頁 > 移動端開發 > GreenDao 3.0 簡介、使用及踩坑

GreenDao 3.0 簡介、使用及踩坑

2021-01-01 13:46:43 移動端開發

一、GreenDao 簡介

GreenDao原理圖

??GreenDAO 是一款開源的面向 Android 的輕便、快捷的 ORM 框架,將 Java 物件映射到 SQLite 資料庫中,我們操作資料庫的時候,不再需要撰寫復雜的 SQL陳述句, 在性能方面,greenDAO 針對 Android 進行了高度優化,最小的記憶體開銷 、依賴體積小 同時還是支持 資料庫加密,

??greenDAO 官網地址:greenrobot.org/greendao/

??greenDAO GitHub 原始碼地址:greenrobot/greenDAO

二、GreenDao 特征

  • 1、支持 protocol buffer(protobuf) 協議
    GreenDao 支持 protocol buffer(protobuf) 協議資料的直接存盤,如果你通過 protobuf 協議與服務器互動,將不需要任何的映射
  • 2、代碼生成
    greenDAO 會根據配置資訊自動生成核心管理類以及 DAO 物件
  • 3、性能
    所有 ORM 資料庫的,greenDAO 是最快的,greenDAO 不作性能方面任何妥協

三、核心類介紹

1、DaoMaster:

??使用 greenDAO 的入口點,DaoMaster 負責管理資料庫物件(SQLiteDatabase)和 DAO 類(物件),我們可以通過它內部類 OpenHelper 和 DevOpenHelper SQLiteOpenHelper 創建不同模式的 SQLite 資料庫,

2、DaoSession :

??管理指定模式下的所有 DAO 物件,DaoSession 提供了一些通用的持久性方法比如插入、負載、更新和洗掉物體,

3、XxxDAO :

??對于每個物體類, greenDAO 都會生成一個與之對應 DAO 物件,如:User 物體,則會生成一個 UserDao 類

4、Entities:

??可持久化物件,通常,物體物件代表一個資料庫行,使用標準 Java 屬性(如一個 POJO 或 JavaBean )

核心類之間關系

四、集成 GreenDao

a、設定倉庫與插件(Project: build.gradle)

buildscript {
    repositories {
        jcenter()
        mavenCentral() // add repository
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'  // add plugin
    }
}

b、配置依賴 ( Module:app build.gradle )

apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'  // apply plugin

dependencies {
    compile 'org.greenrobot:greendao:3.2.2'  // add library

    // This is only needed if you want to use encrypted databases
    compile 'net.zetetic:android-database-sqlcipher:3.5.6' //加密庫依賴(可選項)
}

c、配置資料庫相關資訊 ( Module:app build.gradle )

greendao {
    schemaVersion 1 // 資料庫版本號
    daoPackage 'com.example.zhangruirui.greendao'  // 設定 DaoMaster、DaoSession、Dao 包名
    targetGenDir 'src/main/java'  // 設定 DaoMaster、DaoSession、Dao 目錄
}

d、基本使用步驟

// 生成資料庫檔案,名為 students-db
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "students-db", null);
SQLiteDatabase db = helper.getWritableDatabase();
// 建立特定模式下的所有的 DAO 物件和資料 db 物件的映射
DaoMaster master = new DaoMaster(db);
// 管理特定模式下的所有 DAO 物件,并提供一些通用的 CRUD 持久化方法
DaoSession session = master.newSession();
// 得到指定的 StudentDao 物件
StudentDao dao = session.getStudentDao();
dao.insert(student);
//...

五、實戰

1、我們寫一個簡單的物體類(User),測驗一下

package com.example.zhangruirui;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

@Entity
public class User {

  @Id
  private long id;

  private String name;

  private int age;

  @Generated(hash = 446251977)
  public User(long id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
  }

  @Generated(hash = 586692638)
  public User() {
  }

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
}

2、點擊 Make Project(或者 Make Moudle ‘App’) 編譯一下工程 ,如果配置正確,會在配置的包目錄下自動會生成 DaoMaster,DaoSession 和 UserDao 類 ,

編譯之后的結果圖

3、然后我們定義 OpenHelper 類:UserDBOpenHelper

package com.example.zhangruirui.greendao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

import com.example.zhangruirui.DaoMaster;

public class UserDBOpenHelper extends DaoMaster.DevOpenHelper {
  public UserDBOpenHelper(Context context, String name) {
    super(context, name);
  }

  public UserDBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
    super(context, name, factory);
  }

  @Override
  public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    DaoMaster.dropAllTables(wrap(db), true);
  }
}

4、然后,類似 SQLite ,我們需要定義 DBManager:UserManager 創建單例實體,來供外部操作資料庫

package com.example.zhangruirui.greendao;

import android.content.Context;
import android.support.annotation.WorkerThread;

import com.google.gson.Gson;

import java.io.Serializable;
import java.lang.reflect.Type;

public class UserManager {
  private volatile static UserManager mInstance = null;
  private Context mContext;
  private UserStorage mUserStorage = new UserStorage(mContext);

  private UserManager() {

  }

  public static UserManager getInstance() {
    if (mInstance == null) {
      synchronized (UserManager.class) {
        if (mInstance == null) {
          mInstance = new UserManager();
        }
      }
    }
    return mInstance;
  }

  public <T> T getUserAge(String key, Type typeOfT) {
    try {
      String json = mUserStorage.getUserAge(key);
      Gson gson = new Gson();

      CacheEntry entry = gson.fromJson(json, CacheEntry.class);
      return gson.fromJson(entry.mJson, typeOfT);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  @WorkerThread
  public void setUserAge(String key, Object entity, Type type) {
    setUserInner(key, entity, type);
  }

  @WorkerThread
  private void setUserInner(String key, Object entity, Type type) {
    Gson gson = new Gson();
    String json = gson.toJson(entity, type);
    CacheEntry entry = new CacheEntry(json);
    json = gson.toJson(entry, CacheEntry.class);
    if (entity == null) {
      mUserStorage.removeUser(key);
    } else {
      mUserStorage.addUser(key, json);
    }
  }

  static class CacheEntry implements Serializable {

    final String mJson;

    CacheEntry(String json) {
      mJson = json;
    }
  }
}

5、獲取 UserDao 對資料庫表進行 CRUD 操作即可:UserStorage

package com.example.zhangruirui.greendao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.WorkerThread;

import com.example.zhangruirui.User;

import org.greenrobot.greendao.query.DeleteQuery;
import org.greenrobot.greendao.query.QueryBuilder;

public class UserStorage {

  private SQLiteDatabase mDatabase;
  private UserDao mUserDao; // 獲取 dao 物件
  private final static String DB_NAME = "user_name_age.db"; // 定義資料庫名

  // 獲取核心類的實體
  UserStorage(Context context) {
    DaoMaster.OpenHelper helper = new UserDBOpenHelper(context, DB_NAME, null);
    try {
      mDatabase = helper.getWritableDatabase();
      DaoMaster daoMaster = new DaoMaster(mDatabase);
      DaoSession daoSession = daoMaster.newSession();
      mUserDao = daoSession.getUserDao();

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public User getUserByName(String name) {
    if (!isDataBaseValid()) {
      return null;
    }

    return mUserDao.queryBuilder().where(UserDao.Properties.Name.eq(name)).unique();

  }

  /**
   * 根據用戶的名字洗掉對應的記錄
   */
  public void removeUser(String name) {
    if (!isDataBaseValid()) {
      return;
    }
    try {
      QueryBuilder<User> qb = mUserDao.queryBuilder();
      DeleteQuery<User> bd = qb.where(UserDao.Properties.Name.eq(name))
          .buildDelete();
      bd.executeDeleteWithoutDetachingEntities();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 新增記錄,如果存在則更新,不存在直接插入
   */
  @WorkerThread
  public synchronized void addUser(String name, String json) {
    if (!isDataBaseValid()) {
      return;
    }

    try {
      User user = new User();
      user.setName(name);
      user.setAge(json);

      /**
       * 解決 Exception:
       * Cannot update entity without key - was it inserted before?
       */
      User oldUser = getUserByName(name);
      if (oldUser != null) {
        user.setId(oldUser.getId());
      }
      // mUserDao.save(user);
      if (getUserByName(name) == null) {
        mUserDao.insert(user);
      } else {
        mUserDao.update(user);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 洗掉所有資料
   */
  @WorkerThread
  public synchronized void deleteAll() {
    mUserDao.deleteAll();
  }

  private boolean isDataBaseValid() {
    return mUserDao != null;
  }

  public String getUserAge(String key) {
    return mUserDao.queryBuilder().where(UserDao.Properties.Name.eq(key)).list().get(0).getAge();
  }
}

六、注解

@Entity

表明這個物體類會在資料庫中生成一個與之相對應的表

@Entity(
        // If you have more than one schema, you can tell greenDAO
        // to which schema an entity belongs (pick any string as a name).
        schema = "myschema",
        
        // Flag to make an entity "active": Active entities have update,
        // delete, and refresh methods.
        active = true,
        
        // Specifies the name of the table in the database.
        // By default, the name is based on the entities class name.
        nameInDb = "AWESOME_USERS",
        
        // Define indexes spanning multiple columns here.
        indexes = {
                @Index(value = "name DESC", unique = true)
        },
        
        // Flag if the DAO should create the database table (default is true).
        // Set this to false, if you have multiple entities mapping to one table,
        // or the table creation is done outside of greenDAO.
        createInDb = false,

        // Whether an all properties constructor should be generated.
        // A no-args constructor is always required.
        generateConstructors = true,

        // Whether getters and setters for properties should be generated if missing.
        generateGettersSetters = true
)
public class User {
  ...
}

@Id(autoincrement = true)

對應資料表中的 Id 欄位,主鍵,必須為 Long 型
如果需要使用主鍵自增,此時 id 型別為 Long(注意是大寫的)

/**
 * Marks field is the primary key of the entity's table
 */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface Id {
    /**
     * Specifies that id should be auto-incremented (works only for Long/long fields)
     * Autoincrement on SQLite introduces additional resources usage and usually can be avoided
     * @see <a href="https://www.sqlite.org/autoinc.html">SQLite documentation</a>
     */
    boolean autoincrement() default false;
}

@Transient

添加此標記后不會生成資料庫表的列,僅僅作為一個普通的 java 類欄位,用來臨時存盤資料的,不會被持久化

@Unique

表名該屬性在資料庫中只能有唯一值

@Index(unique = true)

七、重要的 api 方法

注意:這些 api 方法中,提到的 key,都是指的主鍵 Long id

1、查詢

http://greenrobot.org/greendao/documentation/queries/

https://juejin.im/post/5abf6ef9f265da2396128456

QueryBuilder.java

2、插入

/**
     * Insert an entity into the table associated with a concrete DAO.
     *
     * @return row ID of newly inserted entity
     */
    public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(), true);
    }

3、更新

public void update(T entity) {
        assertSinglePk();
        DatabaseStatement stmt = statements.getUpdateStatement();
        if (db.isDbLockedByCurrentThread()) {
            synchronized (stmt) {
                if (isStandardSQLite) {
                    updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
                } else {
                    updateInsideSynchronized(entity, stmt, true);
                }
            }
        } else {
            // Do TX to acquire a connection before locking the stmt to avoid deadlocks
            db.beginTransaction();
            try {
                synchronized (stmt) {
                    updateInsideSynchronized(entity, stmt, true);
                }
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
    }

4、插入or更新

public long insertOrReplace(T entity) {
        return executeInsert(entity, statements.getInsertOrReplaceStatement(), true);
    }

    private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        if (db.isDbLockedByCurrentThread()) {
            rowId = insertInsideTx(entity, stmt);
        } else {
            // Do TX to acquire a connection before locking the stmt to avoid deadlocks
            db.beginTransaction();
            try {
                rowId = insertInsideTx(entity, stmt);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
        if (setKeyAndAttach) {
            updateKeyAfterInsertAndAttach(entity, rowId, true);
        }
        return rowId;
    }

save() 方法:

通過 key 屬性判斷是否存在,如果存在就更新資料,如果 key 為 null 就插入,這里的 key 是什么東東?這就是我今天被坑的地方,這個 key 就是 Long id 這個欄位,也就是表中的主鍵!!!

/**
     * "Saves" an entity to the database: depending on the existence of the key property, it will be inserted
     * (key is null) or updated (key is not null).
     * <p>
     * This is similar to {@link #insertOrReplace(Object)}, but may be more efficient, because if a key is present,
     * it does not have to query if that key already exists.
     */
    public void save(T entity) {
        if (hasKey(entity)) {
            update(entity);
        } else {
            insert(entity);
        }
    }

5、洗掉

/** Deletes the given entity from the database. Currently, only single value PK entities are supported. */
    public void delete(T entity) {
        assertSinglePk();
        K key = getKeyVerified(entity);
        deleteByKey(key);
    }

八、入坑系列

1、https://www.jianshu.com/p/7bb9693ea380

2、https://blog.csdn.net/anyanyan07/article/details/73410053

3、打包混淆問題(打包后的 APK,使用不了): 打包后使用不了,debug 能使用,很明顯,混淆的問題,報錯如下:

https://blog.csdn.net/ddxxii/article/details/54866871

Caused by: org.greenrobot.greendao.DaoException: Could not init DAOConfig

解決辦法:
針對 GreenDao 3.0 ,使用時,發布包需要設定混淆

#greendao
-keep class org.greenrobot.greendao.**{*;}
-keep public interface org.greenrobot.greendao.**
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties

#optional
-keep class net.sqlcipher.database.**{*;}
-keep public interface net.sqlcipher.database.**
-dontwarn net.sqlcipher.database.**
-dontwarn org.greenrobot.greendao.**

------致所有正在努力奮斗的程式猿們!加油!!
有碼走遍天下 無碼寸步難行
1024 - 夢想,永不止步!
愛編程 不愛Bug
愛加班 不愛黑眼圈
固執 但不偏執
瘋狂 但不瘋癲
生活里的菜鳥
作業中的大神
身懷寶藏,一心憧憬星辰大海
追求極致,目標始于高山之巔
一群懷揣好奇,夢想改變世界的孩子
一群追日逐浪,正在改變世界的極客
你們用最美的語言,詮釋著科技的力量
你們用極速的創新,引領著時代的變遷

——樂于分享,共同進步,歡迎補充
——Treat Warnings As Errors
——Any comments greatly appreciated
——Talking is cheap, show me the code
——誠心歡迎各位交流討論!QQ:1138517609
——CSDN:https://blog.csdn.net/u011489043
——簡書:https://www.jianshu.com/u/4968682d58d1
——GitHub:https://github.com/selfconzrr

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

標籤:其他

上一篇:Android回爐系列之Surfaceflinger

下一篇:安卓第一階段實訓專案:基于存盤卡音樂播放器

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