Ionic2 Native插件總結(jié)

ionic2官方提供了很多原生插件,是建立在cordova plugin基礎(chǔ)上,結(jié)合ionic-native的ts庫進(jìn)行引用。本文除總結(jié)官方插件的使用,也介紹項(xiàng)目開發(fā)中遇到的非官方的cordova插件使用方法。

引用步驟

以Broadcaster插件為例:

  1. 下載插件
    npm install --save @ionic-native/broadcaster
    ionic plugin add cordova-plugin-broadcaster
    這里推薦cnpm下載
  2. 在NgModule中引入模塊
import { Broadcaster } from  '@ionic-native/broadcaster';

@NgModule({
  declarations: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    Broadcaster,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})

3.在頁面的ts文件中調(diào)用

export class HomePage {

  constructor(public navCtrl: NavController, private broadcaster: Broadcaster) {

  }

  fireNative(message){
    // Send event to Native
    console.log('send to native', message);
    this.broadcaster.fireNativeEvent('testNative', {item: message}).then(() => console.log('success'));
  }

}
  1. 在安卓項(xiàng)目的MainActivity中接收廣播
 receiver = new BroadcastReceiver() {
   @Override
    public void onReceive(Context context, Intent intent) {
        try {
            final JSONObject data = new JSONObject( intent.getExtras().getString("userdata"));
                                String tn = data.getString("tn");
                                startPay(tn);
                            } catch (JSONException e) {
                               throw new RuntimeException(e);
                            }
                        }
                    };

  mBroadcastManger = LocalBroadcastManager.getInstance(this);
  mBroadcastManger.registerReceiver(receiver, new IntentFilter("testNative"));

bug: 開發(fā)中遇到app重啟后,廣播會多次接收,分析是注冊的接收器沒有在應(yīng)用退出時(shí)注銷。
解決方法:重寫onDestroy方法,將注冊的receiver回收

@override
public void  onDestroy(){
  super.onDestroy();
  mBroadcastManger.unregisterReceiver(receiver);
}
  • 原生插件not installed問題
    編譯過程中,有時(shí)會提示某個(gè)native plugin not installed,然而該插件其實(shí)是已經(jīng)下載安裝好的。原因是ts中雖然調(diào)用了插件,但platform還未準(zhǔn)備好。解決方法:
    首頁 import {Platform} from 'ionic-angular',將首頁中初始化需要用到native plugin的代碼放在platfor.ready().then(()=>{})

HTTP

ionic plugin add cordova-plugin-http
npm install --save @ionic-native/http
實(shí)現(xiàn)HTTP請求的插件,目前只用到了簡單的post請求,但使用中遇到了奇葩的情景,同樣的代碼在安卓上能獲得正確應(yīng)答,IOS上卻收到500。debug發(fā)現(xiàn)后臺接收ios數(shù)據(jù)里有個(gè)字段的值是null,android卻是有值的。查看git上issue也毫無發(fā)現(xiàn),后發(fā)現(xiàn)該字段是個(gè)數(shù)組對象,在post前將對象轉(zhuǎn)為json string后,問題解決了...

后期項(xiàng)目有個(gè)接口的服務(wù)器改成了HTTPS,關(guān)鍵還沒有申請證書,如果只是修改請求url,會返回ssl handshake error,這里需要應(yīng)用初始化的時(shí)候?qū)ttp進(jìn)行一下設(shè)置:

cordovaHTTP.acceptAllCerts(true).then(() => {
    console.log('success!');
});

cordovaHTTP.validateDomainName(false).then(() => {
    console.log('success!');
});

Native Storage

ionic plugin add cordova-plugin-nativestorage
cnpm install --save @ionic-native/native-storage
通過這個(gè)插件實(shí)現(xiàn)應(yīng)用里數(shù)據(jù)的持久化存儲,但使用時(shí)遇到個(gè)問題。通過getItem('key')取值時(shí),若未事先對該key有setItem('key': {}),getItem方法將觸發(fā)error的回調(diào),所以需要在error回調(diào)里返回你需要的默認(rèn)值;
除了使用這個(gè)原生插件,其實(shí)Ionic2默認(rèn)安裝的storage模塊也可以實(shí)現(xiàn),引用步驟如下:
1.cordova plugin add cordova-sqlite-storage --save,安裝sqlite插件
2.在ngModule中引入

import { IonicStorageModule } from '@ionic/storage';
@NgModule({
  declarations: [
    MyApp,
    HomePage,
    ListPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    ListPage
  ],
  providers: [
    ...
  ]
})

3.在ts文件中使用storage, import {Storage } from '@ionic/storage';

Barcode Scanner

ionic plugin add phonegap-plugin-barcodescanner
cnpm install --save @ionic-native/barcode-scanner
關(guān)于生成二維碼的方法:encode(type, text)真是好多坑。。
在Android上,該方法直接打開一個(gè)Activity顯示text生成的二維碼,雖然文檔里寫該方法返回promise,實(shí)際使用時(shí)回調(diào)函數(shù)不起作用。
在IOS上,該方法是在文件系統(tǒng)里生成一個(gè)圖片.jpg,通過success的回調(diào)success.file拿到圖片的路徑。

