跳到主要內容
黯羽輕揚每天積累一點點

ionic 開發跨平台 App 常見問題

免費2016-01-09#Ionic#Solution#ionic白屏#ionic黑屏#ionic闪屏#ionic splash screen#ionic列表回弹#ionic数据共享#ionic安卓签名#ionic性能优化

怎麼說呢,快速開發很棒,後遺症很傷。

寫在前面

ionic 是一個用來快速開發跨平台應用的框架,亮點不少:

  • 學習成本低

對前端開發者來說,學習成本不是很高,如果有接觸過 angular,就幾乎沒有什麼學習成本了

  • 簡單易用

強大的 CLI,start->platform->serve->build->emulate->run,全套服務命令行完成,不用寫配置文件,不用 F5

  • 組件多而強大

提供了很多強大的現成組件,很容易實現流行的交互效果,比如下拉刷新(ion-refresher)、上拉加載/瀑布流(ion-infinite-scroll)、tabs(ion-tabs)、側邊欄菜單(ion-side-menu)等等,只需要寫一點點代碼,就能實現這些流行效果,比 native 開發速度快太多了

  • 支持 cordova 插件

打開了這扇門,意味著我們可以使用大量的原生功能,比如調用相機拍照、響應返回按鈕、打電話發短信發郵件……都只要幾行代碼就能搞定

  • 更新速度快

2015/11/1 是 v1.1.0,現在(2016/1/9)已經是 v1.2.4 了,快速更新意味著有人維護,bug 能被迅速修復

當然,也有缺陷,否則就沒有這篇筆記了,如下:

  • 新版本不完全向後兼容

不兼容沒關係,給個詳細文檔說明下也行啊,沒有

  • bug 難以定位

angular+cordova+ionic+javascript,發現問題後,很難確定是哪塊的問題

  • 性能優化難

動畫卡頓,低端機體驗更差,而優化措施一般都是建議少用動畫少用陰影少用漸變……但是,不用實在太醜,而且與 native 應用體驗差太多

  • 其它

奇奇怪怪的問題找不到答案,大半夜的翻 stackoverflow……

一。jsonp 跨域,php 服務怎麼寫

P.S. 這是 angular 的問題,當時沒整理筆記,後來就忘記了遇到過這麼個問題

angular 的 $http 可以發送 jsonp 請求,用法類似於 jQuery,如下:

// 請求數據
$http.jsonp(sUrl).success(function(res){
    // ...
}).error(function(err){
    // ...
});

sUrl 有特殊要求,必須帶上 callback 參數,而且參數值只能JSON_CALLBACK,angular 文檔:

Relative or absolute URL specifying the destination of the request. The name of the callback should be the string JSON_CALLBACK.

例如:

var sUrl = http://www.ayqy.net/app/rsshelper/index.php?callback=JSON_CALLBACK

而問題是:php 服務怎麼寫?直接返回用 JSON_CALLBACK() 包裹的 json 數據?

不對,因為真正請求的 url 中 callback !== JSON_CALLBACK,被 angular偷偷(文檔沒有說明)替換掉了,所以後台需要這樣寫:

<?php
if (strpos($_SERVER["REQUEST_URI"], 'callback')) {
    $res = json_encode($res);
    // echo $_SERVER["REQUEST_URI"];
    // /angular/test/http/main.php?callback=angular.callbacks._0
    // $res = 'JSON_CALLBACK('.$res.')';
    // 錯的
    $res = $_GET['callback'].'('.$res.')';
}
?>

當然,這是 get 的情況,比較簡單,如果是 post,還需要通過別的方式取 callback,完整例子如下:

<?php
// get
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $res = '{';

    // arg
    if (isset($_GET['arg'])) {
        $res .= '"arg": "'.$_GET['arg'].'", ';
    }
    // method
    if (strpos($_SERVER["REQUEST_URI"], 'callback')) {
        // echo $_SERVER["REQUEST_URI"];
        // /angular/test/http/main.php?callback=angular.callbacks._0
        $res .= '"arg": "'.$_GET['arg'].'", ';
        $res .= '"method": "jsonp"}';
        // $res = 'JSON_CALLBACK('.$res.')';
        // 錯的
        $res = $_GET['callback'].'('.$res.')';
    }
    else {
        $res .= '"method": "get"}';
    }

    echo $res;
}
// post
else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 直接拿拿不到,因為傳過來的是 json 串
    // echo $_POST['arg'];
    $jsonArg = file_get_contents('php://input');
    if (isset($jsonArg)) {
        echo substr($jsonArg, 0, strlen($jsonArg) - 1).', "method": "post"}';
    }
    else {
        echo '{"method": "post"}';
    }
}
?>

