<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                上一篇文章中我們介紹了Android開發中經常會涉及到但又常常被忽視掉的開發者模式。主要講解了包括如何打開手機的開發者模式,開發者模式中各個菜單的意義和作用,如何清除手機App數據,以及清除手機App數據具體清除那些數據等知識點,具體關于Android中開發者模式的知識,可參考我的: [Android產品研發(十六)–>開發者選項](http://blog.csdn.net/qq_23547831/article/details/51809497) 本文將介紹Android中hybrid開發相關的知識點。hybrid開發實際上是混合開發的意思,這里的混合是H5開發與Native開發混合的意思。下面的文章中我們將逐個介紹一下hybrid開發的概念、hybrid開發的優勢、Android中如何實現hybrid開發、簡單的hybrid開發的例子,以及在產品實踐中對hybrid開發的應用,希望通過本篇文章的介紹讓您能夠對Android中的hybrid開發有一個基本的認識。 **一:hybrid開發的概念** 在具體介紹hybrid開發之前,我們先看一下什么是hybrid開發,在這里我們先引用一下百度百科中對hybrid開發的定義: > Hybrid App(混合模式移動應用)是指介于web-app、native-app這兩者之間的app,兼具“Native App良好用戶交互體驗的優勢”和“Web App跨平臺開發的優勢”。 從定義中我們可以看到hybrid開發其實就是在App開發過程中既使用到了web開發技術也使用到了native開發技術,通過這兩種技術混合實現的App就是我們通常說的hybrid app,而通過這兩種技術混合開發就是hybrid開發。 好吧,我們已經知道hybrid開發的具體含義,那么一個問題就產生了,既然我們已經有了native開發了為何還需要hybrid開發呢?它有什么好處么?答案是肯定的,下面我們就來看一下為何需要hybrid開發方式。 **二:為何需要hybrid開發** 下面我們簡單看一下Native開發中存在的弊端以及使用hybrid開發方式的好處,通過對比你就能知道了hybrid開發的優勢,當然了,這里不是推崇使用hybrid開發方式,native也有native開發的優勢,hybrid開發也有hybrid開發的劣勢,這里只是簡單的看一下hybrid相對于native開發的優勢。 * 使用Native開發的方式人員要求高,只是一個簡單的功能就需要IOS程序員和Android程序員各自完成; * 使用Native開發的方式版本迭代周期慢,每次完成版本升級之后都需要上傳到App Store并審核,升級,重新安裝等,升級成本高; * 使用hybrid開發的方式簡單方便,同一套代碼既可以在IOS平臺使用,也可以在Android平臺使用,提高了開發效率與代碼的可維護性; * 使用hybrid開發的方式升級簡單方便,只需要服務器端升級一下就好了,對用戶而言完全是透明了,免去了Native升級中的種種不便; 通過對比可以發現hybrid開發方式現對于native實現主要的優勢就是更新版本快,代碼維護方便,當然了這兩個優點也是我們推崇使用hybrid開發app的主要因素。知道了hybrid開發的好處之后,我們如何在Android中實現hybrid開發呢?下面我們就將介紹這個問題。 **三:Android中如何實現Bybird開發** 其實在Android開發中使用hybrid模式開發app,也是有兩種方案的: * 使用第三方hybrid框架 * 自己使用webview加載 通過這兩種方案實現hybrid開發各有利弊,具體如下: * 使用PhoneGap、AppCan之類的第三方框架,其實現的原理是以WebView作為用戶界面層,以Javascript作為基本邏輯,以及和中間件通訊,再由中間件訪問底層API的方式,進行應用開發。相當于為我們封裝了webview與相應的native組件; * 使用webview控件加載H5網頁的內容,其中客戶端的webview只是作為一個加載H5頁面的殼子,具體的實現效果是由H5實現的,這個需要Native程序員和H5程序員一起合作完成; * 使用第三方框架的方式的好處是許多功能已經被集成好了,只需要簡單的調用即可,但是這種方式集成度高,不容易定制化處理,而且性能上也是一個打的問題; * 使用webview加載H5頁面,定制化程度高,問題可控,但是相對與第三方框架集成度不夠高,但是其已經可以滿足我們日常的開發功能需要了,目前還是比較推薦使用這種方式實現hybrid開發; 下面我們就看一下如何在Android系統中通過webview實現對H5頁面的加載操作。 **四:hybrid開發簡單實現** * 在AndroidManifest.xml中定義網絡請求權限 ~~~ <uses-permission Android:name="Android.permission.INTERNET"/> ~~~ 注意這個權限是必須的,因為加載webview頁面一般而言經常是網絡上的H5頁面,這時候的網絡請求權限就是必須的了,好多時候測試webview加載網絡H5頁面失敗,找了半天不知道是什么原因,最后才發現是網絡權限沒有添加… * 在Layout布局文件中定義Webview控件 ~~~ <WebView Android:layout_width="match_parent" Android:layout_height="match_parent" Android:id="@+id/webView" /> ~~~ 這里的WebView控件就是Android原生的webview控件了,其和普通的Android控件的使用沒有什么不同都是在布局文件中定義,然后在Activity代碼中獲取并執行初始化操作等等。 * 在代碼中獲取Webview控件加載本地或者網絡H5資源 加載本地H5頁面 ~~~ /** * 加載本地H5資源文件 */ webView = (WebView) findViewById(R.id.webView); webView.loadUrl("file:///Android_asset/example.html"); ~~~ 加載網絡H5頁面 ~~~ /** * 加載網絡H5資源 */ webView = (WebView) findViewById(R.id.webView); webView.loadUrl("http://baidu.com"); ~~~ 可以發現在獲取到webview組件之后直接執行一個loadUrl方法傳入一個url地址就可以了,這樣在activity頁面中就可以展示出webview頁面了,契合普通的網頁效果沒什么不同,這里需要說明的是,webview不但能夠加載網頁地址,同樣的也可以加載html代碼,本地html資源等等,相對來說功能還是很強大的。 當然了以上只是最最簡單的webview使用的例子,下面我們可以為我們的webview對象設置各種參數: * 為Webview控件設置參數 ~~~ WebSettings webSettings = h5Fragment.mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setLoadWithOverviewMode(true); webSettings.setAllowFileAccess(false); webSettings.setUseWideViewPort(false); webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); webSettings.setDatabaseEnabled(false); webSettings.setAppCacheEnabled(false); webSettings.setBlockNetworkImage(true); ~~~ 這里的WebSettings就是webview的設置參數對象,我們是通過它為webview設置各種參數值的,見名知意,看見名字我們就知道各個set方法的意思了。比如設置webview中的html頁面js代碼是否可用,是否可以訪問系統文件,H5緩存是否可用,是否立即加載網頁圖片等等。 * 為Webview控件設置WebChromeClient WebChromeClient對象是webview的關于頁面效果回調方法的實現對象,主要用于實現webview頁面上一些效果的回調,我們可以看一下其中實現的一些回調方法: ~~~ /** * 自定義實現WebChromeClient對象 */ public class MWebChromeClient extends WebChromeClient{ /** * 當webview加載進度變化時回調該方法 */ @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } /** * 當加載到H5頁面title的時候回調該方法 */ @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); } /** * 當接收到icon的時候回調該方法 */ @Override public void onReceivedIcon(WebView view, Bitmap icon) { super.onReceivedIcon(view, icon); } /** * 當H5頁面調用js的Alert方法的時候回調該方法 */ @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } /** * 當H5頁面調用js的Confirm方法的時候回調該方法 */ @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result); } /** * 當H5頁面調用js的Prompt方法的時候回調該方法 */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return super.onJsPrompt(view, url, message, defaultValue, result); } } ~~~ 上面的WebChromeClient中我們重寫了其中的幾個字方法,我們已經在方法中添加了注釋標明了各個方法的調用時機,而且通過方法名我們也不難發現各個方法的具體作用,這里就不在具體的介紹了。 * 為Webview主要設置WebviewClient ~~~ /** * 自定義實現WebViewClient類 */ public class MWebViewClient extends WebViewClient { /** * 在webview加載URL的時候可以截獲這個動作, 這里主要說它的返回值的問題: * 1、返回: return true; webview處理url是根據程序來執行的。 * 2、返回: return false; webview處理url是在webview內部執行。 */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { } /** * 在webview開始加載頁面的時候回調該方法 */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } /** * 在webview加載頁面結束的時候回調該方法 */ @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); } /** * 加載頁面失敗的時候回調該方法 */ // 該方法為Android23中新添加的API,Android23中會執行該方法 @TargetApi(21) @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { } /** * 加載頁面失敗的時候回調該方法 */ /** * 在Android23中改方法被onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) 替代 * 因此在Android23中執行替代方法 * 在Android23之前執行該方法 * @param view * @param errorCode * @param description * @param failingUrl */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { } } ~~~ 這里我們只是暫時看一下WebViewClient中的幾個比較重要的方法,shouldOverrideUrlLoading方法,onPageStarted方法,onPageFinished方法,onReceivedError方法等,相關的方法說明已經有注釋了,這里就不在做過多的說明了。好了介紹完了相關的API之后我們來看一下我們在產品中關于hybrid開發的實踐。 * 友友用車中關于hybrid開發的實踐 hybrid這么高逼格的東西友友用車怎么能不涉及呢?在我們的產品開發中也使用到了Webview,并封裝了自己的Webview庫,下面我們就看一下友友用車中關于hybrid開發的實踐。 (1)定義H5Activity類,用于展示H5頁面 ~~~ /** * 自定義實現的H5Activity類,主要用于在頁面中展示H5頁面,整個Activity只有一個Fragment控件 */ public class H5Activity extends BaseActivity { public H5Fragment h5Fragment = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_h5); h5Fragment = new H5Fragment(); getSupportFragmentManager().beginTransaction().replace(R.id.mfl_content_container, h5Fragment).commit(); } } ~~~ (2)在H5Fragment中具體實現對H5頁面的加載操作 ~~~ /** * 具體實現H5頁面加載Fragment,只有一個Webview控件 */ public class H5Fragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener { @BindView(R.id.sswipeRefreshLayout) public SwipeRefreshLayout swipeRefreshLayout; /** * H5頁面 WebView */ @BindView(R.id.mwebview) public WebView mWebView = null; @BindView(R.id.rl) public RelativeLayout rl; /** * 頁面title */ public String title = ""; /** * 頁面當前URL */ public String currentUrl = ""; /** * 判斷網頁是否加載成功 */ public boolean isSuccess = true; /** * 判斷前一頁H5是否需要刷新 */ public boolean isNeedFlushPreH5 = false; private BasePayFragmentUtils payFragmentUtils; public static final String KEY_DIALOG_WEB_VIEW = "dialog_webView"; /** * 是否是彈窗中的WebView */ private boolean isDialogWebView = false; View.OnClickListener errorOnClickListener = new View.OnClickListener() { @Override public void onClick(View view) { mProgressLayout.showLoading(); isSuccess = true; reflush(); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); payFragmentUtils = new BasePayFragmentUtils(this, BasePayFragmentUtils.ORDER_TYPE_H5); Bundle bundle = getArguments(); if (bundle != null && bundle.containsKey(KEY_DIALOG_WEB_VIEW)) { isDialogWebView = bundle.getBoolean(KEY_DIALOG_WEB_VIEW, false); } } @Override public View setView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_h5, null); ButterKnife.bind(this, rootView); if (getActivity() instanceof H5Activity) { H5Activity h5Activity = (H5Activity) getActivity(); mProgressLayout = h5Activity.mProgressLayout; } initView(); initData(); return rootView; } @Override public void onResume() { super.onResume(); payFragmentUtils.onPayResume(); if (H5Constant.isNeedFlush == true || isNeedFlushPreH5 == true) { H5Constant.isNeedFlush = false; isNeedFlushPreH5 = false; // 加載數據 initData(); } } /** * 執行組件初始化的操作 */ private void initView() { // 判斷下拉刷新組件是否可用 isSwipeEnable(); // 初始化WebView組件 H5FragmentUtils.initH5View(this); // 設置WebView的Client mWebView.setWebViewClient(new MWebViewClient(this)); // 設置可現實js的alert彈窗 mWebView.setWebChromeClient(new WebChromeClient()); if (isDialogWebView) { mProgressLayout.setCornerResId(R.drawable.map_confirm_bg); } } /** * 執行初始化加載數據的操作 */ private void initData() { mProgressLayout.showLoading(); // 設置title H5FragmentUtils.setTitle(this, title); // 獲取請求URL currentUrl = H5FragmentUtils.getUrl(this, currentUrl); // 刷新頁面 reflush(); } /** * 判斷下拉刷新組件是否可用 */ private void isSwipeEnable() { if (getActivity() == null) { return; } if (isDialogWebView) { getActivity().getIntent().putExtra(H5Constant.CARFLUSH, false); } //判斷滑動組件是否可用 if (getActivity().getIntent().getBooleanExtra(H5Constant.CARFLUSH, true)) { swipeRefreshLayout.setEnabled(true); swipeRefreshLayout.setColorSchemeResources(R.color.c1, R.color.c1, R.color.c1); swipeRefreshLayout.setOnRefreshListener(this); mWebView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { int downY = (int) event.getY(); if (downY <= DisplayUtil.screenhightPx / 3) { swipeRefreshLayout.setEnabled(true); } else { swipeRefreshLayout.setEnabled(false); } } return false; } }); } else { swipeRefreshLayout.setEnabled(false); } } /** * 執行Webview的下拉刷新操作 */ @Override public void onRefresh() { //判斷是否執行刷新動作 reflush(); } /** * 刷新當前頁面 */ private void reflush() { if (Config.isNetworkConnected(mContext)) { if (!TextUtils.isEmpty(currentUrl)) { H5Cookie.synCookies(mContext, currentUrl, H5Cookie.getToken()); mWebView.loadUrl(currentUrl); } else { swipeRefreshLayout.setRefreshing(false); mProgressLayout.showError(errorOnClickListener); } } else { swipeRefreshLayout.setRefreshing(false); mProgressLayout.showError(errorOnClickListener); } } @Override public void onDestroyView() { super.onDestroyView(); /*swipeRefreshLayout.removeView(mWebView); mWebView.removeAllViews(); mWebView.destroy();*/ } } ~~~ (3)初始化WebView組件 ~~~ /** * 初始化組件WebView * * @param h5Fragment */ public static void initH5View(H5Fragment h5Fragment) { if (h5Fragment == null || h5Fragment.getActivity() == null) { return; } if (h5Fragment.getActivity().getIntent().getBooleanExtra(H5Constant.SOFT_INPUT_IS_CHANGE_LAYOUT, false)) { h5Fragment.getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); } // 設置H5頁面默認能夠長按復制 /*if (!h5Fragment.getActivity().getIntent().getBooleanExtra(H5Constant.OPENLONGCLICK, false)) { h5Fragment.mWebView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return true; } }); }*/ WebSettings webSettings = h5Fragment.mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setLoadWithOverviewMode(true); webSettings.setAllowFileAccess(false); webSettings.setUseWideViewPort(false); webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); webSettings.setDatabaseEnabled(false); webSettings.setAppCacheEnabled(false); webSettings.setBlockNetworkImage(true); } ~~~ (4)自定義實現WebviewClient對象 ~~~ /** * 自定義實現WebviewClient類 */ public class MWebViewClient extends WebViewClient { public H5Fragment h5Fragment = null; public Activity h5Activity = null; public MWebViewClient(H5Fragment h5Fragment) { this.h5Fragment = h5Fragment; if (h5Fragment.getActivity() == null) { h5Activity = Config.currentContext; } else { h5Activity = h5Fragment.getActivity(); } } /** * 攔截H5頁面的a標簽跳轉,解析scheme協議 * 相當于放棄了a標簽的使用,轉而使用自定義的scheme協議 */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { //解析scheme if (url.indexOf(H5Constant.SCHEME) != -1) { try { Uri uri = Uri.parse(url); String[] urlSplit = url.split("\\?"); Map<String, String> queryMap = new HashMap<String, String>(); String h5Url = null; if (urlSplit.length == 2) { queryMap = H5Constant.parseUriQuery(urlSplit[1]); h5Url = queryMap.get(H5Constant.MURL); } // 解析SCHEME跳轉 { // 若設置刷新,則刷新頁面 if (queryMap.containsKey(H5Constant.RELOADPRE) && "1".equals(queryMap.get(H5Constant.RELOADPRE))) { h5Fragment.isNeedFlushPreH5 = true; } Intent intent = new Intent(Intent.ACTION_VIEW, uri); h5Activity.startActivityForResult(intent, H5Constant.h5RequestCode); } } catch (Exception e) { e.printStackTrace(); } return true; } // 打電話 else if (url.indexOf("tel://") != -1) { final String number = url.substring("tel://".length()); Config.callPhoneByNumber(h5Activity, number); return true; } else if (url.indexOf("tel:") != -1) { final String number = url.substring("tel:".length()); Config.callPhoneByNumber(h5Activity, number); return true; } // 其他跳轉方式 else { view.loadUrl(url); //如果不需要其他對點擊鏈接事件的處理返回true,否則返回false return false; } } /** * H5頁面剛剛開始被webview加載時回調該方法 */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } /** * H5頁面結束被加載時回調該方法 */ @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); h5Fragment.swipeRefreshLayout.setRefreshing(false); if (h5Activity.getTitle().toString().equals("找不到網頁")) { h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener); return; } if (h5Fragment.isSuccess) h5Fragment.mProgressLayout.showContent(); else h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener); h5Fragment.onLoadFinish(h5Fragment.isSuccess); if (h5Fragment.isSuccess) { h5Fragment.mWebView.getSettings().setBlockNetworkImage(false); } } // 該方法為Android23中新添加的API,Android23中會執行該方法 @TargetApi(21) @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (Build.VERSION.SDK_INT >= 21) { if (request.isForMainFrame()) { h5Fragment.isSuccess = false; h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener); } } } /** * 在Android23中改方法被onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) 替代 * 因此在Android23中執行替代方法 * 在Android23之前執行該方法 * @param view * @param errorCode * @param description * @param failingUrl */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { if (Build.VERSION.SDK_INT < 23) { h5Fragment.isSuccess = false; h5Fragment.mProgressLayout.showError(h5Fragment.errorOnClickListener); } } } ~~~ (5)打開H5頁面 ~~~ Intent intent = new Intent(context, H5Activity.class); intent.putExtra(H5Constant.MURL, currentUrl); intent.putExtra(H5Constant.TITLE, title); context.startActivity(intent); ~~~ 可以發現在產品實際開發過程中使用webview的頁面都是整個的Activity頁面,也就是說整個Activity頁面只有一個webview控件,所以這時候頁面的內容都是通過H5實現的。 然后當我們需要打開H5頁面的時候可以通過服務器下發H5頁面的url和title,并作為參數傳遞給H5Activity,然后打開該url所表示的網頁。 同時我們使用Fragment用于實現加載H5頁面的所以,所以以后當我們需要在其他地方使用加載H5頁面的時候可以很方便的一直。 在MWebviewClient的shouldOverrideUrlLoading方法中我們攔截了所有的a標簽跳轉,轉而實現我們自身的scheme協議,即a標簽的跳轉鏈接不再是常規的http鏈接,而是我們自定義的scheme協議,具體可參考:[Android產品研發(十一)–>應用內跳轉scheme協議](http://blog.csdn.net/qq_23547831/article/details/51685310) **總結**: * 本文中我們介紹了hybrid開發的概念,hybrid開發的作用,Android中如何實現hybrid開發,Android實現hybrid的例子,產品中對hybrid開發的實踐 * 在定義webview的時候可以設置WebviewSettings,設置WebviewClient,設置WebChromeClient等參數對象 * 可以在WebviewClient的shouldOverrideUrlLoading方法中攔截a標簽的跳轉并執行相應的邏輯 另外對產品研發技術,技巧,實踐方面感興趣的同學可以參考我的: [android產品研發(一)-->實用開發規范 ](http://blog.csdn.net/qq_23547831/article/details/51534013) [android產品研發(二)-->啟動頁優化 ](http://blog.csdn.net/qq_23547831/article/details/51541277) [android產品研發(三)-->基類Activity ](http://blog.csdn.net/qq_23547831/article/details/51546974) [android產品研發(四)-->減小Apk大小](http://blog.csdn.net/qq_23547831/article/details/51559066) [android產品研發(五)-->多渠道打包](http://blog.csdn.net/qq_23547831/article/details/51569261) [Android產品研發(六)–>Apk混淆](http://blog.csdn.net/qq_23547831/article/details/51581491) [android產品研發(七)-->Apk熱修復](http://blog.csdn.net/qq_23547831/article/details/51587927) [Android產品研發(八)–>App數據統計](http://blog.csdn.net/qq_23547831/article/details/51612429) [Android產品研發(九)–>App網絡傳輸協議](http://blog.csdn.net/qq_23547831/article/details/51655330) [Android產品研發(十)–>不使用靜態變量保存數據](http://blog.csdn.net/qq_23547831/article/details/51685310) [Android產品研發(十一)–>應用內跳轉scheme協議](http://blog.csdn.net/qq_23547831/article/details/51685310) [Android產品研發(十二)–>App長連接實現](http://blog.csdn.net/qq_23547831/article/details/51719389) [Android產品研發(十三)–>App輪詢操作](http://blog.csdn.net/qq_23547831/article/details/51764773) [Android產品研發(十四)–>App升級與更新](http://blog.csdn.net/qq_23547831/article/details/51764773) [Android產品研發(十五)–>內存對象序列化](http://blog.csdn.net/qq_23547831/article/details/51779528) [Android產品研發(十六)–>開發者選項](http://blog.csdn.net/qq_23547831/article/details/51809497)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看