File Opener

ionic plugin add cordova-plugin-file-opener2
cnpm install --save @ionic-native/file-opener
因Barcode Scanner沒有提供直接將IOS生成的二維碼生成的圖片顯示出來,可以轉(zhuǎn)換下思路,通過文件打開插件去讀取圖片。

import { FileOpener } from '@ionic-native/file-opener';

constructor(private fileOpener: FileOpener) { }

...

this.fileOpener.open('path/to/file.pdf', 'application/pdf')
  .then(() => console.log('File is opened'))
  .catch(e => console.log('Error openening file', e));

然而,這個(gè)插件也是個(gè)坑,將barcode返回的路徑放進(jìn)去后,調(diào)用open會報(bào)錯(cuò),這里需要修改插件的FileOpener2.m文件:

NSURL *fileURL = [ NSURL URLWithString:path];

修改為
NSURL *fileURL = [ NSURL fileURLWithPath:path];

NFC

ionic plugin add --save phonegap-nfc
npm install --save @ionic-native/nfc
原本需求是讀取設(shè)備的一些基礎(chǔ)信息,如uuid,型號,系統(tǒng)版本等,大部分都可以通過插件Device獲得。但Device并沒有提供是否支持NFC,好在ionic2的NFC插件提供了檢測設(shè)備是否支持NFC的API,如下:

this.nfc.enabled().then(data=>{
  if(data){
    console.log('設(shè)備支持NFC');
  }
}).catch(()=>{
  console.log('設(shè)備不支持NFC');
});

這里引入NFC插件筆者遇到一個(gè)錯(cuò)誤,原因是@ionic-angular/core的版本不匹配,ionic2的base app目前默認(rèn)是3.4.2,修改package.json,根據(jù)下載NFC插件時(shí)的提示修改指定版本號,筆者是改為3.6.0,然后重新cnpm install一下。

Hotspot

ionic plugin add --save cordova-plugin-hotspot
npm install --save @ionic-native/hotspot
需求同上,想獲取設(shè)備的ip和mac地址,Device插件沒有提供API,Hotspot雖然提供了相關(guān)API,但目前只支持Android。

this.hotspot.isAvailable().then(data=>{
      if(data){
        console.log('hotspot'+ data);
        this.hotspot.getConnectionInfo().then(info=>{ console.log(info.IPAddress)});
        this.hotspot.getNetConfig().then((info: HotspotNetworkConfig)=>{
          this.commonService.showToast('info'+ info.deviceIPAddress+ info.gatewayMacAddress);
          if(info){
            let ipAddress = info.deviceIPAddress;
            let macAddress = info.gatewayMacAddress;
            this.deviceInfo.push({name: 'IP', value: ipAddress});
            this.deviceInfo.push({name: 'MAC', value: macAddress});
          }
        }).catch(errorHandler=>{
          console.error('hotspot'+ errorHandler);
        });
        this.hotspot.isRooted().then(data=>{
          if(data){
            this.deviceInfo.push({name: 'ROOT', value: '是'});
          }else {
            this.deviceInfo.push({name: 'ROOT', value: '否'});
          }
        })
      }
    });

筆者在實(shí)際使用過程中遇到權(quán)限引起的錯(cuò)誤,調(diào)用hotspot除isAvailable外的插件均用到了Android的WRITE_SETTINGS權(quán)限,而這個(gè)權(quán)限在Android6.0后被歸為系統(tǒng)權(quán)限,需要系統(tǒng)簽名后的apk才能正常運(yùn)行。
雖然其git上有幾個(gè)提這個(gè)權(quán)限問題的Issue,但似乎并沒有解決。

StreamingMedia

需求:實(shí)現(xiàn)Android、IOS應(yīng)用內(nèi)根據(jù)url播放在線視頻
插件:Cordova Streaming Media plugin
ionic plugin add cordova-plugin-streaming-media
npm install --save @ionic-native/streaming-media
播放視頻的API:

let options = {
      successCallback: function () {
        console.log('playing video' + videoUrl);
      },
      errorCallback: function (err) {
        console.error('加載視頻失敗' + err);
      },
    };
    this.streamingMedia.playVideo(videoUrl, options);

使用過程中遇到視頻URL中帶有中文時(shí),MediaPlayer返回錯(cuò)誤:
MediaPlayer Error: Unknown (1) -1005
git項(xiàng)目里并沒有相關(guān)issue,可能url里帶有中文這點(diǎn)確實(shí)有點(diǎn)蠢,但畢竟也是個(gè)問題,我還是給作者提了個(gè)issue,Media Player Error: Unknown(1) - 1005
解決方法是:在設(shè)置播放url前,對地址進(jìn)行轉(zhuǎn)碼,
Android, SimpleVideoStream.java
using Uri.encode() to format the video url;

