{"id":4415,"date":"2023-03-07T22:08:04","date_gmt":"2023-03-07T14:08:04","guid":{"rendered":"https:\/\/blog.coderfan.org\/?p=4415"},"modified":"2023-04-01T15:17:48","modified_gmt":"2023-04-01T07:17:48","slug":"android12-native-service-to-play-touchbeep","status":"publish","type":"post","link":"https:\/\/blog.coderfan.org\/en\/android12-native-service-to-play-touchbeep.html","title":{"rendered":"Android12 Native\u5b9e\u73b0\u6a21\u62df\u6309\u952e\u97f3"},"content":{"rendered":"<div class='booster-block booster-read-block'>\n                <div class=\"twp-read-time\">\n                \t<i class=\"booster-icon twp-clock\"><\/i> <span>Read Time:<\/span>5 Minute, 51 Second                <\/div>\n\n            <\/div>\n<p class=\"has-text-align-justify\">\u5728\u5b89\u5353\u7cfb\u7edf\u4e2d\uff0c\u6211\u4eec\u4e0e\u7528\u6237\u7684\u4ea4\u4e92\u9664\u4e86\u754c\u9762\u4e0aUI\/UE\u7684\u76f4\u89c2\u5c55\u793a\uff0c\u8fd8\u6709\u6309\u952e\/\u89e6\u5c4f\u97f3\u7684\u4e92\u52a8\u3002\u6309\u952e\u97f3\u53ef\u4ee5\u5e2e\u52a9\u7528\u6237\u66f4\u597d\u5730\u611f\u77e5\u5f53\u524d\u64cd\u4f5c\uff0c\u5224\u65ad\u70b9\u51fb\u884c\u4e3a\u662f\u5426\u6709\u6548\u3002\u5bf9\u4e8e\u5f00\u53d1\u4eba\u5458\u800c\u8a00\uff0c\u5f53\u753b\u9762\u5361\u6ede\u65f6\u53ef\u4ee5\u501f\u7531\u6309\u952e\u97f3\u5224\u65ad\u7cfb\u7edf\u662f\u5426\u4ecd\u5728\u8fd0\u884c\uff0c\u5982Input\u8f93\u5165\u662f\u5426\u5b58\u5728\u5f02\u5e38\uff0cAudio\u7cfb\u7edf\u662f\u5426\u6b63\u5e38\u5de5\u4f5c\u7b49\uff0c\u4fbf\u4e8e\u540e\u7eed\u95ee\u9898\u6392\u67e5\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u5728Android\u7cfb\u7edf\u4e0a\u5c42\uff0c\u5f00\u53d1\u4eba\u5458\u4e00\u822c\u4e0d\u9700\u8981\u53bb\u5173\u6ce8\u7cfb\u7edf\u6309\u952e\u97f3\u662f\u5982\u4f55\u5b9e\u73b0\u7684\u3002\u7531\u4e8eAndroid\u7cfb\u7edf\u826f\u597d\u7684\u5c01\u88c5\u6027\uff0c\u5f00\u53d1\u4eba\u5458\u65e0\u987b\u505a\u4efb\u4f55\u989d\u5916\u8bbe\u5b9a\u5373\u53ef\u8ba9\u81ea\u5df1\u7684View\u63a7\u4ef6\u643a\u5e26\u6309\u952e\u97f3\u3002\u4f46\u5728\u67d0\u4e9b\u65f6\u5019\uff0c\u6211\u4eec\u9700\u8981\u5728Native\u5c42\u5b9e\u73b0\u6309\u952e\u97f3\u64ad\u653e\u3002\u4eca\u5929\u8fd9\u7bc7\u6587\u7ae0\u5c06\u4e3a\u5927\u5bb6\u4ecb\u7ecd\u5982\u4f55\u5728Android Native\u4e2d\u5b9e\u73b0\u6309\u952e\u97f3\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Background<\/h3>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u6211\u4eec\u5f00\u59cb\u5b9e\u73b0\u81ea\u5df1\u7684\u6309\u952e\u97f3\u64ad\u653e\u524d\uff0c\u4e0d\u59a8\u5148\u4e86\u89e3\u4e00\u4e0b\u539f\u751f\u7cfb\u7edf\u4e2d\u662f\u5982\u4f55\u5b9e\u73b0\u6309\u952e\u97f3\u64ad\u653e\u7684\u3002\u901a\u8fc7\u5bf9\u539f\u751f\u7cfb\u7edf\u6309\u952e\u97f3\u64ad\u653e\u7684\u5b66\u4e60\uff0c\u6211\u4eec\u518d\u6765\u601d\u8003\u5982\u4f55\u5b9e\u73b0Native\u4e2d\u64ad\u653e\u6309\u952e\u97f3\u7684\u9700\u6c42\u3002<\/p>\n\n\n\n<p>\u5e9f\u8bdd\u4e0d\u591a\u8bf4\uff0c\u8fd9\u91cc\u5148\u7ed9\u5927\u5bb6\u4e0a\u56fe\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"865\" height=\"549\" src=\"https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/03\/image.png\" alt=\"\" class=\"wp-image-4418\" srcset=\"https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/03\/image.png 865w, https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/03\/image-300x190.png 300w, https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/03\/image-768x487.png 768w, https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/03\/image-18x12.png 18w\" sizes=\"auto, (max-width: 865px) 100vw, 865px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-justify\">\u4ece\u8fd9\u4e2a\u6d3b\u52a8\u56fe\u4e2d\uff0c\u6211\u4eec\u4e0d\u96be\u770b\u51fa\uff0c\u6309\u952e\u97f3\u7684\u5b9e\u73b0\u6700\u7ec8\u662f\u4e0eView\u8fdb\u884c\u7ed1\u5b9a\u7684\u3002\u5f53\u7528\u6237\u70b9\u51fb\u754c\u9762View\u63a7\u4ef6\u65f6\uff0cInput\u4e8b\u4ef6\u7ecf\u7531InputFlinger\u4e2d\u7684Eventhub-&gt;InputReader-&gt;InputDispatcher\u4f20\u9012\u81f3Java Framework\u4e2d\u7684InputManager\uff0c\u7ecf\u7531WindowManager\u5206\u53d1\u81f3ViewRoot-&gt;ViewGroup-&gt;View,\u5728View\u7c7b\u4e2d\u8fdb\u884c\u5904\u7406\uff0c\u6e90\u7801\u5b9e\u73b0\u4f4d\u4e8eframeworks\/base\/core\/java\/android\/view\/View.java\u6587\u4ef6\u5185\uff0c\u6211\u4eec\u53ef\u4ee5\u627e\u5230onTouchEvent\u65b9\u6cd5\uff0c\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"java\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\"> public boolean onTouchEvent(MotionEvent event) {\n        final float x = event.getX();\n        final float y = event.getY();\n        final int viewFlags = mViewFlags;\n        final int action = event.getAction();\n\n        final boolean clickable = ((viewFlags &amp; CLICKABLE) == CLICKABLE\n                || (viewFlags &amp; LONG_CLICKABLE) == LONG_CLICKABLE)\n                || (viewFlags &amp; CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;\n\n        if ((viewFlags &amp; ENABLED_MASK) == DISABLED\n                &amp;&amp; (mPrivateFlags4 &amp; PFLAG4_ALLOW_CLICK_WHEN_DISABLED) == 0) {\n            if (action == MotionEvent.ACTION_UP &amp;&amp; (mPrivateFlags &amp; PFLAG_PRESSED) != 0) {\n                setPressed(false);\n            }\n            mPrivateFlags3 &amp;= ~PFLAG3_FINGER_DOWN;\n            \/\/ A disabled view that is clickable still consumes the touch\n            \/\/ events, it just doesn't respond to them.\n            return clickable;\n        }\n        if (mTouchDelegate != null) {\n            if (mTouchDelegate.onTouchEvent(event)) {\n                return true;\n            }\n        }\n\n        if (clickable || (viewFlags &amp; TOOLTIP) == TOOLTIP) {\n            switch (action) {\n                case MotionEvent.ACTION_UP:\n                    mPrivateFlags3 &amp;= ~PFLAG3_FINGER_DOWN;\n                    if ((viewFlags &amp; TOOLTIP) == TOOLTIP) {\n                        handleTooltipUp();\n                    }\n                    if (!clickable) {\n                        removeTapCallback();\n                        removeLongPressCallback();\n                        mInContextButtonPress = false;\n                        mHasPerformedLongPress = false;\n                        mIgnoreNextUpEvent = false;\n                        break;\n                    }\n                    boolean prepressed = (mPrivateFlags &amp; PFLAG_PREPRESSED) != 0;\n                    if ((mPrivateFlags &amp; PFLAG_PRESSED) != 0 || prepressed) {\n                        \/\/ take focus if we don't have it already and we should in\n                        \/\/ touch mode.\n                        boolean focusTaken = false;\n                        if (isFocusable() &amp;&amp; isFocusableInTouchMode() &amp;&amp; !isFocused()) {\n                            focusTaken = requestFocus();\n                        }\n\n                        if (prepressed) {\n                            \/\/ The button is being released before we actually\n                            \/\/ showed it as pressed.  Make it show the pressed\n                            \/\/ state now (before scheduling the click) to ensure\n                            \/\/ the user sees it.\n                            setPressed(true, x, y);\n                        }\n\n                        if (!mHasPerformedLongPress &amp;&amp; !mIgnoreNextUpEvent) {\n                            \/\/ This is a tap, so remove the longpress check\n                            removeLongPressCallback();\n                            if (!focusTaken) {\n                                \/\/ Use a Runnable and post this rather than calling\n                                \/\/ performClick directly. This lets other visual state\n                                \/\/ of the view update before click actions start.\n                                if (mPerformClick == null) {\n                                    mPerformClick = new PerformClick();\n                                }\n                                if (!post(mPerformClick)) {\n                                    performClickInternal();\n                                }\n                            }\n                        }\n\n                        if (mUnsetPressedState == null) {\n                            mUnsetPressedState = new UnsetPressedState();\n                        }\n\n                        if (prepressed) {\n                            postDelayed(mUnsetPressedState,\n                                    ViewConfiguration.getPressedStateDuration());\n                        } else if (!post(mUnsetPressedState)) {\n                            \/\/ If the post failed, unpress right now\n                            mUnsetPressedState.run();\n                        }\n\n                        removeTapCallback();\n                    }\n                    mIgnoreNextUpEvent = false;\n                    break;\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u4ece\u8fd9\u6bb5\u4ee3\u7801\u4e2d\u6211\u4eec\u53ef\u4ee5\u770b\u5230\uff0c\u5f53\u6211\u4eec\u68c0\u6d4b\u5230\u6309\u952e\u5904\u4e8e\u6309\u538b\u72b6\u6001\u540e\u62ac\u8d77\u65f6\uff08Event\u7531down-&gt;Up\uff09\uff0c\u9700\u8981\u6267\u884cPerformClick\u65b9\u6cd5\uff0c\u5176\u5185\u90e8\u4ee3\u7801\u5982\u56fe\u6240\u793a\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"java\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">public boolean performClick() {\n        \/\/ We still need to call this method to handle the cases where performClick() was called\n        \/\/ externally, instead of through performClickInternal()\n        notifyAutofillManagerOnClick();\n\n        final boolean result;\n        final ListenerInfo li = mListenerInfo;\n        if (li != null &amp;&amp; li.mOnClickListener != null) {\n            playSoundEffect(SoundEffectConstants.CLICK);\n            li.mOnClickListener.onClick(this);\n            result = true;\n        } else {\n            result = false;\n        }\n\n        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);\n\n        notifyEnterOrExitForAutoFillIfNeeded(true);\n\n        return result;\n    }\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u4e4b\u540e\u6211\u4eec\u8fdb\u5165\u5230playSoundEffect\u65b9\u6cd5\uff0c\u4e00\u8def\u56de\u6eaf\u6211\u4eec\u53ef\u4ee5\u56de\u6eaf\u5230frameworks\/base\/core\/java\/android\/view\/ViewRootImpl.java\u6587\u4ef6\u4e2d\u7684 playSoundEffect \u65b9\u6cd5\uff0c\u5176\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"java\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\"> public void playSoundEffect(@SoundEffectConstants.SoundEffect int effectId) {\n        checkThread();\n\n        try {\n            final AudioManager audioManager = getAudioManager();\n\n            if (mFastScrollSoundEffectsEnabled\n                    &amp;&amp; SoundEffectConstants.isNavigationRepeat(effectId)) {\n                audioManager.playSoundEffect(\n                        SoundEffectConstants.nextNavigationRepeatSoundEffectId());\n                return;\n            }\n\n            switch (effectId) {\n                case SoundEffectConstants.CLICK:\n                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);\n                    return;\n                case SoundEffectConstants.NAVIGATION_DOWN:\n                case SoundEffectConstants.NAVIGATION_REPEAT_DOWN:\n                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);\n                    return;\n                case SoundEffectConstants.NAVIGATION_LEFT:\n                case SoundEffectConstants.NAVIGATION_REPEAT_LEFT:\n                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);\n                    return;\n                case SoundEffectConstants.NAVIGATION_RIGHT:\n                case SoundEffectConstants.NAVIGATION_REPEAT_RIGHT:\n                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);\n                    return;\n                case SoundEffectConstants.NAVIGATION_UP:\n                case SoundEffectConstants.NAVIGATION_REPEAT_UP:\n                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);\n                    return;\n                default:\n                    throw new IllegalArgumentException(\"unknown effect id \" + effectId +\n                            \" not defined in \" + SoundEffectConstants.class.getCanonicalName());\n            }\n        } catch (IllegalStateException e) {\n            \/\/ Exception thrown by getAudioManager() when mView is null\n            Log.e(mTag, \"FATAL EXCEPTION when attempting to play sound effect: \" + e);\n            e.printStackTrace();\n        }\n    }<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u8fd9\u91cc\u6211\u4eec\u53ef\u4ee5\u770b\u5230\uff0cView\u4e2dplaySoundEffect\u6700\u7ec8\u4f1a\u8fdb\u5165\u5230AudioManager\uff0c\u5728AudioManager\u4e2d\u901a\u8fc7JNI\u8c03\u7528SoundPool\uff08\/frameworks\/base\/media\/jni\/soundpool\uff09\uff0c\u518d\u901a\u8fc7Stream.cpp\u4e2dplay_l()\u51fd\u6570\u8c03\u7528AudioTrack&nbsp;\uff08\/frameworks\/av\/media\/libaudioclient\uff09\u6765\u8fdb\u884c\u6309\u952e\u97f3\u7684\u64ad\u653e\u3002<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"cpp\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\nif (mMuted) {\n    newTrack->setVolume(0.0f, 0.0f);\n} else {\n     newTrack->setVolume(leftVolume, rightVolume);\n}\nnewTrack->setLoop(0, frameCount, loop);\nmAudioTrack->start();\nmSound = sound;\nmSoundID = sound->getSoundID();\nmPriority = priority;\nmLoop = loop;\nmLeftVolume = leftVolume;\nmRightVolume = rightVolume;\nmRate = rate;<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u7531\u6b64\uff0c\u6211\u4eec\u53ef\u4ee5\u5f88\u660e\u663e\u5730\u770b\u51fa\uff0cAndroid\u4e0a\u5c42\u7684\u6309\u952e\u97f3\u64ad\u653e\u5b9e\u9645\u4e0a\u90fd\u662f\u7531 libaudioclient \u4e2d\u7684AudioTrack\u8fdb\u884c\u64ad\u653e\u7684\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8bbe\u8ba1\u601d\u8def<\/h3>\n\n\n\n<p class=\"has-text-align-justify\">\u65e2\u7136\u6211\u4eec\u5df2\u7ecf\u4e86\u89e3\u5230\u4e86\u539f\u751f\u6309\u952e\u97f3\u7684\u5b9e\u73b0\u65b9\u5f0f\uff0c\u6211\u4eec\u4e5f\u53ef\u4ee5\u4eff\u7167\u539f\u751f\u7684\u6309\u952e\u97f3\u64ad\u653e\u6d41\u7a0b\u6765\u8fdb\u884c\u64ad\u653e\uff0c\u5173\u952e\u5373\u5728\u4e8eAudioTrack\u7684\u4f7f\u7528\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">AudioTrack\u662fAndroid Audio\u7cfb\u7edf\u4e2d\u5f88\u91cd\u8981\u7684\u4e00\u4e2a\u57fa\u7840\uff0c\u53ef\u4ee5\u7528\u4e8e\u64ad\u653e\u5355\u4e00\u97f3\u6e90\u3002\u4e0d\u8fc7\u9700\u8981\u6ce8\u610f\u7684\u662f\uff0cAudioTrack\u6240\u64ad\u653e\u7684\u97f3\u6e90\u5fc5\u987b\u662f\u539f\u59cb\u7684PCM\u6d41\u6570\u636e\uff0c\u800c\u539f\u751f\u7684\u6309\u952e\u97f3\u6e90\u4e3aogg\u683c\u5f0f\uff0c\u662f\u7ecf\u8fc7\u4e86\u89e3\u7801\u540e\u518d\u4f20\u9012\u5230AudioTrack\u5185\u8fdb\u884c\u64ad\u653e\u7684\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u6211\u4eec\u81ea\u5df1\u7684\u5b9e\u73b0\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u63d0\u524d\u5c06\u539f\u59cb\u7684ogg\u683c\u5f0f\u7684\u6309\u952e\u97f3\u6e90\u4f7f\u7528\u5916\u90e8\u7684\u683c\u5f0f\u8f6c\u6362\u5de5\u5177\u8f6c\u6362\u4e3awav\u683c\u5f0f\uff0c\u4f7f\u7528wav\u683c\u5f0f\u662f\u56e0\u4e3awav\u683c\u5f0f\u7684\u6570\u636e\u53ea\u9700\u8981\u7ecf\u8fc7\u7b80\u5355\u89e3\u6790\u5c31\u80fd\u5f97\u5230\u539f\u59cb\u7684PCM\u6570\u636e\u6d41\uff0c\u7136\u540e\u8c03\u7528AudioTrack\u8fdb\u884c\u64ad\u653e\u5373\u53ef\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u5177\u4f53\u5b9e\u73b0\u65f6\uff0c\u6211\u4eec\u53ef\u4ee5\u91c7\u7528\u5355\u4f8b\u5b9e\u73b0\uff0c\u5373\u6240\u6709\u7684\u6309\u952e\u97f3\u64ad\u653e\u90fd\u901a\u8fc7\u4e00\u4e2a\u5b9e\u4f8b\u6765\u8fdb\u884c\u63a7\u5236\uff0c\u5728\u7a0b\u5e8f\u521d\u59cb\u5316\u8fc7\u7a0b\u4e2d\u5c31\u5c06\u97f3\u6e90\u6587\u4ef6\u89e3\u6790\uff0c\u83b7\u53d6\u539f\u59cb\u7684PCM\u6570\u636e\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u5f53\u6211\u4eec\u5728\u63a5\u6536\u5230Input\u4e8b\u4ef6\u65f6\uff0c\u9700\u8981\u5224\u65ad\u4e8b\u4ef6\u7c7b\u578b\uff0c\u5f53\u6536\u5230UP\u4e8b\u4ef6\u65f6\uff0c\u5373\u5f00\u59cb\u64ad\u653e\u6309\u952e\u97f3\u3002\u8fd9\u91cc\u4e3a\u4e86\u907f\u514d\u963b\u585eUI\u54cd\u5e94\uff0c\u9700\u8981\u5355\u72ec\u8d77\u7ebf\u7a0b\u6765\u64ad\u653e\uff0c\u64ad\u653e\u7ed3\u675f\u5219\u7ebf\u7a0b\u7ed3\u675f\uff0c\u81ea\u884c\u9500\u6bc1\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u4ee5\u4e0a\u4e3a\u4e00\u4e2a\u6574\u4f53\u7684\u5b9e\u73b0\u601d\u8def\uff0c\u63a5\u4e0b\u6765\u662f\u5177\u4f53\u7684\u4ee3\u7801\u5b9e\u73b0\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u4ee3\u7801\u5b9e\u73b0<\/h3>\n\n\n\n<p>\u5173\u4e8e\u4ee3\u7801\u5b9e\u73b0\uff0c\u8fd9\u91cc\u4ec5\u63d0\u4f9b\u90e8\u5206\u5173\u952e\u4ee3\u7801\u3002\u9996\u5148\u662f\u97f3\u6e90\u6587\u4ef6\u7684\u52a0\u8f7d\u4e0e\u89e3\u6790\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"cpp\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">bool TouchBeep::loadSoundResource(void)\n{\n    string TouchBeepWav(TOUCH_BEEP_RESOURCE_NAME);\n    if (access(TouchBeepWav.c_str(), F_OK | R_OK) != 0)\n    {\n        LOGE(\"couldn't access TouchBeepSource[%s],error:%s\", TouchBeepWav.c_str(), strerror(errno));\n        return false;\n    }\n    mTouchBeepRes = fopen(TouchBeepWav.c_str(), \"r\");\n    if (mTouchBeepRes == nullptr)\n    {\n        LOGE(\"cloudn't fopen TouchBeepSource[%s],error:%s\", TouchBeepWav.c_str(), strerror(errno));\n        return false;\n    }\n    char index[4] = { 0 };\n    \/*Header\u90e8\u5206*\/\n    \/\/ 4\u5b57\u8282\u5e8f,ChunkId\uff0c\u5176\u503c\u603b\u4e3aRIFF,fread\u540e\u4f1a\u81ea\u52a8\u504f\u79fb\n    fread(index, sizeof(char), 4, mTouchBeepRes);\n    if ((index[0] != 'R') || (index[1] != 'I') || (index[2] != 'F') || (index[3] != 'F'))\n    {\n        LOGE(\"incorrect header,loadSoundResource failed\");\n        return false;\n    }\n    \/\/ 4\u5b57\u8282ChunkSize\n    fseek(mTouchBeepRes, 4, SEEK_CUR);\n    \/\/ 4\u5b57\u8282Format\uff0c\u5176\u503c\u603b\u4e3aWAVE\n    fread(index, sizeof(char), 4, mTouchBeepRes);\n    if ((index[0] != 'W') || (index[1] != 'A') || (index[2] != 'V') || (index[3] != 'E'))\n    {\n        LOGE(\"incorrect format,loadSoundResource failed\");\n        return false;\n    }\n    \/*Format Chunk\u90e8\u5206*\/\n    \/\/ 4\u5b57\u8282Subchunk1ID\n    fread(index, sizeof(char), 4, mTouchBeepRes);\n    if ((index[0] != 'f') || (index[1] != 'm') || (index[2] != 't') || (index[3] != ' '))\n    {\n        LOGE(\"incorrect format,loadSoundResource failed\");\n        return false;\n    }\n    \/\/ 4\u5b57\u8282Subchunk1Size\uff0c\u8df3\u8fc7\n    fseek(mTouchBeepRes, 4, SEEK_CUR);\n    \/\/ 2\u5b57\u8282AudioFormat\n    fread(index, sizeof(char), 2, mTouchBeepRes);\n    if ((index[0] != 1) || (index[1] != 0))\n    {\n        LOGE(\"incorrect AudioFormat,loadSoundResource failed\");\n        return false;\n    }\n    \/\/ 2\u5b57\u8282Channel Number\n    fread(index, sizeof(char), 2, mTouchBeepRes);\n    mChannelNum = index[0];\n    \/\/ 4\u5b57\u8282\u91c7\u6837\u7387\n    fread(index, sizeof(char), 4, mTouchBeepRes);\n    mSamplingRate = (static_cast&lt;uint32_t>(index[0]) &amp; 0xFF) + ((static_cast&lt;uint32_t>(index[1]) &amp; 0xFF) &lt;&lt; 8U) + ((static_cast&lt;uint32_t>(index[2]) &amp; 0xFF) &lt;&lt; 16U) + ((static_cast&lt;uint32_t>(index[3] &amp; 0xFF) &lt;&lt; 24U));\n    \/\/ 4\u5b57\u8282ByteRate\uff0c\u8df3\u8fc7\n    fseek(mTouchBeepRes, 4, SEEK_CUR);\n    \/\/ 2\u5b57\u8282BlockAlign\n    fread(index, sizeof(char), 2, mTouchBeepRes);\n    mFrameSize = index[0];\n    mBitWidth = (index[0] \/ mChannelNum) &lt;&lt; 3U;\n    return true;\n}<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u52a0\u8f7d\u97f3\u6e90\u6587\u4ef6\u540e\uff0c\u6211\u4eec\u9700\u8981\u9010\u5b57\u8282\u8bfb\u53d6\u5176\u5185\u5bb9\uff0c\u89e3\u6790\u51fa\u4e00\u4e9b\u5173\u952e\u53c2\u6570\uff08\u9700\u8981\u4e86\u89e3wav\u7684\u6570\u636e\u5c01\u88c5\u683c\u5f0f\uff09\uff0c\u5982\uff1a\u901a\u9053\u6570\u3001\u91c7\u6837\u7387\u3001\u5e27\u5927\u5c0f\u4e0e\u4f4d\u5bbd\u7b49\u4fe1\u606f\uff0c\u8fd9\u4e9b\u4fe1\u606f\u90fd\u662f\u540e\u7eed\u9700\u8981\u7684\u3002<\/p>\n\n\n\n<p>\u5176\u6b21\uff0c\u6211\u4eec\u6839\u636e\u4e0a\u9762\u5f97\u5230\u7684\u4e00\u4e9b\u53c2\u6570\uff0c\u6765\u8bbe\u7f6eAudioTrack\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"cpp\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">bool TouchBeep::prepareSoundTrack(void)\n{\n    LOGI(\"%s Entered\", __FUNCTION__);\n    if (!loadSoundResource())\n    {\n        LOGE(\"loadSoundResource failed,plz check\");\n        return false;\n    }\n    android::content::AttributionSourceState attributionSource;\n    String16 opPackageName(\"com.example.touchbeep\");\n    attributionSource.packageName =\n                 VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));\n    attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));\n    attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));\n    attributionSource.token = sp&lt;BBinder>::make();\n    mAudioTrack = new AudioTrack(attributionSource);\n    if (mAudioTrack == nullptr)\n    {\n        LOGE(\"new AudioTrack failed, prepareSoundTrack failed\");\n        return false;\n    }\n    mAudioTrack->set(mStreamType, mSamplingRate, getResBitWidthType(), mChannelNum, 0, AUDIO_OUTPUT_FLAG_NONE);\n    if (mAudioTrack->initCheck() != NO_ERROR)\n    {\n        LOGE(\"AudioTrack initCheck failed\");\n        return false;\n    }\n    mAudioTrack->setVolume(1.0, 1.0);\n    LOGI(\"%s Exited\", __FUNCTION__);\n    return true;\n}\n<\/code><\/pre>\n\n\n\n<p>\u6700\u540e\u662f\u8bfb\u53d6wav\u97f3\u6e90\u6587\u4ef6\u4e2d\u7684\u539f\u59cbPCM\u6570\u636e\uff0c\u5199\u5165\u540e\u64ad\u653e\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"cpp\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">void TouchBeep::sendDataFlow(void)\n{\n    LOGI(\"%s Entered\", __FUNCTION__);\n    if (mAudioTrack == nullptr)\n    {\n        LOGE(\"mAudioTrack is null\");\n        return;\n    }\n    \/\/ NOTE:here we will start send data\n    mAudioTrack->start();\n    if (mTouchBeepRes != nullptr)\n    {\n        int16_t readBuf[4];\n        size_t readLen = 0;\n        std::lock_guard&lt;std::mutex> g(mResourceMutex);\n        mWrittingData.store(true);\n        while (mWrittingData &amp;&amp; (feof(mTouchBeepRes) == 0))\n        {\n            readLen = fread(readBuf, 1, mFrameSize, mTouchBeepRes);\n            if (readLen != 0)\n            {\n                mAudioTrack->write(readBuf, readLen, true);\n            }\n            \/\/ LOGI(\"read size is:%zu\", readLen);\n        }\n        mAudioTrack->stop();\n        \/\/ fclose(mTouchBeepRes);\n        \/\/ mTouchBeepRes = nullptr;\n        mWrittingData.store(false);\n        fseek(mTouchBeepRes, 0, SEEK_SET);\n    }\n    LOGI(\"sendDataFlow finished\");\n}<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u64ad\u653e\u5b8c\u6210\u540e\uff0c\u9700\u8981\u9500\u6bc1AudioTrack\u4e0e\u5bf9\u4e8e\u7684\u97f3\u6e90\u6587\u4ef6\u3002\u7531\u4e8e\u6211\u4eec\u8fd9\u91cc\u662f\u5355\u4f8b\u5b9e\u73b0\uff0c\u8fd9\u90e8\u5206\u64cd\u4f5c\u5efa\u8bae\u653e\u5230\u6790\u6784\u51fd\u6570\u4e2d\u6267\u884c\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u603b\u7ed3<\/h3>\n\n\n\n<p class=\"has-text-align-justify\">\u603b\u7684\u6765\u8bf4\uff0c\u8981\u5728Native\u4e2d\u5b9e\u73b0\u6309\u952e\u97f3\u65b9\u6cd5\u8fd8\u662f\u6709\u4e0d\u5c11\u7684\uff0c\u672c\u535a\u5ba2\u4e2d\u63d0\u5230\u7684\u8fd9\u79cd\u65b9\u6cd5\u53ea\u80fd\u7b97\u662f\u5176\u4e2d\u4e00\u79cd\uff0c\u4f46\u8fd9\u79cd\u65b9\u5f0f\u5b58\u5728\u4e00\u4e2a\u7f3a\u70b9\uff1a<strong>\u5f53\u7cfb\u7edf\u6309\u952e\u97f3\u8c03\u6574\u65f6\uff0cNative\u4fa7\u65e0\u6cd5\u53ca\u65f6\u505a\u5230\u540c\u6b65\uff0c\u5fc5\u987b\u8003\u8651\u989d\u5916\u7684\u540c\u6b65\u673a\u5236<\/strong>\u3002\u9664\u4e86\u535a\u5ba2\u4e2d\u7684\u8fd9\u79cd\u65b9\u6cd5\u5916\uff0c\u4e5f\u8fd8\u53ef\u4ee5\u901a\u8fc7\u5199\u5165\u6a21\u62df\u952e\u503c\u7684\u65b9\u5f0f\u5c06\u6309\u952e\u97f3\u7684\u64ad\u653e\u653e\u5230\u66f4\u5e95\u5c42\u53bb\u5b9e\u73b0\uff0c\u8fd9\u6837Native\u670d\u52a1\u53ea\u9700\u8981\u5199\u5165\u952e\u503c\u5373\u53ef\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u4ee5\u4e0a\u5c31\u662f\u672c\u7bc7\u535a\u5ba2\u7684\u5168\u90e8\u5185\u5bb9\u5566~<\/p>\n        <div class=\"booster-block booster-reactions-block\">\n            <div class=\"twp-reactions-icons\">\n                \n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-1\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/happy.svg\" alt=\"Happy\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Happy                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">4<\/span>\n                        \n                                                <span class=\"twp-react-percent\"><span>100<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-2\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/sad.svg\" alt=\"Sad\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Sad                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-3\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/excited.svg\" alt=\"Excited\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Excited                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-6\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/sleepy.svg\" alt=\"Sleepy\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Sleepy                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                        \n                                                <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-4\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/angry.svg\" alt=\"Angry\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">Angry<\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                        \n                    <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-5\" post-id=\"4415\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/surprise.svg\" alt=\"Surprise\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">Surprise<\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n            <\/div>\n        <\/div>","protected":false},"excerpt":{"rendered":"<p>\u5728Android Native\u4e2d\u81ea\u884c\u5b9e\u73b0\u6309\u952e\u97f3\u64ad\u653e\u3002<\/p>","protected":false},"author":1,"featured_media":4483,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":3,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[18],"tags":[46,67],"class_list":["post-4415","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-operating-system","tag-android","tag-audio"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[]}},"aioseo_notices":[],"featured_image_urls":{"full":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"thumbnail":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-150x150.jpg",150,150,true],"medium":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-300x158.jpg",300,158,true],"medium_large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"1536x1536":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"2048x2048":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"trp-custom-language-flag":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-18x9.jpg",18,9,true]},"post_excerpt_stackable":"<p>\u5728Android Native\u4e2d\u81ea\u884c\u5b9e\u73b0\u6309\u952e\u97f3\u64ad\u653e\u3002<\/p>\n","category_list":"<a href=\"https:\/\/blog.coderfan.org\/en\/category\/operating-system\" rel=\"category tag\">\u64cd\u4f5c\u7cfb\u7edf<\/a>","author_info":{"name":"FranzKafka95","url":"https:\/\/blog.coderfan.org\/en\/author\/yushenglonely95"},"comments_num":"0 comments","featured_image_urls_v2":{"full":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"thumbnail":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-150x150.jpg",150,150,true],"medium":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-300x158.jpg",300,158,true],"medium_large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"1536x1536":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"2048x2048":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"trp-custom-language-flag":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-18x9.jpg",18,9,true]},"post_excerpt_stackable_v2":"<p>\u5728Android Native\u4e2d\u81ea\u884c\u5b9e\u73b0\u6309\u952e\u97f3\u64ad\u653e\u3002<\/p>\n","category_list_v2":"<a href=\"https:\/\/blog.coderfan.org\/en\/category\/operating-system\" rel=\"category tag\">\u64cd\u4f5c\u7cfb\u7edf<\/a>","author_info_v2":{"name":"FranzKafka95","url":"https:\/\/blog.coderfan.org\/en\/author\/yushenglonely95"},"comments_num_v2":"0 comments","_links":{"self":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4415","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/comments?post=4415"}],"version-history":[{"count":28,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4415\/revisions"}],"predecessor-version":[{"id":4498,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4415\/revisions\/4498"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/media\/4483"}],"wp:attachment":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/media?parent=4415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/categories?post=4415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/tags?post=4415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}