主頁 > 移動端開發 > Framework 廣播基礎知識和常見問題

Framework 廣播基礎知識和常見問題

2021-09-15 10:01:32 移動端開發

基礎知識

framework層廣播相關的邏輯主要在AMS.java和BroadcastQueue.java中,代表一個廣播的是BroadcastRecord,

分類

  1. 注冊方式:
  • 靜態注冊: android manifest檔案注冊,常駐廣播,分發慢,
  • 動態注冊:代碼中注冊,非常駐,分發快,
  1. 接收者:
  • 顯示廣播:指定接收方的class型別
  • 隱式廣播:只指定action,uri等, android 8.0開始限制了隱式廣播的接收
  1. 發送方式:
  • 有序廣播:串行分發,效率低,可以通過abortBroadcast截斷,在入隊時會根據優先級對receiver進行排序,
  • 無序廣播:默認無序;并行分發;不可以被攔截、終止、修改;無優先級問題;傳遞資料用intent.putExtra

靜態注冊的廣播,一律按照串行廣播處理,因為可能會設計到拉起多個行程等,串行廣播才有超時處理,

  1. 處理型別:
  • 前臺廣播:發送時添加Intent.FLAG_RECEIVER_FOREGROUND,超時時間10s

  • 后臺廣播:默認后臺,超時時間60s

AMS中前臺廣播和后臺廣播分別有一個廣播佇列,互不干擾,

常見命令

  1. dumpsys activity broadcasts
  • Registered Receivers: 列出所有已注冊的receivers串列
  • Receiver Resolver Table: receiver的決議表
  • Historical broadcasts [foreground]: 前臺廣播歷史記錄
  • Historical broadcasts summary [foreground]: 前臺廣播歷史記錄簡要
  • Historical broadcasts [background]:后臺廣播歷史記錄
  • Historical broadcasts summary [background]: 后臺廣播歷史記錄簡要
 // 已注冊的receiver
  * ReceiverList{7657857 3271 com.android.systemui/1000/u0 remote:5d6fdd6}
    app=3271:com.android.systemui/1000 pid=3271 uid=1000 user=0 // 行程相關資訊,pid,uid,user等
    Filter #0: BroadcastFilter{b28ef44} // 列出當前receiver的所有BroadcastFilter
      Action: "android.intent.action.CLOSE_SYSTEM_DIALOGS"

		// receiver的查詢表
        android.intent.action.CLOSE_SYSTEM_DIALOGS:
          BroadcastFilter{cd0b64d 1000/u0 ReceiverList{5cd41e4 2477 system/1000/u0 local:7b2f177}}
          BroadcastFilter{766ac91 1000/u0 ReceiverList{b2bd6b8 2477 system/1000/u0 local:dd6451b}}
          BroadcastFilter{707a39e 1000/u-1 ReceiverList{6db3dd9 2477 system/1000/u-1 local:28e4020}}
          BroadcastFilter{921cf0a 1000/u0 ReceiverList{a168375 3271 com.android.systemui/1000/u0 remote:792eaac}}
          BroadcastFilter{eb0f625 1000/u-1 ReceiverList{f9ba1c 3271 com.android.systemui/1000/u-1 remote:311c78f}}
          BroadcastFilter{b28ef44 1000/u0 ReceiverList{**7657857** 3271 com.android.systemui/1000/u0 remote:5d6fdd6}}
          BroadcastFilter{3dc5af6 1000/u0 ReceiverList{9ad5b91 2477 system/1000/u0 local:ab3e1b8}}
          BroadcastFilter{84a1c2 1000/u0 ReceiverList{27a8e0d 5062 com.miui.securitycenter.remote/1000/u0 remote:49ca0a4}}
          BroadcastFilter{6151148 1000/u0 ReceiverList{6ac8feb 5062 com.miui.securitycenter.remote/1000/u0 remote:17c273a}}
          BroadcastFilter{1525a45 1000/u0 ReceiverList{caf9abc 5062 com.miui.securitycenter.remote/1000/u0 remote:aa646af}}

  Historical Broadcast foreground #1: // 前臺廣播佇列第一條廣播
    BroadcastRecord{8888912 u0 com.xiaomi.mi_connect_service.mi_mover_endpoint_found} to user 0 // user 0
    Intent { act=com.xiaomi.mi_connect_service.mi_mover_endpoint_found flg=0x11000030 (has extras) } // flag有:FLAG_RECEIVER_FOREGROUND | FLAG_RECEIVER_INCLUDE_BACKGROUND | |FLAG_INCLUDE_STOPPED_PACKAGES | FLAG_EXCLUDE_STOPPED_PACKAGES
      extras: Bundle[{STRIPPED=1}]
    caller=com.xiaomi.mi_connect_service 9219:com.xiaomi.mi_connect_service/1000 pid=9219 uid=1000 // 發送廣播app的資訊
    requiredPermissions=[com.xiaomi.mi_connect_service.permission.RECEIVE_ENDPOINT]  appOp=-1 // 廣播要求receiver的權限
    enqueueClockTime=2021-09-11 16:04:57.623 dispatchClockTime=2021-09-11 16:04:57.623 // 入隊時間和分發時間
    dispatchTime=-1m2s207ms (0 since enq) finishTime=-1m2s164ms (+43ms since disp)  // 結束時間 & 分發用時
    resultTo=null resultCode=0 resultData=null // 一般resultCode為0且resultTo為null且無” ordered= “表示靜態注冊的無序廣播
    nextReceiver=1 receiver=null
    Deliver +42ms #0: (manifest) //派發給靜態注冊的receiver
      priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=false //優先級默認為0,
      ActivityInfo: // receiver的資訊
        name=com.miui.huanji.ble.MiConnectBleReceiver
        packageName=com.miui.huanji
        enabled=true exported=true directBootAware=false
        resizeMode=RESIZE_MODE_RESIZEABLE