private void play() {
        mProgressBar.setVisibility(View.VISIBLE);
        mVideoUrl = Uri.encode(mVideoUrl, ":/-![].,%?&=");
        Uri videoUri = Uri.parse(mVideoUrl);
        Log.d(TAG, "video uri: "+ videoUri);
        try {
            mVideoView.setOnCompletionListener(this);
            mVideoView.setOnPreparedListener(this);
            mVideoView.setOnErrorListener(this);
            mVideoView.setVideoURI(videoUri);
            mMediaController = new MediaController(this);
            mMediaController.setAnchorView(mVideoView);
            mMediaController.setMediaPlayer(mVideoView);
            mVideoView.setMediaController(mMediaController);
        } catch (Throwable t) {
            Log.d(TAG, t.toString());
        }
    }

IOS, StreamingMedia.m

-(void)startPlayer:(NSString*)uri {
  NSString *utf8Uri = [uri stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:utf8Uri];

    moviePlayer =  [[MPMoviePlayerController alloc] initWithContentURL:url];
    
         .....
}

非Ionic2官方插件

既然ionic2官方提供的插件里沒有能夠滿足需求的,筆者于是前往cordova search搜索其他可用插件。筆者通過關(guān)鍵字wifi找到多個(gè)相關(guān)插件,

wifiInfo

以wifiinfo這個(gè)插件為例:
項(xiàng)目下命令行輸入ionic plugin add cordova-wifiinfo-plugin下載插件;
因沒有對應(yīng)的ionic-native包,不能通過import在ts中引用,解決方法與引用第三方j(luò)s庫類似,在declarations.d.ts中聲明declare var WifiInfo,然后在ts中直接通過Wifiinfo變量調(diào)接口。
WifiInfo.getWifiInfo((result)=>{})
該接口能夠返回當(dāng)前設(shè)備連接wifi的MacAddress, BSSID, IpAddress等,但筆者發(fā)現(xiàn)其MacAddress并不正確

發(fā)現(xiàn)多個(gè)設(shè)備返回的Mac地址均為02:00:00:00:00:00,百度后找到原因,Google在6.0后為了更好的數(shù)據(jù)保護(hù),原有的mac地址api均返回以上字符串,因此我們需要修改該插件的Wifi.class代碼,完整代碼如下:

package org.apache.cordova.android;

import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.Collections;
import java.util.List;


import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;

import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.Settings;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import org.json.JSONObject;



public class Wifi extends CordovaPlugin {

    public Wifi() {

    }

    public boolean execute(String action, JSONArray args,
            CallbackContext callbackContext) {
        JSONObject r = new JSONObject();
        try {

          if (action.equals("getWifiInfo")) {
                JSONObject wifi = getWifiInfo();
                callbackContext.success(wifi);
                return true;
            }
        else{
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // Only alert and confirm are async.
        callbackContext.success(r);
        return true;
    }


    private String formatIP(int ip) {
        return String.format("%d.%d.%d.%d", (ip & 0xff), (ip >> 8 & 0xff),
                (ip >> 16 & 0xff), (ip >> 24 & 0xff));
    }


    private JSONObject getWifiInfo() {
        WifiManager wifiManager = (WifiManager) cordova.getActivity()
                .getSystemService(Context.WIFI_SERVICE);
        JSONObject wifiData = new JSONObject();
        WifiInfo wifi = wifiManager.getConnectionInfo();
        try {
            wifiData.put("MacAddress", getMacAddr());
            wifiData.put("BSSID", wifi.getBSSID());
            wifiData.put("HiddenSSID", wifi.getHiddenSSID());
            wifiData.put("IpAddress", formatIP(wifi.getIpAddress()));
            wifiData.put("LinkSpeed", wifi.getLinkSpeed());
            wifiData.put("NetworkId", wifi.getNetworkId());
            wifiData.put("Rssi", wifi.getRssi());
            wifiData.put("SSID", wifi.getSSID());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wifiData;
    }

    public static String getMacAddr() {
        try {
            List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface nif : all) {
                if (!nif.getName().equalsIgnoreCase("wlan0")) continue;

                byte[] macBytes = nif.getHardwareAddress();
                if (macBytes == null) {
                    return "";
                }

                StringBuilder res1 = new StringBuilder();
                for (byte b : macBytes) {
                    res1.append(String.format("%02X:",b));
                }

                if (res1.length() > 0) {
                    res1.deleteCharAt(res1.length() - 1);
                }
                return res1.toString();
            }
        } catch (Exception ex) {
        }
        return "02:00:00:00:00:00";
    }
}

這里也順帶提下IOS的獲取Mac地址,雖然cordova上有相關(guān)插件,實(shí)際上是拿不到的,因?yàn)樘O果官方限制IOS7以后不再允許獲取Mac地址,均返回02:00:00:00:00:00;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容