美文网首页
Ionic2 Native插件总结

Ionic2 Native插件总结

作者: charles0427 | 来源:发表于2017-05-04 14:16 被阅读1265次

    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

    相关文章

      网友评论

          本文标题:Ionic2 Native插件总结

          本文链接:https://www.haomeiwen.com/subject/qrortxtx.html