Historical Broadcast background #29: // 后臺廣播佇列第29條
BroadcastRecord{e0ebea0 u-1 android.intent.action.PHONE_STATE{color}} to user -1 // -1代表user_all
Intent { act=android.intent.action.PHONE_STATE flg=0x1000010 (has extras) } // intent資訊
extras: Bundle[\{incoming_number=18381098208, state=IDLE}]
caller=android 1309:system/1000 pid=1309 uid=1000 // 發送廣播app的資訊
requiredPermissions=[android.permission.READ_PHONE_STATE, android.permission.READ_CALL_LOG] appOp=-1  // 廣播要求receiver的權限
enqueueClockTime=2021-06-23 11:41:05.687 dispatchClockTime=2021-06-23 11:41:05.687  // 入隊時間和分發時間
dispatchTime=-1m39s763ms (0 since enq) finishTime=-1m39s755ms (+8ms since disp)  // 結束時間 & 分發用時
// 分發每一個receiver的分發資訊以及狀態
// Deliver代表當前receiver已分發到
Deliver 0 #0: BroadcastFilter{12c9c23 1000/u-1 ReceiverList{5b06152 1309 system/1000/u-1 local:4a651dd}} 
Deliver 0 #1: BroadcastFilter{672acd7 1000/u0 ReceiverList{5be3456 1780 com.android.systemui/1000/u0 remote:b33e71}}
Deliver 0 #2: BroadcastFilter{f932daf 1000/u-1 ReceiverList{1ef518e 1780 com.android.systemui/1000/u-1 remote:35e2d89}}
Deliver 0 #3: BroadcastFilter{d0d467b 1001/u0 ReceiverList{b4a5e0a 1996 com.android.phone/1001/u0 remote:65bee75}}
Deliver 0 #4: BroadcastFilter{4081988 1002/u0 ReceiverList{661912b 2315 com.xiaomi.bluetooth/1002/u0 remote:34b657a}}
Deliver 0 #5: BroadcastFilter{b6bda66 1000/u0 ReceiverList{73672c1 5261 com.miui.powerkeeper/1000/u0 remote:bc317a8}}
// skip表示由于權限等原因,跳過分發到該app
Skipped 0 #6: BroadcastFilter{ebabe72 10243/u0 ReceiverList{5596f7d 21465 com.smile.gifmaker/10243/u0 remote:96d61d4}} 
Skipped 0 #7: BroadcastFilter{f7d90ab 10243/u0 ReceiverList{261b2fa 21465 com.smile.gifmaker/10243/u0 remote:ef8c125}}
Skipped 0 #8: BroadcastFilter{1f05292 10250/u0 ReceiverList{396401d 12339 com.tencent.mobileqq/10250/u0 remote:e8e90f4}}
Skipped 0 #9: BroadcastFilter{d98737f 10250/u0 ReceiverList{469cc9e 12339 com.tencent.mobileqq/10250/u0 remote:b386ad9}}
Skipped 0 #10: BroadcastFilter{4e1588 10250/u0 ReceiverList{851fd2b 29070 com.tencent.mobileqq:tool/10250/u0 remote:64a017a}}

  1. dumpsys activity broadcast-stats :廣播的統計
    android.intent.action.DROPBOX_ENTRY_ADDED:
      Number received: 0, skipped: 302  // 分發0次,跳過302次
      Total dispatch time: +24s731ms, max: +13s538ms // 總分發用時,和最大分發用時
      Package android: 151 times  
      Bg Check Violation com.google.android.gms: 302 times

