Ionic2 Native插件总结

ionic2官方提供了很多原生插件,是建立在cordova plugin基础上,结合ionic-native的ts库进行引用。本文除总结官方插件的使用,也介绍项目开发中遇到的非官方的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文件中调用

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. 在安卓项目的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: 开发中遇到app重启后,广播会多次接收,分析是注册的接收器没有在应用退出时注销。
解决方法:重写onDestroy方法,将注册的receiver回收

@override
public void  onDestroy(){
  super.onDestroy();
  mBroadcastManger.unregisterReceiver(receiver);
}
  • 原生插件not installed问题
    编译过程中,有时会提示某个native plugin not installed,然而该插件其实是已经下载安装好的。原因是ts中虽然调用了插件,但platform还未准备好。解决方法:
    首页 import {Platform} from 'ionic-angular',将首页中初始化需要用到native plugin的代码放在platfor.ready().then(()=>{})

HTTP

ionic plugin add cordova-plugin-http
npm install --save @ionic-native/http
实现HTTP请求的插件,目前只用到了简单的post请求,但使用中遇到了奇葩的情景,同样的代码在安卓上能获得正确应答,IOS上却收到500。debug发现后台接收ios数据里有个字段的值是null,android却是有值的。查看git上issue也毫无发现,后发现该字段是个数组对象,在post前将对象转为json string后,问题解决了...

后期项目有个接口的服务器改成了HTTPS,关键还没有申请证书,如果只是修改请求url,会返回ssl handshake error,这里需要应用初始化的时候对http进行一下设置:

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
通过这个插件实现应用里数据的持久化存储,但使用时遇到个问题。通过getItem('key')取值时,若未事先对该key有setItem('key': {}),getItem方法将触发error的回调,所以需要在error回调里返回你需要的默认值;
除了使用这个原生插件,其实Ionic2默认安装的storage模块也可以实现,引用步骤如下:
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
关于生成二维码的方法:encode(type, text)真是好多坑。。
在Android上,该方法直接打开一个Activity显示text生成的二维码,虽然文档里写该方法返回promise,实际使用时回调函数不起作用。
在IOS上,该方法是在文件系统里生成一个图片.jpg,通过success的回调success.file拿到图片的路径。

File Opener

ionic plugin add cordova-plugin-file-opener2
cnpm install --save @ionic-native/file-opener
因Barcode Scanner没有提供直接将IOS生成的二维码生成的图片显示出来,可以转换下思路,通过文件打开插件去读取图片。

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));

然而,这个插件也是个坑,将barcode返回的路径放进去后,调用open会报错,这里需要修改插件的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
原本需求是读取设备的一些基础信息,如uuid,型号,系统版本等,大部分都可以通过插件Device获得。但Device并没有提供是否支持NFC,好在ionic2的NFC插件提供了检测设备是否支持NFC的API,如下:

this.nfc.enabled().then(data=>{
  if(data){
    console.log('设备支持NFC');
  }
}).catch(()=>{
  console.log('设备不支持NFC');
});

这里引入NFC插件笔者遇到一个错误,原因是@ionic-angular/core的版本不匹配,ionic2的base app目前默认是3.4.2,修改package.json,根据下载NFC插件时的提示修改指定版本号,笔者是改为3.6.0,然后重新cnpm install一下。

Hotspot

ionic plugin add --save cordova-plugin-hotspot
npm install --save @ionic-native/hotspot
需求同上,想获取设备的ip和mac地址,Device插件没有提供API,Hotspot虽然提供了相关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: '否'});
          }
        })
      }
    });

笔者在实际使用过程中遇到权限引起的错误,调用hotspot除isAvailable外的插件均用到了Android的WRITE_SETTINGS权限,而这个权限在Android6.0后被归为系统权限,需要系统签名后的apk才能正常运行。
虽然其git上有几个提这个权限问题的Issue,但似乎并没有解决。

StreamingMedia

需求:实现Android、IOS应用内根据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中带有中文时,MediaPlayer返回错误:
MediaPlayer Error: Unknown (1) -1005
git项目里并没有相关issue,可能url里带有中文这点确实有点蠢,但毕竟也是个问题,我还是给作者提了个issue,Media Player Error: Unknown(1) - 1005
解决方法是:在设置播放url前,对地址进行转码,
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());
        }
    }

IOSStreamingMedia.m

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

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

非Ionic2官方插件

既然ionic2官方提供的插件里没有能够满足需求的,笔者于是前往cordova search搜索其他可用插件。笔者通过关键字wifi找到多个相关插件,

wifiInfo

以wifiinfo这个插件为例:
项目下命令行输入ionic plugin add cordova-wifiinfo-plugin下载插件;
因没有对应的ionic-native包,不能通过import在ts中引用,解决方法与引用第三方js库类似,在declarations.d.ts中声明declare var WifiInfo,然后在ts中直接通过Wifiinfo变量调接口。
WifiInfo.getWifiInfo((result)=>{})
该接口能够返回当前设备连接wifi的MacAddress, BSSID, IpAddress等,但笔者发现其MacAddress并不正确

发现多个设备返回的Mac地址均为02:00:00:00:00:00,百度后找到原因,Google在6.0后为了更好的数据保护,原有的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上有相关插件,实际上是拿不到的,因为苹果官方限制IOS7以后不再允许获取Mac地址,均返回02:00:00:00:00:00

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容