二。ion-content 的阻尼回彈效果沒了

ionic v1.2 取消了 ion-content 預設的阻尼回彈效果,明明一模一樣的代碼,就是沒有回彈效果,後來發現是版本更新的鍋,翻了很久後,在 官方部落格評論裡找到了答案:

Great news, thank you! It seems that the "has-bouncing='true'" does not work with the native scroll, is it correct? I managed though to have it again by going to back go js scroll (overflow-scroll="false")

I only checked in the browser so if any one can confirm that with native scrolling removes bouncing that would be great.

I need this feature for a custom 'pull refresh' method I had.

也就是說,v1.2 之後想要有阻尼回彈效果,需要這麼做:

<ion-content overflow-scroll="false" has-bouncing="true"></ion-content>

三。多個 view 之間的數據共享

也就是多個 controller 之間的數據共享(一般不同 view 對應不同 controller)問題,當然,最簡單的方法是用全域變數($rootScope),但這樣不好,更合理的方式是自己寫個 service 或者 factory,提供數���存取接口,在需要的地方依賴注入即可,比如:

.service('DataServ', ['$http', 'UIServ', function($http, UIServ) {
    // ...
    // 字典
    var oDir = {
        // key: value
    };

    function save(val) {
        var sKey = Date.now() + '';
        oDir[sKey] = val;
        return sKey;
    }

    function get(sKey) {
        return oDir[sKey];
    }

    return {
        // ...
        save: save,
        get: get
    };
}]);

然後通過 url 傳遞 sKey 即可實現數據共享,很乾淨

四。通知視圖更新

在模板中寫好了數據展示,但如果打開頁面後數據還沒到,模板解析完了,由於沒有數據顯示空白頁,過了一會兒數據到了,發現視圖沒有更新,仍然是一片空白,比如:

<!-- html -->
<p ng-cloak>{{data}}</p>

// js
app.controller('MainCtrl', ['$scope', 'DataServ', function($scope, DataServ) {
    // ...
    setTimeout(function() {
        // ...
        $scope.data = 'data';
    })
}]);

因為給 data 賦值的操作跑到了 controller 作用域外,此時需要手動通知視圖更新,如下:

// ...
$scope.data = 'data';
$scope.$apply();

當然,一般不需要手動通知,即便是異步返回的數據,因為這裡只與作用域有關,總之,如果發現視圖需要手動更新,添上 $apply 就好了

五。php 原生 xml 擴展如何獲取<content: encoded>裡的內容

rss 格式中會有 <content: encoded> 標籤,直接取 content 取不到,需要特殊的方式:

$content = (string)$item->children("content", true);
// $encodedContent = $content->encoded;
// $content->encoded 返回轉義過的 html,比如把&amp;轉成&,一般用於<pre>直接展示

當然,前提是使用原生 xml 擴展($xml = simplexml_load_file($url);)解析 xml 才會遇到這個問題,更多用法請查看 php - How to parse CDATA HTML-content of XML using SimpleXML? - Stack Overflow

六。在瀏覽器中打開外部頁面

需要使用一個 cordova 插件,cd 進項目文件夾,然後:

ionic plugin add cordova-plugin-inappbrowser

安裝完成後就可以調用了,不用修改配置文件,不用引入其他 js,如下:

// openInBrowser
window.open('http://example.com', '_system');   Loads in the system browser
window.open('http://example.com', '_blank');    Loads in the InAppBrowser
window.open('http://example.com', '_blank', 'location=no'); Loads in the InAppBrowser with no location bar
window.open('http://example.com', '_self'); Loads in the Cordova web view
// test
window.open(url, '_system'); // 系統預設瀏覽器
// window.open(url, '_blank');  // 很醜的安卓內置瀏覽器
// window.open(url, '_self');  // 同上

一般都用 _system,另外兩個實在太醜,更多內容請查看 Cordova InAppBrowser Plugin Example using ionic framework

七。splash screen 黑屏白屏

P.S. 黑屏白屏其實是同一個問題,但這個問題相當難解決,筆者花了快 1 天才搞定

ionic 預設集成了 splashscreen 插件,這個 cordova 插件效果不是很完美,預設配置只在首次打開 app 時顯示 splash screen,但實際效果是:

When the app starts the splash screen shows for a few seconds as expected, and then the screen goes black for @1 second and then white for @2 seconds and then the main app page appears.

Is there any way to prevent the black and white pages appearing? I read somewhere that a black page appears when there is no splash page but I do have a splash page and it appears fine.

stackoverflow 找到了這個問題描述,簡直太貼切了,但是單靠問題下面的回答無法解決白屏問題,還需要改配置文件

最初發現的現象是黑屏(把上面英文描述裡的 white 換成黑),後來找到了原因:主視圖容器 ion-nav-view 是空的,而它的背景色是 #000,所以修復方法是給裡面塞個 ion-view

<!-- 內容 -->
<ion-nav-view>
    <!-- 防止啟動時黑屏 -->
    <ion-view></ion-view>
</ion-nav-view>

或者添 css,把 ion-nav-view 的背景色改成白色。但問題還沒解決,黑屏問題變成白屏問題了,解決方案比較麻煩

  1. 把 splashscreen 插件降級到 v2.0.0

v2.0.0 之後的版本有 bug,目前(2016/1/9)自帶的版本是 v3.0.0。先 cd 到項目文件夾,然後命令行:

    // 刪掉現有版本
    cordova plugin rm cordova-plugin-inappbrowser
    // 安裝 v2.0.0
    cordova plugin add cordova-plugin-inappbrowser

2. 改配置文件 MyApp/config.xml

    <preference name="SplashScreen" value="screen"/>
    <preference name="AutoHideSplashScreen" value="false"/>
    <preference name="auto-hide-splash-screen" value="false"/>
    <preference name="ShowSplashScreenSpinner" value="false"/>
    <preference name="SplashMaintainAspectRatio" value="true" />
    <preference name="SplashShowOnlyFirstTime" value="false"/>
    <preference name="SplashScreenDelay" value="10000"/>

取消自動隱藏(改為代碼控制隱藏),把持續時間改為較大的值(10 秒),設置每次打開應用都顯示 splash screen

P.S. 預設只有 SplashScreenSplashScreenDelay,需要把其它的(SplashMaintainAspectRatio 可選)都添上

  1. 改 app.js

手動隱藏 splash screen,在 run 裡面添上

    .run(['$rootScope', function($rootScope) {
            // init
            // $rootScope.isLoading = false;

            // hide splash immediately
            if(navigator && navigator.splashscreen) {
                navigator.splashscreen.hide();
            }
        });
    }])

這樣就好了,不要延時調用 hide,否則仍然會出現白屏(有些解決方案要求$timeout 50 毫秒 hide,仍然會出現白屏,不要這樣做)

最怨念的問題結束了,看似簡單的功能,想要有完美的原生體驗卻很難,奇奇怪怪的問題很難解決,目前可行的解決方案可能過段時間就不行了,可以查看 White page showing after splash screen before app load 感受一下

八。安卓版本簽名發布

各種簽名方法都過時了,目前(2016/1/9)可以用的簽名方法如下:

  1. 在 MyApp\platforms\android 創建 keystore

具體步驟請查看:Ionic toturial for building a release.apk

  1. 創建 release-signing.properties 文件

具體步驟請查看:How to automatically sign your Android apk using Ionic framework and Crosswalk

  1. build

cd 到項目文件夾,然後命令行 ionic build --release android,成功後會生成 2 個東西,在 MyApp\platforms\android\build\outputs\apk 下,分別是 android-armv7-release.apk 和 android-x86-release.apk,一般平板和 PC 用 x86,手機用 arm7,如果要上傳 google play 的話,2 個都要傳,下載時有自動識別

至於 crosswalk,提供了 chrome 內核,能讓低端機支持高端東西,但會讓 apk變大很多(3.5M->23M),添上 crosswalk 後,感覺。。。嗯,變卡了,但為了支持低端機用戶,一般都會添上 crosswork

九。總結

怎麼說呢,快速開發很棒,後遺症很傷。

筆者從 1 月 3 號開始搞,1 月 8 號中午 release v1.0.0,靚照如下:

[caption id="attachment_933" align="alignnone" width="625"]rsshelper rsshelper[/caption]

[caption id="attachment_934" align="alignnone" width="625"]rsshelper rsshelper[/caption]

看著還不錯吧,非賣品,不贈送~

評論

暫無評論,快來發表你的看法吧

提交評論