常見的一些Intent中的flag

  1. FLAG_RECEIVER_REGISTERED_ONLY

表示當前廣播只允許動態注冊的receiver接收,避免避免一些系統廣播(如TIME_TICK等)拉起靜態注冊的app行程,

  1. FLAG_RECEIVER_REPLACE_PENDING
    如果廣播佇列中存在沒有被處理的相同廣播,則直接替換掉之前的廣播,無序再入隊分發

  2. FLAG_RECEIVER_FOREGROUND
    發送廣播時添加這個flag,廣播將會被添加到前臺廣播佇列中,

AMS中的一些屬性

// 已注冊的動態廣播的receiver,receiver IBinder為key, ReceiverList為value;ReceiverList繼承自ArrayList<BroadcastFilter>
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();  

// 記錄著已注冊廣播的receiver的resolver,廣播發送時可根據intent和type等查詢得到對應的動態廣播接收者
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
            = new IntentResolver<BroadcastFilter, BroadcastFilter>();

BroadcastRecord的一些屬性

int state; //當前廣播狀態,初始是idle
final int[] delivery; // 每一個receiver的處理狀態
final List receivers; // 廣播的接收者串列,包括靜態注冊的ResolveInfo和動態注冊的BroadcastFilter
int nextReceiver; // 下一個要處理的廣播接收者的索引,對應于receivers的索引
IIntentReceiver resultTo; // 有序廣播指定的最后一個接收者
long enqueueClockTime;  // 廣播的入隊時間
long dispatchTime; // 分發時間
long receiverTime; //開始派發廣播到app端的時間,用于計算anr超時時間
long finishTime;  //結束廣播時間
IBinder receiver; // 當前正在處理的receiver(ReceiverDispatcher.InnerReceiver),有序廣播和靜態注冊的才會賦值

ResolveInfo的一些屬性

public int priority; // 優先級
public int preferredOrder; //偏好優先級
public int match; // 系統評估活動與IntentFilter的匹配情況,
public int specificIndex = -1; //僅當由 queryIntentActivityOptions(ComponentName, Intent[], Intent, int)回傳時設定,這告訴你這個結果來自哪個給定的特定意圖,
public boolean isDefault; //此過濾器指定了Intent.CATEGORY_DEFAULT,這意味著它將被視為用戶可以對此資料執行的默認操作,

廣播的狀態

    static final int IDLE = 0;  // 廣播的初始狀態,派發時只處理這種狀態的廣播
    static final int APP_RECEIVE = 1; // 表示廣播正在派發到app行程,靜態receiver
    static final int CALL_IN_RECEIVE = 2; // 表示廣播正在派發到app行程,有序廣播,動態receiver
    static final int CALL_DONE_RECEIVE = 3; // 廣播已經派發到app行程,有序廣播,動態receiver
    static final int WAITING_SERVICES = 4; // 等待后臺service啟動

廣播接收者的處理狀態

    static final int DELIVERY_PENDING = 0; // 默認狀態
    static final int DELIVERY_DELIVERED = 1; // 表示已經派發到app行程
    static final int DELIVERY_SKIPPED = 2; // 表示由于某種原因,未派發到app行程
    static final int DELIVERY_TIMEOUT = 3; // 表示派發到app端,超時未結束派發

廣播不被處理的情況

實際上廣播不被處理的情況有很多種,這里只是列出常遇到的一些情況,

接收端未宣告廣播指定權限

W/BroadcastQueue: Permission Denial: receiving Intent { act=android.intent.action.ORDER_BROADCAST flg=0x10 } to com.example.myapplication/com.example.receive.MyReceiver requires com.android.permission.ORDER_PERMISSION due to sender com.example.testapplication (uid 10255)

在BroadcastQueue的deliverToRegisteredReceiverLocked(動態注冊)或processNextBroadcastLocked(靜態注冊)方法中,會去校驗接收端是否都具備廣播要求的權限,如果不具備,會跳過此次廣播的分發到app端,

      if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
            for (int i = 0; i < r.requiredPermissions.length; i++) {
                String requiredPermission = r.requiredPermissions[i];
                int perm = mService.checkComponentPermission(requiredPermission,
                        filter.receiverList.pid, filter.receiverList.uid, -1, true);
                if (perm != PackageManager.PERMISSION_GRANTED) {
                    Slog.w(TAG, "Permission Denial: receiving " + r.intent.toString() + " to " + filter.receiverList.app + " (pid=" + filter.receiverList.pid
                            + ", uid=" + filter.receiverList.uid + ")" + " requires " + requiredPermission + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
                    skip = true;
                    break;
                }

android 8.0對隱式廣播的限制

W BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_RESTARTED dat=package:com.android.soundrecorder flg=0x10 (has extras) } to com.xiaomi.misubscreenui/.receiver.LightDeviceStatusReceiver

限制如下兩種情況下的廣播分發

  • Intent中添加了FLAG_RECEIVER_EXCLUDE_BACKGROUND
  • Intent中無FLAG_RECEIVER_INCLUDE_BACKGROUND flag的隱式廣播

FLAG_RECEIVER_INCLUDE_BACKGROUND 添加的場景

也就是如下這些隱式廣播處于后臺清單檔案注冊的也能接收到

  • ACTION_BOOT_COMPLETED
@BroadcastBehavior(includeBackground = true)
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";

allow-implicit-broadcast 串列內的intent會在broadcastIntentLocked中添加

        if (action != null) {
            if (getBackgroundLaunchBroadcasts().contains(action)) {
                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            }

<!-- Broadcast actions that are currently exempted from O+ background
         delivery restrictions. -->
    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED" />
    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED" />
    <allow-implicit-broadcast action="android.intent.action.DATA_SMS_RECEIVED" />
    <allow-implicit-broadcast action="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
    <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
    <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
    <allow-implicit-broadcast action="android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION" />
    <allow-implicit-broadcast action="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION" />
    <allow-implicit-broadcast action="android.provider.Telephony.SECRET_CODE" />
    <allow-implicit-broadcast action="android.provider.Telephony.SMS_CB_RECEIVED" />
    <allow-implicit-broadcast action="android.provider.Telephony.SMS_DELIVER" />
    <allow-implicit-broadcast action="android.provider.Telephony.SMS_RECEIVED" />
    <allow-implicit-broadcast action="android.provider.Telephony.SMS_REJECTED" />
    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_DELIVER" />
    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_RECEIVED" />
    <allow-implicit-broadcast action="android.telephony.action.CARRIER_CONFIG_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.SECRET_CODE" />
    <allow-implicit-broadcast action="android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.SIM_CARD_STATE_CHANGED" />
    <allow-implicit-broadcast action="android.telephony.action.SIM_SLOT_STATUS_CHANGED" />

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj, boolean parallelOnce) {
    .....
    boolean skip = false;
    if (!skip) {
		// 檢查當前app的啟動權限
        final int allowed = mService.getAppStartModeLocked(
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
                info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false, r.callerPackage);
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
            if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                skip = true;
			// 1. FLAG_RECEIVER_EXCLUDE_BACKGROUND , 2. 隱式廣播的限制
            } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
                    || (r.intent.getComponent() == null
                        && r.intent.getPackage() == null
                        && ((r.intent.getFlags()
                                & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
                        && !isSignaturePerm(r.requiredPermissions))) {
                mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
                        component.getPackageName());
                Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to "
                        + component.flattenToShortString());
                skip = true;
            }
        }
    }
 }

行程頻繁crash

W BroadcastQueue: Unable to launch app com.bsp.catchlog/1000 for broadcast Intent { act=android.provider.Telephony.SECRET_CODE dat=android_secret_code://284 flg=0x1400010 }: process is bad

在AppErrors中處理當前crash時會去判斷是否標記當前行程為bad行程,

    boolean handleAppCrashLocked(ProcessRecord app, String reason,
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
      .......
        if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
            Slog.w(TAG, "Process " + app.info.processName
                    + " has crashed too many times: killing!");
            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                    app.userId, app.info.processName, app.uid);
            mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
            if (!app.isPersistent()) {
                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
                        app.info.processName);
                if (!app.isolated) {
              
                    mBadProcesses.put(app.info.processName, app.uid,
                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                    mProcessCrashTimes.remove(app.info.processName, app.uid);
                }
                app.bad = true;
                app.removed = true;
            .......
    }

而在啟動行程時,ProcessList的startProcessLocked中會去判斷如果時后臺啟動且行程為bad,則回傳null,終止啟動行程

        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
                if (mService.mAppErrors.isBadProcessLocked(info)) {
                    return null;
                }
            }

BroadcastQueue中processNextBroadcastLocked中行程不存在去創建行程時如果失敗,就會列印上面那行log

        if ((r.curApp=mService.startProcessLocked(targetProcess,
                info.activityInfo.applicationInfo, true,
                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                new HostingRecord("broadcast", r.curComponent),
                isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
                // MIUI MOD
                // (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false, r.callerPackage))
                        == null) {
            // Ah, this recipient is unavailable.  Finish it if necessary,
            // and mark the broadcast record as ready for the next.
            Slog.w(TAG, "Unable to launch app "
                    + info.activityInfo.applicationInfo.packageName + "/"
                    + receiverUid + " for broadcast "
                    + r.intent + ": process is bad");
            logBroadcastReceiverDiscardLocked(r);
            finishReceiverLocked(r, r.resultCode, r.resultData,
                    r.resultExtras, r.resultAbort, false);
            scheduleBroadcastsLocked();
            r.state = BroadcastRecord.IDLE;
            return;
        }

廣播的三千問

  1. 有序廣播的receiver在什么時候進行排序?
  2. 廣播的入隊和分發流程是同步還是異步?
  3. 靜態注冊的receiver(有序&無序)對應的廣播入有序廣播佇列還是無序(并行)廣播佇列?
  4. 廣播有哪些狀態?分別代表什么?
  5. receiver有哪些狀態,分別代表什么?
  6. 廣播程序中涉及到哪些超時邏輯?
  7. 系統有幾個廣播佇列?
  8. 如何發送有序廣播?粘性廣播?前臺廣播?
  9. android O 加入了后臺啟動限制,主要限制邏輯是?
  10. 什么情況下發送的廣播不會入隊?
  11. 通過廣播Receiver拉起行程的行程優先級有什么特點?
  12. 如何通過log查看廣播的是否分發超時?以及廣播是否派給對應的receiver?分發狀態?
  13. 如果通過log查看廣播是否是有序廣播?靜態注冊?
  14. 哪個廣播享受總分發超時豁免?
  15. 哪些隱式廣播可以派發給后臺行程?
  16. 有序廣播可以通過什么傳遞資料?無序廣播呢?
  17. 有序廣播的resultTo(最終接收者)的派發時機?
  18. 發送受保護的廣播有什么限制?
  19. 發送sticky廣播有什么限制?
  20. 如何做到廣播只派發給動態注冊的receiver?
  21. 派發靜態廣播時,行程不存在,如何處理?
  22. 廣播入隊前如何獲取靜態receivers?如何獲取動態receivers?

為方便學習了解到更多的Handler知識點,特此我將一些 Android 開發相關的學習檔案、面試題、Android 核心筆記等檔案進行了整理,并上傳之我GitHub專案中,如有需要參考的可以直接去我 GitHub ,希望能幫助到大家學習提升,


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

標籤:其他

上一篇:Android Jetpack系列之MVVM使用及封裝

下一篇:學習筆記-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